diff options
Diffstat (limited to 'engines')
246 files changed, 4267 insertions, 4074 deletions
diff --git a/engines/access/access.cpp b/engines/access/access.cpp index ea2144459d..1855280a24 100644 --- a/engines/access/access.cpp +++ b/engines/access/access.cpp @@ -153,7 +153,7 @@ AccessEngine::~AccessEngine() { } void AccessEngine::setVGA() { - initGraphics(320, 200, false); + initGraphics(320, 200); } void AccessEngine::initialize() { diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp index 61890af37a..9370363a3f 100644 --- a/engines/adl/adl.cpp +++ b/engines/adl/adl.cpp @@ -681,7 +681,7 @@ void AdlEngine::gameLoop() { } Common::Error AdlEngine::run() { - initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, true); + initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2); _console = new Console(this); _display = new Display(); diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index 64f462a936..9d695058dd 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -325,17 +325,30 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) return Common::kNoError; } -void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps) const { - // TODO: This message should be cleaned up / made more specific. - // For example, we should specify at least which engine triggered this. - // - // Might also be helpful to display the full path (for when this is used - // from the mass detector). +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 unknown.\n" - "Please, report the following data to the ScummVM team along with name\n" - "of the game you tried to add and its version, language, etc.:"), path.getPath().c_str()) + "\n"; - report += "\n"; + _("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); @@ -444,6 +457,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons } ADGameDescList matched; + ADGameIdList matchedGameIds; int maxFilesMatched = 0; bool gotAnyMatchesWithAllFiles = false; @@ -466,6 +480,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons bool allFilesPresent = true; int curFilesMatched = 0; + bool hashOrSizeMismatch = false; // Try to match all files for this game for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { @@ -477,16 +492,21 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons break; } + if (hashOrSizeMismatch) + continue; + if (fileDesc->md5 != NULL && fileDesc->md5 != filesProps[tstr].md5) { debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str()); fileMissing = true; - break; + hashOrSizeMismatch = true; + continue; } if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) { debug(3, "Size Mismatch. Skipping"); fileMissing = true; - break; + hashOrSizeMismatch = true; + continue; } debug(3, "Matched file: %s", tstr.c_str()); @@ -502,8 +522,11 @@ 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) + if (allFilesPresent) { gotAnyMatchesWithAllFiles = true; + if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), g->gameId) != 0) + matchedGameIds.push_back(g->gameId); + } if (!fileMissing) { debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra, @@ -530,7 +553,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // We didn't find a match if (matched.empty()) { if (!filesProps.empty() && gotAnyMatchesWithAllFiles) { - reportUnknown(parent, filesProps); + reportUnknown(parent, filesProps, matchedGameIds); } // Filename based fallback diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index e218c41b52..61693d10fa 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -117,6 +117,11 @@ struct ADGameDescription { typedef Common::Array<const ADGameDescription *> ADGameDescList; /** + * A list of raw game ID strings. + */ +typedef Common::Array<const char *> ADGameIdList; + +/** * End marker for a table of ADGameDescription structs. Use this to * terminate a list to be passed to the AdvancedDetector API. */ @@ -328,7 +333,7 @@ protected: * 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; + void reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps, const ADGameIdList &matchedGameIds = ADGameIdList()) const; // TODO void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const; diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index fa1f11cce4..119ccfea1d 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -210,7 +210,7 @@ int GfxMgr::initVideo() { _displayPixels = _displayScreenWidth * _displayScreenHeight; _displayScreen = (byte *)calloc(_displayPixels, 1); - initGraphics(_displayScreenWidth, _displayScreenHeight, _displayScreenWidth > 320); + initGraphics(_displayScreenWidth, _displayScreenHeight); setPalette(true); // set gfx-mode palette diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp index 48b170da75..108d5a94d7 100644 --- a/engines/agos/agos.cpp +++ b/engines/agos/agos.cpp @@ -577,7 +577,7 @@ Common::Error AGOSEngine::init() { _screenHeight = 200; } - initGraphics(_screenWidth, _screenHeight, getGameType() == GType_FF || getGameType() == GType_PP); + initGraphics(_screenWidth, _screenHeight); _midi = new MidiPlayer(); diff --git a/engines/avalanche/graphics.cpp b/engines/avalanche/graphics.cpp index 03c9e9e3cb..6ce6ef2603 100644 --- a/engines/avalanche/graphics.cpp +++ b/engines/avalanche/graphics.cpp @@ -68,7 +68,7 @@ GraphicManager::~GraphicManager() { } void GraphicManager::init() { - initGraphics(kScreenWidth, kScreenHeight * 2, true); // Doubling the height. + initGraphics(kScreenWidth, kScreenHeight * 2); // Doubling the height. for (int i = 0; i < 64; ++i) { _egaPalette[i][0] = (i >> 2 & 1) * 0xaa + (i >> 5 & 1) * 0x55; @@ -799,7 +799,7 @@ void GraphicManager::menuRefreshScreen() { } void GraphicManager::menuInitialize() { - initGraphics(kScreenWidth, kMenuScreenHeight, true); + initGraphics(kScreenWidth, kMenuScreenHeight); _menu.create(kScreenWidth, kMenuScreenHeight, Graphics::PixelFormat::createFormatCLUT8()); } @@ -808,7 +808,7 @@ void GraphicManager::menuFree() { } void GraphicManager::menuRestoreScreen() { - initGraphics(kScreenWidth, 2 * kScreenHeight, true); + initGraphics(kScreenWidth, 2 * kScreenHeight); } void GraphicManager::menuLoadPictures() { diff --git a/engines/bbvs/bbvs.cpp b/engines/bbvs/bbvs.cpp index e4dcb1a84b..f5daa602ac 100644 --- a/engines/bbvs/bbvs.cpp +++ b/engines/bbvs/bbvs.cpp @@ -170,7 +170,7 @@ Common::Error BbvsEngine::run() { _isSaveAllowed = false; _hasSnapshot = false; - initGraphics(320, 240, false); + initGraphics(320, 240); _screen = new Screen(_system); _gameModule = new GameModule(); @@ -496,8 +496,8 @@ void BbvsEngine::buildDrawList(DrawList &drawList) { Animation *anim = sceneObject->anim; if (anim) { drawList.add(anim->frameSpriteIndices[sceneObject->frameIndex], - (sceneObject->x >> 16) - _cameraPos.x, (sceneObject->y >> 16) - _cameraPos.y, - sceneObject->y >> 16); + (sceneObject->x / 65536) - _cameraPos.x, (sceneObject->y / 65536) - _cameraPos.y, + sceneObject->y / 65536); } } @@ -774,8 +774,8 @@ void BbvsEngine::updateScene(bool clicked) { SceneObject *sceneObject = &_sceneObjects[i]; if (sceneObject->anim) { Common::Rect frameRect = sceneObject->anim->frameRects1[sceneObject->frameIndex]; - const int objY = sceneObject->y >> 16; - frameRect.translate(sceneObject->x >> 16, objY); + const int objY = sceneObject->y / 65536; + frameRect.translate(sceneObject->x / 65536, objY); if (lastPriority <= objY && frameRect.width() > 0 && frameRect.contains(_mousePos)) { lastPriority = objY; _activeItemIndex = i; @@ -898,7 +898,7 @@ void BbvsEngine::updateScene(bool clicked) { if (_beavisObject->anim) { Common::Rect frameRect = _beavisObject->anim->frameRects2[_beavisObject->frameIndex]; - frameRect.translate(_beavisObject->x >> 16, (_beavisObject->y >> 16) + 1); + frameRect.translate(_beavisObject->x / 65536, (_beavisObject->y / 65536) + 1); if (!frameRect.isEmpty() && frameRect.contains(_walkMousePos)) _walkMousePos.y = frameRect.bottom; } @@ -938,7 +938,7 @@ bool BbvsEngine::performActionCommand(ActionCommand *actionCommand) { { SceneObject *sceneObject = &_sceneObjects[actionCommand->sceneObjectIndex]; debug(5, "[%s] walks from (%d, %d) to (%d, %d)", sceneObject->sceneObjectDef->name, - sceneObject->x >> 16, sceneObject->y >> 16, actionCommand->walkDest.x, actionCommand->walkDest.y); + sceneObject->x / 65536, sceneObject->y / 65536, actionCommand->walkDest.x, actionCommand->walkDest.y); walkObject(sceneObject, actionCommand->walkDest, actionCommand->param); } return true; @@ -946,8 +946,8 @@ bool BbvsEngine::performActionCommand(ActionCommand *actionCommand) { case kActionCmdMoveObject: { SceneObject *sceneObject = &_sceneObjects[actionCommand->sceneObjectIndex]; - sceneObject->x = actionCommand->walkDest.x << 16; - sceneObject->y = actionCommand->walkDest.y << 16; + sceneObject->x = actionCommand->walkDest.x * 65536; + sceneObject->y = actionCommand->walkDest.y * 65536; sceneObject->xIncr = 0; sceneObject->yIncr = 0; sceneObject->walkCount = 0; @@ -1074,7 +1074,7 @@ bool BbvsEngine::processCurrAction() { if (sceneObject->walkDestPt.x != -1) { debug(5, "waiting for walk to finish"); actionsFinished = false; - } else if ((int16)(sceneObject->x >> 16) != soAction->walkDest.x || (int16)(sceneObject->y >> 16) != soAction->walkDest.y) { + } else if ((int16)(sceneObject->x / 65536) != soAction->walkDest.x || (int16)(sceneObject->y / 65536) != soAction->walkDest.y) { debug(5, "starting to walk"); sceneObject->walkDestPt = soAction->walkDest; actionsFinished = false; @@ -1214,8 +1214,8 @@ void BbvsEngine::updateCommon() { } if (!_currAction && _buttheadObject) { - int16 buttheadX = _buttheadObject->x >> 16; - int16 buttheadY = _buttheadObject->y >> 16; + int16 buttheadX = _buttheadObject->x / 65536; + int16 buttheadY = _buttheadObject->y / 65536; CameraInit *cameraInit = _gameModule->getCameraInit(_currCameraNum); for (int i = 0; i < 8; ++i) { if (cameraInit->rects[i].contains(buttheadX, buttheadY)) { @@ -1252,8 +1252,8 @@ void BbvsEngine::updateCommon() { // Check if Butthead is inside a scene exit if (_newSceneNum == 0 && !_currAction && _buttheadObject) { - int16 buttheadX = _buttheadObject->x >> 16; - int16 buttheadY = _buttheadObject->y >> 16; + int16 buttheadX = _buttheadObject->x / 65536; + int16 buttheadY = _buttheadObject->y / 65536; for (int i = 0; i < _gameModule->getSceneExitsCount(); ++i) { SceneExit *sceneExit = _gameModule->getSceneExit(i); if (sceneExit->rect.contains(buttheadX, buttheadY)) { diff --git a/engines/bbvs/logic.cpp b/engines/bbvs/logic.cpp index 2f9925f7a0..47f8dd938b 100644 --- a/engines/bbvs/logic.cpp +++ b/engines/bbvs/logic.cpp @@ -82,7 +82,7 @@ bool BbvsEngine::evalCondition(Conditions &conditions) { break; case kCondIsButtheadAtBgObject: result = _buttheadObject && - _gameModule->getBgObject(condition.value2)->rect.contains(_buttheadObject->x >> 16, _buttheadObject->y >> 16); + _gameModule->getBgObject(condition.value2)->rect.contains(_buttheadObject->x / 65536, _buttheadObject->y / 65536); break; case kCondIsNotSceneVisited: result = _sceneVisited[_currSceneNum] == 0; @@ -209,7 +209,7 @@ int BbvsEngine::evalDialogCondition(Conditions &conditions) { break; case kCondIsButtheadAtBgObject: success = _buttheadObject && - _gameModule->getBgObject(condition.value2)->rect.contains(_buttheadObject->x >> 16, _buttheadObject->y >> 16); + _gameModule->getBgObject(condition.value2)->rect.contains(_buttheadObject->x / 65536, _buttheadObject->y / 65536); break; case kCondIsNotSceneVisited: success = _sceneVisited[_currSceneNum] == 0; diff --git a/engines/bbvs/minigames/bbant.cpp b/engines/bbvs/minigames/bbant.cpp index 72c2d62b13..5310d53357 100644 --- a/engines/bbvs/minigames/bbant.cpp +++ b/engines/bbvs/minigames/bbant.cpp @@ -85,7 +85,7 @@ void MinigameBbAnt::buildDrawList1(DrawList &drawList) { Obj *obj = &_objects[i]; if (obj->kind) { drawList.add(obj->anim->frameIndices[obj->frameIndex], - _stompX + (obj->x >> 16), _stompY + (obj->y >> 16), + _stompX + (obj->x / 65536), _stompY + (obj->y / 65536), obj->priority); } } @@ -256,8 +256,8 @@ void MinigameBbAnt::initObjects1() { for (int i = 3; i < 12; ++i) { const ObjInit *objInit = getObjInit(i - 3); _objects[i].kind = 6; - _objects[i].x = objInit->x << 16; - _objects[i].y = objInit->y << 16; + _objects[i].x = objInit->x * 65536; + _objects[i].y = objInit->y * 65536; _objects[i].xIncr = 0; _objects[i].yIncr = 0; _objects[i].anim = objInit->anim1; @@ -429,7 +429,7 @@ bool MinigameBbAnt::updateStatus1(int mouseX, int mouseY, uint mouseButtons) { if ((mouseButtons & kLeftButtonClicked) && _objects[2].status == 0 && isMagGlassAtBeavisLeg(2)) { if (_vm->getRandom(10) == 1 && !isAnySoundPlaying(kSoundTbl4, 10)) playSound(16); - insertSmokeObj(_objects[0].x << 16, _objects[0].y << 16); + insertSmokeObj(_objects[0].x * 65536, _objects[0].y * 65536); } if (_skullBugCtr > 0) { @@ -515,8 +515,8 @@ bool MinigameBbAnt::updateStatus2(int mouseX, int mouseY, uint mouseButtons) { Obj *obj = &_objects[i]; if (obj->status == 13) { const ObjInit *objInit = getObjInit(i - 3); - obj->x = objInit->x << 16; - obj->y = objInit->y << 16; + obj->x = objInit->x * 65536; + obj->y = objInit->y * 65536; obj->anim = objInit->anim3; obj->frameIndex = 0; obj->ticks = _objects[0].anim->frameTicks[0]; @@ -588,7 +588,7 @@ void MinigameBbAnt::insertBugSmokeObj(int x, int y, int bugObjIndex) { obj->priority = 950; if (bugObj->status >= 4 && (bugObj->status <= 6 || bugObj->status == 8)) { obj->xIncr = 0; - obj->yIncr = -0x10000; + obj->yIncr = (-1 * 65536); } else { obj->xIncr = bugObj->xIncr / 8; obj->yIncr = bugObj->yIncr / 8; @@ -648,11 +648,11 @@ void MinigameBbAnt::insertBugObj(int kind, int animIndexIncr, int always0, int x obj->field30 = field30; obj->animIndexIncr = animIndexIncr; obj->kind = kind; - obj->x = x << 16; - obj->y = y << 16; + obj->x = x * 65536; + obj->y = y * 65536; obj->priority = 610; - obj->xIncr = kPosIncrTbl1[0].x << 16; - obj->yIncr = kPosIncrTbl1[0].y << 16; + obj->xIncr = kPosIncrTbl1[0].x * 65536; + obj->yIncr = kPosIncrTbl1[0].y * 65536; obj->anim = objKindAnimTable[0]; obj->frameIndex = 0; obj->ticks = obj->anim->frameTicks[0]; @@ -692,8 +692,8 @@ void MinigameBbAnt::updateBugObjAnim(int objIndex) { break; } const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind); - obj->xIncr = kPosIncrTbl1[obj->animIndexIncr].x << 16; - obj->yIncr = kPosIncrTbl1[obj->animIndexIncr].y << 16; + obj->xIncr = kPosIncrTbl1[obj->animIndexIncr].x * 65536; + obj->yIncr = kPosIncrTbl1[obj->animIndexIncr].y * 65536; obj->anim = objKindAnimTable[obj->animIndexIncr]; obj->frameIndex = 0; obj->ticks = obj->anim->frameTicks[0]; @@ -711,8 +711,8 @@ void MinigameBbAnt::updateObjAnim2(int objIndex) { if (obj->animIndexIncr >= 8) obj->animIndexIncr %= 8; const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind); - obj->xIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].x << 16; - obj->yIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].y << 16; + obj->xIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].x * 65536; + obj->yIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].y * 65536; obj->anim = objKindAnimTable[obj->animIndex + obj->animIndexIncr]; obj->frameIndex = 0; obj->ticks = obj->anim->frameTicks[0]; @@ -730,8 +730,8 @@ bool MinigameBbAnt::isBugOutOfScreen(int objIndex) { Obj *obj = &_objects[objIndex]; return - obj->x < -0xa0000 || obj->x > (330 << 16) || - obj->y < -0xa0000 || obj->y > (250 << 16); + obj->x < (-10 * 65536) || obj->x > (330 * 65536) || + obj->y < (-10 * 65536) || obj->y > (250 * 65536); } void MinigameBbAnt::updateObjAnim3(int objIndex) { @@ -743,8 +743,8 @@ void MinigameBbAnt::updateObjAnim3(int objIndex) { if (obj->animIndexIncr > 7) obj->animIndexIncr = 0; const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind); - obj->xIncr = kPosIncrTbl1[obj->animIndexIncr].x << 16; - obj->yIncr = kPosIncrTbl1[obj->animIndexIncr].y << 16; + obj->xIncr = kPosIncrTbl1[obj->animIndexIncr].x * 65536; + obj->yIncr = kPosIncrTbl1[obj->animIndexIncr].y * 65536; obj->anim = objKindAnimTable[obj->animIndexIncr]; } @@ -824,8 +824,8 @@ void MinigameBbAnt::updateBugObj1(int objIndex) { _objects[obj->otherObjIndex].y = obj->y; if (isBugOutOfScreen(objIndex)) { _objects[obj->otherObjIndex].status = 13; - _objects[obj->otherObjIndex].x = (500 << 16); - _objects[obj->otherObjIndex].y = (500 << 16); + _objects[obj->otherObjIndex].x = (500 * 65536); + _objects[obj->otherObjIndex].y = (500 * 65536); removeBugObj(objIndex); --_counter1; } @@ -847,8 +847,8 @@ void MinigameBbAnt::updateBugObj1(int objIndex) { if (flag1) { const ObjAnimation * const *objKindAnimTable = getObjKindAnimTable(obj->kind); obj->status = 7; - obj->xIncr = kPosIncrTbl2[obj->animIndexIncr].x << 16; - obj->yIncr = kPosIncrTbl2[obj->animIndexIncr].y << 16; + obj->xIncr = kPosIncrTbl2[obj->animIndexIncr].x * 65536; + obj->yIncr = kPosIncrTbl2[obj->animIndexIncr].y * 65536; obj->anim = objKindAnimTable[obj->animIndexIncr + 8]; obj->frameIndex = 0; obj->ticks = obj->anim->frameTicks[0]; @@ -873,8 +873,8 @@ void MinigameBbAnt::updateBugObj1(int objIndex) { obj->anim = obj->anim2; obj->frameIndex = obj->frameIndex2; obj->ticks = obj->ticks2; - obj->xIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].x << 16; - obj->yIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].y << 16; + obj->xIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].x * 65536; + obj->yIncr = kPosIncrTbl1[obj->animIndex + obj->animIndexIncr].y * 65536; obj->priority = 610; } break; @@ -931,7 +931,7 @@ void MinigameBbAnt::updateFootObj(int objIndex) { case 1: obj->xIncr = -0x8000; - obj->yIncr = -0x40000; + obj->yIncr = (-4 * 65536); obj->status = 2; _stompCounter1 += 5; _stompCounter2 = 100; @@ -941,9 +941,9 @@ void MinigameBbAnt::updateFootObj(int objIndex) { obj->x += obj->xIncr; obj->y += obj->yIncr; obj->yIncr += 0x2000; - if (obj->y < (20 << 16)) { + if (obj->y < (20 * 65536)) { obj->xIncr = 0x8000; - obj->yIncr = (7 << 16); + obj->yIncr = (7 * 65536); obj->status = 3; } break; @@ -953,8 +953,8 @@ void MinigameBbAnt::updateFootObj(int objIndex) { obj->y += obj->yIncr; obj->yIncr += 0x2000; if (obj->y >= 0x4B0000) { - obj->x = (40 << 16); - obj->y = (75 << 16); + obj->x = (40 * 65536); + obj->y = (75 * 65536); obj->status = 4; _stompDelay1 = 6; _stompY = 0; @@ -1023,15 +1023,15 @@ bool MinigameBbAnt::isBugAtCandy(int objIndex, int &candyObjIndex) { if (obj->kind >= 1 && obj->kind <= 4) { const BBRect &frameRect1 = obj->anim->frameRects[obj->frameIndex]; - const int obj1X1 = frameRect1.x + (obj->x >> 16); - const int obj1Y1 = frameRect1.y + (obj->y >> 16); + const int obj1X1 = frameRect1.x + (obj->x / 65536); + const int obj1Y1 = frameRect1.y + (obj->y / 65536); const int obj1X2 = obj1X1 + frameRect1.width; const int obj1Y2 = obj1Y1 + frameRect1.height; for (int i = 3; i < 12 && !result; ++i) { Obj *obj2 = &_objects[i]; const BBRect &frameRect2 = obj->anim->frameRects[obj2->frameIndex]; // sic - const int obj2X1 = (obj2->x >> 16) + frameRect2.x; - const int obj2Y1 = (obj2->y >> 16) + frameRect2.y; + const int obj2X1 = (obj2->x / 65536) + frameRect2.x; + const int obj2Y1 = (obj2->y / 65536) + frameRect2.y; const int obj2X2 = obj2X1 + frameRect2.width; const int obj2Y2 = obj2Y1 + frameRect2.height; if (obj2->status == 9 && obj1X1 <= obj2X2 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1) { @@ -1055,8 +1055,8 @@ bool MinigameBbAnt::isMagGlassAtBug(int objIndex) { const int obj1X2 = obj1X1 + frameRect1.width; const int obj1Y2 = obj1Y1 + frameRect1.height; const BBRect &frameRect2 = obj->anim->frameRects[obj->frameIndex]; - const int obj2X1 = (obj->x >> 16) + frameRect2.x; - const int obj2Y1 = (obj->y >> 16) + frameRect2.y; + const int obj2X1 = (obj->x / 65536) + frameRect2.x; + const int obj2Y1 = (obj->y / 65536) + frameRect2.y; const int obj2X2 = obj2X1 + frameRect2.width; const int obj2Y2 = obj2Y1 + frameRect2.height; if (obj2X2 >= obj1X1 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1) @@ -1076,8 +1076,8 @@ bool MinigameBbAnt::isMagGlassAtBeavisLeg(int objIndex) { const int obj1X2 = obj1X1 + frameRect1.width; const int obj1Y2 = obj1Y1 + frameRect1.height; const BBRect &frameRect2 = obj->anim->frameRects[obj->frameIndex]; - const int obj2X1 = (obj->x >> 16) + frameRect2.x; - const int obj2Y1 = (obj->y >> 16) + frameRect2.y; + const int obj2X1 = (obj->x / 65536) + frameRect2.x; + const int obj2Y1 = (obj->y / 65536) + frameRect2.y; const int obj2X2 = obj2X1 + frameRect2.width; const int obj2Y2 = obj2Y1 + frameRect2.height; if (obj2X2 >= obj1X1 && obj1X2 >= obj2X1 && obj1Y1 <= obj2Y2 && obj1Y2 >= obj2Y1) @@ -1089,8 +1089,8 @@ bool MinigameBbAnt::testObj5(int objIndex) { Obj *obj = &_objects[objIndex]; bool result = false; if (obj->kind >= 1 && obj->kind <= 5) { - const int x = obj->x >> 16; - const int y = obj->y >> 16; + const int x = obj->x / 65536; + const int y = obj->y / 65536; if (x < 0 || x >= 110 || y < 0 || y >= 110) { obj->flag = 0; } else if (!obj->flag) { diff --git a/engines/bbvs/scene.cpp b/engines/bbvs/scene.cpp index 5d22ba0cc5..74bbe0618d 100644 --- a/engines/bbvs/scene.cpp +++ b/engines/bbvs/scene.cpp @@ -118,8 +118,8 @@ void BbvsEngine::initScene(bool sounds) { sceneObject->animIndex = soInit->animIndex; sceneObject->frameIndex = sceneObject->anim->frameCount - 1; sceneObject->frameTicks = 1; - sceneObject->x = soInit->x << 16; - sceneObject->y = soInit->y << 16; + sceneObject->x = soInit->x * 65536; + sceneObject->y = soInit->y * 65536; } } @@ -142,7 +142,7 @@ void BbvsEngine::initScene(bool sounds) { int minDistance = 0xFFFFFF; for (int cameraNum = 0; cameraNum < 4; ++cameraNum) { CameraInit *cameraInit = _gameModule->getCameraInit(cameraNum); - int curDistance = ABS(cameraInit->cameraPos.x - (int)(_buttheadObject->x >> 16) + 160); + int curDistance = ABS(cameraInit->cameraPos.x - (int)(_buttheadObject->x / 65536) + 160); if (curDistance < minDistance) { minDistance = curDistance; _currCameraNum = cameraNum; diff --git a/engines/bbvs/videoplayer.cpp b/engines/bbvs/videoplayer.cpp index 2ab5c1556b..f48c98005e 100644 --- a/engines/bbvs/videoplayer.cpp +++ b/engines/bbvs/videoplayer.cpp @@ -37,7 +37,7 @@ void BbvsEngine::playVideo(int videoNum) { videoFilename = Common::String::format("vid/video%03d.avi", videoNum - 1); // Set the correct video mode - initGraphics(320, 240, false, 0); + initGraphics(320, 240, nullptr); if (_system->getScreenFormat().bytesPerPixel == 1) { warning("Couldn't switch to a RGB color video mode to play a video."); return; @@ -84,7 +84,7 @@ void BbvsEngine::playVideo(int videoNum) { delete videoDecoder; - initGraphics(320, 240, false); + initGraphics(320, 240); } diff --git a/engines/bbvs/walk.cpp b/engines/bbvs/walk.cpp index f3207be1a8..ea47c24080 100644 --- a/engines/bbvs/walk.cpp +++ b/engines/bbvs/walk.cpp @@ -48,8 +48,8 @@ void BbvsEngine::startWalkObject(SceneObject *sceneObject) { return; initWalkAreas(sceneObject); - _sourceWalkAreaPt.x = sceneObject->x >> 16; - _sourceWalkAreaPt.y = sceneObject->y >> 16; + _sourceWalkAreaPt.x = sceneObject->x / 65536; + _sourceWalkAreaPt.y = sceneObject->y / 65536; _sourceWalkArea = getWalkAreaAtPos(_sourceWalkAreaPt); if (!_sourceWalkArea) @@ -107,8 +107,8 @@ void BbvsEngine::updateWalkObject(SceneObject *sceneObject) { } void BbvsEngine::walkObject(SceneObject *sceneObject, const Common::Point &destPt, int walkSpeed) { - int deltaX = destPt.x - (sceneObject->x >> 16); - int deltaY = destPt.y - (sceneObject->y >> 16); + int deltaX = destPt.x - (sceneObject->x / 65536); + int deltaY = destPt.y - (sceneObject->y / 65536); float distance = (float)sqrt((double)(deltaX * deltaX + deltaY * deltaY)); // NOTE The original doesn't have this check but without it the whole pathfinding breaks if (distance > 0.0f) { @@ -190,19 +190,19 @@ WalkInfo *BbvsEngine::addWalkInfo(int16 x, int16 y, int delta, int direction, in } void BbvsEngine::initWalkAreas(SceneObject *sceneObject) { - int16 objX = sceneObject->x >> 16; - int16 objY = sceneObject->y >> 16; + int16 objX = sceneObject->x / 65536; + int16 objY = sceneObject->y / 65536; Common::Rect rect; bool doRect = false; Common::Rect *workWalkableRects; if (_buttheadObject == sceneObject && _beavisObject->anim) { rect = _beavisObject->anim->frameRects2[_beavisObject->frameIndex]; - rect.translate(_beavisObject->x >> 16, 1 + (_beavisObject->y >> 16)); + rect.translate(_beavisObject->x / 65536, 1 + (_beavisObject->y / 65536)); doRect = !rect.isEmpty(); } else if (_buttheadObject->anim) { rect = _buttheadObject->anim->frameRects2[_buttheadObject->frameIndex]; - rect.translate(_buttheadObject->x >> 16, 1 + (_buttheadObject->y >> 16)); + rect.translate(_buttheadObject->x / 65536, 1 + (_buttheadObject->y / 65536)); doRect = !rect.isEmpty(); } @@ -293,8 +293,8 @@ bool BbvsEngine::canButtheadWalkToDest(const Common::Point &destPt) { _walkReachedDestArea = false; initWalkAreas(_buttheadObject); - srcPt.x = _buttheadObject->x >> 16; - srcPt.y = _buttheadObject->y >> 16; + srcPt.x = _buttheadObject->x / 65536; + srcPt.y = _buttheadObject->y / 65536; _sourceWalkArea = getWalkAreaAtPos(srcPt); if (_sourceWalkArea) { _destWalkArea = getWalkAreaAtPos(destPt); @@ -441,7 +441,7 @@ void BbvsEngine::updateWalkableRects() { Animation *anim = sceneObject->anim; if (anim && _buttheadObject != sceneObject && _beavisObject != sceneObject) { Common::Rect rect = sceneObject->anim->frameRects2[sceneObject->frameIndex]; - rect.translate(sceneObject->x >> 16, sceneObject->y >> 16); + rect.translate(sceneObject->x / 65536, sceneObject->y / 65536); int count = _walkableRectsCount; _walkableRectsCount = 0; for (int j = 0; j < count; ++j) diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp index 1d29bde75f..0c5dabe136 100644 --- a/engines/bladerunner/bladerunner.cpp +++ b/engines/bladerunner/bladerunner.cpp @@ -139,7 +139,7 @@ bool BladeRunnerEngine::hasFeature(EngineFeature f) const { Common::Error BladeRunnerEngine::run() { Graphics::PixelFormat format = createRGB555(); - initGraphics(640, 480, true, &format); + initGraphics(640, 480, &format); _system->showMouse(true); diff --git a/engines/cge/cge.cpp b/engines/cge/cge.cpp index 7058314b9d..32126835f8 100644 --- a/engines/cge/cge.cpp +++ b/engines/cge/cge.cpp @@ -219,7 +219,7 @@ Common::Error CGEEngine::run() { } // Initialize graphics using following: - initGraphics(kScrWidth, kScrHeight, false); + initGraphics(kScrWidth, kScrHeight); // Setup necessary game objects init(); diff --git a/engines/cge/snail.cpp b/engines/cge/snail.cpp index 1a6ca0789e..bfeb436e94 100644 --- a/engines/cge/snail.cpp +++ b/engines/cge/snail.cpp @@ -550,7 +550,7 @@ void CGEEngine::snGame(Sprite *spr, int num) { switch (spr->_ref) { case 1: _commandHandler->addCommand(kCmdSay, 20001, 20011, NULL); // Say "It'a my turn" - _commandHandler->addCommand(kCmdSeq, 20001, 1, NULL); // Throw dice + _commandHandler->addCommand(kCmdSeq, 20001, 1, NULL); // Throw dice _commandHandler->addCommand(kCmdWait, 20001, 1, NULL); // wait _commandHandler->addCommand(kCmdSetZ, 20700, 2, NULL); // hide dice _commandHandler->addCommand(kCmdHide, 20007, 1, NULL); // hide dice @@ -564,7 +564,7 @@ void CGEEngine::snGame(Sprite *spr, int num) { case 20001: _commandHandler->addCommand(kCmdSay, 20002, 20012, NULL); // Say "Now it's mine" - _commandHandler->addCommand(kCmdSeq, 20002, 1, NULL); // Throw dice + _commandHandler->addCommand(kCmdSeq, 20002, 1, NULL); // Throw dice _commandHandler->addCommand(kCmdWait, 20002, 3, NULL); // wait _commandHandler->addCommand(kCmdSetZ, 20700, 2, NULL); // hide dice _commandHandler->addCommand(kCmdHide, 20007, 1, NULL); // hide dice @@ -581,7 +581,7 @@ void CGEEngine::snGame(Sprite *spr, int num) { _commandHandler->addCommand(kCmdWalk, 20005, -1, NULL); // Walk to table _commandHandler->addCommand(kCmdWait, 1, -1, NULL); // Wait _commandHandler->addCommand(kCmdCover, 1, 20101, NULL); // grasol ?? - _commandHandler->addCommand(kCmdSeq, 20101, 1, NULL); // Start Chief animation (20solgra) + _commandHandler->addCommand(kCmdSeq, 20101, 1, NULL); // Start Chief animation (20solgra) _commandHandler->addCommand(kCmdWait, 20101, 5, NULL); // Wait _commandHandler->addCommand(kCmdSetZ, 20700, 2, NULL); // Hide dice _commandHandler->addCommand(kCmdHide, 20007, 1, NULL); // Hide dice diff --git a/engines/cge2/cge2.cpp b/engines/cge2/cge2.cpp index ee62e20ac6..04b9db14c4 100644 --- a/engines/cge2/cge2.cpp +++ b/engines/cge2/cge2.cpp @@ -191,7 +191,7 @@ bool CGE2Engine::hasFeature(EngineFeature f) const { Common::Error CGE2Engine::run() { syncSoundSettings(); - initGraphics(kScrWidth, kScrHeight, false); + initGraphics(kScrWidth, kScrHeight); init(); cge2_main(); diff --git a/engines/chewy/chewy.cpp b/engines/chewy/chewy.cpp index 9df45b1dfb..75c2a8d51b 100644 --- a/engines/chewy/chewy.cpp +++ b/engines/chewy/chewy.cpp @@ -83,8 +83,8 @@ void ChewyEngine::initialize() { Common::Error ChewyEngine::run() { // Initialize backend - //initGraphics(640, 480, true); - initGraphics(320, 200, false); + //initGraphics(640, 480); + initGraphics(320, 200); initialize(); diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index 414fe49eb6..5ecf78d8b4 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -94,12 +94,18 @@ void CineEngine::syncSoundSettings() { } Common::Error CineEngine::run() { + Graphics::ModeList modes; + modes.push_back(Graphics::Mode(320, 200)); if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) { + modes.push_back(Graphics::Mode(640, 480)); + initGraphicsModes(modes); showSplashScreen(); + } else { + initGraphicsModes(modes); } // Initialize backend - initGraphics(320, 200, false); + initGraphics(320, 200); if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) checkCD(); @@ -259,7 +265,7 @@ void CineEngine::showSplashScreen() { const Graphics::Surface *surface = decoder.getSurface(); if (surface->w == 640 && surface->h == 480) { - initGraphics(640, 480, true); + initGraphics(640, 480); const byte *palette = decoder.getPalette(); int paletteColorCount = decoder.getPaletteColorCount(); diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 13ba76191b..a04f4b650b 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -101,7 +101,7 @@ Common::Error ComposerEngine::run() { uint height = 480; if (_bookIni.hasKey("Height", "Common")) height = atoi(getStringFromConfig("Common", "Height").c_str()); - initGraphics(width, height, true); + initGraphics(width, height); _screen.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor(); diff --git a/engines/cruise/actor.cpp b/engines/cruise/actor.cpp index 7d5c16c3d2..84a8d4e734 100644 --- a/engines/cruise/actor.cpp +++ b/engines/cruise/actor.cpp @@ -433,7 +433,8 @@ void chemin0(int depart, int arrivee) { for (int i = 0; i < 20 + 1; i++) fl[i] = -1; - X = 0, Y = 30; + X = 0; + Y = 30; explore(depart, arrivee); } diff --git a/engines/cruise/cruise.cpp b/engines/cruise/cruise.cpp index eebd8fdc15..df525c0791 100644 --- a/engines/cruise/cruise.cpp +++ b/engines/cruise/cruise.cpp @@ -84,7 +84,7 @@ bool CruiseEngine::hasFeature(EngineFeature f) const { Common::Error CruiseEngine::run() { // Initialize backend - initGraphics(320, 200, false); + initGraphics(320, 200); if (!loadLanguageStrings()) { error("Could not setup language data for your version"); diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp index b477dcdf9c..57dcfcea8f 100644 --- a/engines/cruise/sound.cpp +++ b/engines/cruise/sound.cpp @@ -41,7 +41,11 @@ class PCSoundDriver { public: typedef void (*UpdateCallback)(void *); - PCSoundDriver() { _upCb = NULL, _upRef = NULL, _musicVolume = 0, _sfxVolume = 0; } + PCSoundDriver() : + _upCb(nullptr), + _upRef(nullptr), + _musicVolume(0), + _sfxVolume(0) {} virtual ~PCSoundDriver() {} virtual void setupChannel(int channel, const byte *data, int instrument, int volume) = 0; diff --git a/engines/cryo/cryo.cpp b/engines/cryo/cryo.cpp index 3574105053..52fb2ae8d2 100644 --- a/engines/cryo/cryo.cpp +++ b/engines/cryo/cryo.cpp @@ -74,7 +74,7 @@ Common::Error CryoEngine::run() { _timerTicks = 0; // incremented in realtime // Initialize graphics using following: - initGraphics(320, 200, false); + initGraphics(320, 200); _screen.create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); _game->run(); diff --git a/engines/director/detection.cpp b/engines/director/detection.cpp index a3f9b7c2f5..16d838fcca 100644 --- a/engines/director/detection.cpp +++ b/engines/director/detection.cpp @@ -100,7 +100,7 @@ class DirectorMetaEngine : public AdvancedMetaEngine { public: DirectorMetaEngine() : AdvancedMetaEngine(Director::gameDescriptions, sizeof(Director::DirectorGameDescription), directorGames) { _singleId = "director"; - _maxScanDepth = 2, + _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } diff --git a/engines/director/graphics.cpp b/engines/director/graphics.cpp index 46dab0f763..3f9a5383b1 100644 --- a/engines/director/graphics.cpp +++ b/engines/director/graphics.cpp @@ -228,7 +228,7 @@ void DirectorEngine::testFontScaling() { int w = 640; int h = 480; - initGraphics(w, h, true); + initGraphics(w, h); _system->getPaletteManager()->setPalette(defaultPalette, 0, 256); Graphics::ManagedSurface surface; diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h index 6cf7ceb923..10ac868592 100644 --- a/engines/director/lingo/lingo.h +++ b/engines/director/lingo/lingo.h @@ -24,6 +24,7 @@ #define DIRECTOR_LINGO_LINGO_H #include "audio/audiostream.h" +#include "common/hash-ptr.h" #include "common/hash-str.h" #include "director/director.h" @@ -86,22 +87,7 @@ struct FuncDesc { FuncDesc(Common::String n, const char *p) { name = n; proto = p; } }; -struct Pointer_EqualTo { - bool operator()(const void *x, const void *y) const { return x == y; } -}; - -struct Pointer_Hash { - uint operator()(const void *x) const { -#ifdef SCUMM_64BITS - uint64 v = (uint64)x; - return (v >> 32) ^ (v & 0xffffffff); -#else - return (uint)x; -#endif - } -}; - -typedef Common::HashMap<void *, FuncDesc *, Pointer_Hash, Pointer_EqualTo> FuncHash; +typedef Common::HashMap<void *, FuncDesc *> FuncHash; struct Symbol { /* symbol table entry */ Common::String name; diff --git a/engines/director/score.cpp b/engines/director/score.cpp index 350fccb176..30cbe1d4c3 100644 --- a/engines/director/score.cpp +++ b/engines/director/score.cpp @@ -1170,7 +1170,7 @@ Common::Rect Score::readRect(Common::ReadStreamEndian &stream) { } void Score::startLoop() { - initGraphics(_movieRect.width(), _movieRect.height(), true); + initGraphics(_movieRect.width(), _movieRect.height()); _surface->create(_movieRect.width(), _movieRect.height()); _trailSurface->create(_movieRect.width(), _movieRect.height()); diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp index 646e7cec8d..5c08acdc84 100644 --- a/engines/dm/champion.cpp +++ b/engines/dm/champion.cpp @@ -2015,7 +2015,8 @@ void ChampionMan::addCandidateChampionToParty(uint16 championPortraitIndex) { int16 curMapX = dungeon._partyMapX; int16 curMapY = dungeon._partyMapY; uint16 championObjectsCell = _vm->returnOppositeDir(dungeon._partyDir); - curMapX += _vm->_dirIntoStepCountEast[dungeon._partyDir], curMapY += _vm->_dirIntoStepCountNorth[dungeon._partyDir]; + curMapX += _vm->_dirIntoStepCountEast[dungeon._partyDir]; + curMapY += _vm->_dirIntoStepCountNorth[dungeon._partyDir]; curThing = dungeon.getSquareFirstThing(curMapX, curMapY); int16 slotIdx = kDMSlotBackpackLine1_1; while (curThing != _vm->_thingEndOfList) { diff --git a/engines/dm/dm.cpp b/engines/dm/dm.cpp index 8fdb1c8d88..29ba665a47 100644 --- a/engines/dm/dm.cpp +++ b/engines/dm/dm.cpp @@ -357,7 +357,7 @@ Common::Error DMEngine::run() { initConstants(); // scummvm/engine specific - initGraphics(320, 200, false); + initGraphics(320, 200); _console = new Console(this); _displayMan = new DisplayMan(this); _dungeonMan = new DungeonMan(this); @@ -922,7 +922,8 @@ void DMEngine::fuseSequence() { fuseSequenceUpdate(); int16 lordChaosMapX = _dungeonMan->_partyMapX; int16 lordChaosMapY = _dungeonMan->_partyMapY; - lordChaosMapX += _dirIntoStepCountEast[_dungeonMan->_partyDir], lordChaosMapY += _dirIntoStepCountNorth[_dungeonMan->_partyDir]; + lordChaosMapX += _dirIntoStepCountEast[_dungeonMan->_partyDir]; + lordChaosMapY += _dirIntoStepCountNorth[_dungeonMan->_partyDir]; Thing lordChaosThing = _groupMan->groupGetThing(lordChaosMapX, lordChaosMapY); Group *lordGroup = (Group*)_dungeonMan->getThingData(lordChaosThing); lordGroup->_health[0] = 10000; diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp index 3859fc90b2..f424672b67 100644 --- a/engines/dm/eventman.cpp +++ b/engines/dm/eventman.cpp @@ -1369,7 +1369,8 @@ void EventManager::processType80_clickInDungeonView_grabLeaderHandObject(uint16 int16 mapX = _vm->_dungeonMan->_partyMapX; int16 mapY = _vm->_dungeonMan->_partyMapY; if (viewCell >= kDMViewCellBackRight) { - mapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_partyDir], mapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_partyDir]; + mapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_partyDir]; + mapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_partyDir]; Thing groupThing = _vm->_groupMan->groupGetThing(mapX, mapY); if ((groupThing != _vm->_thingEndOfList) && !_vm->_moveSens->isLevitating(groupThing) && @@ -1397,8 +1398,10 @@ void EventManager::clickInDungeonViewDropLeaderHandObject(uint16 viewCell) { if (droppingIntoAnAlcove) viewCell = kDMViewCellBackRight; - if (viewCell > kDMViewCellFrontRight) - mapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_partyDir], mapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_partyDir]; + if (viewCell > kDMViewCellFrontRight) { + mapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_partyDir]; + mapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_partyDir]; + } uint16 currCell = _vm->normalizeModulo4(_vm->_dungeonMan->_partyDir + viewCell); Thing removedThing = _vm->_championMan->getObjectRemovedFromLeaderHand(); diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp index 9bfeb1da1d..dfdcdc017f 100644 --- a/engines/dm/group.cpp +++ b/engines/dm/group.cpp @@ -819,7 +819,8 @@ T0209058_MoveInRandomDirection: do { AL0450_i_DestinationMapX = eventMapX; AL0451_i_DestinationMapY = eventMapY; - AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction], AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction]; + AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction]; + AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction]; if (((activeGroup->_priorMapX != AL0450_i_DestinationMapX) || (activeGroup->_priorMapY != AL0451_i_DestinationMapY) || (moveToPriorLocation = !_vm->getRandomNumber(4))) /* 1/4 chance of moving back to the square that the creature comes from */ @@ -910,7 +911,8 @@ T0209085_SingleSquareMove: (!_vm->getRandomNumber(4) && isMovementPossible(&creatureInfo, eventMapX, eventMapY, AL0446_i_Direction = _vm->returnOppositeDir((Direction)primaryDirectionToOrFromParty), false))) { AL0450_i_DestinationMapX = eventMapX; AL0451_i_DestinationMapY = eventMapY; - AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction], AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction]; + AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction]; + AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction]; goto T0209061_MoveGroup; } if (isArchEnemy) { @@ -922,7 +924,8 @@ T0209089_DoubleSquareMove: ((_fluxCageCount >= 2) && isArchenemyDoubleMovementPossible(&creatureInfo, eventMapX, eventMapY, AL0446_i_Direction = _vm->returnOppositeDir((Direction)primaryDirectionToOrFromParty)))) { AL0450_i_DestinationMapX = eventMapX; AL0451_i_DestinationMapY = eventMapY; - AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction] * 2, AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction] * 2; + AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction] * 2; + AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction] * 2; _vm->_sound->requestPlay(kDMSoundIndexBuzz, AL0450_i_DestinationMapX, AL0451_i_DestinationMapY, kDMSoundModePlayIfPrioritized); goto T0209061_MoveGroup; } @@ -1451,7 +1454,8 @@ bool GroupMan::isArchenemyDoubleMovementPossible(CreatureInfo *info, int16 mapX, if (_fluxCages[dir]) return false; - mapX += _vm->_dirIntoStepCountEast[dir], mapY += _vm->_dirIntoStepCountNorth[dir]; + mapX += _vm->_dirIntoStepCountEast[dir]; + mapY += _vm->_dirIntoStepCountNorth[dir]; return isMovementPossible(info, mapX, mapY, dir, false); } diff --git a/engines/dm/menus.cpp b/engines/dm/menus.cpp index 9c8e219851..1600f589e9 100644 --- a/engines/dm/menus.cpp +++ b/engines/dm/menus.cpp @@ -1263,7 +1263,8 @@ bool MenuMan::isActionPerformed(uint16 champIndex, int16 actionIndex) { setChampionDirectionToPartyDirection(curChampion); nextMapX = dungeon._partyMapX; nextMapY = dungeon._partyMapY; - nextMapX += _vm->_dirIntoStepCountEast[dungeon._partyDir], nextMapY += _vm->_dirIntoStepCountNorth[dungeon._partyDir]; + nextMapX += _vm->_dirIntoStepCountEast[dungeon._partyDir]; + nextMapY += _vm->_dirIntoStepCountNorth[dungeon._partyDir]; _vm->_groupMan->fuseAction(nextMapX, nextMapY); break; case kDMActionHeal: { diff --git a/engines/dm/movesens.cpp b/engines/dm/movesens.cpp index 410ede740a..1dc36f7979 100644 --- a/engines/dm/movesens.cpp +++ b/engines/dm/movesens.cpp @@ -352,7 +352,8 @@ bool MovesensMan::getMoveResult(Thing thing, int16 mapX, int16 mapY, int16 destM dungeon.setCurrentMap(mapIndexDestination); } direction = dungeon.getStairsExitDirection(destMapX, destMapY); - destMapX += _vm->_dirIntoStepCountEast[direction], destMapY += _vm->_dirIntoStepCountNorth[direction]; + destMapX += _vm->_dirIntoStepCountEast[direction]; + destMapY += _vm->_dirIntoStepCountNorth[direction]; direction = _vm->returnOppositeDir((Direction)direction); thingCell = thing.getCell(); thingCell = _vm->normalizeModulo4((((thingCell - direction + 1) & 0x0002) >> 1) + direction); diff --git a/engines/dm/projexpl.cpp b/engines/dm/projexpl.cpp index 85767cd633..e8b0f4a143 100644 --- a/engines/dm/projexpl.cpp +++ b/engines/dm/projexpl.cpp @@ -429,7 +429,8 @@ void ProjExpl::processEvents48To49(TimelineEvent *event) { if (projectileMovesToOtherSquare) { sourceMapX = destinationMapX; sourceMapY = destinationMapY; - destinationMapX += _vm->_dirIntoStepCountEast[projectileDirection], destinationMapY += _vm->_dirIntoStepCountNorth[projectileDirection]; + destinationMapX += _vm->_dirIntoStepCountEast[projectileDirection]; + destinationMapY += _vm->_dirIntoStepCountNorth[projectileDirection]; Square destSquare = _vm->_dungeonMan->getSquare(destinationMapX, destinationMapY); ElementType destSquareType = destSquare.getType(); if ((destSquareType == kDMElementTypeWall) || diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp index e3a97bf59a..0de1121d91 100644 --- a/engines/draci/draci.cpp +++ b/engines/draci/draci.cpp @@ -150,7 +150,7 @@ static SoundArchive* openAnyPossibleDubbing() { int DraciEngine::init() { // Initialize graphics using following: - initGraphics(kScreenWidth, kScreenHeight, false); + initGraphics(kScreenWidth, kScreenHeight); // Open game's archives _initArchive = new BArchive(initPath); diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 56a2d638f7..d0d2790c0e 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -236,7 +236,7 @@ bool DrasculaEngine::hasFeature(EngineFeature f) const { Common::Error DrasculaEngine::run() { // Initialize backend - initGraphics(320, 200, false); + initGraphics(320, 200); switch (getLanguage()) { case Common::EN_ANY: diff --git a/engines/dreamweb/stubs.cpp b/engines/dreamweb/stubs.cpp index 4aa487d13f..401ecf038c 100644 --- a/engines/dreamweb/stubs.cpp +++ b/engines/dreamweb/stubs.cpp @@ -22,6 +22,7 @@ #include "dreamweb/sound.h" #include "dreamweb/dreamweb.h" +#include "engines/util.h" #include "common/config-manager.h" #include "common/file.h" @@ -563,6 +564,11 @@ void DreamWebEngine::dreamweb() { break; } + Graphics::ModeList modes; + modes.push_back(Graphics::Mode(320, 200)); + modes.push_back(Graphics::Mode(640, 480)); + initGraphicsModes(modes); + allocateBuffers(); // setMouse diff --git a/engines/dreamweb/titles.cpp b/engines/dreamweb/titles.cpp index bd0957c1bd..f0b77b9691 100644 --- a/engines/dreamweb/titles.cpp +++ b/engines/dreamweb/titles.cpp @@ -99,7 +99,7 @@ void DreamWebEngine::gettingShot() { } void DreamWebEngine::bibleQuote() { - initGraphics(640, 480, true); + initGraphics(640, 480); showPCX("I00"); fadeScreenUps(); @@ -285,7 +285,7 @@ void DreamWebEngine::realCredits() { _sound->loadRoomsSample(_roomsSample); _sound->volumeSet(0); - initGraphics(640, 480, true); + initGraphics(640, 480); hangOn(35); showPCX("I01"); diff --git a/engines/dreamweb/vgagrafx.cpp b/engines/dreamweb/vgagrafx.cpp index 4a7acd2344..aa58352e81 100644 --- a/engines/dreamweb/vgagrafx.cpp +++ b/engines/dreamweb/vgagrafx.cpp @@ -151,7 +151,7 @@ void DreamWebEngine::doShake() { void DreamWebEngine::setMode() { waitForVSync(); - initGraphics(kScreenwidth, kScreenheight, false); + initGraphics(kScreenwidth, kScreenheight); } void DreamWebEngine::showPCX(const Common::String &suffix) { diff --git a/engines/engine.cpp b/engines/engine.cpp index bb51e77f0d..8f412ba911 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -32,6 +32,7 @@ #include "engines/engine.h" #include "engines/dialogs.h" +#include "engines/util.h" #include "common/config-manager.h" #include "common/events.h" @@ -194,58 +195,22 @@ void Engine::initializePath(const Common::FSNode &gamePath) { SearchMan.addDirectory(gamePath.getPath(), gamePath, 0, 4); } -void initCommonGFX(bool defaultTo1XScaler) { - const Common::ConfigManager::Domain *transientDomain = ConfMan.getDomain(Common::ConfigManager::kTransientDomain); +void initCommonGFX() { const Common::ConfigManager::Domain *gameDomain = ConfMan.getActiveDomain(); - assert(transientDomain); - - const bool useDefaultGraphicsMode = - (!transientDomain->contains("gfx_mode") || - !scumm_stricmp(transientDomain->getVal("gfx_mode").c_str(), "normal") || - !scumm_stricmp(transientDomain->getVal("gfx_mode").c_str(), "default") - ) - && - ( - !gameDomain || - !gameDomain->contains("gfx_mode") || - !scumm_stricmp(gameDomain->getVal("gfx_mode").c_str(), "normal") || - !scumm_stricmp(gameDomain->getVal("gfx_mode").c_str(), "default") - ); - - // See if the game should default to 1x scaler - if (useDefaultGraphicsMode && defaultTo1XScaler) { - g_system->resetGraphicsScale(); - } else { - // Override global scaler with any game-specific define - if (ConfMan.hasKey("gfx_mode")) { - Common::String gfxMode = ConfMan.get("gfx_mode"); - g_system->setGraphicsMode(gfxMode.c_str()); - - // HACK: For OpenGL modes, we will still honor the graphics scale override - if (defaultTo1XScaler && gfxMode.equalsIgnoreCase("opengl")) - g_system->resetGraphicsScale(); - } - } - - // Note: The following code deals with the fullscreen / ASR settings. This - // is a bit tricky, because there are three ways the user can affect these - // settings: Via the config file, via the command line, and via in-game - // hotkeys. // Any global or command line settings already have been applied at the time - // we get here. Hence we only do something + // we get here, so we only do something if the game domain overrides those + // values + if (gameDomain) { + if (gameDomain->contains("aspect_ratio")) + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, ConfMan.getBool("aspect_ratio")); - // (De)activate aspect-ratio correction as determined by the config settings - if (gameDomain && gameDomain->contains("aspect_ratio")) - g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, ConfMan.getBool("aspect_ratio")); + if (gameDomain->contains("fullscreen")) + g_system->setFeatureState(OSystem::kFeatureFullscreenMode, ConfMan.getBool("fullscreen")); - // (De)activate fullscreen mode as determined by the config settings - if (gameDomain && gameDomain->contains("fullscreen")) - g_system->setFeatureState(OSystem::kFeatureFullscreenMode, ConfMan.getBool("fullscreen")); - - // (De)activate filtering mode as determined by the config settings - if (gameDomain && gameDomain->contains("filtering")) - g_system->setFeatureState(OSystem::kFeatureFilteringMode, ConfMan.getBool("filtering")); + if (gameDomain->contains("filtering")) + g_system->setFeatureState(OSystem::kFeatureFilteringMode, ConfMan.getBool("filtering")); + } } // Please leave the splash screen in working order for your releases, even if they're commercial. @@ -307,11 +272,15 @@ void splashScreen() { splash = true; } -void initGraphics(int width, int height, bool defaultTo1xScaler, const Graphics::PixelFormat *format) { +void initGraphicsModes(const Graphics::ModeList &modes) { + g_system->initSizeHint(modes); +} + +void initGraphics(int width, int height, const Graphics::PixelFormat *format) { g_system->beginGFXTransaction(); - initCommonGFX(defaultTo1xScaler); + initCommonGFX(); #ifdef USE_RGB_COLOR if (format) g_system->initSize(width, height, format); @@ -375,9 +344,6 @@ void initGraphics(int width, int height, bool defaultTo1xScaler, const Graphics: } } - -using Graphics::PixelFormat; - /** * Determines the first matching format between two lists. * @@ -386,33 +352,33 @@ using Graphics::PixelFormat; * @return The first item on the backend list that also occurs on the frontend list * or PixelFormat::createFormatCLUT8() if no matching formats were found. */ -inline PixelFormat findCompatibleFormat(Common::List<PixelFormat> backend, Common::List<PixelFormat> frontend) { +inline Graphics::PixelFormat findCompatibleFormat(const Common::List<Graphics::PixelFormat> &backend, const Common::List<Graphics::PixelFormat> &frontend) { #ifdef USE_RGB_COLOR - for (Common::List<PixelFormat>::iterator i = backend.begin(); i != backend.end(); ++i) { - for (Common::List<PixelFormat>::iterator j = frontend.begin(); j != frontend.end(); ++j) { + for (Common::List<Graphics::PixelFormat>::const_iterator i = backend.begin(); i != backend.end(); ++i) { + for (Common::List<Graphics::PixelFormat>::const_iterator j = frontend.begin(); j != frontend.end(); ++j) { if (*i == *j) return *i; } } #endif - return PixelFormat::createFormatCLUT8(); + return Graphics::PixelFormat::createFormatCLUT8(); } -void initGraphics(int width, int height, bool defaultTo1xScaler, const Common::List<Graphics::PixelFormat> &formatList) { +void initGraphics(int width, int height, const Common::List<Graphics::PixelFormat> &formatList) { Graphics::PixelFormat format = findCompatibleFormat(g_system->getSupportedFormats(), formatList); - initGraphics(width, height, defaultTo1xScaler, &format); + initGraphics(width, height, &format); } -void initGraphics(int width, int height, bool defaultTo1xScaler) { +void initGraphics(int width, int height) { Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); - initGraphics(width, height, defaultTo1xScaler, &format); + initGraphics(width, height, &format); } void GUIErrorMessage(const Common::String &msg) { g_system->setWindowCaption("Error"); g_system->beginGFXTransaction(); - initCommonGFX(false); + initCommonGFX(); g_system->initSize(320, 200); if (g_system->endGFXTransaction() == OSystem::kTransactionSuccess) { GUI::MessageDialog dialog(msg); diff --git a/engines/fullpipe/fullpipe.cpp b/engines/fullpipe/fullpipe.cpp index 61e7eef692..ad9b6a42f6 100644 --- a/engines/fullpipe/fullpipe.cpp +++ b/engines/fullpipe/fullpipe.cpp @@ -280,7 +280,7 @@ Common::Error FullpipeEngine::saveGameState(int slot, const Common::String &desc Common::Error FullpipeEngine::run() { const Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0); // Initialize backend - initGraphics(800, 600, true, &format); + initGraphics(800, 600, &format); _backgroundSurface = new Graphics::Surface; _backgroundSurface->create(800, 600, format); diff --git a/engines/fullpipe/utils.h b/engines/fullpipe/utils.h index bb8c9651da..d19def663a 100644 --- a/engines/fullpipe/utils.h +++ b/engines/fullpipe/utils.h @@ -23,6 +23,7 @@ #ifndef FULLPIPE_UTILS_H #define FULLPIPE_UTILS_H +#include "common/hash-ptr.h" #include "common/hash-str.h" #include "common/array.h" #include "common/file.h" @@ -32,22 +33,7 @@ namespace Fullpipe { class CObject; class NGIArchive; -struct Pointer_EqualTo { - bool operator()(const void *x, const void *y) const { return x == y; } -}; - -struct Pointer_Hash { - uint operator()(const void *x) const { -#ifdef SCUMM_64BITS - uint64 v = (uint64)x; - return (v >> 32) ^ (v & 0xffffffff); -#else - return (uint)x; -#endif - } -}; - -typedef Common::HashMap<void *, int, Pointer_Hash, Pointer_EqualTo> ObjHash; +typedef Common::HashMap<void *, int> ObjHash; typedef Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ClassMap; diff --git a/engines/gnap/gnap.cpp b/engines/gnap/gnap.cpp index 9289053048..cf8710c64d 100644 --- a/engines/gnap/gnap.cpp +++ b/engines/gnap/gnap.cpp @@ -200,7 +200,7 @@ Common::Error GnapEngine::run() { #else Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); #endif - initGraphics(800, 600, true, &format); + initGraphics(800, 600, &format); // We do not support color conversion yet if (_system->getScreenFormat() != format) diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp index b0aa78f416..e204ced1e8 100644 --- a/engines/gob/detection/detection.cpp +++ b/engines/gob/detection/detection.cpp @@ -77,7 +77,10 @@ const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, return 0; } - reportUnknown(fslist.begin()->getParent(), filesProps); + ADGameIdList gameIds; + gameIds.push_back(game->desc.gameId); + + reportUnknown(fslist.begin()->getParent(), filesProps, gameIds); return (const ADGameDescription *)game; } diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 605f1ed5b9..46f32a9f32 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -25,6 +25,7 @@ #include "backends/audiocd/audiocd.h" #include "base/plugins.h" #include "common/config-manager.h" +#include "engines/util.h" #include "audio/mididrv.h" #include "audio/mixer.h" @@ -260,7 +261,7 @@ void GobEngine::setTrueColor(bool trueColor) { _features = (_features & ~kFeaturesTrueColor) | (trueColor ? kFeaturesTrueColor : 0); - _video->setSize(is640x480()); + _video->setSize(); _pixelFormat = g_system->getScreenFormat(); @@ -708,7 +709,14 @@ Common::Error GobEngine::initGraphics() { _mode = 0x14; } - _video->setSize(is640x480()); + Graphics::ModeList modes; + modes.push_back(Graphics::Mode(_width, _height)); + if (getGameType() == kGameTypeLostInTime) { + modes.push_back(Graphics::Mode(640, 400)); + } + initGraphicsModes(modes); + + _video->setSize(); _pixelFormat = g_system->getScreenFormat(); diff --git a/engines/gob/inter_v2.cpp b/engines/gob/inter_v2.cpp index 3928985dcd..1e8eddeef0 100644 --- a/engines/gob/inter_v2.cpp +++ b/engines/gob/inter_v2.cpp @@ -797,7 +797,7 @@ void Inter_v2::o2_initScreen() { height = _vm->_height = 400; _vm->_global->_colorCount = 16; - _vm->_video->setSize(true); + _vm->_video->setSize(); } else if (_vm->_global->_videoMode == 0x10) { @@ -810,7 +810,7 @@ void Inter_v2::o2_initScreen() { _vm->_height = 200; _vm->_global->_colorCount = 256; - _vm->_video->setSize(false); + _vm->_video->setSize(); } } diff --git a/engines/gob/inter_v5.cpp b/engines/gob/inter_v5.cpp index 50176e0a27..91342cf326 100644 --- a/engines/gob/inter_v5.cpp +++ b/engines/gob/inter_v5.cpp @@ -140,13 +140,13 @@ void Inter_v5::o5_initScreen() { _vm->_width = 320; _vm->_height = 200; - _vm->_video->setSize(false); + _vm->_video->setSize(); } else if (_vm->_global->_videoMode == 0x13) { width = _vm->_width = 640; height = _vm->_height = 480; - _vm->_video->setSize(true); + _vm->_video->setSize(); } _vm->_global->_fakeVideoMode = videoMode; diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp index 19e6e35e0b..6bb4274421 100644 --- a/engines/gob/util.cpp +++ b/engines/gob/util.cpp @@ -37,7 +37,7 @@ namespace Gob { Util::Util(GobEngine *vm) : _vm(vm) { - _mouseButtons = kMouseButtonsNone, + _mouseButtons = kMouseButtonsNone; _keyBufferHead = 0; _keyBufferTail = 0; _fastMode = 0; diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp index 1dc51d994c..6480fe89f0 100644 --- a/engines/gob/video.cpp +++ b/engines/gob/video.cpp @@ -237,11 +237,11 @@ void Video::clearScreen() { g_system->fillScreen(0); } -void Video::setSize(bool defaultTo1XScaler) { +void Video::setSize() { if (_vm->isTrueColor()) - initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler, 0); + initGraphics(_vm->_width, _vm->_height, nullptr); else - initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler); + initGraphics(_vm->_width, _vm->_height); } void Video::retrace(bool mouse) { diff --git a/engines/gob/video.h b/engines/gob/video.h index 2c547baca0..57ec4fb6d4 100644 --- a/engines/gob/video.h +++ b/engines/gob/video.h @@ -112,7 +112,7 @@ public: void initPrimary(int16 mode); SurfacePtr initSurfDesc(int16 width, int16 height, int16 flags = 0); - void setSize(bool defaultTo1XScaler); + void setSize(); void clearScreen(); void retrace(bool mouse = true); diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp index 2ba4ed18ab..291e3a1fde 100644 --- a/engines/groovie/groovie.cpp +++ b/engines/groovie/groovie.cpp @@ -106,7 +106,7 @@ Common::Error GroovieEngine::run() { case kGroovieV2: { // Request the mode with the highest precision available Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0); - initGraphics(640, 480, true, &format); + initGraphics(640, 480, &format); if (_system->getScreenFormat() != format) return Common::kUnsupportedColorMode; @@ -116,7 +116,7 @@ Common::Error GroovieEngine::run() { break; } case kGroovieT7G: - initGraphics(640, 480, true); + initGraphics(640, 480); _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); break; } diff --git a/engines/hopkins/graphics.cpp b/engines/hopkins/graphics.cpp index 7227c3e833..3cdf6add8f 100644 --- a/engines/hopkins/graphics.cpp +++ b/engines/hopkins/graphics.cpp @@ -106,7 +106,7 @@ GraphicsManager::~GraphicsManager() { void GraphicsManager::setGraphicalMode(int width, int height) { if (!_initGraphicsFl) { Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0); - initGraphics(width, height, true, &pixelFormat16); + initGraphics(width, height, &pixelFormat16); // Init surfaces _backBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT); diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp index 6881278cee..5f6892bd20 100644 --- a/engines/hugo/hugo.cpp +++ b/engines/hugo/hugo.cpp @@ -218,7 +218,7 @@ void HugoEngine::gameOverMsg() { Common::Error HugoEngine::run() { s_Engine = this; - initGraphics(320, 200, false); + initGraphics(320, 200); _mouse = new MouseHandler(this); _inventory = new InventoryHandler(this); diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index eaa074ff0a..6076879876 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -253,10 +253,8 @@ void Screen::setResolution() { _system->getPaletteManager()->grabPalette(palette, 0, 256); int width = 320, height = 200; - bool defaultTo1xScaler = false; if (_vm->gameFlags().useHiRes) { - defaultTo1xScaler = true; height = 400; if (_debugEnabled) @@ -270,7 +268,7 @@ void Screen::setResolution() { width = 320; } - initGraphics(width, height, defaultTo1xScaler); + initGraphics(width, height); _system->getPaletteManager()->setPalette(palette, 0, 256); } diff --git a/engines/lab/lab.cpp b/engines/lab/lab.cpp index 39b2feb93d..ee67d3a99d 100644 --- a/engines/lab/lab.cpp +++ b/engines/lab/lab.cpp @@ -160,9 +160,9 @@ LabEngine::~LabEngine() { Common::Error LabEngine::run() { if (getFeatures() & GF_LOWRES) - initGraphics(320, 200, false); + initGraphics(320, 200); else - initGraphics(640, 480, true); + initGraphics(640, 480); _interface = new Interface(this); _event = new EventManager(this); diff --git a/engines/lastexpress/lastexpress.cpp b/engines/lastexpress/lastexpress.cpp index 90b684ab0b..91f8a6a346 100644 --- a/engines/lastexpress/lastexpress.cpp +++ b/engines/lastexpress/lastexpress.cpp @@ -111,7 +111,7 @@ LastExpressEngine::~LastExpressEngine() { Common::Error LastExpressEngine::run() { // Initialize the graphics const Graphics::PixelFormat dataPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - initGraphics(640, 480, true, &dataPixelFormat); + initGraphics(640, 480, &dataPixelFormat); // We do not support color conversion if (_system->getScreenFormat() != dataPixelFormat) diff --git a/engines/lure/lure.cpp b/engines/lure/lure.cpp index 8eac519d8c..13417b3251 100644 --- a/engines/lure/lure.cpp +++ b/engines/lure/lure.cpp @@ -55,7 +55,7 @@ Common::Error LureEngine::init() { _initialized = false; _saveLoadAllowed = false; - initGraphics(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT, false); + initGraphics(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT); // Check the version of the lure.dat file Common::File f; diff --git a/engines/lure/res.cpp b/engines/lure/res.cpp index 7a79f48b87..dc9ce29af8 100644 --- a/engines/lure/res.cpp +++ b/engines/lure/res.cpp @@ -87,7 +87,7 @@ void Resources::freeData() { } struct AnimRecordTemp { - uint16 *offset; + uint16 offset; MovementDataList *list; }; @@ -235,12 +235,12 @@ void Resources::reloadData() { // Handle any direction frames AnimRecordTemp dirEntries[4] = { - {&animRec->leftOffset, &newEntry->leftFrames}, - {&animRec->rightOffset, &newEntry->rightFrames}, - {&animRec->upOffset, &newEntry->upFrames}, - {&animRec->downOffset, &newEntry->downFrames}}; + {FROM_LE_16(animRec->leftOffset), &newEntry->leftFrames}, + {FROM_LE_16(animRec->rightOffset), &newEntry->rightFrames}, + {FROM_LE_16(animRec->upOffset), &newEntry->upFrames}, + {FROM_LE_16(animRec->downOffset), &newEntry->downFrames}}; for (int dirCtr = 0; dirCtr < 4; ++dirCtr) { - offsetVal = READ_LE_UINT16(dirEntries[dirCtr].offset); + offsetVal = dirEntries[dirCtr].offset; if (offsetVal != 0) { MovementResource *moveRec = (MovementResource *) (mb->data() + offsetVal); diff --git a/engines/macventure/macventure.cpp b/engines/macventure/macventure.cpp index 483281f583..ddc6d90a81 100644 --- a/engines/macventure/macventure.cpp +++ b/engines/macventure/macventure.cpp @@ -146,7 +146,7 @@ void MacVentureEngine::initDebugChannels() { Common::Error MacVentureEngine::run() { debug("MacVenture::MacVentureEngine::init()"); - initGraphics(kScreenWidth, kScreenHeight, true); + initGraphics(kScreenWidth, kScreenHeight); _debugger = new Console(this); diff --git a/engines/made/made.cpp b/engines/made/made.cpp index a29aa2512f..181d778a02 100644 --- a/engines/made/made.cpp +++ b/engines/made/made.cpp @@ -292,7 +292,7 @@ Common::Error MadeEngine::run() { syncSoundSettings(); // Initialize backend - initGraphics(320, 200, false); + initGraphics(320, 200); resetAllTimers(); diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp index 414473bb82..c86593ed0e 100644 --- a/engines/mads/mads.cpp +++ b/engines/mads/mads.cpp @@ -159,7 +159,7 @@ void MADSEngine::saveOptions() { } Common::Error MADSEngine::run() { - initGraphics(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT, false); + initGraphics(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); initialize(); // Run the game diff --git a/engines/mohawk/cstime_graphics.cpp b/engines/mohawk/cstime_graphics.cpp index 29336525da..7dc1cf339c 100644 --- a/engines/mohawk/cstime_graphics.cpp +++ b/engines/mohawk/cstime_graphics.cpp @@ -32,7 +32,7 @@ namespace Mohawk { CSTimeGraphics::CSTimeGraphics(MohawkEngine_CSTime *vm) : GraphicsManager(), _vm(vm) { _bmpDecoder = new MohawkBitmap(); - initGraphics(640, 480, true); + initGraphics(640, 480); } CSTimeGraphics::~CSTimeGraphics() { diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 4069ac5133..e35fc842e6 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -195,6 +195,23 @@ static const MohawkGameDescription gameDescriptions[] = { 0, }, + // Myst - Russian + // From AndyILC in bug #10303 + { + { + "myst", + "", + AD_ENTRY1("MYST.DAT", "196384f87e8bcb51731bce8416ab6a07"), + Common::RU_RUS, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUI_OPTIONS_MYST + }, + GType_MYST, + 0, + 0, + }, + // Making of Myst // English Windows 3.11 // From clone2727 @@ -313,7 +330,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "71145fdecbd68a0cfc292c2fbddf8e08"), Common::EN_ANY, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -331,7 +348,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "d8ccae34a0e3c709135a73f449b783be"), Common::EN_ANY, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -349,7 +366,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "249e8c995d191b03ee94c892c0eac775"), Common::ES_ESP, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -367,7 +384,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"), Common::FR_FRA, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -385,7 +402,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "0e21e89df7788f32056b6521abf2e81a"), Common::IT_ITA, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -402,7 +419,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1s("a_Data.MHK", "2a840ed74fe5dc3a388bced674d379d5", 12024358), Common::RU_RUS, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -419,7 +436,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "59bd2e3ccbae2f1faa1b23a18dc316eb"), Common::RU_RUS, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -438,7 +455,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1s("a_Data.MHK", "3a2b4764979dc007a0e6ded64e4b7889", 10014314), Common::JA_JPN, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -456,7 +473,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "08fcaa5d5a2a01d7a5a6960f497212fe"), Common::EN_ANY, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -474,7 +491,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "a5fe1c91a6033eb6ee54b287578b74b9"), Common::DE_DEU, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -492,7 +509,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "733a710cf5f848b441ec72d988ab8a3d"), Common::PL_POL, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -509,7 +526,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "b5f40e6e6b843bf3abea291faa0911f4"), Common::RU_RUS, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -527,7 +544,7 @@ static const MohawkGameDescription gameDescriptions[] = { AD_ENTRY1("a_Data.MHK", "bae6b03bd8d6eb350d35fd13f0e3139f"), Common::EN_ANY, Common::kPlatformWindows, - ADGF_DEMO | ADGF_TESTING, + ADGF_DEMO, GUI_OPTIONS_RIVEN_DEMO }, GType_RIVEN, @@ -2839,7 +2856,7 @@ static const MohawkGameDescription fallbackDescs[] = { AD_ENTRY1(0, 0), Common::UNK_LANG, Common::kPlatformWindows, - ADGF_UNSTABLE, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, @@ -2854,7 +2871,7 @@ static const MohawkGameDescription fallbackDescs[] = { AD_ENTRY1(0, 0), Common::UNK_LANG, Common::kPlatformWindows, - ADGF_UNSTABLE, + ADGF_NO_FLAGS, GUI_OPTIONS_RIVEN }, GType_RIVEN, diff --git a/engines/mohawk/livingbooks_graphics.cpp b/engines/mohawk/livingbooks_graphics.cpp index fae0c99f6e..bb521eee22 100644 --- a/engines/mohawk/livingbooks_graphics.cpp +++ b/engines/mohawk/livingbooks_graphics.cpp @@ -34,7 +34,7 @@ namespace Mohawk { LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height) : GraphicsManager(), _vm(vm) { _bmpDecoder = _vm->isPreMohawk() ? new LivingBooksBitmap_v1() : new MohawkBitmap(); - initGraphics(width, height, true); + initGraphics(width, height); } LBGraphics::~LBGraphics() { diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index 9b0d33e30b..3f8d15cea5 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -40,13 +40,13 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { if (_vm->getFeatures() & GF_ME) { // High color - initGraphics(_viewport.width(), _viewport.height(), true, nullptr); + initGraphics(_viewport.width(), _viewport.height(), nullptr); if (_vm->_system->getScreenFormat().bytesPerPixel == 1) error("Myst ME requires greater than 256 colors to run"); } else { // Paletted - initGraphics(_viewport.width(), _viewport.height(), true); + initGraphics(_viewport.width(), _viewport.height()); clearScreenPalette(); } diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp index c80e9e5899..d0a5c05636 100644 --- a/engines/mohawk/riven_graphics.cpp +++ b/engines/mohawk/riven_graphics.cpp @@ -308,7 +308,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm // Restrict ourselves to a single pixel format to simplify the effects implementation _pixelFormat = Graphics::createPixelFormat<565>(); - initGraphics(608, 436, true, &_pixelFormat); + initGraphics(608, 436, &_pixelFormat); // The actual game graphics only take up the first 392 rows. The inventory // occupies the rest of the screen and we don't use the buffer to hold that. diff --git a/engines/mortevielle/mortevielle.cpp b/engines/mortevielle/mortevielle.cpp index 59004cba7f..92d1ca8839 100644 --- a/engines/mortevielle/mortevielle.cpp +++ b/engines/mortevielle/mortevielle.cpp @@ -247,7 +247,7 @@ void MortevielleEngine::pauseEngineIntern(bool pause) { */ Common::ErrorCode MortevielleEngine::initialize() { // Initialize graphics mode - initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, true); + initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT); // Set up an intermediate screen surface _screenSurface->create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8()); diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp index 0dc271997b..7fe3e77ccb 100644 --- a/engines/neverhood/neverhood.cpp +++ b/engines/neverhood/neverhood.cpp @@ -65,7 +65,7 @@ NeverhoodEngine::~NeverhoodEngine() { } Common::Error NeverhoodEngine::run() { - initGraphics(640, 480, true); + initGraphics(640, 480); const Common::FSNode gameDataDir(ConfMan.get("path")); diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 162671b68a..b732141971 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -731,7 +731,7 @@ Gfx::Gfx(Parallaction* vm) : _gameType = _vm->getGameType(); _doubleBuffering = _gameType != GType_Nippon; - initGraphics(_vm->_screenWidth, _vm->_screenHeight, _gameType == GType_BRA); + initGraphics(_vm->_screenWidth, _vm->_screenHeight); setPalette(_palette); diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp index 7748c266f5..d620fe3d4e 100644 --- a/engines/pegasus/graphics.cpp +++ b/engines/pegasus/graphics.cpp @@ -35,7 +35,7 @@ namespace Pegasus { GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) { - initGraphics(640, 480, true, NULL); + initGraphics(640, 480, nullptr); if (_vm->_system->getScreenFormat().bytesPerPixel == 1) error("No true color mode available"); diff --git a/engines/plumbers/plumbers.cpp b/engines/plumbers/plumbers.cpp index 74f622358b..f0445e51eb 100644 --- a/engines/plumbers/plumbers.cpp +++ b/engines/plumbers/plumbers.cpp @@ -88,7 +88,7 @@ static const byte cursorPalette[] = { }; Common::Error PlumbersGame::run() { - initGraphics(640, 480, true); + initGraphics(640, 480); _console = new Console(this); CursorMan.replaceCursor(MOUSECURSOR_SCI, 11, 16, 0, 0, 0); diff --git a/engines/prince/graphics.cpp b/engines/prince/graphics.cpp index 6be1782f1c..c25157c697 100644 --- a/engines/prince/graphics.cpp +++ b/engines/prince/graphics.cpp @@ -31,7 +31,7 @@ namespace Prince { GraphicsMan::GraphicsMan(PrinceEngine *vm) : _vm(vm), _changed(false) { - initGraphics(640, 480, true); + initGraphics(640, 480); _frontScreen = new Graphics::Surface(); _frontScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); diff --git a/engines/prince/videoplayer.cpp b/engines/prince/videoplayer.cpp index 2fc9003be5..2e73c78a0d 100644 --- a/engines/prince/videoplayer.cpp +++ b/engines/prince/videoplayer.cpp @@ -30,7 +30,7 @@ namespace Prince { void PrinceEngine::playVideo(Common::String videoFilename) { // Set the correct video mode - initGraphics(640, 480, true, 0); + initGraphics(640, 480, nullptr); if (_system->getScreenFormat().bytesPerPixel == 1) { warning("Couldn't switch to a RGB color video mode to play a video."); return; @@ -42,7 +42,7 @@ void PrinceEngine::playVideo(Common::String videoFilename) { if (!videoDecoder->loadFile(videoFilename)) { delete videoDecoder; warning("Unable to open video %s", videoFilename.c_str()); - initGraphics(640, 480, true); + initGraphics(640, 480); return; } @@ -78,7 +78,7 @@ void PrinceEngine::playVideo(Common::String videoFilename) { delete videoDecoder; - initGraphics(640, 480, true); + initGraphics(640, 480); } } // End of namespace Prince diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp index 0aea6122ca..16d88ee6f3 100644 --- a/engines/queen/queen.cpp +++ b/engines/queen/queen.cpp @@ -324,7 +324,7 @@ bool Queen::QueenEngine::hasFeature(EngineFeature f) const { } Common::Error QueenEngine::run() { - initGraphics(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT, false); + initGraphics(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT); _resource = new Resource(); diff --git a/engines/saga/detection_tables.h b/engines/saga/detection_tables.h index 71225ceb2f..20fdfb09ee 100644 --- a/engines/saga/detection_tables.h +++ b/engines/saga/detection_tables.h @@ -498,6 +498,30 @@ static const SAGAGameDescription gameDescriptions[] = { ITEPatch_Files, }, + // Inherit the earth - French Windows/Linux/DOS CD version (fan translation) + { + { + "ite", + "Windows/Linux/DOS CD Version", + { + {"ite.rsc", GAME_RESOURCEFILE, "8f4315a9bb10ec839253108a032c8b54", 8929384}, + {"scripts.rsc", GAME_SCRIPTFILE, "a891405405edefc69c9d6c420c868b84", 355877}, + { NULL, 0, NULL, 0} + }, + Common::FR_FRA, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + GID_ITE, + 0, + ITE_DEFAULT_SCENE, + &ITE_Resources, + ARRAYSIZE(ITE_GameFonts), + ITE_GameFonts, + ITEPatch_Files, + }, + // Inherit the earth - DOS CD version { { diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp index ceb86ebb18..79bd66250a 100644 --- a/engines/saga/gfx.cpp +++ b/engines/saga/gfx.cpp @@ -40,7 +40,7 @@ namespace Saga { #define RID_IHNM_HOURGLASS_CURSOR 11 // not in demo Gfx::Gfx(SagaEngine *vm, OSystem *system, int width, int height) : _vm(vm), _system(system) { - initGraphics(width, height, width > 320); + initGraphics(width, height); debug(5, "Init screen %dx%d", width, height); // Convert surface data to R surface data diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp index 3c10cbe1dd..824a94a663 100644 --- a/engines/saga/introproc_ite.cpp +++ b/engines/saga/introproc_ite.cpp @@ -42,6 +42,7 @@ namespace Saga { #define INTRO_CAPTION_Y 170 #define INTRO_DE_CAPTION_Y 160 #define INTRO_IT_CAPTION_Y 160 +#define INTRO_FR_CAPTION_Y 160 #define INTRO_VOICE_PAD 50 #define INTRO_VOICE_LETTERLEN 90 @@ -128,6 +129,8 @@ EventColumns *Scene::queueIntroDialogue(EventColumns *eventColumns, int n_dialog textEntry.rect.top = INTRO_DE_CAPTION_Y; } else if (_vm->getLanguage() == Common::IT_ITA) { textEntry.rect.top = INTRO_IT_CAPTION_Y; + } else if (_vm->getLanguage() == Common::FR_FRA) { + textEntry.rect.top = INTRO_FR_CAPTION_Y; } else { textEntry.rect.top = INTRO_CAPTION_Y; } @@ -379,6 +382,8 @@ int Scene::ITEIntroCaveCommonProc(int param, int caveScene) { lang = 1; else if (_vm->getLanguage() == Common::IT_ITA) lang = 2; + else if (_vm->getLanguage() == Common::FR_FRA) + lang = 3; int n_dialogues = 0; diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp index e50378b9c0..cff415868e 100644 --- a/engines/saga/isomap.cpp +++ b/engines/saga/isomap.cpp @@ -1350,7 +1350,7 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo bool first; bestDistance = SAGA_DRAGON_SEARCH_DIAMETER; - bestU = SAGA_DRAGON_SEARCH_CENTER, + bestU = SAGA_DRAGON_SEARCH_CENTER; bestV = SAGA_DRAGON_SEARCH_CENTER; uBase = (start.u() >> 4) - SAGA_DRAGON_SEARCH_CENTER; @@ -1505,7 +1505,7 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio bestDistance = SAGA_SEARCH_DIAMETER; - bestU = SAGA_SEARCH_CENTER, + bestU = SAGA_SEARCH_CENTER; bestV = SAGA_SEARCH_CENTER; uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER; diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp index 3577499399..7ef957b8f9 100644 --- a/engines/saga/itedata.cpp +++ b/engines/saga/itedata.cpp @@ -446,6 +446,27 @@ const char *ITEinterfaceTextStrings[][53] = { NULL, NULL, " \xC7\xE0\xE3\xF0\xF3\xE7\xEA\xE0 " + }, + // French fan translation : ACTIONS + { + "Aller vers", "Examiner", "Prendre", "Parler \205", "Ouvrir", + "Fermer", "Utiliser", "Donner", "Options", "Test", + "Demo", "Aide", "Quitter le Jeu", "Vite", "Lent", + "On", "Off", "Continuer \205 jouer", "Charger", "Sauver", + "Options du Jeu", "Vitesse texte", "Musique", "Son", "Annuler", + "Quitter", "OK", "Mid", "Click", "10%", + "20%", "30%", "40%", "50%", "60%", + "70%", "80%", "90%", "Max", "Quitter le jeu?", + "Chargement OK!", "Nommer la sauvegarde", "Donner %s \205 %s", "Utiliser %s avec %s", + "[Nouvelle sauvegarde]", + "Je ne peux pas prendre ça.", + "Je ne vois rien de sp\202cial.", + "Il n'y a pas la place pour l'ouvrir.", + "Il n'y a pas d'ouverture \205 fermer.", + "Je ne sais pas comment l'utiliser.", + "Sous-titre", + "Que r\202pond Rif?", + "Charger une sauvegarde" } }; @@ -467,6 +488,7 @@ const RawPoint pieceOrigins[PUZZLE_PIECES] = { { 271, 103 } }; +// Objects const char *pieceNames[][PUZZLE_PIECES] = { { "screwdriver", "pliers", "c-clamp", "wood clamp", "level", @@ -482,9 +504,15 @@ const char *pieceNames[][PUZZLE_PIECES] = { "cacciavite", "pinze", "morsa", "morsa da legno", "livella", "spago", "pialla", "martello", "metro a nastro", "accetta", "cesoie", "righello", "sega", "mazza", "pennello" + }, + { + "tournevis", "pince", "\202tau \205 vis", "pince \205 bois", "niveau", + "ficelle", "rabot \205 bois", "marteau", "m\212tre ruban", "hachette", + "ciseaux", "r\212gle", "scie", "maillet", "pinceau" } }; +// hints const char *hintStr[][4] = { { "Check which pieces could fit in each corner first.", @@ -503,6 +531,12 @@ const char *hintStr[][4] = { "Controlla quale angolo ha il minor numero di pezzi che combaciano, e parti da quello.", "Controlla ogni nuovo angolo e lato per ogni pezzo che combacia.", "Non vedo nulla fuori posto." + }, + { + "Trouve d'abord les pi\212ces qui correspondent aux coins.", + "V\202rifie quel coin a le moins de pi\212ces qui correspondent et part de l\205.", + "Evalue chaque coin et bord pour voir les pi\212ces qui collent.", + "Je ne vois rien de mal plac\202." } }; @@ -527,6 +561,13 @@ const char *solicitStr[][NUM_SOLICIT_REPLIES] = { "Umm...Umm...", "Psst! Serve un aiutino?", "Io, sai, l'avrei fatto diversamente." + }, + { + "H\202, Renard! Tu veux un coup de pouce?", + "T'as besoin d'aide?", + "Umm...Umm...", + "Psst! Un indice?", + "Tu sais, j'aurais fait ça autrement." } }; @@ -545,6 +586,11 @@ const char *sakkaStr[][NUM_SAKKA] = { "Hey, non si dovrebbero aiutare i candidati!", "Ragazzi! Questo dovrebbe essere un test!", "Forza ragazzi, non si pu\225!" + }, + { + "H\202, vous n'\210tes pas suppos\202s aider les concurrents!", + "Les gars! C'est suppos\202 être un test!", + "Allez les gars, c'est pas dans les r\212gles!" } }; @@ -569,6 +615,13 @@ const char *whineStr[][NUM_WHINES] = { "Sigh...", "Credo che questa faccenda ti abbia dato alla testa, Sakka!", "Beh, non ricordo regole specifiche contro i suggerimenti." + }, + { + "Rohh, allez Sakka!", + "Un indice ne peut pas faire de mal.", + "Pfff...", + "Je crois que ton carnet te monte \205 la t\210te, Sakka!", + "Bon, je ne me souviens d'aucune r\212gle concernant les indices." } }; @@ -590,6 +643,12 @@ const char *optionsStr[][4] = { "\"Si, grazie. Ne avrei bisogno.\"", "\"No, grazie, voglio provare a risolverlo da solo.\"", "Penso che la tessera %s sia nel posto sbagliato." + }, + { + "\"Je r\202soudrai cette \202nigme plus tard.\"", + "\"Oui, j'aimerais un indice s'il vous plait.\"", + "\"Non merci, je voudrais r\202soudre cela par moi m\210me.\"", + "Je crois que t'as mal plac\202 l'%s." } }; @@ -657,6 +716,29 @@ const IntroDialogue introDialogueCave1[][4] = { "Conoscevano il Segreto del Volo, il Segreto della " "Felicit\205, ed altri segreti oltre ogni nostra " "immaginazione." + } }, + // ----------------------------------------------------- + { { // French fan translation + 0, // cave voice 0 + "Nous voyons le ciel, nous voyons les terres, " + "nous voyons la mer et nous nous demandons: " + "Sommes-nous vraiment seuls?" + }, + { + 1, // cave voice 1 + "Bien avant notre av\212nement, les humains " + "r\202gnaient sur Terre." + }, + { + 2, // cave voice 2 + "Ils firent des choses merveilleuses, et " + "d\202plac\212rent des montagnes." + }, + { + 3, // cave voice 3 + "Ils savaient comment Voler, poss\202daient le " + "secret du Bonheur et d'autres secrets au " + "del\205 de notre imagination." } } }; @@ -706,6 +788,21 @@ const IntroDialogue introDialogueCave2[][3] = { { 6, // cave voice 6 "Siamo i loro figli." + } }, + { { // Fench fan translation + 4, // cave voice 4 + "Les humains connaissaient aussi le secret de " + "la Vie, et l'utilis\212rent pour nous offrir " + "Quatres Dons:" + }, + { + 5, // cave voice 5 + "La Pens\202e, les Sentiments, la Parole et, " + "l'Habilet\202 manuelle." + }, + { + 6, // cave voice 6 + "Nous sommes leurs enfants." } } }; @@ -756,6 +853,21 @@ const IntroDialogue introDialogueCave3[][3] = { "Ci amarono, ed una volta pronti, ci avrebbero " "sicuramente svelato il Segreto della Felicit\205." + } }, + { { // French fan translation + 7, // cave voice 7 + "Ils nous apprirent \205 utiliser nos mains, et \205 " + "parler." + }, + { + 8, // cave voice 8 + "Ils nous apprirent les joies de la pens\202e." + }, + { + 9, // cave voice 9 + "Ils nous aim\212rent, et le moment venu, ils " + "nous auraient s\223rement livr\202 le Secret du " + "Bonheur." } } }; @@ -819,6 +931,26 @@ const IntroDialogue introDialogueCave4[][4] = { 13, // cave voice 13 "E un giorno, condivideremo anche noi lo stesso " "destino?" + } }, + { { // French fan translation + 10, // cave voice 10 + "Aujourd'hui nous voyons le ciel, les terres, et " + "l'eau dont nous sommes les h\202ritiers, Et " + "nous nous demandons: pourquoi sont-ils partis?" // Partis \205 la ligne ????? + }, + { + 11, // cave voice 11 + "Vivent-ils encore, dans les \202toiles? dans les " + "profondeurs des oc\202ans? dans le vent?" + }, + { + 12, // cave voice 12 + "Nous nous demandons: leur destin f\223t-il bon " + "ou mauvais?" + }, + { + 13, // cave voice 13 + "Et aurons-nous un jour un sort identique?" } } }; @@ -826,15 +958,18 @@ const IntroCredit creditsValley[] = { {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Producer"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Produzent"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Produttore"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Producteur"}, {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Walter Hochbrueckner"}, {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Executive Producer"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Ausf\201hrender Produzent"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Produttore Esecutivo"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Producteur Executif"}, {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Robert McNally"}, {Common::UNK_LANG, kITECreditsWyrmKeep, kITECreditsHeader, "2nd Executive Producer"}, {Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsHeader, "Publisher"}, {Common::DE_DEU, kITECreditsNotWyrmKeep, kITECreditsHeader, "Herausgeber"}, {Common::IT_ITA, kITECreditsNotWyrmKeep, kITECreditsHeader, "Editore"}, + {Common::FR_FRA, kITECreditsNotWyrmKeep, kITECreditsHeader, "Editeur"}, {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Jon Van Caneghem"} }; @@ -842,16 +977,20 @@ const IntroCredit creditsTreeHouse1[] = { {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Game Design"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Spielentwurf"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Progetto"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Conception"}, {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Talin, Joe Pearce, Robert McNally"}, {Common::EN_ANY, kITECreditsAny, kITECreditsText, "and Carolly Hauksdottir"}, {Common::DE_DEU, kITECreditsAny, kITECreditsText, "und Carolly Hauksdottir"}, {Common::IT_ITA, kITECreditsAny, kITECreditsText, "e Carolly Hauksdottir"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsText, "et Carolly Hauksdottir"}, {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Screenplay and Dialog"}, {Common::EN_ANY, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein, and Bill Rotsler"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Geschichte und Dialoge"}, {Common::DE_DEU, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein und Bill Rotsler"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Sceneggiatura e Dialoghi"}, - {Common::IT_ITA, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein e Bill Rotsler"} + {Common::IT_ITA, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein e Bill Rotsler"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Sc\202nario et Dialogues"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein et Bill Rotsler"} }; const IntroCredit creditsTreeHouse2[] = { @@ -860,19 +999,23 @@ const IntroCredit creditsTreeHouse2[] = { {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Art"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Grafiken"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Grafica"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Graphismes"}, {Common::UNK_LANG, kITECreditsWyrmKeep, kITECreditsText, "Ed Lacabanne, Glenn Price, April Lee,"}, {Common::UNK_LANG, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price, April Lee,"}, {Common::UNK_LANG, kITECreditsWyrmKeep, kITECreditsText, "Lisa Sample, Brian Dowrick, Reed Waller,"}, {Common::EN_ANY, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey and Talin"}, {Common::DE_DEU, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey und Talin"}, {Common::IT_ITA, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey e Talin"}, + {Common::FR_FRA, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey et Talin"}, {Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsText, "Lisa Iennaco, Brian Dowrick, Reed"}, {Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsText, "Waller, Allison Hershey and Talin"}, {Common::DE_DEU, kITECreditsAny, kITECreditsText, "Waller, Allison Hershey und Talin"}, {Common::IT_ITA, kITECreditsAny, kITECreditsText, "Waller, Allison Hershey e Talin"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsText, "Waller, Allison Hershey et Talin"}, {Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsHeader, "Art Direction"}, {Common::DE_DEU, kITECreditsNotWyrmKeep, kITECreditsHeader, "Grafische Leitung"}, {Common::IT_ITA, kITECreditsNotWyrmKeep, kITECreditsHeader, "Direzione Grafica"}, + {Common::FR_FRA, kITECreditsNotWyrmKeep, kITECreditsHeader, "Directeur Artistique"}, {Common::UNK_LANG, kITECreditsNotWyrmKeep, kITECreditsText, "Allison Hershey"} }; @@ -880,17 +1023,22 @@ const IntroCredit creditsFairePath1[] = { {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Programming"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Programmiert von"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Programmazione"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Programmeurs"}, {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Talin, Walter Hochbrueckner,"}, {Common::EN_ANY, kITECreditsAny, kITECreditsText, "Joe Burks and Robert Wiggins"}, {Common::DE_DEU, kITECreditsAny, kITECreditsText, "Joe Burks und Robert Wiggins"}, {Common::IT_ITA, kITECreditsAny, kITECreditsText, "Joe Burks e Robert Wiggins"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsText, "Joe Burks et Robert Wiggins"}, {Common::EN_ANY, kITECreditsPCCD | kITECreditsWyrmKeep, kITECreditsHeader, "Additional Programming"}, + {Common::FR_FRA, kITECreditsPCCD | kITECreditsWyrmKeep, kITECreditsHeader, "Programmeur Additionnel"}, {Common::EN_ANY, kITECreditsPCCD | kITECreditsWyrmKeep, kITECreditsText, "John Bolton"}, + {Common::FR_FRA, kITECreditsPCCD | kITECreditsWyrmKeep, kITECreditsText, "John Bolton"}, {Common::UNK_LANG, kITECreditsMac, kITECreditsHeader, "Macintosh Version"}, {Common::UNK_LANG, kITECreditsMac, kITECreditsText, "Michael McNally and Robert McNally"}, {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Music and Sound"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Musik und Sound"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Musica e Sonoro"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Musique et Sons"}, {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Matt Nathan"} }; @@ -898,7 +1046,10 @@ const IntroCredit creditsFairePath2[] = { {Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Directed by"}, {Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Regie"}, {Common::IT_ITA, kITECreditsAny, kITECreditsHeader, "Regia"}, - {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Talin"} + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Dirig\202 par"}, + {Common::UNK_LANG, kITECreditsAny, kITECreditsText, "Talin"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Traduction Francaise"}, + {Common::FR_FRA, kITECreditsAny, kITECreditsText, "Ryfatwork, Momo le Yetty et Darknior"} }; } // End of namespace Saga diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h index f9416652bf..1ad73f7137 100644 --- a/engines/saga/itedata.h +++ b/engines/saga/itedata.h @@ -137,11 +137,11 @@ extern const IntroDialogue introDialogueCave2[][3]; extern const IntroDialogue introDialogueCave3[][3]; extern const IntroDialogue introDialogueCave4[][4]; -extern const IntroCredit creditsValley[13]; -extern const IntroCredit creditsTreeHouse1[13]; -extern const IntroCredit creditsTreeHouse2[19]; -extern const IntroCredit creditsFairePath1[15]; -extern const IntroCredit creditsFairePath2[4]; +extern const IntroCredit creditsValley[16]; +extern const IntroCredit creditsTreeHouse1[17]; +extern const IntroCredit creditsTreeHouse2[23]; +extern const IntroCredit creditsFairePath1[20]; +extern const IntroCredit creditsFairePath2[7]; } // End of namespace Saga diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp index 2c9a02beec..1d014052ed 100644 --- a/engines/saga/puzzle.cpp +++ b/engines/saga/puzzle.cpp @@ -73,6 +73,8 @@ Puzzle::Puzzle(SagaEngine *vm) : _vm(vm), _solved(false), _active(false) { _lang = 1; else if (_vm->getLanguage() == Common::IT_ITA) _lang = 2; + else if (_vm->getLanguage() == Common::FR_FRA) + _lang = 3; _hintRqState = kRQNoHint; _hintOffer = 0; diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 649888e7ea..fb5c4a651e 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -506,6 +506,9 @@ const char *SagaEngine::getTextString(int textStringId) { case Common::RU_RUS: lang = 4; break; + case Common::FR_FRA: + lang = 5; + break; default: lang = 0; break; diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 75fa60632c..28ed12275f 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -300,7 +300,7 @@ void Console::postEnter() { if (duckMode) { Common::List<Graphics::PixelFormat> formats; formats.push_back(videoDecoder->getPixelFormat()); - initGraphics(640, 480, true, formats); + initGraphics(640, 480, formats); if (g_system->getScreenFormat().bytesPerPixel != videoDecoder->getPixelFormat().bytesPerPixel) error("Could not switch screen format for the duck video"); @@ -316,7 +316,7 @@ void Console::postEnter() { #ifdef ENABLE_SCI32 // Switch back to 8bpp if we played a duck video if (duckMode) - initGraphics(oldWidth, oldHeight, oldWidth > 320); + initGraphics(oldWidth, oldHeight); #endif _engine->_gfxCursor->kernelShow(); diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h index 92cc45a162..7a1c6f59aa 100644 --- a/engines/sci/engine/features.h +++ b/engines/sci/engine/features.h @@ -128,7 +128,7 @@ public: inline bool hasTransparentPicturePlanes() const { const SciGameId &gid = g_sci->getGameId(); - // NOTE: MGDX is assumed to not have transparent picture planes since it + // MGDX is assumed to not have transparent picture planes since it // was released before SQ6, but this has not been verified since it // cannot be disassembled at the moment (Phar Lap Windows-only release) return getSciVersion() >= SCI_VERSION_2_1_MIDDLE && diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index b7fae2e05d..f9c1edee42 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -77,7 +77,7 @@ reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv) { Ratio scaleX; if (getSciVersion() < SCI_VERSION_2_1_LATE) { - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); scaleX = Ratio(scriptWidth, celObj._xResolution); } @@ -102,9 +102,9 @@ reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv) { reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv) { const bool found = g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]); - // NOTE: MGDX is assumed to use the older kSetNowSeen since it was - // released before SQ6, but this has not been verified since it cannot be - // disassembled at the moment (Phar Lap Windows-only release) + // MGDX is assumed to use the older kSetNowSeen since it was released before + // SQ6, but this has not been verified since it cannot be disassembled at + // the moment (Phar Lap Windows-only release) // (See also getNowSeenRect) if (getSciVersion() <= SCI_VERSION_2_1_EARLY || g_sci->getGameId() == GID_SQ6 || @@ -160,11 +160,12 @@ reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv) { } reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) { - const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer(); - if (buffer.screenWidth < 640 || buffer.screenHeight < 400) - return make_reg(0, 0); + const GfxFrameout *gfxFrameout = g_sci->_gfxFrameout; + if (gfxFrameout->getScreenWidth() < 640 || gfxFrameout->getScreenHeight() < 400) { + return NULL_REG; + } - return make_reg(0, 1); + return TRUE_REG; } reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { @@ -368,10 +369,10 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { const uint16 type = argv[0].toUint16(); reg_t planeObj = argv[1]; int16 seconds = argv[2].toSint16(); - // NOTE: This value seems to indicate whether the transition is an - // “exit†transition (0) or an “enter†transition (-1) for fade - // transitions. For other types of transitions, it indicates a palette - // index value to use when filling the screen. + // This value indicates whether the transition is an "exit" transition (0) + // or an "enter" transition (-1) for fade transitions. For other types of + // transitions, it indicates a palette index value to use when filling the + // screen. int16 back = argv[3].toSint16(); int16 priority = argv[4].toSint16(); int16 animate = argv[5].toSint16(); @@ -404,34 +405,33 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj)); } - // NOTE: The order of planeObj and showStyle are reversed - // because this is how SCI3 called the corresponding method - // on the KernelMgr + // The order of planeObj and showStyle are reversed because this is how + // SSCI3 called the corresponding method on the KernelMgr g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, (ShowStyleType)type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); return s->r_acc; } reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv) { - GuiResourceId resourceId = argv[0].toUint16(); - int16 loopNo = argv[1].toSint16(); - int16 celNo = argv[2].toSint16(); - CelObjView celObj(resourceId, loopNo, celNo); + const GuiResourceId resourceId = argv[0].toUint16(); + const int16 loopNo = argv[1].toSint16(); + const int16 celNo = argv[2].toSint16(); + const CelObjView celObj(resourceId, loopNo, celNo); int16 height = celObj._height; if (getSciVersion() < SCI_VERSION_2_1_LATE) { - height = mulru(height, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight, celObj._yResolution)); + height = mulru(height, Ratio(g_sci->_gfxFrameout->getScriptHeight(), celObj._yResolution)); } return make_reg(0, height); } reg_t kCelWide32(EngineState *s, int argc, reg_t *argv) { - GuiResourceId resourceId = argv[0].toUint16(); - int16 loopNo = argv[1].toSint16(); - int16 celNo = argv[2].toSint16(); - CelObjView celObj(resourceId, loopNo, celNo); + const GuiResourceId resourceId = argv[0].toUint16(); + const int16 loopNo = argv[1].toSint16(); + const int16 celNo = argv[2].toSint16(); + const CelObjView celObj(resourceId, loopNo, celNo); int16 width = celObj._width; if (getSciVersion() < SCI_VERSION_2_1_LATE) { - width = mulru(width, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth, celObj._xResolution)); + width = mulru(width, Ratio(g_sci->_gfxFrameout->getScriptWidth(), celObj._xResolution)); } return make_reg(0, width); } @@ -881,8 +881,8 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { const int16 deltaY = argv[2].toSint16(); const GuiResourceId pictureId = argv[3].toUint16(); const bool animate = argv[4].toUint16(); - // NOTE: speed was accepted as an argument, but then never actually used - // const int16 speed = argc > 5 ? (bool)argv[5].toSint16() : -1; + // argv[5] was some speed argument, but it was not actually used by SSCI, so + // we ignore it here const bool mirrorX = argc > 6 ? (bool)argv[6].toUint16() : false; g_sci->_gfxTransitions32->kernelSetScroll(plane, deltaX, deltaY, pictureId, animate, mirrorX); @@ -1058,9 +1058,9 @@ reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv) { const int16 from = argv[1].toSint16(); const int16 to = argv[2].toSint16(); const int16 base = argv[3].toSint16(); - // NOTE: There is an optional last parameter after `base` - // which was only used by the priority map debugger, which - // does not exist in release versions of SSCI + // There is an optional last parameter after `base` which was only used by + // the priority map debugger which does not exist in release versions of + // SSCI g_sci->_gfxRemap32->remapByRange(color, from, to, base); return s->r_acc; } @@ -1068,9 +1068,9 @@ reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv) { reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv) { const uint8 color = argv[0].toUint16(); const int16 percent = argv[1].toSint16(); - // NOTE: There is an optional last parameter after `percent` - // which was only used by the priority map debugger, which - // does not exist in release versions of SSCI + // There is an optional last parameter after `percent` which was only used + // by the priority map debugger, which does not exist in release versions of + // SSCI g_sci->_gfxRemap32->remapByPercent(color, percent); return s->r_acc; } @@ -1078,9 +1078,9 @@ reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv) { reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv) { const uint8 color = argv[0].toUint16(); const int16 gray = argv[1].toSint16(); - // NOTE: There is an optional last parameter after `gray` - // which was only used by the priority map debugger, which - // does not exist in release versions of SSCI + // There is an optional last parameter after `gray` which was only used by + // the priority map debugger, which does not exist in release versions of + // SSCI g_sci->_gfxRemap32->remapToGray(color, gray); return s->r_acc; } @@ -1089,9 +1089,9 @@ reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv) { const uint8 color = argv[0].toUint16(); const int16 gray = argv[1].toSint16(); const int16 percent = argv[2].toSint16(); - // NOTE: There is an optional last parameter after `percent` - // which was only used by the priority map debugger, which - // does not exist in release versions of SSCI + // There is an optional last parameter after `percent` which was only used + // by the priority map debugger, which does not exist in release versions of + // SSCI g_sci->_gfxRemap32->remapToPercentGray(color, gray, percent); return s->r_acc; } diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index bbfadf1c53..4d171bdd1c 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -384,10 +384,6 @@ reg_t kDoAudioPosition(EngineState *s, int argc, reg_t *argv) { } reg_t kDoAudioRate(EngineState *s, int argc, reg_t *argv) { - // NOTE: In the original engine this would set the hardware - // DSP sampling rate; ScummVM mixer does not need this, so - // we only store the value to satisfy engine compatibility. - if (argc > 0) { const uint16 sampleRate = argv[0].toUint16(); if (sampleRate != 0) { @@ -407,10 +403,6 @@ reg_t kDoAudioGetCapability(EngineState *s, int argc, reg_t *argv) { } reg_t kDoAudioBitDepth(EngineState *s, int argc, reg_t *argv) { - // NOTE: In the original engine this would set the hardware - // DSP bit depth; ScummVM mixer does not need this, so - // we only store the value to satisfy engine compatibility. - if (argc > 0) { const uint16 bitDepth = argv[0].toUint16(); if (bitDepth != 0) { @@ -426,10 +418,6 @@ reg_t kDoAudioMixing(EngineState *s, int argc, reg_t *argv) { } reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv) { - // NOTE: In the original engine this would set the hardware - // DSP stereo output; ScummVM mixer does not need this, so - // we only store the value to satisfy engine compatibility. - if (argc > 0) { const int16 numChannels = argv[0].toSint16(); if (numChannels != 0) { @@ -441,11 +429,6 @@ reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv) { } reg_t kDoAudioPreload(EngineState *s, int argc, reg_t *argv) { - // NOTE: In the original engine this would cause audio - // data for new channels to be preloaded to memory when - // the channel was initialized; we do not need this, so - // we only store the value to satisfy engine compatibility. - if (argc > 0) { g_sci->_audio32->setPreload(argv[0].toUint16()); } @@ -477,11 +460,8 @@ reg_t kDoAudioPanOff(EngineState *s, int argc, reg_t *argv) { } reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) { - // This is used by script 90 of MUMG Deluxe from the main menu to toggle - // the audio language between English and Spanish. - // Basically, it instructs the interpreter to switch the audio resources - // (resource.aud and associated map files) and load them from the "Spanish" - // subdirectory instead. + // Used by script 90 of MUMG Deluxe from the main menu to toggle between + // English and Spanish. const Common::String audioDirectory = s->_segMan->getString(argv[0]); g_sci->getResMan()->changeAudioDirectory(audioDirectory); return s->r_acc; diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 98b2ba2b85..5851b015aa 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -140,7 +140,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // The only argument is the string for the video // HACK: Switch to 16bpp graphics for Cinepak. - initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); + initGraphics(screenWidth, screenHeight, nullptr); if (g_system->getScreenFormat().bytesPerPixel == 1) { warning("This video requires >8bpp color to be displayed, but could not switch to RGB color mode"); @@ -176,7 +176,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // The only known movie to do use this codec is the GK2 demo trailer // If another video turns up that uses Indeo, we may have to add a better // check. - initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); + initGraphics(screenWidth, screenHeight, nullptr); if (g_system->getScreenFormat().bytesPerPixel == 1) { warning("This video requires >8bpp color to be displayed, but could not switch to RGB color mode"); @@ -206,7 +206,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // HACK: Switch back to 8bpp if we played a true color video. // We also won't be copying the screen to the SCI screen... if (g_system->getScreenFormat().bytesPerPixel != 1) - initGraphics(screenWidth, screenHeight, screenWidth > 320); + initGraphics(screenWidth, screenHeight); else if (getSciVersion() < SCI_VERSION_2) { g_sci->_gfxScreen->kernelSyncWithFramebuffer(); g_sci->_gfxPalette16->kernelSyncScreenPalette(); @@ -460,8 +460,8 @@ reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv) { } reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv) { - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); Common::Rect blackoutArea; blackoutArea.left = MAX<int16>(0, argv[0].toSint16()); diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h index ffc4ac2f3d..9ab6ca34da 100644 --- a/engines/sci/engine/object.h +++ b/engines/sci/engine/object.h @@ -169,7 +169,6 @@ public: } } - // NOTE: In real engine, -info- is treated as byte size void clearInfoSelectorFlag(infoSelectorFlags flag) { if (getSciVersion() == SCI_VERSION_3) { _infoSelectorSci3 &= ~flag; diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index b0fa31a1bb..a7ebe0b4df 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -801,7 +801,7 @@ void SciBitmap::saveLoadWithSerializer(Common::Serializer &s) { s.syncBytes(_data, _dataSize); if (s.isLoading()) { - _buffer = Buffer(getWidth(), getHeight(), getPixels()); + _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8()); } } #endif @@ -866,25 +866,25 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) { } #ifdef ENABLE_SCI32 -static void saveLoadPalette32(Common::Serializer &s, Palette *const palette) { - s.syncAsUint32LE(palette->timestamp); - for (int i = 0; i < ARRAYSIZE(palette->colors); ++i) { - s.syncAsByte(palette->colors[i].used); - s.syncAsByte(palette->colors[i].r); - s.syncAsByte(palette->colors[i].g); - s.syncAsByte(palette->colors[i].b); +static void saveLoadPalette32(Common::Serializer &s, Palette &palette) { + s.syncAsUint32LE(palette.timestamp); + for (int i = 0; i < ARRAYSIZE(palette.colors); ++i) { + s.syncAsByte(palette.colors[i].used); + s.syncAsByte(palette.colors[i].r); + s.syncAsByte(palette.colors[i].g); + s.syncAsByte(palette.colors[i].b); } } -static void saveLoadOptionalPalette32(Common::Serializer &s, Palette **const palette) { +static void saveLoadOptionalPalette32(Common::Serializer &s, Common::ScopedPtr<Palette> &palette) { bool hasPalette = false; if (s.isSaving()) { - hasPalette = (*palette != nullptr); + hasPalette = palette; } s.syncAsByte(hasPalette); if (hasPalette) { if (s.isLoading()) { - *palette = new Palette; + palette.reset(new Palette); } saveLoadPalette32(s, *palette); } @@ -899,14 +899,11 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) { ++_version; for (int i = 0; i < kNumCyclers; ++i) { - delete _cyclers[i]; - _cyclers[i] = nullptr; + _cyclers[i].reset(); } - delete _varyTargetPalette; - _varyTargetPalette = nullptr; - delete _varyStartPalette; - _varyStartPalette = nullptr; + _varyTargetPalette.reset(); + _varyStartPalette.reset(); } s.syncAsSint16LE(_varyDirection); @@ -928,14 +925,14 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) { if (g_sci->_features->hasLatePaletteCode() && s.getVersion() >= 41) { s.syncAsSint16LE(_gammaLevel); - saveLoadPalette32(s, &_sourcePalette); + saveLoadPalette32(s, _sourcePalette); ++_version; _needsUpdate = true; _gammaChanged = true; } - saveLoadOptionalPalette32(s, &_varyTargetPalette); - saveLoadOptionalPalette32(s, &_varyStartPalette); + saveLoadOptionalPalette32(s, _varyTargetPalette); + saveLoadOptionalPalette32(s, _varyStartPalette); // _nextPalette is not saved by SSCI @@ -944,14 +941,15 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) { bool hasCycler = false; if (s.isSaving()) { - cycler = _cyclers[i]; + cycler = _cyclers[i].get(); hasCycler = (cycler != nullptr); } s.syncAsByte(hasCycler); if (hasCycler) { if (s.isLoading()) { - _cyclers[i] = cycler = new PalCycler; + cycler = new PalCycler; + _cyclers[i].reset(cycler); } s.syncAsByte(cycler->fromColor); diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 162d97e15b..96ac9388cc 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -99,6 +99,10 @@ static const char *const selectorNameTable[] = { "play", // system selector "number", // system selector "setScript", // system selector + "setCycle", // system selector + "setStep", // system selector + "cycleSpeed", // system selector + "handsOff", // system selector "localize", // Freddy Pharkas "put", // Police Quest 1 VGA "say", // Quest For Glory 1 VGA @@ -131,7 +135,6 @@ static const char *const selectorNameTable[] = { "iconV", // Phant2 "update", // Phant2 "xOff", // Phant2 - "setCycle", // GK1 "fore", // KQ7 "back", // KQ7 "font", // KQ7 @@ -145,6 +148,7 @@ static const char *const selectorNameTable[] = { "priority", // RAMA "plane", // RAMA "state", // RAMA + "getSubscriberObj", // RAMA #endif NULL }; @@ -167,6 +171,10 @@ enum ScriptPatcherSelectors { SELECTOR_play, SELECTOR_number, SELECTOR_setScript, + SELECTOR_setCycle, + SELECTOR_setStep, + SELECTOR_cycleSpeed, + SELECTOR_handsOff, SELECTOR_localize, SELECTOR_put, SELECTOR_say, @@ -200,7 +208,6 @@ enum ScriptPatcherSelectors { SELECTOR_iconV, SELECTOR_update, SELECTOR_xOff, - SELECTOR_setCycle, SELECTOR_fore, SELECTOR_back, SELECTOR_font, @@ -213,7 +220,8 @@ enum ScriptPatcherSelectors { SELECTOR_saveFilePtr, SELECTOR_priority, SELECTOR_plane, - SELECTOR_state + SELECTOR_state, + SELECTOR_getSubscriberObj #endif }; @@ -2289,10 +2297,10 @@ static const uint16 lighthouseFlagResetPatch[] = { }; // When doing a system check on the portal computer in the lighthouse, the game -// counts up to 1024MB, one megabyte at a time. In the original engine, this -// count speed would be video speed dependent, but with our frame rate -// throttler, it takes 17 seconds. So, replace this slowness with a much faster -// POST that is more accurate to the original game. +// counts up to 1024MB, one megabyte at a time. In SSCI, this count speed would +// be video speed dependent, but with our frame rate throttler, it takes 17 +// seconds. So, replace this slowness with a much faster POST that is more +// accurate to the original game. // Applies to at least: US English 1.0c static const uint16 lighthouseMemoryCountSignature[] = { SIG_MAGICDWORD, @@ -2474,6 +2482,130 @@ static const SciScriptPatcherEntry longbowSignatures[] = { }; // =========================================================================== +// Leisure Suit Larry 1 (Spanish) +// +// It seems originally the Spanish version of Larry 1 used some beta code at +// least for the man wearing a barrel, who walks around in front of the casino. +// The script inside the resource files even uses a class, that does not exist +// inside those resource files, which causes a hard error. +// The patch files included with the Spanish version (300.scr,300.tex, 927.scr) +// add this class, but at least inside ScummVM a write to a non-existent selector +// happens right after the player tries to buy an apple from that man. +// +// In the original English releases (2.0+2.1) this was handled differently. +// Which is why this script patch changes that code to work just like in the English release. +// +// Attention: for at least some release of this game, view 302 (man wearing a barrel) is fully +// broken! Which also causes a crash. The original interpreter crashes as well. +// The only way to fix this is to dump that view from another release of Larry 1 +// and then use the view patch file on this release. +// +// Applies to at least: Spanish floppy +// Responsible method: sBuyApple::changeScript(2) +// Fixes bug: #10240 +static const uint16 larry1SignatureBuyApple[] = { + // end of state 0 + 0x35, 0x01, // ldi 01 + 0x65, 0x10, // aTop cycles + 0x32, SIG_UINT16(0x0248), // jmp [ret] + 0x3c, // dup + 0x35, 0x01, // ldi 01 + 0x1a, // eq? + 0x30, SIG_UINT16(0x0007), // bnt [step 2 check] + // state 1 code + 0x35, 0x01, // ldi 01 + 0x65, 0x10, // aTop cycles + 0x32, SIG_UINT16(0x023a), // jmp [ret] + 0x3c, // dup + 0x35, 0x02, // ldi 02 + 0x1a, // eq? + 0x30, SIG_UINT16(0x0036), // bnt [step 3 check] + // state 2 code + 0x35, 0x02, // ldi 02 + 0x38, SIG_UINT16(0x0091), // pushi setCycle + 0x78, // push1 + 0x51, 0x18, // class Walk + 0x36, // push + 0x38, SIG_UINT16(0x0126), // pushi setAvoider + 0x78, // push1 + 0x51, SIG_ADDTOOFFSET(+1), // class PAvoider (original 0x25, w/ patch file 0x6d) + 0x36, // push + 0x38, SIG_UINT16(0x0116), // pushi setMotion + SIG_MAGICDWORD, + 0x39, 0x04, // pushi 04 + 0x51, 0x24, // class PolyPath + 0x36, // push + 0x39, 0x04, // pushi 04 + 0x76, // push0 + 0x72, SIG_UINT16(0x0f4e), // lofsa aAppleMan + 0x4a, 0x04, // send 04 + 0x36, // push + 0x35, 0x1d, // ldi 1Dh + 0x02, // add + 0x36, // push + 0x39, 0x03, // pushi 03 + 0x76, // push0 + 0x72, SIG_UINT16(0x0f4e), // lofsa aAppleMan + 0x4a, 0x04, // send 04 + 0x36, // push + 0x7c, // pushSelf + 0x81, 0x00, // lag global[0] + 0x4a, 0x18, // send 18h + 0x32, SIG_UINT16(0x01fd), // jmp [ret] + SIG_END +}; + +static const uint16 larry1PatchBuyApple[] = { + PATCH_ADDTOOFFSET(+11), + 0x2f, 0xf3, // bt [jump to end of step 1 code], saves 8 bytes + 0x3c, // dup + 0x35, 0x02, // ldi 02 + 0x1a, // eq? + 0x31, 0x3f, // bnt [step 3 check] + 0x38, PATCH_UINT16(0x00e1), // pushi distanceTo + 0x78, // push1 + 0x72, PATCH_UINT16(0x0f4e), // lofsa sAppleMan + 0x36, // push + 0x81, 0x00, // lag global[0] + 0x4a, 0x06, // send 06 + 0x36, // push + 0x35, 0x1e, // ldi 1Eh + 0x1e, // gt? + 0x31, 0xdb, // bnt [jump to end of step 1 code] + 0x38, PATCH_SELECTOR16(setCycle), // pushi setCycle + 0x78, // push1 + 0x51, 0x18, // class Walk + 0x36, // push + 0x38, PATCH_SELECTOR16(setMotion), // pushi setMotion + 0x39, 0x04, // pushi 04 + 0x51, 0x24, // class PolyPath + 0x36, // push + 0x39, 0x04, // pushi 04 + 0x76, // push0 + 0x72, PATCH_UINT16(0x0f4e), // lofsa aAppleMan + 0x4a, 0x04, // send 04 + 0x36, // push + 0x35, 0x1d, // ldi 1Dh + 0x02, // add + 0x36, // push + 0x39, 0x03, // pushi 03 + 0x76, // push0 + 0x72, PATCH_UINT16(0x0f4e), // lofsa aAppleMan + 0x4a, 0x04, // send 04 + 0x36, // push + 0x7c, // pushSelf + 0x81, 0x00, // lag global[0] + 0x4a, 0x12, // send 12h + PATCH_END +}; + +// script, description, signature patch +static const SciScriptPatcherEntry larry1Signatures[] = { + { true, 300, "Spanish: buy apple from barrel man", 1, larry1SignatureBuyApple, larry1PatchBuyApple }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + +// =========================================================================== // Leisure Suit Larry 2 // On the plane, Larry is able to wear the parachute. This grants 4 points. // In early versions of LSL2, it was possible to get "unlimited" points by @@ -3726,8 +3858,37 @@ static const uint16 phant1RatPatch[] = { PATCH_END }; +// In Phantasmagoria the cursor's hover state will not trigger on any of the +// buttons in the main menu after returning to the main menu from a game, or +// when choosing "Quit" on the main menu and then cancelling the quit in the +// confirmation dialogue, until another button has been hovered and unhovered +// once. +// This happens because the quit confirmation dialogue creates its own +// event handling loop which prevents the main event loop from handling the +// cursor leaving the button (which would reset global 193 to 0), and the +// dialogue does not reset global193 itself, so it remains at 2 until a new +// button gets hovered and unhovered. +// There is not enough space in the confirmation dialogue code to add a reset +// of global 193, so we just remove the check entirely, since it is only used +// to avoid resetting the cursor's view on every mouse movement, and this +// button type is only used on the main menu and the in-game control panel. +// +// Applies to at least: English CD +static const uint16 phant1RedQuitCursorSignature[] = { + SIG_MAGICDWORD, + 0x89, 0xc1, // lsg $c1 + 0x35, 0x02, // ldi 02 + SIG_END +}; + +static const uint16 phant1RedQuitCursorPatch[] = { + 0x33, 0x05, // jmp [past global193 check] + PATCH_END +}; + // script, description, signature patch static const SciScriptPatcherEntry phantasmagoriaSignatures[] = { + { true, 23, "make cursor red after clicking quit", 1, phant1RedQuitCursorSignature, phant1RedQuitCursorPatch }, { true, 901, "fix invalid array construction", 1, sci21IntArraySignature, sci21IntArrayPatch }, { true, 1111, "ignore audio settings from save game", 1, phant1SavedVolumeSignature, phant1SavedVolumePatch }, { true, 20200, "fix broken rat init in sEnterFromAlcove", 1, phant1RatSignature, phant1RatPatch }, @@ -5722,14 +5883,89 @@ static const uint16 qfg4BenchmarkPatch[] = { PATCH_END }; -// script, description, signature patch +// Right at the start of the game inside room 800, when automatically sliding down a slope +// an error may happen inside Grooper::doit caused by a timing issue. +// +// We delay a bit, so that hero::cycler should always be set. +// +// Applies to: English CD, English floppy, German floppy +// Responsible method: sFallsBackSide::changeState (script 803) +// Fixes bug #9801 +static const uint16 qfg4SlidingDownSlopeSignature[] = { + 0x87, 0x01, // lap param[1] + 0x65, SIG_ADDTOOFFSET(+1), // aTop state + 0x36, // push + 0x3c, // dup + 0x35, 0x00, // ldi 00 + 0x1a, // eq? + 0x31, 0x30, // bnt [skip state 0] + 0x38, SIG_SELECTOR16(handsOff), // pushi handsOff + 0x76, // push0 + 0x81, 0x01, // lag global[1] + 0x4a, SIG_UINT16(0x0004), // send 04 + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16(cycleSpeed), // pushi cycleSpeed + 0x76, // push0 + 0x81, 0x00, // lag global[0] + 0x4a, SIG_UINT16(0x0004), // send 04 + 0xa3, 0x00, // sal local[0] + 0x38, SIG_SELECTOR16(setStep), // pushi setStep + 0x7a, // push2 + 0x78, // push1 + 0x78, // push1 + 0x38, SIG_SELECTOR16(setMotion), // pushi setMotion + 0x38, SIG_UINT16(0x0004), // pushi 04 + 0x51, SIG_ADDTOOFFSET(+1), // class PolyPath + 0x36, // push + 0x39, 0x49, // pushi $49 + 0x39, 0x50, // pushi $50 + 0x7c, // pushSelf + 0x81, 0x00, // lag global[0] + 0x4a, SIG_UINT16(0x0014), // send $14 + SIG_END +}; + +static const uint16 qfg4SlidingDownSlopePatch[] = { + PATCH_ADDTOOFFSET(+5), + 0x2f, 0x34, // bt [skip state 0] + 0x38, PATCH_SELECTOR16(handsOff), // pushi handsOff + 0x76, // push0 + 0x81, 0x01, // lag global[1] + 0x4a, PATCH_UINT16(0x0004), // send 04 + + 0x78, // push1 + 0x39, 0x20, // pushi $20 + 0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, $2 + 0x38, PATCH_SELECTOR16(setStep), // pushi setStep + 0x7a, // push2 + 0x78, // push1 + 0x78, // push1 + 0x38, PATCH_SELECTOR16(setMotion), // pushi setMotion + 0x38, PATCH_UINT16(0x0004), // pushi 04 + 0x51, PATCH_GETORIGINALBYTE(+44), // class PolyPath + 0x36, // push + 0x39, 0x49, // pushi $49 + 0x39, 0x50, // pushi $50 + 0x7c, // pushSelf + 0x81, 0x00, // lag global[0] + 0x38, PATCH_SELECTOR16(cycleSpeed), // pushi cycleSpeed + 0x76, // push0 + 0x4a, PATCH_UINT16(0x0018), // send $18 + 0xa3, 0x00, // sal local[0] + 0x3a, // toss + 0x48, // ret + PATCH_END +}; + +// script, description, signature patch static const SciScriptPatcherEntry qfg4Signatures[] = { - { true, 1, "disable volume reset on startup", 1, sci2VolumeResetSignature, sci2VolumeResetPatch }, - { true, 1, "disable video benchmarking", 1, qfg4BenchmarkSignature, qfg4BenchmarkPatch }, - { true, 83, "fix incorrect array type", 1, qfg4TrapArrayTypeSignature, qfg4TrapArrayTypePatch }, - { true, 64990, "increase number of save games (1/2)", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 }, - { true, 64990, "increase number of save games (2/2)", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 }, - { true, 64990, "disable change directory button", 1, sci2ChangeDirSignature, sci2ChangeDirPatch }, + { true, 1, "disable volume reset on startup", 1, sci2VolumeResetSignature, sci2VolumeResetPatch }, + { true, 1, "disable video benchmarking", 1, qfg4BenchmarkSignature, qfg4BenchmarkPatch }, + { true, 83, "fix incorrect array type", 1, qfg4TrapArrayTypeSignature, qfg4TrapArrayTypePatch }, + { true, 803, "fix sliding down slope", 1, qfg4SlidingDownSlopeSignature, qfg4SlidingDownSlopePatch }, + { true, 64990, "increase number of save games (1/2)", 1, sci2NumSavesSignature1, sci2NumSavesPatch1 }, + { true, 64990, "increase number of save games (2/2)", 1, sci2NumSavesSignature2, sci2NumSavesPatch2 }, + { true, 64990, "disable change directory button", 1, sci2ChangeDirSignature, sci2ChangeDirPatch }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -6411,13 +6647,12 @@ static const uint16 ramaBenchmarkPatch[] = { }; // RAMA uses a custom save game format that game scripts read and write -// manually. The save game format serialises object references, which in the -// original engine could be done just by writing int16s (since object references -// were just 16-bit indexes), but in ScummVM we have to write the full 32-bit -// reg_t. We hijack kFileIOReadWord/kFileIOWriteWord to do this for us, but we -// need the game to agree to use those kFileIO calls instead of doing raw reads -// and creating its own numbers, as it tries to do here in -// `SaveManager::readWord`. +// manually. The save game format serialises object references, which SSCI could +// be done just by writing int16s (since object references were just 16-bit +// indexes), but in ScummVM we have to write the full 32-bit reg_t. We hijack +// kFileIOReadWord/kFileIOWriteWord to do this for us, but we need the game to +// agree to use those kFileIO calls instead of doing raw reads and creating its +// own numbers, as it tries to do here in `SaveManager::readWord`. static const uint16 ramaSerializeRegTSignature1[] = { SIG_MAGICDWORD, 0x38, SIG_SELECTOR16(newWith), // pushi $10b (newWith) @@ -6438,6 +6673,31 @@ static const uint16 ramaSerializeRegTPatch1[] = { PATCH_END }; +// When restoring a NukeTimer client, the game makes a self-call to +// `NukeTimer::getSubscriberObj` from `NukeTimer::serialize`, but forgets to +// pass a required argument. In SSCI this happens to work because the value on +// the stack where the first argument should be is the `getSubscriberObj` +// selector, so it evaluates to true, but currently ScummVM defaults +// uninitialised param reads to 0 so the game was following the wrong path and +// breaking. +// Applies to at least: US English +static const uint16 ramaNukeTimerSignature[] = { + 0x7e, SIG_ADDTOOFFSET(+2), // line whatever + SIG_MAGICDWORD, + 0x38, SIG_SELECTOR16(getSubscriberObj), // pushi $3ca (getSubscriberObj) + 0x76, // push0 + 0x54, SIG_UINT16(0x04), // self 4 + SIG_END +}; + +static const uint16 ramaNukeTimerPatch[] = { + 0x38, PATCH_SELECTOR16(getSubscriberObj), // pushi $3ca (getSubscriberObj) + 0x78, // push1 + 0x38, PATCH_UINT16(0x01), // pushi 1 (wasting bytes) + 0x54, PATCH_UINT16(0x06), // self 6 + PATCH_END +}; + // When opening a datacube on the pocket computer, `DocReader::init` will try // to perform arithmetic on a pointer to `thighComputer::plane` and then use the // resulting value as the priority for the DocReader. This happened to work in @@ -6483,10 +6743,11 @@ static const uint16 ramaChangeDirPatch[] = { }; static const SciScriptPatcherEntry ramaSignatures[] = { - { true, 55, "fix bad DocReader::init priority calculation", 1, ramaDocReaderInitSignature, ramaDocReaderInitPatch }, - { true, 85, "fix SaveManager to use normal readWord calls", 1, ramaSerializeRegTSignature1, ramaSerializeRegTPatch1 }, - { true, 64908, "disable video benchmarking", 1, ramaBenchmarkSignature, ramaBenchmarkPatch }, - { true, 64990, "disable change directory button", 1, ramaChangeDirSignature, ramaChangeDirPatch }, + { true, 55, "fix bad DocReader::init priority calculation", 1, ramaDocReaderInitSignature, ramaDocReaderInitPatch }, + { true, 85, "fix SaveManager to use normal readWord calls", 1, ramaSerializeRegTSignature1, ramaSerializeRegTPatch1 }, + { true, 201, "fix crash restoring save games using NukeTimer", 1, ramaNukeTimerSignature, ramaNukeTimerPatch }, + { true, 64908, "disable video benchmarking", 1, ramaBenchmarkSignature, ramaBenchmarkPatch }, + { true, 64990, "disable change directory button", 1, ramaChangeDirSignature, ramaChangeDirPatch }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -7364,6 +7625,9 @@ void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) { case GID_LONGBOW: signatureTable = longbowSignatures; break; + case GID_LSL1: + signatureTable = larry1Signatures; + break; case GID_LSL2: signatureTable = larry2Signatures; break; diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index bae1fb4757..b4dcbef0d4 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -260,11 +260,11 @@ reg_t disassemble(EngineState *s, reg32_t pos, const Object *obj, bool printBWTa retval.incOffset(2); } - const uint32 offset = findOffset(param_value, script_entity, retval.getOffset()); + const uint32 offset = findOffset(param_value, script_entity, pos.getOffset() + bytecount); reg_t addr; addr.setSegment(retval.getSegment()); addr.setOffset(offset); - if (getSciVersion() == SCI_VERSION_3 && !s->_segMan->isObject(addr)) { + if (!s->_segMan->isObject(addr)) { debugN("\t\"%s\"", s->_segMan->derefString(addr)); } else { debugN("\t%s", s->_segMan->getObjectName(addr)); @@ -277,11 +277,11 @@ reg_t disassemble(EngineState *s, reg32_t pos, const Object *obj, bool printBWTa if (opsize) { int8 offset = (int8)scr[retval.getOffset()]; retval.incOffset(1); - debugN("\t%02x [%04x]", 0xff & offset, kOffsetMask & (retval.getOffset() + offset)); + debugN("\t%02x [%04x]", 0xff & offset, kOffsetMask & (pos.getOffset() + bytecount + offset)); } else { int16 offset = (int16)READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]); retval.incOffset(2); - debugN("\t%04x [%04x]", 0xffff & offset, kOffsetMask & (retval.getOffset() + offset)); + debugN("\t%04x [%04x]", 0xffff & offset, kOffsetMask & (pos.getOffset() + bytecount + offset)); } break; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index aa70d5f838..45666336e9 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -614,7 +614,25 @@ static inline void setChar(const SegmentRef &ref, uint offset, byte value) { val->setOffset((val->getOffset() & 0xff00) | value); } -// TODO: memcpy, strcpy and strncpy could maybe be folded into a single function +template <bool STRING> +static void forwardCopy(byte *dest, const byte *src, size_t n) { + const bool zeroPad = (STRING && n != 0xFFFFFFFFU); + + while (n) { + --n; + const byte b = *src++; + *dest++ = b; + if (STRING && b == '\0') { + break; + } + } + if (zeroPad) { + while (n--) { + *dest++ = '\0'; + } + } +} + void SegManager::strncpy(reg_t dest, const char* src, size_t n) { SegmentRef dest_r = dereference(dest); if (!dest_r.isValid()) { @@ -624,11 +642,7 @@ void SegManager::strncpy(reg_t dest, const char* src, size_t n) { if (dest_r.isRaw) { - // raw -> raw - if (n == 0xFFFFFFFFU) - ::strcpy((char *)dest_r.raw, src); - else - ::strncpy((char *)dest_r.raw, src, n); + forwardCopy<true>(dest_r.raw, (const byte *)src, n); } else { // raw -> non-raw for (uint i = 0; i < n; i++) { @@ -711,7 +725,7 @@ void SegManager::memcpy(reg_t dest, const byte* src, size_t n) { if (dest_r.isRaw) { // raw -> raw - ::memcpy((char *)dest_r.raw, src, n); + forwardCopy<false>(dest_r.raw, src, n); } else { // raw -> non-raw for (uint i = 0; i < n; i++) @@ -767,7 +781,7 @@ void SegManager::memcpy(byte *dest, reg_t src, size_t n) { if (src_r.isRaw) { // raw -> raw - ::memcpy(dest, src_r.raw, n); + forwardCopy<false>(dest, src_r.raw, n); } else { // non-raw -> raw for (uint i = 0; i < n; i++) { diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index d6e2dd3c24..14744e3e39 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -977,7 +977,7 @@ public: _data = (byte *)malloc(other._dataSize); memcpy(_data, other._data, other._dataSize); if (_dataSize) { - _buffer = Buffer(getWidth(), getHeight(), getPixels()); + _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8()); } _gc = other._gc; } @@ -998,7 +998,7 @@ public: _data = (byte *)malloc(other._dataSize); memcpy(_data, other._data, _dataSize); if (_dataSize) { - _buffer = Buffer(getWidth(), getHeight(), getPixels()); + _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8()); } _gc = other._gc; @@ -1032,7 +1032,7 @@ public: setXResolution(xResolution); setYResolution(yResolution); - _buffer = Buffer(getWidth(), getHeight(), getPixels()); + _buffer.init(getWidth(), getHeight(), getWidth(), getPixels(), Graphics::PixelFormat::createFormatCLUT8()); } inline int getRawSize() const { @@ -1102,12 +1102,11 @@ public: BITMAP_PROPERTY(32, DataOffset, 24); - // NOTE: This property is used as a "magic number" for - // validating that a block of memory is a valid bitmap, - // and so is always set to the size of the header. + // This property is used as a "magic number" for validating that a block of + // memory is a valid bitmap, and so is always set to the size of the header. BITMAP_PROPERTY(32, UncompressedDataOffset, 28); - // NOTE: This property always seems to be zero + // This property always seems to be zero in SSCI BITMAP_PROPERTY(32, ControlOffset, 32); inline uint16 getXResolution() const { diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 42c71c4d31..d2f6a14797 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -165,6 +165,10 @@ void Kernel::mapSelectors() { FIND_SELECTOR(vanishingY); FIND_SELECTOR(iconIndex); FIND_SELECTOR(select); + FIND_SELECTOR(handsOff); + FIND_SELECTOR(setStep); + FIND_SELECTOR(setMotion); + FIND_SELECTOR(cycleSpeed); #ifdef ENABLE_SCI32 FIND_SELECTOR(data); diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 2debd3204d..2c1a34e938 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -131,6 +131,11 @@ struct SelectorCache { Selector iconIndex; ///< Used to index icon bar objects Selector select; + Selector handsOff; + Selector setStep; + Selector setMotion; + Selector cycleSpeed; + #ifdef ENABLE_SCI32 Selector data; // Used by Array()/String() Selector picture; // Used to hold the picture ID for SCI32 pictures diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 9c271db250..ecdf0cb695 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -27,7 +27,7 @@ #include "sci/engine/script_patches.h" #include "sci/engine/workarounds.h" -#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, NULL, 0, { WORKAROUND_NONE, 0 } } +#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, NULL, 0, 0, { WORKAROUND_NONE, 0 } } namespace Sci { @@ -72,23 +72,23 @@ static const uint16 sig_arithmetic_ecoq2_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry arithmeticWorkarounds[] = { - { GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", sig_arithmetic_camelot_1, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #5237 - { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", sig_arithmetic_ecoq2_1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #4939, Spanish version - bug #5750 - { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #5124 - { GID_GK1, 800,64992, 0, "Fwd", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 - { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_add: while bidding in Bridge, an object ("Bid") is added to an object in another segment ("hand3") - { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl - { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version) - { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #5101 - { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. - { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria - { GID_QFG1VGA, 301, 928, 0, "Blink", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object - { GID_QFG2, 200, 200, 0, "astro", "messages", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152 - { GID_QFG3, 780, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692 - { GID_QFG4, 710,64941, 0, "RandCycle", "doit", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves - { GID_TORIN, 51400,64928, 0, "Blink", "init", NULL, 0, { WORKAROUND_FAKE, 1 } }, // op_div: when Lycentia knocks Torin out after he removes her collar + { GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", sig_arithmetic_camelot_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #5237 + { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", sig_arithmetic_ecoq2_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #4939, Spanish version - bug #5750 + { GID_FANMADE, 516, 983, 0, "Wander", "setTarget", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #5124 + { GID_GK1, 800,64992, 0, "Fwd", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7 + { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // op_add: while bidding in Bridge, an object ("Bid") is added to an object in another segment ("hand3") + { GID_ICEMAN, 199, 977, 0, "Grooper", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl + { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version) + { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #5101 + { GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue. + { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria + { GID_QFG1VGA, 301, 928, 0, "Blink", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object + { GID_QFG2, 200, 200, 0, "astro", "messages", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer - bug #5152 + { GID_QFG3, 780, 999, 0, "", "export 6", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // op_add: trying to talk to yourself at the top of the giant tree - bug #6692 + { GID_QFG4, 710,64941, 0, "RandCycle", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves + { GID_TORIN, 51400,64928, 0, "Blink", "init", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // op_div: when Lycentia knocks Torin out after he removes her collar SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -269,240 +269,243 @@ static const uint16 sig_uninitread_sq1_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry uninitializedReadWorkarounds[] = { - { GID_CAMELOT, 40, 40, 0, "Rm40", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when looking at the ground at the pool of Siloam - bug #6401 - { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", NULL, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #5143). Moves the cursor to the view with the ID returned (in this case, the robot hand) - { GID_CNICK_KQ, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // checkers/backgammon, like in hoyle 3 - temps 504 and 505 - bug #6255 - { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3 - { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #6426 (same as the theDoubleCube::make workaround for Hoyle 3) - { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", NULL, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled (same as the theDoubleCube::accept workaround for Hoyle 3) - { GID_CNICK_LAURABOW,500, 0, 1, "<no name>", "<noname446>", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Yacht, like in hoyle 3 - temps 504 and 505 - bug #6424 - { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (same as the gcWindow workaround for Hoyle 3) - { GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #6429 (same as the dominoHand2 workaround for Hoyle 3) - { GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 - { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game - { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms - { GID_FANMADE, 516, 979, 0, "", "export 0", NULL, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos - { GID_FANMADE, 528, 990, 0, "GDialog", "doit", NULL, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #5116 - { GID_FANMADE, 488, 1, 0, "RoomScript", "doit", sig_uninitread_fanmade_1, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #5335 - { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu - { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu - { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game - bug #5232 - { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking - { GID_GK1, -1, 64937, -1, "GKControls", "dispatchEvent", NULL, 6, { WORKAROUND_FAKE, 0 } }, // when using keyboard navigation (tab) in the game settings and hitting 'enter' when over a slider - { GID_GK2, -1, 11, 0, "", "export 10", NULL, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts - { GID_GK2, -1, 11, 0, "", "export 10", NULL, 4, { WORKAROUND_FAKE, 0 } }, // called during the game - { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", NULL, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts - { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game - { GID_HOYLE1, 3, 16, 0, "", "export 0", sig_uninitread_hoyle1_1, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #5299 - { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #5512 - { GID_HOYLE3, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505 - { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #5042 - { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 - { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "make", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "accept", NULL, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled - { GID_HOYLE4, -1, 0, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #5132 - { GID_HOYLE4, 910, 18, 0, NULL, "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #5213 - { GID_HOYLE4, 910, 910, 0, NULL, "setup", NULL, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #5132 - { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", NULL, 3, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always) - { GID_HOYLE4, 700, 710, 1, "BridgeStrategyPlay", "checkSplitTops", NULL, 10, { WORKAROUND_FAKE, 0 } }, // while playing bridge, objects LeadReturn_Trump, SecondSeat_Trump, ThirdSeat_Trump and others - bug #5794 - { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", NULL, -1, { WORKAROUND_FAKE, 0 } }, // sometimes while playing bridge, temp var 3, 17 and others, objects LeadReturn_Trump, ThirdSeat_Trump and others - { GID_HOYLE4, 700, 730, 1, "BridgeDefense", "beatTheirBest", NULL, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge - { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always), temp var 11, 24, 27, 46, 75, objects compete_tree, compwe_tree, other1_tree, b1 - bugs #5663 and #5794 - { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 0, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614 - { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 1, { WORKAROUND_FAKE, 1 } }, // see above, Text-control saves its coordinates to temp[0] and temp[1], Edit-control adjusts to those uninitialized temps, who by accident were left over from the Text-control - { GID_HOYLE4, 300, 300, 0, "", "export 2", sig_uninitread_hoyle4_1, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts - { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", NULL, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #5665 - { GID_HOYLE4, 500, 17, 1, "Character", "say", NULL, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662 - { GID_HOYLE4, 800, 870, 0, "EuchreStrategy", "thinkLead", NULL, 0, { WORKAROUND_FAKE, 0 } }, // while playing Euchre, happens at least on 2nd or 3rd turn - bug #6602 - { GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", NULL, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603 - { GID_HOYLE5, -1, 14, -1, NULL, "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // dragging the sliders in game settings - { GID_HOYLE5, -1, 64937, -1, NULL, "select", NULL, 7, { WORKAROUND_FAKE, 0 } }, // clicking the "control" and "options" buttons in the icon bar - { GID_HOYLE5, -1, 64937, -1, "IconBar", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // clicking on any button in the icon bar - { GID_HOYLE5, 300, 300, 0, "", "export 2", sig_uninitread_hoyle5_1, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts - { GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", NULL, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #5241 - { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", NULL, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0 - { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", NULL, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon - { GID_JONES, 1, 232, 0, "weekendText", "draw", sig_uninitread_jones_1, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game - { GID_JONES, 1, 255, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14 - { GID_JONES, 764, 255, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14 - //{ GID_KQ5, -1, 0, 0, "", "export 29", NULL, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #4961 + { GID_CAMELOT, 40, 40, 0, "Rm40", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when looking at the ground at the pool of Siloam - bug #6401 + { GID_CASTLEBRAIN, 280, 280, 0, "programmer", "dispatchEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0xf } }, // pressing 'q' on the computer screen in the robot room, and closing the help dialog that pops up (bug #5143). Moves the cursor to the view with the ID returned (in this case, the robot hand) + { GID_CASTLEBRAIN, 320, 325, 0, "word", "dispatchEvent", NULL, 14, 15, { WORKAROUND_FAKE, 0 } }, // holding down enter key during the word search puzzle, temp 14 and 15 - bug #9783 + { GID_CNICK_KQ, -1, 0, 1, "Character", "say", NULL, 504, 505, { WORKAROUND_FAKE, 0 } }, // checkers/backgammon, like in hoyle 3 - temps 504 and 505 - bug #6255 + { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3 + { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", NULL, 5, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #6426 (same as the theDoubleCube::make workaround for Hoyle 3) + { GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", NULL, 9, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled (same as the theDoubleCube::accept workaround for Hoyle 3) + { GID_CNICK_LAURABOW,500, 0, 1, "<no name>", "<noname446>", NULL, 504, 505, { WORKAROUND_FAKE, 0 } }, // Yacht, like in hoyle 3 - temps 504 and 505 - bug #6424 + { GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (same as the gcWindow workaround for Hoyle 3) + { GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #6429 (same as the dominoHand2 workaround for Hoyle 3) + { GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 + { GID_CNICK_LSL, 250, 250, 0, "increase", "handleEvent", NULL, 2, 2, { WORKAROUND_FAKE, 0 } }, // when increasing own bet for blackjack - bug #10184 + { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game + { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms + { GID_FANMADE, 516, 979, 0, "", "export 0", NULL, 20, 20, { WORKAROUND_FAKE, 0 } }, // Happens in Grotesteing after the logos + { GID_FANMADE, 528, 990, 0, "GDialog", "doit", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // Happens in Cascade Quest when closing the glossary - bug #5116 + { GID_FANMADE, 488, 1, 0, "RoomScript", "doit", sig_uninitread_fanmade_1, 1, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #5335 + { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", NULL, 5, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", NULL, 5, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", NULL, 0, 1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game, all temps - 0+1 - bug #5232 + { GID_GK1, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking + { GID_GK1, -1, 64937, -1, "GKControls", "dispatchEvent", NULL, 6, 6, { WORKAROUND_FAKE, 0 } }, // when using keyboard navigation (tab) in the game settings and hitting 'enter' when over a slider + { GID_GK2, -1, 11, 0, "", "export 10", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts + { GID_GK2, -1, 11, 0, "", "export 10", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // called during the game + { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts + { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", NULL, 2, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game + { GID_HOYLE1, 3, 16, 0, "", "export 0", sig_uninitread_hoyle1_1, 3, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #5299 + { GID_HOYLE1, -1, 997, 0, "MenuBar", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When changing game speed settings - bug #5512 + { GID_HOYLE3, -1, 0, 1, "Character", "say", NULL, 504, 505, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505 + { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu + { GID_HOYLE3, 100, 100, 0, "dominoHand2", "cue", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #5042 + { GID_HOYLE3, 100, 110, 0, "OKButton", "doit", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430 + { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "make", NULL, 5, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled + { GID_HOYLE3, 300, 303, 0, "theDoubleCube", "accept", NULL, 9, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled + { GID_HOYLE4, -1, 0, 0, NULL, "open", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when selecting "Control" from the menu (temp vars 0-3) - bug #5132 + { GID_HOYLE4, 910, 18, 0, NULL, "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #5213 + { GID_HOYLE4, 910, 910, 0, NULL, "setup", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // when selecting "Tutorial" from the main menu - bug #5132 + { GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always) + { GID_HOYLE4, 700, 710, 1, "BridgeStrategyPlay", "checkSplitTops", NULL, 10, 10, { WORKAROUND_FAKE, 0 } }, // while playing bridge, objects LeadReturn_Trump, SecondSeat_Trump, ThirdSeat_Trump and others - bug #5794 + { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // sometimes while playing bridge, temp var 3, 17 and others, objects LeadReturn_Trump, ThirdSeat_Trump and others + { GID_HOYLE4, 700, 730, 1, "BridgeDefense", "beatTheirBest", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge + { GID_HOYLE4, 700, -1, 1, "Code", "doit", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when placing a bid in bridge (always), temp var 11, 24, 27, 46, 75, objects compete_tree, compwe_tree, other1_tree, b1 - bugs #5663 and #5794 + { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 0, 0, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614 + { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", NULL, 1, 1, { WORKAROUND_FAKE, 1 } }, // see above, Text-control saves its coordinates to temp[0] and temp[1], Edit-control adjusts to those uninitialized temps, who by accident were left over from the Text-control + { GID_HOYLE4, 300, 300, 0, "", "export 2", sig_uninitread_hoyle4_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts + { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #5665 + { GID_HOYLE4, 500, 17, 1, "Character", "say", NULL, 504, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662 + { GID_HOYLE4, 800, 870, 0, "EuchreStrategy", "thinkLead", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // while playing Euchre, happens at least on 2nd or 3rd turn - bug #6602 + { GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", NULL, 408, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603 + { GID_HOYLE5, -1, 14, -1, NULL, "select", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // dragging the sliders in game settings + { GID_HOYLE5, -1, 64937, -1, NULL, "select", NULL, 7, 7, { WORKAROUND_FAKE, 0 } }, // clicking the "control" and "options" buttons in the icon bar + { GID_HOYLE5, -1, 64937, -1, "IconBar", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // clicking on any button in the icon bar + { GID_HOYLE5, 300, 300, 0, "", "export 2", sig_uninitread_hoyle5_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts + { GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", NULL, 58, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #5241 + { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", NULL, 3, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0 + { GID_ISLANDBRAIN, 180, 190, 0, "word", "dispatchEvent", NULL, 14, 15, { WORKAROUND_FAKE, 0 } }, // holding down enter key during the word search puzzle, temps 14 and 15 + { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon + { GID_JONES, 1, 232, 0, "weekendText", "draw", sig_uninitread_jones_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game + { GID_JONES, 1, 255, 0, "", "export 0", NULL, 13, 14, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14 + { GID_JONES, 764, 255, 0, "", "export 0", NULL, 13, 14, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14 + //{ GID_KQ5, -1, 0, 0, "", "export 29", NULL, 3, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #4961 // ^^ shouldn't be needed anymore, we got a script patch instead (kq5PatchCdHarpyVolume) - { GID_KQ5, 25, 25, 0, "rm025", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is - { GID_KQ5, 55, 55, 0, "helpScript", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198 - { GID_KQ5, -1, 755, 0, "gcWin", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version - { GID_KQ6, -1, 30, 0, "rats", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #4958, #4998, #5017 - { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", NULL, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953 - { GID_KQ6, 500, 500, 0, "rm500", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast - { GID_KQ6, 520, 520, 0, "rm520", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle - { GID_KQ6, -1, 903, 0, "controlWin", "open", NULL, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc) - { GID_KQ6, -1, 907, 0, "tomato", "doVerb", NULL, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #5331 - { GID_KQ6, -1, 928, 0, NULL, "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) - { GID_KQ7, -1, 64996, 0, "User", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key - { GID_KQ7, 2450, 2450, 0, "exBridge", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // called when walking up to the throne in the cave in chapter 2 - { GID_KQ7, 2450, 2450, 0, "maliciaComes", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when malicia appears at the southeast exit of the main chamber near the end of chapter 2 - { GID_KQ7, 5300, 5302, 0, "putOnMask", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // in chapter 3, after using the mask on Valanice, click the jackalope hair in inventory - bug Trac#9759 - { GID_KQ7, 6060, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // after entering the harp crystal in chapter 5 - { GID_LAURABOW, 37, 0, 0, "CB1", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs - bug #5084 - { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", NULL, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971 - { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", NULL, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu - { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", NULL, 1, { WORKAROUND_FAKE, 0x7fff } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy). Must be big, as the game scripts perform lt on it and start deleting journal entries - bugs #4979, #5026 - { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", NULL, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum - bug #5197 - { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #5028 - { GID_LAURABOW2, -1, 928, 0, NULL, "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) - { GID_LIGHTHOUSE, -1, 17, 0, NULL, "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when operating the joystick in the puzzle to lower the bridge at the entrance to the workshop, or the joystick that moves the robotic arm in the mini-sub - { GID_LONGBOW, -1, 0, 0, "Longbow", "restart", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #5244 - { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room - { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", sig_uninitread_longbow_1, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #5035 - { GID_LSL1, 250, 250, 0, "increase", "handleEvent", NULL, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet - { GID_LSL1, 720, 720, 0, "rm720", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // age check room - { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", NULL, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #5034 - { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203) - { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's') - { GID_LSL6, 820, 82, 0, "", "export 0", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence - bug #5103 - { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory - { GID_LSL6, -1, 928, -1, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class - { GID_LSL6HIRES, -1, 85, 0, "LL6Inv", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when creating a new game - { GID_LSL6HIRES, -1, 85, 0, "washcloth", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when interacting with the wet washcloth in the inventory - Trac#9811 - { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area - { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game - { GID_LSL7, -1, 64029, 0, "oMessager", "nextMsg", NULL, 4, { WORKAROUND_FAKE, 0 } }, // when running the game with subtitles only - { GID_LSL7, -1, 64017, 0, "oFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // demo version, when it starts, and whenever the player chooses to go to the "Strip Liar's Dice" mini game - { GID_LSL7, -1, 64017, 0, "oActorFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // after an NPC walks off the left side of the screen at the Clothing Optional Pool - { GID_LSL7, -1, 64892, 0, "oEventHandler", "killAllEventHogs", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when looking at the swordfish in the kitchen - { GID_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", NULL, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game - bug #5224 - { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #5269 - { GID_MOTHERGOOSE256, 90, 90, 0, "introScript", "changeState", NULL, 65, { WORKAROUND_FAKE, 0 } }, // SCI1(CD): At the very end, after the game is completed and restarted - bug #5626 - { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", NULL, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #5294 - { GID_MOTHERGOOSEHIRES,-1,64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later - { GID_MOTHERGOOSEHIRES,-1,64950, -1, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // see above - { GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154 - { GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper - { GID_PHANTASMAGORIA, -1, 64921, -1, "Print", "addEdit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // When trying to use the game debugger's flag setting command - { GID_PHANTASMAGORIA2,-1, 64926, -1, "Thumb", "action", NULL, 1, { WORKAROUND_FAKE, 0 } }, // When dragging one of the volume sliders and releasing the mouse button over the +/- buttons - { GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not - { GID_PQ4, 170, 170, -1, "hideAndSeek", "handleEvent", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when clicking to move right while still moving left during the Emo shootout - bug #9847 - { GID_PQ4, 275, 64964, -1, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when Sherry walks out of the morgue on day 3 - { GID_PQ4, 240, 64964, -1, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when encountering Sherry and the reporter outside the morgue at the end of day 3 - { GID_PQ4, 2010, 2010, -1, "enemyShip1", "cantBeHere", NULL, 7, { WORKAROUND_FAKE, 0 } }, // when crashing into a UFO in the asteroids minigame in the Shortstop Bar - { GID_PQ4, 2010, 2010, -1, "enemyShip2", "cantBeHere", NULL, 7, { WORKAROUND_FAKE, 0 } }, // when crashing into a UFO in the asteroids minigame in the Shortstop Bar - { GID_PQ4, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // floppy: when walking within room 395 (city hall, day 3) - { GID_PQSWAT, -1, 64950, 0, NULL, "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using any menus in-game - { GID_PQSWAT, -1, 73, 0, "theLashInterface", "transmit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Clicking the transmit button in LASH - { GID_PQSWAT, 2990, 2990, 0, "talkToSchienbly", "changeState", NULL, 1, { WORKAROUND_FAKE, 0 } }, // When the video of Schienbly talking for the first time ends - { GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout - { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309 - { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515 - { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory - { GID_QFG2, -1, 79, 0, "TryToMoveTo", "onTarget", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when throwing pot at air elemental, happens when client coordinates are the same as airElemental coordinates. happened to me right after room change - bug #6859 - { GID_QFG2, -1, 701, -1, "Alley", "at", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #5019 & #5106 - { GID_QFG2, -1, 990, 0, "Restore", "doit", NULL, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present - { GID_QFG2, 260, 260, 0, "abdulS", "changeState", sig_uninitread_qfg2_1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #5153, temps 1 and 2 - { GID_QFG2, 260, 260, 0, "jabbarS", "changeState", sig_uninitread_qfg2_1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #5164, temps 1 and 2 - { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #5566 - { GID_QFG2, -1, 700, 0, NULL, "showSign", NULL, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #5627, #5635 - { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", NULL, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #5277. Must be non-zero, otherwise the prize is awarded twice - bug #6160 - { GID_QFG3, 140, 140, 0, "rm140", "init", sig_uninitread_qfg3_1, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #5163 - { GID_QFG3, 330, 330, -1, "Teller", "doChild", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #5033, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #5148, temp 1) - { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #5169 - { GID_QFG3, 470, 470, -1, "rm470", "notify", NULL, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #5165 - { GID_QFG3, 470, 470, -1, "<invalid name>", "notify", NULL, 0, { WORKAROUND_FAKE, 0 } }, // same as previous, with rm470::name used for temp storage by fan patches added by GOG - { GID_QFG3, 490, 490, -1, "computersMove", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #5167 - { GID_QFG3, 490, 490, -1, "computersMove", "changeState", sig_uninitread_qfg3_2, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game - { GID_QFG3, 851, 32, -1, "ProjObj", "doit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #5282 - { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", NULL, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen - { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", NULL, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu - { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", NULL, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes - { GID_QFG4, 380, 80, -1, "myButton", "select", NULL, 2, { WORKAROUND_FAKE, 1 } }, // CD version, when clicking on a puzzle piece for the keyhole scrambled picture puzzle - { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave - { GID_QFG4, 780, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // CD version, walking down to the monastery basement - { GID_QFG4, 800, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar - { GID_RAMA, -1, 64950, -1, NULL, "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the main game interface, or the main menu buttons, or mousing over things in the main game window - { GID_RAMA, -1, 64923, -1, "Inset", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When receiving a message on the pocket computer at the start of the game - { GID_RAMA, 6107, 6107, -1, NULL, "doVerb", sig_uninitread_rama_2, 0, { WORKAROUND_FAKE, 0 } }, // When pressing keys on the final console in the Avian Lair - { GID_RAMA, 6110, 6110, -1, "securityKeypad", "newRoom", sig_uninitread_rama_1, 0, { WORKAROUND_FAKE, 0 } }, // When entering the correct key combination on the security console in the Avian Lair - { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo - { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning - { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning - { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll - { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", NULL, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc... - { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", NULL, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc... - { GID_SQ1, 103, 103, 0, "hand", "internalEvent", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2 - { GID_SQ1, -1, 703, 0, "", "export 1", NULL, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser - { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", sig_uninitread_sq1_1, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) - { GID_SQ4, -1, 398, 0, "showBox", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin - { GID_SQ4, -1, 928, -1, "Narrator", "startText", NULL, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously - { GID_SQ4, 395, 395, -1, "fromStoreScript", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // CD: happens when shoplifting in Galaxy Galleria - bug #10229 - { GID_SQ4, -1, 708, -1, "exitBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button in the sq4 hintbook - bug #6447 - { GID_SQ4, -1, 708, -1, "", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button... in Russian version - bug #5573 - { GID_SQ4, -1, 708, -1, "prevBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button in the sq4 hintbook - bug #6447 - { GID_SQ4, -1, 708, -1, "\xA8\xE6\xE3 \xAD\xA0\xA7\xA0\xA4.", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button... in Russian version - bug #5573 - { GID_SQ4, -1, 708, -1, "nextBut", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button in the sq4 hintbook - bug #6447 - { GID_SQ4, -1, 708, -1, ".", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button... in Russian version - bug #5573 - { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", NULL, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #5112 - { GID_SQ6, -1, 0, 0, "SQ6", "init", NULL, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100) - { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places - { GID_SQ6, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game - { GID_SQ6, 210, 210, 0, "buttonSecret", "doVerb", NULL, 0, { WORKAROUND_FAKE, 0 } }, // after winning the first round of stooge fighter 3 - { GID_SQ6, -1, 64994, -1, "Game", "restore", NULL, 1, { WORKAROUND_FAKE, 0 } }, // When trying to load an invalid save game from the launcher - { GID_SQ6, -1, 64921, -1, "Print", "addEdit", NULL, 1, { WORKAROUND_FAKE, 0 } }, // When trying to use the game debugger's flag setting command - { GID_TORIN, -1, 64017, 0, "oFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version - { GID_TORIN, -1, 64029, 0, "oMessager", "nextMsg", NULL, 3, { WORKAROUND_FAKE, 0 } }, // start of chapter one, or when running with subtitles only - { GID_TORIN, -1, 64892, 0, "oEventHandler", "killAllEventHogs", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when pressing the hint button when the game is about to transition to a new room (race condition) - Trac#9810 - { GID_TORIN, 20100, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // going down the cliff at the first screen of chapter 2 (washing area) - { GID_TORIN, 61100, 64888, 0, "Torin", "autorestore", NULL, 11, { WORKAROUND_FAKE, 0 } }, // after attempting to restore a save game saved with the wrong game version + { GID_KQ5, 25, 25, 0, "rm025", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is + { GID_KQ5, 55, 55, 0, "helpScript", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198 + { GID_KQ5, -1, 755, 0, "gcWin", "open", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version + { GID_KQ6, -1, 30, 0, "rats", "changeState", NULL, 0, 5, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 0-5, all temps!) - bugs #4958, #4998, #5017 + { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953 + { GID_KQ6, 500, 500, 0, "rm500", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast + { GID_KQ6, 520, 520, 0, "rm520", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle + { GID_KQ6, -1, 903, 0, "controlWin", "open", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc) + { GID_KQ6, -1, 907, 0, "tomato", "doVerb", NULL, 2, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #5331 + { GID_KQ6, -1, 928, 0, NULL, "startText", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) + { GID_KQ7, -1, 64996, 0, "User", "handleEvent", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key + { GID_KQ7, 2450, 2450, 0, "exBridge", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // called when walking up to the throne in the cave in chapter 2 + { GID_KQ7, 2450, 2450, 0, "maliciaComes", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when malicia appears at the southeast exit of the main chamber near the end of chapter 2 + { GID_KQ7, 5300, 5302, 0, "putOnMask", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // in chapter 3, after using the mask on Valanice, click the jackalope hair in inventory - bug Trac#9759 + { GID_KQ7, 6060, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // after entering the harp crystal in chapter 5 + { GID_LAURABOW, 37, 0, 0, "CB1", "doit", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when going up the stairs - bug #5084 + { GID_LAURABOW, -1, 967, 0, "myIcon", "cycle", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // having any portrait conversation coming up - initial bug #4971 + { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", NULL, 5, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", NULL, 1, 1, { WORKAROUND_FAKE, 0x7fff } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy). Must be big, as the game scripts perform lt on it and start deleting journal entries - bugs #4979, #5026 + { GID_LAURABOW2, -1, 90, 1, "MuseumActor", "init", NULL, 6, 6, { WORKAROUND_FAKE, 0 } }, // Random actors in museum - bug #5197 + { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #5028 + { GID_LAURABOW2, -1, 928, 0, NULL, "startText", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // gets caused by Text+Audio support (see script patcher) + { GID_LIGHTHOUSE, -1, 17, 0, NULL, "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when operating the joystick in the puzzle to lower the bridge at the entrance to the workshop, or the joystick that moves the robotic arm in the mini-sub + { GID_LONGBOW, -1, 0, 0, "Longbow", "restart", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #5244 + { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When giving an answer using the druid hand sign code in any room + { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", sig_uninitread_longbow_1, 1, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #5035 + { GID_LSL1, 250, 250, 0, "increase", "handleEvent", NULL, 2, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet + { GID_LSL1, 720, 720, 0, "rm720", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // age check room + { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #5034 + { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203) + { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", NULL, 1, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's') + { GID_LSL6, 820, 82, 0, "", "export 0", NULL, 0, 326, { WORKAROUND_FAKE, 0 } }, // when touching the electric fence (temp 193 for English release, temp 293 for French/German, temp 313 for Spanish - used for setting the loop of the death animation), it's not setting it for this death - bug #5103 + { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory + { GID_LSL6, -1, 928, -1, "Narrator", "startText", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class + { GID_LSL6HIRES, -1, 85, 0, "LL6Inv", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when creating a new game + { GID_LSL6HIRES, -1, 85, 0, "washcloth", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when interacting with the wet washcloth in the inventory - Trac#9811 + { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area + { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // during the game + { GID_LSL7, -1, 64029, 0, "oMessager", "nextMsg", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // when running the game with subtitles only + { GID_LSL7, -1, 64017, 0, "oFlags", "clear", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // demo version, when it starts, and whenever the player chooses to go to the "Strip Liar's Dice" mini game + { GID_LSL7, -1, 64017, 0, "oActorFlags", "clear", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // after an NPC walks off the left side of the screen at the Clothing Optional Pool + { GID_LSL7, -1, 64892, 0, "oEventHandler", "killAllEventHogs", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when looking at the swordfish in the kitchen + { GID_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", NULL, 5, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game - bug #5224 + { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #5269 + { GID_MOTHERGOOSE256, 90, 90, 0, "introScript", "changeState", NULL, 65, 65, { WORKAROUND_FAKE, 0 } }, // SCI1(CD): At the very end, after the game is completed and restarted - bug #5626 + { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", NULL, 367, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #5294 + { GID_MOTHERGOOSEHIRES,-1,64950, -1, "Feature", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later + { GID_MOTHERGOOSEHIRES,-1,64950, -1, "View", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // see above + { GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154 + { GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper + { GID_PHANTASMAGORIA, -1, 64921, -1, "Print", "addEdit", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // When trying to use the game debugger's flag setting command + { GID_PHANTASMAGORIA2,-1, 64926, -1, "Thumb", "action", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // When dragging one of the volume sliders and releasing the mouse button over the +/- buttons + { GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not + { GID_PQ4, 170, 170, -1, "hideAndSeek", "handleEvent", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when clicking to move right while still moving left during the Emo shootout - bug #9847 + { GID_PQ4, 275, 64964, -1, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when Sherry walks out of the morgue on day 3 + { GID_PQ4, 240, 64964, -1, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when encountering Sherry and the reporter outside the morgue at the end of day 3 + { GID_PQ4, 2010, 2010, -1, "enemyShip1", "cantBeHere", NULL, 7, 7, { WORKAROUND_FAKE, 0 } }, // when crashing into a UFO in the asteroids minigame in the Shortstop Bar + { GID_PQ4, 2010, 2010, -1, "enemyShip2", "cantBeHere", NULL, 7, 7, { WORKAROUND_FAKE, 0 } }, // when crashing into a UFO in the asteroids minigame in the Shortstop Bar + { GID_PQ4, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // floppy: when walking within room 395 (city hall, day 3) + { GID_PQSWAT, -1, 64950, 0, NULL, "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Using any menus in-game + { GID_PQSWAT, -1, 73, 0, "theLashInterface", "transmit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Clicking the transmit button in LASH + { GID_PQSWAT, 2990, 2990, 0, "talkToSchienbly", "changeState", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // When the video of Schienbly talking for the first time ends + { GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout + { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309 + { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515 + { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory + { GID_QFG2, -1, 79, 0, "TryToMoveTo", "onTarget", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when throwing pot at air elemental, happens when client coordinates are the same as airElemental coordinates. happened to me right after room change - bug #6859 + { GID_QFG2, -1, 701, -1, "Alley", "at", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #5019 & #5106 + { GID_QFG2, -1, 990, 0, "Restore", "doit", NULL, 364, 364, { WORKAROUND_FAKE, 0 } }, // when pressing enter in restore dialog w/o any saved games present + { GID_QFG2, 260, 260, 0, "abdulS", "changeState", sig_uninitread_qfg2_1, -1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Abdul is about to enter the house (where you have to hide in the wardrobe), bug #5153, temps 1 and 2 + { GID_QFG2, 260, 260, 0, "jabbarS", "changeState", sig_uninitread_qfg2_1, -1, -1, { WORKAROUND_FAKE, 0 } }, // During the thief's first mission (in the house), just before Jabbar is about to enter the house (where you have to hide in the wardrobe), bug #5164, temps 1 and 2 + { GID_QFG2, 500, 500, 0, "lightNextCandleS", "changeState", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // Inside the last room, while Ad Avis performs the ritual to summon the genie - bug #5566 + { GID_QFG2, -1, 700, 0, NULL, "showSign", NULL, 10, 10, { WORKAROUND_FAKE, 0 } }, // Occurs sometimes when reading a sign in Raseir, Shapeir et al - bugs #5627, #5635 + { GID_QFG3, 510, 510, 0, "awardPrize", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #5277. Must be non-zero, otherwise the prize is awarded twice - bug #6160 + { GID_QFG3, 140, 140, 0, "rm140", "init", sig_uninitread_qfg3_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // when importing a character and selecting the previous profession - bug #5163 + { GID_QFG3, 330, 330, -1, "Teller", "doChild", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" (bug #5033, temp 1) or "Tarna" (temp 0), or when clicking on yourself and saying "Greet" (bug #5148, temp 1) + { GID_QFG3, 700, 700, -1, "monsterIsDead", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // in the jungle, after winning any fight, bug #5169 + { GID_QFG3, 470, 470, -1, "rm470", "notify", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // closing the character screen in the Simbani village in the room with the bridge, bug #5165 + { GID_QFG3, 470, 470, -1, "<invalid name>", "notify", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // same as previous, with rm470::name used for temp storage by fan patches added by GOG + { GID_QFG3, 490, 490, -1, "computersMove", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #5167 + { GID_QFG3, 490, 490, -1, "computersMove", "changeState", sig_uninitread_qfg3_2, 4, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game + { GID_QFG3, 851, 32, -1, "ProjObj", "doit", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #5282 + { GID_QFG4, -1, 15, -1, "charInitScreen", "dispatchEvent", NULL, 5, 5, { WORKAROUND_FAKE, 0 } }, // floppy version, when viewing the character screen + { GID_QFG4, -1, 64917, -1, "controlPlane", "setBitmap", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, when entering the game menu + { GID_QFG4, -1, 64917, -1, "Plane", "setBitmap", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // floppy version, happens sometimes in fight scenes + { GID_QFG4, 380, 80, -1, "myButton", "select", NULL, 2, 2, { WORKAROUND_FAKE, 1 } }, // CD version, when clicking on a puzzle piece for the keyhole scrambled picture puzzle + { GID_QFG4, 520, 64950, 0, "fLake2", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // CD version, at the lake, when meeting the Rusalka and attempting to leave + { GID_QFG4, 780, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // CD version, walking down to the monastery basement + { GID_QFG4, 800, 64950, 0, "View", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // CD version, in the room with the spider pillar, when climbing on the pillar + { GID_RAMA, -1, 64950, -1, NULL, "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the main game interface, or the main menu buttons, or mousing over things in the main game window + { GID_RAMA, -1, 64923, -1, "Inset", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When receiving a message on the pocket computer at the start of the game + { GID_RAMA, 6107, 6107, -1, NULL, "doVerb", sig_uninitread_rama_2, 0, 0, { WORKAROUND_FAKE, 0 } }, // When pressing keys on the final console in the Avian Lair + { GID_RAMA, 6110, 6110, -1, "securityKeypad", "newRoom", sig_uninitread_rama_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // When entering the correct key combination on the security console in the Avian Lair + { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", NULL, 2, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo + { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning + { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning + { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll + { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc... + { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", NULL, 4, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc... + { GID_SQ1, 103, 103, 0, "hand", "internalEvent", NULL, -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2 + { GID_SQ1, -1, 703, 0, "", "export 1", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser + { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", sig_uninitread_sq1_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) + { GID_SQ4, -1, 398, 0, "showBox", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin + { GID_SQ4, -1, 928, -1, "Narrator", "startText", NULL, 1000, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously + { GID_SQ4, 395, 395, -1, "fromStoreScript", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // CD: happens when shoplifting in Galaxy Galleria - bug #10229 + { GID_SQ4, -1, 708, -1, "exitBut", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, "", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "close" button... in Russian version - bug #5573 + { GID_SQ4, -1, 708, -1, "prevBut", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, "\xA8\xE6\xE3 \xAD\xA0\xA7\xA0\xA4.", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "previous" button... in Russian version - bug #5573 + { GID_SQ4, -1, 708, -1, "nextBut", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button in the sq4 hintbook - bug #6447 + { GID_SQ4, -1, 708, -1, ".", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // Floppy: happens, when looking at the "next" button... in Russian version - bug #5573 + { GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #5112 + { GID_SQ6, -1, 0, 0, "SQ6", "init", NULL, 2, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100) + { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places + { GID_SQ6, -1, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // during the game + { GID_SQ6, 210, 210, 0, "buttonSecret", "doVerb", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // after winning the first round of stooge fighter 3 + { GID_SQ6, -1, 64994, -1, "Game", "restore", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // When trying to load an invalid save game from the launcher + { GID_SQ6, -1, 64921, -1, "Print", "addEdit", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // When trying to use the game debugger's flag setting command + { GID_TORIN, -1, 64017, 0, "oFlags", "clear", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version + { GID_TORIN, -1, 64029, 0, "oMessager", "nextMsg", NULL, 3, 3, { WORKAROUND_FAKE, 0 } }, // start of chapter one, or when running with subtitles only + { GID_TORIN, -1, 64892, 0, "oEventHandler", "killAllEventHogs", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // when pressing the hint button when the game is about to transition to a new room (race condition) - Trac#9810 + { GID_TORIN, 20100, 64964, 0, "DPath", "init", NULL, 1, 1, { WORKAROUND_FAKE, 0 } }, // going down the cliff at the first screen of chapter 2 (washing area) + { GID_TORIN, 61100, 64888, 0, "Torin", "autorestore", NULL, 11, 11, { WORKAROUND_FAKE, 0 } }, // after attempting to restore a save game saved with the wrong game version SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kAbs_workarounds[] = { - { GID_HOYLE1, 1, 1, 0, "room1", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers - { GID_HOYLE1, 2, 2, 0, "room2", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers - { GID_HOYLE1, 3, 3, 0, "room3", "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers - { GID_QFG1VGA, -1, -1, 0, NULL, "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - { GID_QFG3 , -1, -1, 0, NULL, "doit", NULL, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - bugs #6042, #6043 + { GID_HOYLE1, 1, 1, 0, "room1", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers + { GID_HOYLE1, 2, 2, 0, "room2", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers + { GID_HOYLE1, 3, 3, 0, "room3", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers + { GID_QFG1VGA, -1, -1, 0, NULL, "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch + { GID_QFG3 , -1, -1, 0, NULL, "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch - bugs #6042, #6043 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kArraySetElements_workarounds[] = { - { GID_GK1, 302, 64918, 0, "Str", "callKernel", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when erasing a letter on the wall in St Louis Cemetery - { GID_PHANTASMAGORIA, -1, 64918, 0, "Str", "callKernel", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when starting a new game and selecting a chapter above 1, or when quitting the chase (in every chase room), or when completing chase successfully + { GID_GK1, 302, 64918, 0, "Str", "callKernel", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when erasing a letter on the wall in St Louis Cemetery + { GID_PHANTASMAGORIA, -1, 64918, 0, "Str", "callKernel", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when starting a new game and selecting a chapter above 1, or when quitting the chase (in every chase room), or when completing chase successfully SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kArrayFill_workarounds[] = { - { GID_PQ4, 540, 64918, 0, "Str", "callKernel", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when clicking on Hate Crimes in the computer on day 2 + { GID_PQ4, 540, 64918, 0, "Str", "callKernel", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when clicking on Hate Crimes in the computer on day 2 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kCelHigh_workarounds[] = { - { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 - { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects - { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 - { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 + { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 + { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects + { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 + { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kCelWide_workarounds[] = { - { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 - { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects - { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 - { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 - { GID_LSL6HIRES, -1, 94, 0, "ll6ControlPanel", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when opening the "controls" panel from the main menu, the third argument is missing + { GID_KQ5, -1, 255, 0, "deathIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when getting beaten up in the inn and probably more, called with 2nd parameter as object - bug #5049 + { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects + { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 + { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 + { GID_LSL6HIRES, -1, 94, 0, "ll6ControlPanel", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when opening the "controls" panel from the main menu, the third argument is missing SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDeleteKey_workarounds[] = { - { GID_HOYLE4, 300, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // restarting hearts, while tray is shown - bug #6604 - { GID_HOYLE4, 500, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // restarting cribbage, while tray is shown - bug #6604 - { GID_HOYLE4, 975, 999, 0, "handleEventList", "delete", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // going back to gamelist from hearts/cribbage, while tray is shown - bug #6604 + { GID_HOYLE4, 300, 999, 0, "handleEventList", "delete", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // restarting hearts, while tray is shown - bug #6604 + { GID_HOYLE4, 500, 999, 0, "handleEventList", "delete", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // restarting cribbage, while tray is shown - bug #6604 + { GID_HOYLE4, 975, 999, 0, "handleEventList", "delete", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // going back to gamelist from hearts/cribbage, while tray is shown - bug #6604 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -527,13 +530,13 @@ static const uint16 sig_kDeviceInfo_Fanmade_2[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDeviceInfo_workarounds[] = { - { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant - { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_2, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant - { GID_FANMADE, -1, 994, 0, "Black", "save", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_IGNORE, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice (Black Cauldron Remake) - { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_1, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant - { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_2, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant + { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant + { GID_FANMADE, -1, 994, 1, "Game", "save", sig_kDeviceInfo_Fanmade_2, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant + { GID_FANMADE, -1, 994, 0, "Black", "save", sig_kDeviceInfo_Fanmade_1, 0, 0, { WORKAROUND_IGNORE, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice (Black Cauldron Remake) + { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 01 variant + { GID_FANMADE, -1, 994, 1, "Game", "restore", sig_kDeviceInfo_Fanmade_2, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // In fanmade games (SCI Studio), this is called with one parameter for CurDevice LDI 0001 variant SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -568,110 +571,110 @@ static const uint16 sig_kDisplay_sq4_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDisplay_workarounds[] = { - { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object - { GID_LONGBOW, 95, 95, 0, "countDown", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during title screen "Robin Hood! Your bow is needed" - { GID_LONGBOW, 220, 220, 0, "moveOn", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during second room "Outwit and outfight..." - { GID_LONGBOW, 210, 210, 0, "mama", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during third room "Fall under the spell..." - { GID_LONGBOW, 320, 320, 0, "flyin", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during fourth room "Conspiracies, love..." - { GID_PQ2, 23, 23, 0, "rm23Script", "elements", sig_kDisplay_pq2_1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of files in jail - 0x75 as id - bug #5223 - { GID_PQ2, 23, 23, 0, "rm23Script", "handleEvent", sig_kDisplay_pq2_1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of file in jail - 0x75 as id - bug #9670 - { GID_QFG1, 11, 11, 0, "battle", "init", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id - { GID_SQ4, 397, 0, 0, "", "export 12", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227 - { GID_SQ4, 391, 391, 0, "doCatalog", "changeState", sig_kDisplay_sq4_1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object - { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object + { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object + { GID_LONGBOW, 95, 95, 0, "countDown", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during title screen "Robin Hood! Your bow is needed" + { GID_LONGBOW, 220, 220, 0, "moveOn", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during second room "Outwit and outfight..." + { GID_LONGBOW, 210, 210, 0, "mama", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during third room "Fall under the spell..." + { GID_LONGBOW, 320, 320, 0, "flyin", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during fourth room "Conspiracies, love..." + { GID_PQ2, 23, 23, 0, "rm23Script", "elements", sig_kDisplay_pq2_1, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of files in jail - 0x75 as id - bug #5223 + { GID_PQ2, 23, 23, 0, "rm23Script", "handleEvent", sig_kDisplay_pq2_1, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of file in jail - 0x75 as id - bug #9670 + { GID_QFG1, 11, 11, 0, "battle", "init", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id + { GID_SQ4, 397, 0, 0, "", "export 12", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store - bug #5227 + { GID_SQ4, 391, 391, 0, "doCatalog", "changeState", sig_kDisplay_sq4_1, 0, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object + { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDirLoop_workarounds[] = { - { GID_KQ4, 4, 992, 0, "Avoid", "doit", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #5217 + { GID_KQ4, 4, 992, 0, "Avoid", "doit", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #5217 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDisposeScript_workarounds[] = { - { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #4967 - { GID_LSL2, -1, 54, 0, "rm54", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // Amiga: room 55, script tries to kDisposeScript an object (does not happen for DOS) - bug #6818 - { GID_MOTHERGOOSEHIRES,37, 337, 0, "rhymeScript", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the rhyme with the king - { GID_QFG1, -1, 64, 0, "rm64", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object - { GID_SQ4, -1, 151, 0, "fightScript", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with Vohaul, parameter 0 is an object, happens in at least room 150 - { GID_SQ4, -1, 152, 0, "driveCloseUp", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object, may happen in room 150 and 900 (900 see bug #9812) - { GID_SQ4, 150, 152, 0, "", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download"... in Russian version - bug #5573 - { GID_SQ4, 900, 152, 0, "", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // same as above, but for "game over" room + { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #4967 + { GID_LSL2, -1, 54, 0, "rm54", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // Amiga: room 55, script tries to kDisposeScript an object (does not happen for DOS) - bug #6818 + { GID_MOTHERGOOSEHIRES,37, 337, 0, "rhymeScript", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // after the rhyme with the king + { GID_QFG1, -1, 64, 0, "rm64", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object + { GID_SQ4, -1, 151, 0, "fightScript", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with Vohaul, parameter 0 is an object, happens in at least room 150 + { GID_SQ4, -1, 152, 0, "driveCloseUp", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object, may happen in room 150 and 900 (900 see bug #9812) + { GID_SQ4, 150, 152, 0, "", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download"... in Russian version - bug #5573 + { GID_SQ4, 900, 152, 0, "", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // same as above, but for "game over" room SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDoAudioResume_workarounds[] = { - { GID_HOYLE5, -1, 17, 0, NULL, "startAudio", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when a character talks during a game + { GID_HOYLE5, -1, 17, 0, NULL, "startAudio", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when a character talks during a game SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDoSoundPlay_workarounds[] = { - { GID_LSL6HIRES, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument - { GID_QFG4, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument - { GID_PQ4, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument - { GID_KQ7, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument - { GID_SQ6, -1, 0, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Demo passes an extra null argument on startup - { GID_GK1, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Mac version always passes an extra null argument - { GID_GK2, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Demo passes an extra null argument when clicking on buttons + { GID_LSL6HIRES, -1, 64989, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + { GID_QFG4, -1, 64989, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + { GID_PQ4, -1, 64989, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + { GID_KQ7, -1, 64989, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + { GID_SQ6, -1, 0, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Demo passes an extra null argument on startup + { GID_GK1, -1, 64989, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Mac version always passes an extra null argument + { GID_GK2, -1, 64989, 0, NULL, "play", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Demo passes an extra null argument when clicking on buttons SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kDoSoundFade_workarounds[] = { - { GID_KQ5, 213, 989, 0, "globalSound3", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078 - { GID_KQ6, 105, 989, 0, "globalSound", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object - { GID_KQ6, 460, 989, 0, "globalSound2", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #4954 - { GID_QFG4, -1, 64989, 0, "longSong", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong) - { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #6341 + { GID_KQ5, 213, 989, 0, "globalSound3", "fade", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078 + { GID_KQ6, 105, 989, 0, "globalSound", "fade", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object + { GID_KQ6, 460, 989, 0, "globalSound2", "fade", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #4954 + { GID_QFG4, -1, 64989, 0, "longSong", "fade", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // CD version: many places, parameter 4 is an object (longSong) + { GID_SQ5, 800, 989, 0, "sq5Music1", "fade", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when cutting the wrong part of Goliath with the laser - bug #6341 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGetAngle_workarounds[] = { - { GID_FANMADE, 516, 992, 0, "Motion", "init", NULL, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects - { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #4959 & #5203 - { GID_SQ1, -1, 927, 0, "PAvoider", "doit", NULL, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #6016 + { GID_FANMADE, 516, 992, 0, "Motion", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects + { GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #4959 & #5203 + { GID_SQ1, -1, 927, 0, "PAvoider", "doit", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #6016 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kFileIOOpen_workarounds[] = { - { GID_TORIN, 61000, 61000, 0, "roSierraLogo", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Missing second argument when the game checks for autosave.cat after the Sierra logo - { GID_TORIN, 61100, 61100, 0, "roPickAChapter", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Missing second argument when the game checks for autosave.cat after the Sierra logo in the demo + { GID_TORIN, 61000, 61000, 0, "roSierraLogo", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Missing second argument when the game checks for autosave.cat after the Sierra logo + { GID_TORIN, 61100, 61100, 0, "roPickAChapter", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Missing second argument when the game checks for autosave.cat after the Sierra logo in the demo SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kFindKey_workarounds[] = { - { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #4987 - { GID_HOYLE4, 300, 999, 0, "Piles", "contains", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When passing the three cards in Hearts, a null reference to a list is passed - bug #5664 + { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #4987 + { GID_HOYLE4, 300, 999, 0, "Piles", "contains", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When passing the three cards in Hearts, a null reference to a list is passed - bug #5664 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kFrameOut_workarounds[] = { - { GID_PQ4, 360, 360, 0, "csFrontInset", "init", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When clicking hand on the impounded white car on day 4 - bug #9848 - { GID_PQ4, 360, 360, 0, "csFrontInset", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When clicking hand on the impounded white car on day 4 - { GID_PQ4, 360, 360, 0, "copCarInset", "init", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When clicking hand on the impounded police car on day 3 - { GID_PQ4, 360, 360, 0, "copCarInset", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When exiting the impounded police car on day 3 - { GID_PQ4, 275, 275, 0, "checkSherry", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When encountering Sherry and Sam in the morgue on day 3 - { GID_PQ4, 725, 725, 0, "fridgeInset", "init", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When opening the refrigerator at the end of day 4 - { GID_PQ4, 725, 725, 0, "fridgeInset", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When exiting the refrigerator at the end of day 4 - { GID_PQ4, 735, 735, 0, "medInset", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 1 } }, // When exiting the medicine cabinet at the end of day 4 + { GID_PQ4, 360, 360, 0, "csFrontInset", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When clicking hand on the impounded white car on day 4 - bug #9848 + { GID_PQ4, 360, 360, 0, "csFrontInset", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When clicking hand on the impounded white car on day 4 + { GID_PQ4, 360, 360, 0, "copCarInset", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When clicking hand on the impounded police car on day 3 + { GID_PQ4, 360, 360, 0, "copCarInset", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When exiting the impounded police car on day 3 + { GID_PQ4, 275, 275, 0, "checkSherry", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When encountering Sherry and Sam in the morgue on day 3 + { GID_PQ4, 725, 725, 0, "fridgeInset", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When opening the refrigerator at the end of day 4 + { GID_PQ4, 725, 725, 0, "fridgeInset", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When exiting the refrigerator at the end of day 4 + { GID_PQ4, 735, 735, 0, "medInset", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 1 } }, // When exiting the medicine cabinet at the end of day 4 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphDrawLine_workarounds[] = { - { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter - { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter - { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #5176 - { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #5177 + { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter + { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter + { GID_SQ1, 71, 71, 0, "destroyXenon", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // during the Xenon destruction cutscene (which results in death), gets called with 1 extra parameter - bug #5176 + { GID_SQ1, 53, 53, 0, "blastEgo", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when Roger is found and zapped by the cleaning robot, gets called with 1 extra parameter - bug #5177 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -687,32 +690,32 @@ static const uint16 sig_kGraphSaveBox_ibrain_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphSaveBox_workarounds[] = { - { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter - { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #4943 - { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // see above - { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution) - { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 + { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter + { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #4943 + { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // see above + { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState", sig_kGraphSaveBox_ibrain_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution) + { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = { - { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter + { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = { - { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter + { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = { - { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function - { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack + { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function + { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -728,111 +731,111 @@ static const uint16 sig_kGraphRedrawBox_sq4_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = { - { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 405, 405, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 - { GID_SQ4, 406, 406, 0, "egoFollowed", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // FLOPPY: when getting shot by the police - accidental additional parameter specified - { GID_SQ4, -1, 406, 0, "swimAndShoot", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, -1, 406, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 (is for both egoFollowed and swimAndShoot) - { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 410, 410, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 - { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified - { GID_SQ4, 411, 411, 0, "", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 - { GID_SQ4, 150, 150, 0, "laserScript", "changeState", sig_kGraphRedrawBox_sq4_1, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #5479, German - bug #5527 - { GID_SQ4, 150, 150, 0, "", "changeState", sig_kGraphRedrawBox_sq4_1, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the Russian version - bug #5573 - { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified - { GID_KQ5, -1, 981, 0, "myWindow", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #5031 - { GID_KQ5, -1, 995, 0, "invW", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified - { GID_KQ5, -1, 995, 0, "", "export 0", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #5138 - { GID_KQ5, -1, 403, 0, "KQ5Window", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the FM Towns version when closing any dialog box, accidental additional parameter specified + { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 405, 405, 0, "", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 + { GID_SQ4, 406, 406, 0, "egoFollowed", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // FLOPPY: when getting shot by the police - accidental additional parameter specified + { GID_SQ4, -1, 406, 0, "swimAndShoot", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, -1, 406, 0, "", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 (is for both egoFollowed and swimAndShoot) + { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 410, 410, 0, "", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 + { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 411, 411, 0, "", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air... Russian version - bug #5573 + { GID_SQ4, 150, 150, 0, "laserScript", "changeState", sig_kGraphRedrawBox_sq4_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #5479, German - bug #5527 + { GID_SQ4, 150, 150, 0, "", "changeState", sig_kGraphRedrawBox_sq4_1, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // same as above, for the Russian version - bug #5573 + { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified + { GID_KQ5, -1, 981, 0, "myWindow", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #5031 + { GID_KQ5, -1, 995, 0, "invW", "doit", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified + { GID_KQ5, -1, 995, 0, "", "export 0", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when opening the gem pouch, accidental additional parameter specified - bug #5138 + { GID_KQ5, -1, 403, 0, "KQ5Window", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the FM Towns version when closing any dialog box, accidental additional parameter specified SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = { - { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function - { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 - { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 - { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters + { GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function + { GID_PQ3, 202, 202, 0, "MapEdit", "addPt", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 + { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #5099 + { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kIsObject_workarounds[] = { - { GID_GK1DEMO, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 - { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989 - { GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter + { GID_GK1DEMO, 50, 999, 0, "List", "eachElementDo", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 + { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989 + { GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kListAt_workarounds[] = { - { GID_HOYLE5, 100, 64999, 0, "theHands", "at", NULL, 0, { WORKAROUND_FAKE, 0 } }, // After the first hand is dealt in Crazy Eights game in demo, an object is passed instead of a number - { GID_LIGHTHOUSE, 24, 64999, 0, "LightInv", "at", NULL, 0, { WORKAROUND_FAKE, 0 } }, // When taking the car keys from the table at the start of the game + { GID_HOYLE5, 100, 64999, 0, "theHands", "at", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // After the first hand is dealt in Crazy Eights game in demo, an object is passed instead of a number + { GID_LIGHTHOUSE, 24, 64999, 0, "LightInv", "at", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // When taking the car keys from the table at the start of the game SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kMemory_workarounds[] = { - { GID_LAURABOW2, -1, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #4944 - { GID_SQ1, -1, 999, 0, "", "export 6", NULL, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #6017 + { GID_LAURABOW2, -1, 999, 0, "", "export 6", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #4944 + { GID_SQ1, -1, 999, 0, "", "export 6", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #6017 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kMoveCursor_workarounds[] = { - { GID_KQ5, -1, 937, 0, "IconBar", "handleEvent", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when pressing escape to open the menu, gets called with one parameter instead of 2 - bug #5575 + { GID_KQ5, -1, 937, 0, "IconBar", "handleEvent", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when pressing escape to open the menu, gets called with one parameter instead of 2 - bug #5575 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kNewWindow_workarounds[] = { - { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #4976 + { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #4976 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kPalVarySetVary_workarounds[] = { - { GID_KQ7, 4600, 4600, 0, "sRosDogDeath2", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when dying by letting the dog find you under the house. Trac#9763 + { GID_KQ7, 4600, 4600, 0, "sRosDogDeath2", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when dying by letting the dog find you under the house. Trac#9763 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kPalVarySetPercent_workarounds[] = { - { GID_GK1, 370, 370, 0, "graceComeOut", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // there's an extra parameter in GK1, when changing chapters. This extra parameter seems to be a bug or just unimplemented functionality, as there's no visible change from the original in the chapter change room + { GID_GK1, 370, 370, 0, "graceComeOut", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // there's an extra parameter in GK1, when changing chapters. This extra parameter seems to be a bug or just unimplemented functionality, as there's no visible change from the original in the chapter change room SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kPalVarySetStart_workarounds[] = { - { GID_PQ4, 170, 170, 0, "getHit", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Three extra parameters passed during the gunfight at the end of day 1 + { GID_PQ4, 170, 170, 0, "getHit", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Three extra parameters passed during the gunfight at the end of day 1 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kPalVaryMergeStart_workarounds[] = { - { GID_PQ4, 170, 170, 0, "getHit", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // Three extra parameters passed during the gunfight at the end of day 1 + { GID_PQ4, 170, 170, 0, "getHit", "changeState", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Three extra parameters passed during the gunfight at the end of day 1 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kPlatform32_workarounds[] = { - { GID_HOYLE5, -1, 0, 0, "hoyle4", "newRoom", NULL, 0, { WORKAROUND_FAKE, 1 } }, // at the start of the game, incorrectly uses SCI16 calling convention for kPlatform + { GID_HOYLE5, -1, 0, 0, "hoyle4", "newRoom", NULL, 0, 0, { WORKAROUND_FAKE, 1 } }, // at the start of the game, incorrectly uses SCI16 calling convention for kPlatform SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kRandom_workarounds[] = { - { GID_TORIN, 51400, 64928, 0, "Blink", "init", NULL, -1, { WORKAROUND_FAKE, 0 } }, // at the end of the game, during the cutscene after touching the collar on Lycentia; Trac#9779 - { GID_TORIN, 51400, 64928, 0, "Blink", "cycleDone", NULL, -1, { WORKAROUND_FAKE, 0 } }, // at the end of the game, during the cutscene after touching the collar on Lycentia; Trac#9779 + { GID_TORIN, 51400, 64928, 0, "Blink", "init", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // at the end of the game, during the cutscene after touching the collar on Lycentia; Trac#9779 + { GID_TORIN, 51400, 64928, 0, "Blink", "cycleDone", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // at the end of the game, during the cutscene after touching the collar on Lycentia; Trac#9779 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kReadNumber_workarounds[] = { - { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 - { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 + { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 + { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -848,29 +851,29 @@ static const uint16 sig_kResCheck_lsl6hires_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kResCheck_workarounds[] = { - { GID_LSL6HIRES, 740, 740, -1, "myCreditText", "handleEvent", sig_kResCheck_lsl6hires_1, -1, { WORKAROUND_IGNORE, 0 } }, // when clicking quit during the final credits + { GID_LSL6HIRES, 740, 740, -1, "myCreditText", "handleEvent", sig_kResCheck_lsl6hires_1, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when clicking quit during the final credits SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = { - { GID_QFG4DEMO, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 + { GID_QFG4DEMO, 100, 100, 0, "doMovie", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kSetCursor_workarounds[] = { - { GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters - { GID_MOTHERGOOSEHIRES,-1, 0, -1, "MG", "setCursor", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // At the start of the game, an object is passed as the cel number + { GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters + { GID_MOTHERGOOSEHIRES,-1, 0, -1, "MG", "setCursor", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // At the start of the game, an object is passed as the cel number SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kSetPort_workarounds[] = { - { GID_LSL6, 740, 740, 0, "rm740", "drawPic", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters - { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters - bug #5174 + { GID_LSL6, 740, 740, 0, "rm740", "drawPic", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters + { GID_QFG3, 830, 830, 0, "portalOpens", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when the portal appears during the end, gets called with 4 parameters - bug #5174 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -886,16 +889,16 @@ static const uint16 sig_kStrAt_ibrain_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kStrAt_workarounds[] = { - { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #5127 - { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState", sig_kStrAt_ibrain_1, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #5088 + { GID_CASTLEBRAIN, 220, 220, 0, "robotJokes", "animateOnce", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when trying to view the terminal at the end of the maze without having collected any robot jokes - bug #5127 + { GID_ISLANDBRAIN, 300, 310, 0, "childBreed", "changeState", sig_kStrAt_ibrain_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // when clicking Breed to get the second-generation cyborg hybrid (Standard difficulty), the two parameters are swapped - bug #5088 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kStrCpy_workarounds[] = { - { GID_MOTHERGOOSE, 23, 23, 0, "talkScript", "changeState", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when talking to the girl in scene 23, there's no destination parameter (script bug - wrong instruction order). The original source is used directly afterwards in kDisplay, to show the girl's text - bug #6485 + { GID_MOTHERGOOSE, 23, 23, 0, "talkScript", "changeState", NULL, 0, 0, { WORKAROUND_FAKE, 0 } }, // when talking to the girl in scene 23, there's no destination parameter (script bug - wrong instruction order). The original source is used directly afterwards in kDisplay, to show the girl's text - bug #6485 SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -911,29 +914,29 @@ static const uint16 sig_kStrLen_qfg2_1[] = { SIG_END }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kStrLen_workarounds[] = { - { GID_QFG2, 210, 2, 0, "", "export 21", sig_kStrLen_qfg2_1, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #5489 + { GID_QFG2, 210, 2, 0, "", "export 21", sig_kStrLen_qfg2_1, 0, 0, { WORKAROUND_FAKE, 0 } }, // When saying something incorrect at the WIT, an integer is passed instead of a reference - bug #5489 SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kUnLoad_workarounds[] = { - { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident - { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #5483 - { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #4966 - { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident - { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident - { GID_LSL6HIRES, 740, 740, 0, "showCartoon", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, multiple additional parameters are passed by accident - { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident - { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error - { GID_QFG4, -1, 110, 0, "dreamer", "dispose", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident + { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident + { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #5483 + { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #4966 + { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident + { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident + { GID_LSL6HIRES, 740, 740, 0, "showCartoon", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, multiple additional parameters are passed by accident + { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident + { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error + { GID_QFG4, -1, 110, 0, "dreamer", "dispose", NULL, 0, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident SCI_WORKAROUNDENTRY_TERMINATOR }; -// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround const SciWorkaroundEntry kScrollWindowAdd_workarounds[] = { - { GID_PHANTASMAGORIA, 45, 64907, 0, "ScrollableWindow", "addString", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // ScrollWindow interface passes the last two parameters twice + { GID_PHANTASMAGORIA, 45, 64907, 0, "ScrollableWindow", "addString", NULL, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // ScrollWindow interface passes the last two parameters twice SCI_WORKAROUNDENTRY_TERMINATOR }; @@ -971,7 +974,7 @@ SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroun && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel)) && objectNameMatches && workaround->methodName == g_sci->getSciLanguageString(curMethodName, K_LANG_ENGLISH) - && ((workaround->index == -1) || (workaround->index == index))) { + && ((workaround->fromIndex == -1) || ((workaround->fromIndex <= index) && (workaround->toIndex >= index)))) { // Workaround found if ((workaround->localCallSignature) || (curLocalCallOffset >= 0)) { // local call signature found and/or subcall was made diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index a965a14a42..a462fddcaa 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -55,7 +55,8 @@ struct SciWorkaroundEntry { const char *objectName; const char *methodName; const uint16 *localCallSignature; - int index; + int fromIndex; + int toIndex; SciWorkaroundSolution newValue; }; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index e69095a6b1..aef84e212d 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -164,7 +164,7 @@ SciEvent EventManager::getScummVMEvent() { #if ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { - const Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer(); + const GfxFrameout *gfxFrameout = g_sci->_gfxFrameout; // This will clamp `mousePos` according to the restricted zone, // so any cursor or screen item associated with the mouse position @@ -172,7 +172,7 @@ SciEvent EventManager::getScummVMEvent() { g_sci->_gfxCursor32->deviceMoved(mousePos); Common::Point mousePosSci = mousePos; - mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight)); + mulru(mousePosSci, Ratio(gfxFrameout->getScriptWidth(), gfxFrameout->getScreenWidth()), Ratio(gfxFrameout->getScriptHeight(), gfxFrameout->getScreenHeight())); noEvent.mousePosSci = input.mousePosSci = mousePosSci; if (_hotRectanglesActive) { diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index a49690a703..fb7abe8a77 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -35,7 +35,7 @@ namespace Sci { #pragma mark CelScaler -CelScaler *CelObj::_scaler = nullptr; +Common::ScopedPtr<CelScaler> CelObj::_scaler; void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { for (int i = 0; i < ARRAYSIZE(_scaleTables); ++i) { @@ -74,9 +74,9 @@ void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) } } -const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio &scaleY) { +const CelScalerTable &CelScaler::getScalerTable(const Ratio &scaleX, const Ratio &scaleY) { activateScaleTables(scaleX, scaleY); - return &_scaleTables[_activeIndex]; + return _scaleTables[_activeIndex]; } #pragma mark - @@ -87,21 +87,13 @@ void CelObj::init() { CelObj::deinit(); _drawBlackLines = false; _nextCacheId = 1; - _scaler = new CelScaler(); - _cache = new CelCache; - _cache->resize(100); + _scaler.reset(new CelScaler()); + _cache.reset(new CelCache(100)); } void CelObj::deinit() { - delete _scaler; - _scaler = nullptr; - if (_cache != nullptr) { - for (CelCache::iterator it = _cache->begin(); it != _cache->end(); ++it) { - delete it->celObj; - } - } - delete _cache; - _cache = nullptr; + _scaler.reset(); + _cache.reset(); } #pragma mark - @@ -172,10 +164,9 @@ struct SCALER_Scale { _minX(targetRect.left), _maxX(targetRect.right - 1), #endif - // The maximum width of the scaled object may not be as - // wide as the source data it requires if downscaling, - // so just always make the reader decompress an entire - // line of source data when scaling + // The maximum width of the scaled object may not be as wide as the source + // data it requires if downscaling, so just always make the reader + // decompress an entire line of source data when scaling _reader(celObj, celObj._width) { #ifndef NDEBUG assert(_minX <= _maxX); @@ -203,39 +194,39 @@ struct SCALER_Scale { // games which use global scaling are the ones that use low-resolution // script coordinates too. - const CelScalerTable *table = CelObj::_scaler->getScalerTable(scaleX, scaleY); + const CelScalerTable &table = CelObj::_scaler->getScalerTable(scaleX, scaleY); - if (g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth == kLowResX) { + if (g_sci->_gfxFrameout->getScriptWidth() == kLowResX) { const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); if (FLIP) { const int lastIndex = celObj._width - 1; for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); + _valuesX[x] = lastIndex - (table.valuesX[x] - unscaledX); } } else { for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = table->valuesX[x] - unscaledX; + _valuesX[x] = table.valuesX[x] - unscaledX; } } const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { - _valuesY[y] = table->valuesY[y] - unscaledY; + _valuesY[y] = table.valuesY[y] - unscaledY; } } else { if (FLIP) { const int lastIndex = celObj._width - 1; for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = lastIndex - table->valuesX[x - scaledPosition.x]; + _valuesX[x] = lastIndex - table.valuesX[x - scaledPosition.x]; } } else { for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = table->valuesX[x - scaledPosition.x]; + _valuesX[x] = table.valuesX[x - scaledPosition.x]; } } for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { - _valuesY[y] = table->valuesY[y - scaledPosition.y]; + _valuesY[y] = table.valuesY[y - scaledPosition.y]; } } } @@ -412,8 +403,8 @@ struct MAPPER_NoMDNoSkip { struct MAPPER_Map { inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { if (pixel != skipColor) { - // NOTE: For some reason, SSCI never checks if the source - // pixel is *above* the range of remaps. + // For some reason, SSCI never checks if the source pixel is *above* + // the range of remaps, so we do not either. if (pixel < g_sci->_gfxRemap32->getStartColor()) { *target = pixel; } else if (g_sci->_gfxRemap32->remapEnabled(pixel)) { @@ -429,8 +420,8 @@ struct MAPPER_Map { */ struct MAPPER_NoMap { inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { - // NOTE: For some reason, SSCI never checks if the source - // pixel is *above* the range of remaps. + // For some reason, SSCI never checks if the source pixel is *above* the + // range of remaps, so we do not either. if (pixel != skipColor && pixel < g_sci->_gfxRemap32->getStartColor()) { *target = pixel; } @@ -444,9 +435,9 @@ void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Re _drawBlackLines = screenItem._drawBlackLines; if (_remap) { - // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, - // but since we are already in a `_remap` branch, there is no reason to check it - // again + // In SSCI, this check was `g_Remap_numActiveRemaps && _remap`, but + // since we are already in a `_remap` branch, there is no reason to + // check that again if (g_sci->_gfxRemap32->getRemapCount()) { if (scaleX.isOne() && scaleY.isOne()) { if (_compressionType == kCelCompressionNone) { @@ -612,7 +603,7 @@ void CelObj::submitPalette() const { #pragma mark CelObj - Caching int CelObj::_nextCacheId = 1; -CelCache *CelObj::_cache = nullptr; +Common::ScopedPtr<CelCache> CelObj::_cache; int CelObj::searchCache(const CelInfo32 &celInfo, int *const nextInsertIndex) const { *nextInsertIndex = -1; @@ -648,12 +639,7 @@ void CelObj::putCopyInCache(const int cacheIndex) const { } CelCacheEntry &entry = (*_cache)[cacheIndex]; - - if (entry.celObj != nullptr) { - delete entry.celObj; - } - - entry.celObj = duplicate(); + entry.celObj.reset(duplicate()); entry.id = ++_nextCacheId; } @@ -672,9 +658,9 @@ struct RENDERER { _skipColor(skipColor) {} inline void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - byte *targetPixel = (byte *)target.getPixels() + target.screenWidth * targetRect.top + targetRect.left; + byte *targetPixel = (byte *)target.getPixels() + target.w * targetRect.top + targetRect.left; - const int16 skipStride = target.screenWidth - targetRect.width(); + const int16 skipStride = target.w - targetRect.width(); const int16 targetWidth = targetRect.width(); const int16 targetHeight = targetRect.height(); for (int16 y = 0; y < targetHeight; ++y) { @@ -807,8 +793,8 @@ void CelObj::drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targ } void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - // In SSCI the checks are > because their rects are BR-inclusive; - // our checks are >= because our rects are BR-exclusive + // In SSCI the checks are > because their rects are BR-inclusive; our checks + // are >= because our rects are BR-exclusive if (g_sci->_features->hasEmptyScaleDrawHack() && (targetRect.left >= targetRect.right || targetRect.top >= targetRect.bottom)) { @@ -822,8 +808,8 @@ void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &sca } void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - // In SSCI the checks are > because their rects are BR-inclusive; - // our checks are >= because our rects are BR-exclusive + // In SSCI the checks are > because their rects are BR-inclusive; our checks + // are >= because our rects are BR-exclusive if (g_sci->_features->hasEmptyScaleDrawHack() && (targetRect.left >= targetRect.right || targetRect.top >= targetRect.bottom)) { @@ -861,11 +847,11 @@ int16 CelObjView::getNumCels(const GuiResourceId viewId, int16 loopNo) { const uint16 loopCount = data[2]; - // Every version of SCI32 has a logic error in this function that causes - // random memory to be read if a script requests the cel count for one - // past the maximum loop index. For example, GK1 room 808 does this, and - // gets stuck in an infinite loop because the game script expects this - // method to return a non-zero value. + // Every version of SSCI has a logic error in this function that causes + // random memory to be read if a script requests the cel count for one past + // the maximum loop index. For example, GK1 room 808 does this, and gets + // stuck in an infinite loop because the game script expects this method to + // return a non-zero value. // This bug is triggered in basically every SCI32 game and appears to be // universally fixable simply by always using the next lowest loop instead. if (loopNo == loopCount) { @@ -904,7 +890,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int const int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; - const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj); + const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj.get()); if (cachedCelObj == nullptr) { error("Expected a CelObjView in cache slot %d", cacheIndex); } @@ -915,7 +901,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); - // NOTE: SCI2.1/SQ6 just silently returns here. + // SSCI just silently returns here if (!resource) { error("View resource %d not found", viewId); } @@ -944,8 +930,6 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _info.loopNo = loopCount - 1; } - // NOTE: This is the actual check, in the actual location, - // from SCI engine. if (loopNo < 0) { error("Loop is less than 0"); } @@ -1011,10 +995,8 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int error("Compression type not supported - V: %d L: %d C: %d", _info.resourceId, _info.loopNo, _info.celNo); } - if (celHeader[10] & 128) { - // NOTE: This is correct according to SCI2.1/SQ6/DOS; - // the engine re-reads the byte value as a word value - const uint16 flags = celHeader.getUint16SEAt(10); + const uint16 flags = celHeader.getUint16SEAt(10); + if (flags & 0x80) { _transparent = flags & 1 ? true : false; _remap = flags & 2 ? true : false; } else if (_compressionType == kCelCompressionNone) { @@ -1096,7 +1078,7 @@ Common::Point CelObjView::getLinkPosition(const int16 linkId) const { Common::Point point; point.x = linkTable.getInt16SEAt(0); if (_mirrorX) { - // NOTE: SSCI had an off-by-one error here (missing -1) + // SSCI had an off-by-one error here (missing -1) point.x = _width - point.x - 1; } point.y = linkTable.getInt16SEAt(2); @@ -1127,7 +1109,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { const int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; - const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj); + const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj.get()); if (cachedCelObj == nullptr) { error("Expected a CelObjPic in cache slot %d", cacheIndex); } @@ -1138,7 +1120,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false); - // NOTE: SCI2.1/SQ6 just silently returns here. + // SSCI just silently returns here if (!resource) { error("Pic resource %d not found", picId); } @@ -1183,10 +1165,9 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _yResolution = 400; } - if (celHeader.getUint8At(10) & 128) { - // NOTE: This is correct according to SCI2.1/SQ6/DOS; - // the engine re-reads the byte value as a word value - const uint16 flags = celHeader.getUint16SEAt(10); + + const uint16 flags = celHeader.getUint16SEAt(10); + if (flags & 0x80) { _transparent = flags & 1 ? true : false; _remap = flags & 2 ? true : false; } else { @@ -1251,7 +1232,8 @@ CelObjMem::CelObjMem(const reg_t bitmapObject) { SciBitmap *bitmap = g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject); - // NOTE: SSCI did no error checking here at all. + // SSCI did no error checking here at all so would just end up reading + // garbage or crashing if this ever happened if (!bitmap) { error("Bitmap %04x:%04x not found", PRINT_REG(bitmapObject)); } @@ -1283,8 +1265,8 @@ CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 heigh _info.color = color; _origin.x = 0; _origin.y = 0; - _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + _xResolution = g_sci->_gfxFrameout->getScriptWidth(); + _yResolution = g_sci->_gfxFrameout->getScriptHeight(); _hunkPaletteOffset = 0; _mirrorX = false; _remap = false; @@ -1293,8 +1275,8 @@ CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 heigh } void CelObjColor::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX) { - // TODO: The original engine sets this flag but why? One cannot - // draw a solid color mirrored. + // One cannot draw a solid color mirrored, but SSCI sets it anyway, so we do + // too _drawMirrored = mirrorX; draw(target, targetRect); } diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 6e50f7232e..02b2859f5c 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -33,14 +33,14 @@ namespace Sci { typedef Common::Rational Ratio; // SCI32 has four different coordinate systems: -// 1. low resolution, 2. game/script resolution, -// 3. text/bitmap resolution, 4. screen resolution +// 1. low resolution, 2. game/script resolution, 3. text/bitmap resolution, +// 4. screen resolution // -// In CelObj, these values are used when there is -// no baked in resolution of cels. +// In CelObj, these values are used when there is no baked in resolution of +// cels. // -// In ScreenItem, it is used when deciding which -// path to take to calculate dimensions. +// In ScreenItem, it is used when deciding which path to take to calculate +// dimensions. enum { kLowResX = 320, kLowResY = 200 @@ -60,8 +60,7 @@ enum CelCompressionType { }; /** - * A CelInfo32 object describes the basic properties of a - * cel object. + * A CelInfo32 object describes the basic properties of a cel object. */ struct CelInfo32 { /** @@ -70,26 +69,24 @@ struct CelInfo32 { CelType type; /** - * For cel objects that draw from resources, the ID of - * the resource to load. + * For cel objects that draw from resources, the ID of the resource to load. */ GuiResourceId resourceId; /** - * For CelObjView, the loop number to draw from the - * view resource. + * For CelObjView, the loop number to draw from the view resource. */ int16 loopNo; /** - * For CelObjView and CelObjPic, the cel number to draw - * from the view or pic resource. + * For CelObjView and CelObjPic, the cel number to draw from the view or pic + * resource. */ int16 celNo; /** - * For CelObjMem, a segment register pointing to a heap - * resource containing headered bitmap data. + * For CelObjMem, a segment register pointing to a heap resource containing + * headered bitmap data. */ reg_t bitmap; @@ -98,18 +95,16 @@ struct CelInfo32 { */ uint8 color; - // NOTE: In at least SCI2.1/SQ6, color is left - // uninitialised. CelInfo32() : + // In SSCI, color is left uninitialised type(kCelTypeMem), resourceId(0), loopNo(0), celNo(0), bitmap(NULL_REG) {} - // NOTE: This is the equivalence criteria used by - // CelObj::searchCache in at least SCI2.1/SQ6. Notably, - // it does not check the color field. + // This is the equivalence criteria used by CelObj::searchCache in at least + // SSCI SQ6. Notably, it does not check the color field. inline bool operator==(const CelInfo32 &other) { return ( type == other.type && @@ -143,13 +138,12 @@ struct CelInfo32 { class CelObj; struct CelCacheEntry { /** - * A monotonically increasing cache ID used to identify - * the least recently used item in the cache for - * replacement. + * A monotonically increasing cache ID used to identify the least recently + * used item in the cache for replacement. */ int id; - CelObj *celObj; - CelCacheEntry() : id(0), celObj(nullptr) {} + Common::ScopedPtr<CelObj> celObj; + CelCacheEntry() : id(0) {} }; typedef Common::Array<CelCacheEntry> CelCache; @@ -166,9 +160,9 @@ enum { struct CelScalerTable { /** - * A lookup table of indexes that should be used to find - * the correct column to read from the source bitmap - * when drawing a scaled version of the source bitmap. + * A lookup table of indexes that should be used to find the correct column + * to read from the source bitmap when drawing a scaled version of the + * source bitmap. */ int valuesX[kCelScalerTableSize]; @@ -178,9 +172,9 @@ struct CelScalerTable { Ratio scaleX; /** - * A lookup table of indexes that should be used to find - * the correct row to read from a source bitmap when - * drawing a scaled version of the source bitmap. + * A lookup table of indexes that should be used to find the correct row to + * read from a source bitmap when drawing a scaled version of the source + * bitmap. */ int valuesY[kCelScalerTableSize]; @@ -202,25 +196,23 @@ class CelScaler { int _activeIndex; /** - * Activates a scale table for the given X and Y ratios. - * If there is no table that matches the given ratios, - * the least most recently used table will be replaced - * and activated. + * Activates a scale table for the given X and Y ratios. If there is no + * table that matches the given ratios, the least most recently used table + * will be replaced and activated. */ void activateScaleTables(const Ratio &scaleX, const Ratio &scaleY); /** - * Builds a pixel lookup table in `table` for the given - * ratio. The table will be filled up to the specified - * size, which should be large enough to draw across the - * entire target buffer. + * Builds a pixel lookup table in `table` for the given ratio. The table + * will be filled up to the specified size, which should be large enough to + * draw across the entire target buffer. */ void buildLookupTable(int *table, const Ratio &ratio, const int size); public: CelScaler() : - _scaleTables(), - _activeIndex(0) { + _scaleTables(), + _activeIndex(0) { CelScalerTable &table = _scaleTables[0]; table.scaleX = Ratio(); table.scaleY = Ratio(); @@ -236,7 +228,7 @@ public: /** * Retrieves scaler tables for the given X and Y ratios. */ - const CelScalerTable *getScalerTable(const Ratio &scaleX, const Ratio &scaleY); + const CelScalerTable &getScalerTable(const Ratio &scaleX, const Ratio &scaleY); }; #pragma mark - @@ -244,53 +236,47 @@ public: class ScreenItem; /** - * A cel object is the lowest-level rendering primitive in - * the SCI engine and draws itself directly to a target - * pixel buffer. + * A cel object is the lowest-level rendering primitive in the SCI engine and + * draws itself directly to a target pixel buffer. */ class CelObj { protected: /** - * When true, every second line of the cel will be - * rendered as a black line. + * When true, every second line of the cel will be rendered as a black line. * * @see ScreenItem::_drawBlackLines - * @note Using a static member because otherwise this - * would otherwise need to be copied down through - * several calls. (SSCI did similar, using a global - * variable.) + * @note Using a static member because otherwise this would otherwise need + * to be copied down through several calls. (SSCI did similar, using a + * global variable.) */ static bool _drawBlackLines; /** - * When true, this cel will be horizontally mirrored - * when it is drawn. This is an internal flag that is - * set by draw methods based on the combination of the - * cel's `_mirrorX` property and the owner screen item's - * `_mirrorX` property. + * When true, this cel will be horizontally mirrored when it is drawn. This + * is an internal flag that is set by draw methods based on the combination + * of the cel's `_mirrorX` property and the owner screen item's `_mirrorX` + * property. */ bool _drawMirrored; public: - static CelScaler *_scaler; + static Common::ScopedPtr<CelScaler> _scaler; /** - * The basic identifying information for this cel. This - * information effectively acts as a composite key for - * a cel object, and any cel object can be recreated - * from this data alone. + * The basic identifying information for this cel. This information + * effectively acts as a composite key for a cel object, and any cel object + * can be recreated from this data alone. */ CelInfo32 _info; /** - * The offset to the cel header for this cel within the - * raw resource data. + * The offset to the cel header for this cel within the raw resource data. */ uint32 _celHeaderOffset; /** - * The offset to the embedded palette for this cel - * within the raw resource data. + * The offset to the embedded palette for this cel within the raw resource + * data. */ uint32 _hunkPaletteOffset; @@ -300,36 +286,32 @@ public: uint16 _width, _height; /** - * TODO: Documentation + * The origin of the cel, relative to the top-left corner, in cel + * coordinates. */ Common::Point _origin; /** - * The dimensions of the original coordinate system for - * the cel. Used to scale cels from their native size - * to the correct size on screen. + * The dimensions of the original coordinate system for the cel. Used to + * scale cels from their native size to the correct size on screen. * - * @note This is set to scriptWidth/Height for - * CelObjColor. For other cel objects, the value comes - * from the raw resource data. For text bitmaps, this is - * the width/height of the coordinate system used to - * generate the text, which also defaults to - * scriptWidth/Height but seems to typically be changed - * to more closely match the native screen resolution. + * @note This is set to scriptWidth/Height for CelObjColor. For other cel + * objects, the value comes from the raw resource data. For text bitmaps, + * this is the width/height of the coordinate system used to generate the + * text, which also defaults to scriptWidth/Height but seems to typically be + * changed to more closely match the native screen resolution. */ uint16 _xResolution, _yResolution; /** - * The skip (transparent) color for the cel. When - * compositing, any pixels matching this color will not - * be copied to the buffer. + * The skip (transparent) color for the cel. When compositing, any pixels + * matching this color will not be copied to the buffer. */ uint8 _skipColor; /** - * Whether or not this cel has any transparent regions. - * This is used for optimised drawing of non-transparent - * cels. + * Whether or not this cel has any transparent regions. This is used for + * optimised drawing of non-transparent cels. */ bool _transparent; @@ -339,15 +321,14 @@ public: CelCompressionType _compressionType; /** - * Whether or not this cel should be palette-remapped? + * Whether or not this cel contains remap pixels. */ bool _remap; /** - * If true, the cel contains pre-mirrored picture data. - * This value comes directly from the resource data and - * is XORed with the `_mirrorX` property of the owner - * screen item when rendering. + * If true, the cel contains pre-mirrored picture data. This value comes + * directly from the resource data and is XORed with the `_mirrorX` property + * of the owner screen item when rendering. */ bool _mirrorX; @@ -364,71 +345,63 @@ public: virtual ~CelObj() {}; /** - * Draws the cel to the target buffer using the priority - * and positioning information from the given screen - * item. The mirroring of the cel will be unchanged from - * any previous call to draw. + * Draws the cel to the target buffer using the priority and positioning + * information from the given screen item. The mirroring of the cel will be + * unchanged from any previous call to draw. */ void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const; /** - * Draws the cel to the target buffer using the priority - * and positioning information from the given screen - * item and the given mirror flag. + * Draws the cel to the target buffer using the priority and positioning + * information from the given screen item and the given mirror flag. * - * @note In SCI engine, this function was a virtual - * function, but CelObjView, CelObjPic, and CelObjMem - * all used the same function and the compiler - * deduplicated the copies; we deduplicate the source by - * putting the implementation on CelObj instead of - * copying it to 3/4 of the subclasses. + * @note In SSCI, this function was a virtual function, but CelObjView, + * CelObjPic, and CelObjMem all used the same function and the compiler + * deduplicated the copies; we deduplicate the source by putting the + * implementation on CelObj instead of copying it to 3/4 of the subclasses. */ virtual void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX); /** - * Draws the cel to the target buffer using the - * positioning and mirroring information from the - * provided arguments. + * Draws the cel to the target buffer using the positioning and mirroring + * information from the provided arguments. * - * @note In SCI engine, this function was a virtual - * function, but CelObjView, CelObjPic, and CelObjMem - * all used the same function and the compiler - * deduplicated the copies; we deduplicate the source by - * putting the implementation on CelObj instead of - * copying it to 3/4 of the subclasses. + * @note In SSCI, this function was a virtual function, but CelObjView, + * CelObjPic, and CelObjMem all used the same function and the compiler + * deduplicated the copies; we deduplicate the source by putting the + * implementation on CelObj instead of copying it to 3/4 of the subclasses. */ virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX); /** - * Draws the cel to the target buffer using the given - * position and scaling parameters. The mirroring of the - * cel will be unchanged from any previous call to draw. + * Draws the cel to the target buffer using the given position and scaling + * parameters. The mirroring of the cel will be unchanged from any previous + * call to draw. */ void drawTo(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const; /** - * Creates a copy of this cel on the free store and - * returns a pointer to the new object. The new cel will - * point to a shared copy of bitmap/resource data. + * Creates a copy of this cel on the free store and returns a pointer to the + * new object. The new cel will point to a shared copy of bitmap/resource + * data. */ virtual CelObj *duplicate() const = 0; /** - * Retrieves a pointer to the raw resource data for this - * cel. This method cannot be used with a CelObjColor. + * Retrieves a pointer to the raw resource data for this cel. This method + * cannot be used with a CelObjColor. */ virtual const SciSpan<const byte> getResPointer() const = 0; /** - * Reads the pixel at the given coordinates. This method - * is valid only for CelObjView and CelObjPic. + * Reads the pixel at the given coordinates. This method is valid only for + * CelObjView and CelObjPic. */ virtual uint8 readPixel(const uint16 x, const uint16 y, const bool mirrorX) const; /** - * Submits the palette from this cel to the palette - * manager for integration into the master screen - * palette. + * Submits the palette from this cel to the palette manager for integration + * into the master screen palette. */ void submitPalette() const; @@ -454,7 +427,8 @@ private: void drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32 + // SSCI includes versions of the above functions with priority parameters + // which are not actually used in SCI32 void drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; @@ -464,37 +438,34 @@ private: void drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32 + // SSCI includes versions of the above functions with priority parameters + // which are not actually used in SCI32 #pragma mark - #pragma mark CelObj - Caching protected: /** - * A monotonically increasing cache ID used to identify - * the least recently used item in the cache for - * replacement. + * A monotonically increasing cache ID used to identify the least recently + * used item in the cache for replacement. */ static int _nextCacheId; /** - * A cache of cel objects used to avoid reinitialisation - * overhead for cels with the same CelInfo32. + * A cache of cel objects used to avoid reinitialisation overhead for cels + * with the same CelInfo32. */ - // NOTE: At least SQ6 uses a fixed cache size of 100. - static CelCache *_cache; + static Common::ScopedPtr<CelCache> _cache; /** - * Searches the cel cache for a CelObj matching the - * provided CelInfo32. If not found, -1 is returned. - * nextInsertIndex will receive the index of the oldest - * item in the cache, which can be used to replace - * the oldest item with a newer item. + * Searches the cel cache for a CelObj matching the provided CelInfo32. If + * not found, -1 is returned. `nextInsertIndex` will receive the index of + * the oldest item in the cache, which can be used to replace the oldest + * item with a newer item. */ int searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const; /** - * Puts a copy of this CelObj into the cache at the - * given cache index. + * Puts a copy of this CelObj into the cache at the given cache index. */ void putCopyInCache(int index) const; }; @@ -503,22 +474,20 @@ protected: #pragma mark CelObjView /** - * A CelObjView is the drawing primitive for a View type - * resource. Each CelObjView corresponds to a single cel - * within a single loop of a view. + * A CelObjView is the drawing primitive for a View type resource. Each + * CelObjView corresponds to a single cel within a single loop of a view. */ class CelObjView : public CelObj { private: /** - * Analyses resources without baked-in remap flags - * to determine whether or not they should be remapped. + * Analyses resources without baked-in remap flags to determine whether or + * not they should be remapped. */ bool analyzeUncompressedForRemap() const; /** - * Analyses compressed resources without baked-in remap - * flags to determine whether or not they should be - * remapped. + * Analyses compressed resources without baked-in remap flags to determine + * whether or not they should be remapped. */ bool analyzeForRemap() const; @@ -532,9 +501,8 @@ public: static int16 getNumCels(const GuiResourceId viewId, const int16 loopNo); /** - * Draws the cel to the target buffer using the - * positioning, mirroring, and scaling information from - * the provided arguments. + * Draws the cel to the target buffer using the positioning, mirroring, and + * scaling information from the provided arguments. */ void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX, const Ratio &scaleX, const Ratio &scaleY); @@ -548,16 +516,14 @@ public: #pragma mark CelObjPic /** - * A CelObjPic is the drawing primitive for a Picture type - * resource. Each CelObjPic corresponds to a single cel - * within a picture. + * A CelObjPic is the drawing primitive for a Picture type resource. Each + * CelObjPic corresponds to a single cel within a picture. */ class CelObjPic : public CelObj { private: /** - * Analyses uncompressed resources without baked-in skip - * flags to determine whether or not they can use fast - * blitting. + * Analyses uncompressed resources without baked-in skip flags to determine + * whether or not they can use fast blitting. */ bool analyzeUncompressedForSkip() const; @@ -568,14 +534,13 @@ public: uint8 _celCount; /** - * The position of this cel relative to the top-left - * corner of the picture. + * The position of this cel relative to the top-left corner of the picture. */ Common::Point _relativePosition; /** - * The z-buffer priority for this cel. Higher prorities - * are drawn on top of lower priorities. + * The z-buffer priority for this cel. Higher prorities are drawn on top of + * lower priorities. */ int16 _priority; @@ -593,10 +558,9 @@ public: #pragma mark CelObjMem /** - * A CelObjMem is the drawing primitive for arbitrary - * bitmaps generated in memory. Generated bitmaps in SCI32 - * include text & vector drawings and per-pixel screen - * transitions like dissolves. + * A CelObjMem is the drawing primitive for arbitrary bitmaps generated in + * memory. Generated bitmaps in SCI32 include text & vector drawings and + * per-pixel screen transitions like dissolves. */ class CelObjMem : public CelObj { public: diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index 52a9dfa9b3..fa4396c1d8 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -43,8 +43,8 @@ GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *tex _gfxText32(text), _overwriteMode(false), _nextCursorFlashTick(0), - // SSCI used a memory handle for a ScrollWindow object - // as ID. We use a simple numeric handle instead. + // SSCI used a memory handle for a ScrollWindow object as ID. We use a + // simple numeric handle instead. _nextScrollWindowId(10000) {} GfxControls32::~GfxControls32() { @@ -137,27 +137,27 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo()); plane->_screenItemList.add(screenItem); - // frameOut must be called after the screen item is - // created, and before it is updated at the end of the - // event loop, otherwise it has both created and updated - // flags set which crashes the engine (it runs updates - // before creations) + // frameOut must be called after the screen item is created, and before it + // is updated at the end of the event loop, otherwise it has both created + // and updated flags set which crashes the engine (updates are handled + // before creations, but the screen item is not in the correct state for an + // update) g_sci->_gfxFrameout->frameOut(true); EventManager *eventManager = g_sci->getEventManager(); bool clearTextOnInput = true; bool textChanged = false; for (;;) { - // We peek here because the last event needs to be allowed to - // dispatch a second time to the normal event handling system. - // In the actual engine, the event is always consumed and then - // the last event just gets posted back to the event manager for - // reprocessing, but instead, we only remove the event from the - // queue *after* we have determined it is not a defocusing event + // We peek here because the last event needs to be allowed to dispatch a + // second time to the normal event handling system. In SSCI, the event + // is always consumed and then the last event just gets posted back to + // the event manager for reprocessing, but instead, we only remove the + // event from the queue *after* we have determined it is not a + // defocusing event const SciEvent event = eventManager->getSciEvent(kSciEventAny | kSciEventPeek); bool focused = true; - // Original engine did not have a QUIT event but we have to handle it + // SSCI did not have a QUIT event, but we do, so we have to handle it if (event.type == kSciEventQuit) { focused = false; } else if (event.type == kSciEventMousePress && !editorPlaneRect.contains(event.mousePosSci)) { @@ -179,18 +179,16 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { break; } - // Consume the event now that we know it is not one of the - // defocusing events above + // Consume the event now that we know it is not one of the defocusing + // events above if (event.type != kSciEventNone) eventManager->getSciEvent(kSciEventAny); - // NOTE: In the original engine, the font and bitmap were - // reset here on each iteration through the loop, but it - // doesn't seem like this should be necessary since - // control is not yielded back to the VM until input is - // received, which means there is nothing that could modify - // the GfxText32's state with a different font in the - // meantime + // In SSCI, the font and bitmap were reset here on each iteration + // through the loop, but this is not necessary since control is not + // yielded back to the VM until input is received, which means there is + // nothing that could modify the GfxText32's state with a different font + // in the meantime bool shouldDeleteChar = false; bool shouldRedrawText = false; @@ -223,8 +221,8 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { case kSciKeyInsert: clearTextOnInput = false; - // Redrawing also changes the cursor rect to - // reflect the new insertion mode + // Redrawing also changes the cursor rect to reflect the new + // insertion mode shouldRedrawText = true; _overwriteMode = !_overwriteMode; break; @@ -324,10 +322,9 @@ void GfxControls32::drawCursor(TextEditor &editor) { const int16 scaledFontHeight = _gfxText32->scaleUpHeight(_gfxText32->_font->getHeight()); - // NOTE: The original code branched on borderColor here but - // the two branches appeared to be identical, differing only - // because the compiler decided to be differently clever - // when optimising multiplication in each branch + // SSCI branched on borderColor here but the two branches appeared to be + // identical, differing only because the compiler decided to be + // differently clever when optimising multiplication in each branch if (_overwriteMode) { editor.cursorRect.top = editor.textRect.top; editor.cursorRect.setHeight(scaledFontHeight); @@ -394,8 +391,8 @@ ScrollWindow::ScrollWindow(SegManager *segMan, const Common::Rect &gameRect, con _gfxText32.setFont(_fontId); _pointSize = _gfxText32._font->getHeight(); - const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const uint16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const uint16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); Common::Rect bitmapRect(gameRect); mulinc(bitmapRect, Ratio(_gfxText32._xResolution, scriptWidth), Ratio(_gfxText32._yResolution, scriptHeight)); @@ -465,9 +462,8 @@ reg_t ScrollWindow::add(const Common::String &text, const GuiResourceId fontId, if (_entries.size() == _maxNumEntries) { ScrollWindowEntry removedEntry = _entries.remove_at(0); _text.erase(0, removedEntry.text.size()); - // `_firstVisibleChar` will be reset shortly if - // `scrollTo` is true, so there is no reason to - // update it + // `_firstVisibleChar` will be reset shortly if `scrollTo` is true, so + // there is no reason to update it if (!scrollTo) { _firstVisibleChar -= removedEntry.text.size(); } @@ -476,17 +472,17 @@ reg_t ScrollWindow::add(const Common::String &text, const GuiResourceId fontId, _entries.push_back(ScrollWindowEntry()); ScrollWindowEntry &entry = _entries.back(); - // NOTE: In SSCI the line ID was a memory handle for the - // string of this line. We use a numeric ID instead. + // In SSCI, the line ID was a memory handle for the string of this line. We + // use a numeric ID instead. entry.id = make_reg(0, _nextEntryId++); if (_nextEntryId > _maxNumEntries) { _nextEntryId = 1; } - // NOTE: In SSCI this was updated after _text was - // updated, which meant there was an extra unnecessary - // subtraction operation (subtracting `entry.text` size) + // In SSCI, this was updated after _text was updated, which meant there was + // an extra unnecessary subtraction operation (subtracting `entry.text` + // size) if (scrollTo) { _firstVisibleChar = _text.size(); } @@ -517,9 +513,8 @@ void ScrollWindow::fillEntry(ScrollWindowEntry &entry, const Common::String &tex // with properties -1 can inherit properties from the previously rendered // line instead of the defaults. - // NOTE: SSCI added "|s<lineIndex>|" here, but |s| is - // not a valid control code, so it just always ended up - // getting skipped + // SSCI added "|s<lineIndex>|" here, but |s| is not a valid control code, so + // it just always ended up getting skipped by the text rendering code if (entry.fontId != -1) { formattedText += Common::String::format("|f%d|", entry.fontId); } @@ -705,9 +700,8 @@ void ScrollWindow::pageDown() { void ScrollWindow::computeLineIndices() { _gfxText32.setFont(_fontId); - // NOTE: Unlike SSCI, foreColor and alignment are not - // set since these properties do not affect the width of - // lines + // Unlike SSCI, foreColor and alignment are not set since these properties + // do not affect the width of lines if (_gfxText32._font->getHeight() != _pointSize) { error("Illegal font size font = %d pointSize = %d, should be %d.", _fontId, _gfxText32._font->getHeight(), _pointSize); @@ -717,8 +711,8 @@ void ScrollWindow::computeLineIndices() { _startsOfLines.clear(); - // NOTE: The original engine had a 1000-line limit; we - // do not enforce any limit + // SSCI had a 1000-line limit; we do not enforce any limit since we use + // dynamic containers for (uint charIndex = 0; charIndex < _text.size(); ) { _startsOfLines.push_back(charIndex); charIndex += _gfxText32.getTextCount(_text, charIndex, lineRect, false); diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h index 680c70d2d6..de292da3b7 100644 --- a/engines/sci/graphics/controls32.h +++ b/engines/sci/graphics/controls32.h @@ -32,8 +32,8 @@ class GfxScreen; class GfxText32; enum MessageBoxStyle { - kMessageBoxOK = 0x0, - kMessageBoxYesNo = 0x4 + kMessageBoxOK = 0x0, + kMessageBoxYesNo = 0x4 }; struct TextEditor { @@ -89,14 +89,12 @@ struct TextEditor { uint16 cursorCharPosition; /** - * Whether or not the cursor is currently drawn to the - * screen. + * Whether or not the cursor is currently drawn to the screen. */ bool cursorIsDrawn; /** - * The rectangle for drawing the input cursor, in bitmap - * pixels. + * The rectangle for drawing the input cursor, in bitmap pixels. */ Common::Rect cursorRect; @@ -111,30 +109,26 @@ struct TextEditor { */ struct ScrollWindowEntry { /** - * ID of the line. In SSCI this was actually a memory - * handle for the string of this line. We use a simple - * numeric ID instead. + * ID of the line. In SSCI this was actually a memory handle for the string + * of this line. We use a simple numeric ID instead. */ reg_t id; /** - * The alignment to use when rendering this line of - * text. If -1, the default alignment from the - * corresponding ScrollWindow will be used. + * The alignment to use when rendering this line of text. If -1, the default + * alignment from the corresponding ScrollWindow will be used. */ TextAlign alignment; /** - * The color to use to render this line of text. If -1, - * the default foreground color from the corresponding - * ScrollWindow will be used. + * The color to use to render this line of text. If -1, the default + * foreground color from the corresponding ScrollWindow will be used. */ int16 foreColor; /** - * The font to use to render this line of text. If -1, - * the default font from the corresponding ScrollWindow - * will be used. + * The font to use to render this line of text. If -1, the default font from + * the corresponding ScrollWindow will be used. */ GuiResourceId fontId; @@ -155,16 +149,15 @@ public: ~ScrollWindow(); /** - * Adds a new text entry to the window. If `fontId`, - * `foreColor`, or `alignment` are `-1`, the - * ScrollWindow's default values will be used. + * Adds a new text entry to the window. If `fontId`, `foreColor`, or + * `alignment` are `-1`, the ScrollWindow's default values will be used. */ reg_t add(const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment, const bool scrollTo); /** - * Modifies an existing text entry with the given ID. If - * `fontId`, `foreColor`, or `alignment` are `-1`, the - * ScrollWindow's default values will be used. + * Modifies an existing text entry with the given ID. If `fontId`, + * `foreColor`, or `alignment` are `-1`, the ScrollWindow's default values + * will be used. */ reg_t modify(const reg_t id, const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment, const bool scrollTo); @@ -179,9 +172,8 @@ public: void hide(); /** - * Gets the number of lines that the content of a - * ScrollWindow is scrolled upward, as a ratio of the - * total number of lines of content. + * Gets the number of lines that the content of a ScrollWindow is scrolled + * upward, as a ratio of the total number of lines of content. */ Ratio where() const; @@ -221,8 +213,8 @@ public: void pageDown(); /** - * Gets a reference to the in-memory bitmap that - * is used to render the text in the ScrollWindow. + * Gets a reference to the in-memory bitmap that is used to render the text + * in the ScrollWindow. */ const reg_t getBitmap() const { return _bitmap; } @@ -232,31 +224,26 @@ private: typedef Common::Array<ScrollWindowEntry> EntriesList; /** - * A convenience function that fills a - * ScrollWindowEntry's properties. + * A convenience function that fills a ScrollWindowEntry's properties. */ void fillEntry(ScrollWindowEntry &entry, const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment); /** - * Rescans the entire text of the ScrollWindow when an - * entry is added or modified, calculating the character - * offsets of all line endings, the total number of - * lines of text, the height of the viewport (in lines - * of text), the last character visible in the viewport - * (assuming the viewport is scrolled to the top), and - * the line index of the bottommost visible line - * (assuming the viewport is scrolled to the top). + * Rescans the entire text of the ScrollWindow when an entry is added or + * modified, calculating the character offsets of all line endings, the + * total number of lines of text, the height of the viewport (in lines of + * text), the last character visible in the viewport (assuming the viewport + * is scrolled to the top), and the line index of the bottommost visible + * line (assuming the viewport is scrolled to the top). */ void computeLineIndices(); /** - * Calculates which text is visible within the - * ScrollWindow's viewport and renders the text to the - * internal bitmap. + * Calculates which text is visible within the ScrollWindow's viewport and + * renders the text to the internal bitmap. * - * If `doFrameOut` is true, the screen will be refreshed - * immediately instead of waiting for the next call to - * `kFrameOut`. + * If `doFrameOut` is true, the screen will be refreshed immediately instead + * of waiting for the next call to `kFrameOut`. */ void update(const bool doFrameOut); @@ -266,21 +253,18 @@ private: GfxText32 _gfxText32; /** - * The individual text entries added to the - * ScrollWindow. + * The individual text entries added to the ScrollWindow. */ EntriesList _entries; /** - * The maximum number of entries allowed. Once this - * limit is reached, the oldest entry will be removed - * when a new entry is added. + * The maximum number of entries allowed. Once this limit is reached, the + * oldest entry will be removed when a new entry is added. */ uint _maxNumEntries; /** - * A mapping from a line index to the line's character - * offset in `_text`. + * A mapping from a line index to the line's character offset in `_text`. */ Common::Array<int> _startsOfLines; @@ -300,39 +284,36 @@ private: int _firstVisibleChar; /** - * The index of the line that is at the top of the - * viewport. + * The index of the line that is at the top of the viewport. */ int _topVisibleLine; /** - * The index of the last visible character in `_text`, - * or -1 if there is no text. + * The index of the last visible character in `_text`, or -1 if there is no + * text. */ int _lastVisibleChar; /** - * The index of the line that is at the bottom of the - * viewport, or -1 if there is no text. + * The index of the line that is at the bottom of the viewport, or -1 if + * there is no text. */ int _bottomVisibleLine; /** - * The total number of lines in the backbuffer. This - * number may be higher than the total number of entries - * if an entry contains newlines. + * The total number of lines in the backbuffer. This number may be higher + * than the total number of entries if an entry contains newlines. */ int _numLines; /** - * The number of lines that are currently visible in the - * text area of the window. + * The number of lines that are currently visible in the text area of the + * window. */ int _numVisibleLines; /** - * The plane in which the ScrollWindow should be - * rendered. + * The plane in which the ScrollWindow should be rendered. */ reg_t _plane; @@ -347,20 +328,18 @@ private: uint8 _backColor; /** - * The default border color of the text bitmap. If -1, - * the viewport will have no border. + * The default border color of the text bitmap. If -1, the viewport will + * have no border. */ int16 _borderColor; /** - * The default font used for rendering text into the - * ScrollWindow. + * The default font used for rendering text into the ScrollWindow. */ GuiResourceId _fontId; /** - * The default text alignment used for rendering text - * into the ScrollWindow. + * The default text alignment used for rendering text into the ScrollWindow. */ TextAlign _alignment; @@ -370,22 +349,20 @@ private: bool _visible; /** - * The dimensions of the text box inside the font - * bitmap, in text-system coordinates. + * The dimensions of the text box inside the font bitmap, in text-system + * coordinates. */ Common::Rect _textRect; /** - * The top-left corner of the ScrollWindow's screen - * item, in game script coordinates, relative to the - * parent plane. + * The top-left corner of the ScrollWindow's screen item, in game script + * coordinates, relative to the parent plane. */ Common::Point _position; /** - * The height of the default font in screen pixels. All - * fonts rendered into the ScrollWindow must have this - * same height. + * The height of the default font in screen pixels. All fonts rendered into + * the ScrollWindow must have this same height. */ uint8 _pointSize; @@ -395,8 +372,8 @@ private: reg_t _bitmap; /** - * A monotonically increasing ID used to identify - * text entries added to the ScrollWindow. + * A monotonically increasing ID used to identify text entries added to the + * ScrollWindow. */ uint16 _nextEntryId; @@ -407,7 +384,8 @@ private: }; /** - * Controls class, handles drawing of controls in SCI32 (SCI2, SCI2.1, SCI3) games + * Controls class, handles drawing of UI controls in SCI32 games that use kernel + * controls instead of custom script controls. */ class GfxControls32 { public: @@ -426,14 +404,13 @@ public: private: /** - * If true, typing will overwrite text that already - * exists at the text cursor's current position. + * If true, typing will overwrite text that already exists at the text + * cursor's current position. */ bool _overwriteMode; /** - * The tick at which the text cursor should be toggled - * by `flashCursor`. + * The tick at which the text cursor should be toggled by `flashCursor`. */ uint32 _nextCursorFlashTick; @@ -448,8 +425,8 @@ private: void eraseCursor(TextEditor &editor); /** - * Toggles the text cursor for the given editor to be - * either drawn or erased. + * Toggles the text cursor for the given editor to be either drawn or + * erased. */ void flashCursor(TextEditor &editor); @@ -457,9 +434,8 @@ private: #pragma mark Scrollable window control public: /** - * Creates a new scrollable window and returns the ID - * for the new window, which is used by game scripts to - * interact with scrollable windows. + * Creates a new scrollable window and returns the ID for the new window, + * which is used by game scripts to interact with scrollable windows. */ reg_t makeScrollWindow(const Common::Rect &gameRect, const Common::Point &position, const reg_t plane, const uint8 defaultForeColor, const uint8 defaultBackColor, const GuiResourceId defaultFontId, const TextAlign defaultAlignment, const int16 defaultBorderColor, const uint16 maxNumEntries); @@ -477,8 +453,7 @@ private: typedef Common::HashMap<uint16, ScrollWindow *> ScrollWindowMap; /** - * Monotonically increasing ID used to identify - * ScrollWindow instances. + * Monotonically increasing ID used to identify ScrollWindow instances. */ uint16 _nextScrollWindowId; @@ -497,8 +472,7 @@ public: private: /** - * Convenience function for creating and showing a - * message box. + * Convenience function for creating and showing a message box. */ int16 showMessageBox(const Common::String &message, const char *const okLabel, const char *const altLabel, const int16 okValue, const int16 altValue); }; diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp index 74fbafa2f0..a189f7405d 100644 --- a/engines/sci/graphics/cursor32.cpp +++ b/engines/sci/graphics/cursor32.cpp @@ -34,22 +34,22 @@ namespace Sci { GfxCursor32::GfxCursor32() : _hideCount(0), _position(0, 0), - _writeToVMAP(false) { + _needsPaint(false) { } -void GfxCursor32::init(const Buffer &vmap) { - _vmap = vmap; - _vmapRegion.rect = Common::Rect(_vmap.screenWidth, _vmap.screenHeight); - _vmapRegion.data = (byte *)_vmap.getPixels(); - _restrictedArea = _vmapRegion.rect; +void GfxCursor32::init(const Buffer &outputBuffer) { + _screen = outputBuffer; + _screenRegion.rect = Common::Rect(_screen.w, _screen.h); + _screenRegion.data = static_cast<byte *>(_screen.getPixels()); + _restrictedArea = _screenRegion.rect; } GfxCursor32::~GfxCursor32() { free(_cursor.data); free(_cursorBack.data); - free(_drawBuff1.data); - free(_drawBuff2.data); - free(_savedVmapRegion.data); + free(_scratch1.data); + free(_scratch2.data); + free(_savedScreenRegion.data); } void GfxCursor32::hide() { @@ -57,26 +57,28 @@ void GfxCursor32::hide() { return; } + g_system->showMouse(false); if (!_cursorBack.rect.isEmpty()) { - drawToHardware(_cursorBack); + drawToScreen(_cursorBack); } } void GfxCursor32::revealCursor() { _cursorBack.rect = _cursor.rect; - _cursorBack.rect.clip(_vmapRegion.rect); + _cursorBack.rect.clip(_screenRegion.rect); if (_cursorBack.rect.isEmpty()) { return; } - readVideo(_cursorBack); - _drawBuff1.rect = _cursor.rect; - copy(_drawBuff1, _cursorBack); - paint(_drawBuff1, _cursor); - drawToHardware(_drawBuff1); + copyFromScreen(_cursorBack); + _scratch1.rect = _cursor.rect; + copy<false>(_scratch1, _cursorBack); + copy<true>(_scratch1, _cursor); + drawToScreen(_scratch1); } -void GfxCursor32::paint(DrawRegion &target, const DrawRegion &source) { +template <bool SKIP> +void GfxCursor32::copy(DrawRegion &target, const DrawRegion &source) { if (source.rect.isEmpty()) { return; } @@ -96,25 +98,33 @@ void GfxCursor32::paint(DrawRegion &target, const DrawRegion &source) { const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; const uint8 skipColor = source.skipColor; - const int16 sourceStride = source.rect.width() - drawRectWidth; - const int16 targetStride = target.rect.width() - drawRectWidth; + int16 sourceStride = source.rect.width(); + int16 targetStride = target.rect.width(); + if (SKIP) { + sourceStride -= drawRectWidth; + targetStride -= drawRectWidth; + } for (int16 y = 0; y < drawRectHeight; ++y) { - for (int16 x = 0; x < drawRectWidth; ++x) { - if (*sourcePixel != skipColor) { - *targetPixel = *sourcePixel; + if (SKIP) { + for (int16 x = 0; x < drawRectWidth; ++x) { + if (*sourcePixel != skipColor) { + *targetPixel = *sourcePixel; + } + ++targetPixel; + ++sourcePixel; } - ++targetPixel; - ++sourcePixel; + } else { + memcpy(targetPixel, sourcePixel, drawRectWidth); } - sourcePixel += sourceStride; targetPixel += targetStride; + sourcePixel += sourceStride; } } -void GfxCursor32::drawToHardware(const DrawRegion &source) { +void GfxCursor32::drawToScreen(const DrawRegion &source) { Common::Rect drawRect(source.rect); - drawRect.clip(_vmapRegion.rect); + drawRect.clip(_screenRegion.rect); const int16 sourceXOffset = drawRect.left - source.rect.left; const int16 sourceYOffset = drawRect.top - source.rect.top; byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; @@ -127,12 +137,14 @@ void GfxCursor32::unhide() { return; } + g_system->showMouse(true); _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y); revealCursor(); } void GfxCursor32::show() { if (_hideCount) { + g_system->showMouse(true); _hideCount = 0; _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y); revealCursor(); @@ -142,10 +154,10 @@ void GfxCursor32::show() { void GfxCursor32::setRestrictedArea(const Common::Rect &rect) { _restrictedArea = rect; - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); mulru(_restrictedArea, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight), 0); @@ -174,7 +186,7 @@ void GfxCursor32::setRestrictedArea(const Common::Rect &rect) { } void GfxCursor32::clearRestrictedArea() { - _restrictedArea = _vmapRegion.rect; + _restrictedArea = _screenRegion.rect; } void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) { @@ -250,7 +262,7 @@ void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const // threshold size because inventory items usually have a // high-resolution cursor representation. bool pixelDouble = false; - if (g_sci->_gfxFrameout->_isHiRes && + if (g_sci->_gfxFrameout->isHiRes() && (g_sci->getGameId() == GID_GK1 || (g_sci->getGameId() == GID_PQ4 && _width <= 22 && _height <= 22))) { @@ -266,7 +278,8 @@ void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const memset(_cursor.data, 255, _width * _height); _cursor.skipColor = 255; - Buffer target(_width, _height, _cursor.data); + Buffer target; + target.init(_width, _height, _width, _cursor.data, Graphics::PixelFormat::createFormatCLUT8()); if (pixelDouble) { view.draw(target, _cursor.rect, Common::Point(0, 0), false, 2, 2); } else { @@ -279,65 +292,36 @@ void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const _cursor.rect = Common::Rect(_width, _height); *_cursor.data = _cursor.skipColor; _cursorBack.rect = _cursor.rect; - _cursorBack.rect.clip(_vmapRegion.rect); + _cursorBack.rect.clip(_screenRegion.rect); if (!_cursorBack.rect.isEmpty()) { - readVideo(_cursorBack); + copyFromScreen(_cursorBack); } } _cursorBack.data = (byte *)realloc(_cursorBack.data, _width * _height); memset(_cursorBack.data, 0, _width * _height); - _drawBuff1.data = (byte *)realloc(_drawBuff1.data, _width * _height); - _drawBuff2.data = (byte *)realloc(_drawBuff2.data, _width * _height * 4); - _savedVmapRegion.data = (byte *)realloc(_savedVmapRegion.data, _width * _height); + _scratch1.data = (byte *)realloc(_scratch1.data, _width * _height); + _scratch2.data = (byte *)realloc(_scratch2.data, _width * _height * 4); + _savedScreenRegion.data = (byte *)realloc(_savedScreenRegion.data, _width * _height); unhide(); } -void GfxCursor32::readVideo(DrawRegion &target) { - // NOTE: In SSCI, mouse events were received via hardware interrupt, so - // there was a separate branch here that would read from VRAM instead of - // from the game's back buffer when a mouse event was received while the - // back buffer was being updated. In ScummVM, mouse events are polled, which - // means it is not possible to receive a mouse event during a back buffer - // update, so the code responsible for handling that is removed. - copy(target, _vmapRegion); -} - -void GfxCursor32::copy(DrawRegion &target, const DrawRegion &source) { - if (source.rect.isEmpty()) { - return; - } - - Common::Rect drawRect(source.rect); - drawRect.clip(target.rect); - if (drawRect.isEmpty()) { - return; - } - - const int16 sourceXOffset = drawRect.left - source.rect.left; - const int16 sourceYOffset = drawRect.top - source.rect.top; - const int16 drawWidth = drawRect.width(); - const int16 drawHeight = drawRect.height(); - - byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left); - const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; - - const int16 sourceStride = source.rect.width(); - const int16 targetStride = target.rect.width(); - - for (int y = 0; y < drawHeight; ++y) { - memcpy(targetPixel, sourcePixel, drawWidth); - targetPixel += targetStride; - sourcePixel += sourceStride; - } +void GfxCursor32::copyFromScreen(DrawRegion &target) { + // In SSCI, mouse events were received via hardware interrupt, so there was + // a separate branch here that would read from VRAM instead of from the + // game's back buffer when a mouse event was received while the back buffer + // was being updated. In ScummVM, mouse events are polled, which means it is + // not possible to receive a mouse event during a back buffer update, so the + // code responsible for handling that is removed. + copy<false>(target, _screenRegion); } void GfxCursor32::setPosition(const Common::Point &position) { - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); Common::Point newPosition; newPosition.x = (position.x * Ratio(screenWidth, scriptWidth)).toInt(); @@ -349,32 +333,32 @@ void GfxCursor32::setPosition(const Common::Point &position) { } void GfxCursor32::gonnaPaint(Common::Rect paintRect) { - if (!_hideCount && !_writeToVMAP && !_cursorBack.rect.isEmpty()) { + if (!_hideCount && !_needsPaint && !_cursorBack.rect.isEmpty()) { paintRect.left &= ~3; paintRect.right |= 3; if (_cursorBack.rect.intersects(paintRect)) { - _writeToVMAP = true; + _needsPaint = true; } } } void GfxCursor32::paintStarting() { - if (_writeToVMAP) { - _savedVmapRegion.rect = _cursor.rect; - copy(_savedVmapRegion, _vmapRegion); - paint(_vmapRegion, _cursor); + if (_needsPaint) { + _savedScreenRegion.rect = _cursor.rect; + copy<false>(_savedScreenRegion, _screenRegion); + copy<true>(_screenRegion, _cursor); } } void GfxCursor32::donePainting() { - if (_writeToVMAP) { - copy(_vmapRegion, _savedVmapRegion); - _savedVmapRegion.rect = Common::Rect(); - _writeToVMAP = false; + if (_needsPaint) { + copy<false>(_screenRegion, _savedScreenRegion); + _savedScreenRegion.rect = Common::Rect(); + _needsPaint = false; } if (!_hideCount && !_cursorBack.rect.isEmpty()) { - copy(_cursorBack, _vmapRegion); + copy<false>(_cursorBack, _screenRegion); } } @@ -423,45 +407,45 @@ void GfxCursor32::move() { } // Cursor moved offscreen - if (!_cursor.rect.intersects(_vmapRegion.rect)) { - drawToHardware(_cursorBack); + if (!_cursor.rect.intersects(_screenRegion.rect)) { + drawToScreen(_cursorBack); return; } if (!_cursor.rect.intersects(_cursorBack.rect)) { // Cursor moved to a completely different part of the screen - _drawBuff1.rect = _cursor.rect; - _drawBuff1.rect.clip(_vmapRegion.rect); - readVideo(_drawBuff1); + _scratch1.rect = _cursor.rect; + _scratch1.rect.clip(_screenRegion.rect); + copyFromScreen(_scratch1); - _drawBuff2.rect = _drawBuff1.rect; - copy(_drawBuff2, _drawBuff1); + _scratch2.rect = _scratch1.rect; + copy<false>(_scratch2, _scratch1); - paint(_drawBuff1, _cursor); - drawToHardware(_drawBuff1); + copy<true>(_scratch1, _cursor); + drawToScreen(_scratch1); - drawToHardware(_cursorBack); + drawToScreen(_cursorBack); _cursorBack.rect = _cursor.rect; - _cursorBack.rect.clip(_vmapRegion.rect); - copy(_cursorBack, _drawBuff2); + _cursorBack.rect.clip(_screenRegion.rect); + copy<false>(_cursorBack, _scratch2); } else { // Cursor moved, but still overlaps the previous cursor location Common::Rect mergedRect(_cursorBack.rect); mergedRect.extend(_cursor.rect); - mergedRect.clip(_vmapRegion.rect); + mergedRect.clip(_screenRegion.rect); - _drawBuff2.rect = mergedRect; - readVideo(_drawBuff2); + _scratch2.rect = mergedRect; + copyFromScreen(_scratch2); - copy(_drawBuff2, _cursorBack); + copy<false>(_scratch2, _cursorBack); _cursorBack.rect = _cursor.rect; - _cursorBack.rect.clip(_vmapRegion.rect); - copy(_cursorBack, _drawBuff2); + _cursorBack.rect.clip(_screenRegion.rect); + copy<false>(_cursorBack, _scratch2); - paint(_drawBuff2, _cursor); - drawToHardware(_drawBuff2); + copy<true>(_scratch2, _cursor); + drawToScreen(_scratch2); } } diff --git a/engines/sci/graphics/cursor32.h b/engines/sci/graphics/cursor32.h index 562a2c50de..fb07c49dbb 100644 --- a/engines/sci/graphics/cursor32.h +++ b/engines/sci/graphics/cursor32.h @@ -23,6 +23,7 @@ #ifndef SCI_GRAPHICS_CURSOR32_H #define SCI_GRAPHICS_CURSOR32_H +#include "common/array.h" // for Array #include "common/rect.h" // for Point, Rect #include "common/scummsys.h" // for int16, byte, uint8 #include "common/serializer.h" // for Serializable, Serializer (ptr only) @@ -37,11 +38,10 @@ public: ~GfxCursor32(); /** - * Initialises the cursor system with the given - * buffer to use as the output buffer for - * rendering the cursor. + * Initialises the cursor system with the given buffer to use as the output + * buffer for rendering the cursor. */ - void init(const Buffer &vmap); + void init(const Buffer &outputBuffer); /** * Called when the hardware mouse moves. @@ -49,41 +49,35 @@ public: bool deviceMoved(Common::Point &position); /** - * Called by GfxFrameout once for each show - * rectangle that is going to be drawn to - * hardware. + * Called by GfxFrameout once for each show rectangle that is going to be + * drawn to hardware. */ void gonnaPaint(Common::Rect paintRect); /** - * Called by GfxFrameout when the rendering to - * hardware begins. + * Called by GfxFrameout when the rendering to hardware begins. */ void paintStarting(); /** - * Called by GfxFrameout when the output buffer - * has finished rendering to hardware. + * Called by GfxFrameout when the output buffer has finished rendering to + * hardware. */ void donePainting(); /** - * Hides the cursor. Each call to `hide` will - * increment a hide counter, which must be - * returned to 0 before the cursor will be - * shown again. + * Hides the cursor. Each call to `hide` will increment a hide counter, + * which must be returned to 0 before the cursor will be shown again. */ void hide(); /** - * Shows the cursor, if the hide counter is - * returned to 0. + * Shows the cursor, if the hide counter is returned to 0. */ void unhide(); /** - * Shows the cursor regardless of the state of - * the hide counter. + * Shows the cursor regardless of the state of the hide counter. */ void show(); @@ -93,14 +87,12 @@ public: void setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo); /** - * Explicitly sets the position of the cursor, - * in game script coordinates. + * Explicitly sets the position of the cursor, in game script coordinates. */ void setPosition(const Common::Point &position); /** - * Sets the region that the mouse is allowed - * to move within. + * Sets the region that the mouse is allowed to move within. */ void setRestrictedArea(const Common::Rect &rect); @@ -109,10 +101,6 @@ public: */ void clearRestrictedArea(); -#ifdef ENABLE_SCI32_MAC - void setMacCursorRemapList(int cursorCount, reg_t *cursors); -#endif - virtual void saveLoadWithSerializer(Common::Serializer &ser); private: @@ -121,42 +109,41 @@ private: byte *data; uint8 skipColor; - DrawRegion() : rect(), data(nullptr) {} + DrawRegion() : data(nullptr) {} }; /** - * Information about the current cursor. - * Used to restore cursor when loading a - * savegame. + * Information about the current cursor. Used to restore cursor when loading + * a savegame. */ CelInfo32 _cursorInfo; /** - * Content behind the cursor? TODO + * The content of the frame buffer which was behind the cursor prior to its + * being drawn. */ DrawRegion _cursorBack; /** * Scratch buffer. */ - DrawRegion _drawBuff1; + DrawRegion _scratch1; /** * Scratch buffer 2. */ - DrawRegion _drawBuff2; + DrawRegion _scratch2; /** - * A draw region representing the current - * output buffer. + * A draw region representing the entire output buffer. */ - DrawRegion _vmapRegion; + DrawRegion _screenRegion; /** - * The content behind the cursor in the + * The region behind the cursor immediately before it is painted to the * output buffer. */ - DrawRegion _savedVmapRegion; + DrawRegion _savedScreenRegion; /** * The cursor bitmap. @@ -164,93 +151,82 @@ private: DrawRegion _cursor; /** - * The width and height of the cursor, - * in screen coordinates. + * The width and height of the cursor, in screen coordinates. */ int16 _width, _height; /** - * The output buffer where the cursor is - * rendered. + * The output buffer where the cursor is rendered. */ - Buffer _vmap; + Buffer _screen; /** - * The number of times the cursor has been - * hidden. + * The number of times the cursor has been hidden. */ int _hideCount; /** - * The rendered position of the cursor, in - * screen coordinates. + * The rendered position of the cursor, in screen coordinates. */ Common::Point _position; /** - * The position of the cursor hot spot, relative - * to the cursor origin, in screen pixels. + * The position of the cursor hot spot, relative to the cursor origin, in + * screen pixels. */ Common::Point _hotSpot; /** - * The area within which the cursor is allowed - * to move, in screen pixels. + * The area within which the cursor is allowed to move, in screen pixels. */ Common::Rect _restrictedArea; /** - * Indicates whether or not the cursor needs to - * be repainted on the output buffer due to a - * change of graphics in the area underneath the - * cursor. - */ - bool _writeToVMAP; - - // Mac versions of games use a remap list to remap their cursors - Common::Array<uint16> _macCursorRemap; - - /** - * Reads data from the output buffer or hardware - * to the given draw region. + * Indicates whether or not the cursor needs to be repainted on the output + * buffer due to a change of graphics in the area underneath the cursor. */ - void readVideo(DrawRegion &target); + bool _needsPaint; /** - * Reads data from the output buffer to the - * given draw region. + * Reads data from the output buffer to the given draw region. */ - void readVideoFromVmap(DrawRegion &target); + void copyFromScreen(DrawRegion &target); /** - * Copies pixel data from the given source to - * the given target. + * Copies pixel data from the given source to the given target. If SKIP is + * true, pixels that match the `skipColor` property of the source will be + * skipped. + * + * @note In SSCI, the function that did not handle skip color was called + * `copy` and the one that did was called `paint`. */ + template <bool SKIP> void copy(DrawRegion &target, const DrawRegion &source); /** - * Draws from the given source onto the given - * target, skipping pixels in the source that - * match the `skipColor` property. - */ - void paint(DrawRegion &target, const DrawRegion &source); - - /** - * Draws the cursor to the position it was - * drawn to prior to moving offscreen or being - * hidden by a call to `hide`. + * Draws the cursor to the position it was drawn to prior to moving + * offscreen or being hidden by a call to `hide`. */ void revealCursor(); /** * Draws the given source to the output buffer. */ - void drawToHardware(const DrawRegion &source); + void drawToScreen(const DrawRegion &source); /** * Renders the cursor at its new location. */ void move(); + +#ifdef ENABLE_SCI32_MAC +public: + void setMacCursorRemapList(int cursorCount, reg_t *cursors); + +private: + // Mac versions of games use a remap list to remap their cursors + Common::Array<uint16> _macCursorRemap; +#endif }; } // End of namespace Sci diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index f2ae0dfdba..434c5a0fec 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -61,7 +61,7 @@ namespace Sci { GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor) : - _isHiRes(gameIsHiRes()), + _isHiRes(detectHiRes()), _palette(palette), _cursor(cursor), _segMan(segMan), @@ -74,15 +74,13 @@ GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitio _lastScreenUpdateTick(0) { if (g_sci->getGameId() == GID_PHANTASMAGORIA) { - _currentBuffer = Buffer(630, 450, nullptr); + _currentBuffer.create(630, 450, Graphics::PixelFormat::createFormatCLUT8()); } else if (_isHiRes) { - _currentBuffer = Buffer(640, 480, nullptr); + _currentBuffer.create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); } else { - _currentBuffer = Buffer(320, 200, nullptr); + _currentBuffer.create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); } - _currentBuffer.setPixels(calloc(1, _currentBuffer.screenWidth * _currentBuffer.screenHeight)); - _screenRect = Common::Rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); - initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes); + initGraphics(_currentBuffer.w, _currentBuffer.h); switch (g_sci->getGameId()) { case GID_HOYLE5: @@ -91,18 +89,20 @@ GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitio case GID_PHANTASMAGORIA2: case GID_TORIN: case GID_RAMA: - _currentBuffer.scriptWidth = 640; - _currentBuffer.scriptHeight = 480; + _scriptWidth = 640; + _scriptHeight = 480; break; case GID_GK2: case GID_PQSWAT: if (!g_sci->isDemo()) { - _currentBuffer.scriptWidth = 640; - _currentBuffer.scriptHeight = 480; + _scriptWidth = 640; + _scriptHeight = 480; + break; } - break; + // fall through default: - // default script width for other games is 320x200 + _scriptWidth = 320; + _scriptHeight = 200; break; } } @@ -110,7 +110,7 @@ GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitio GfxFrameout::~GfxFrameout() { clear(); CelObj::deinit(); - free(_currentBuffer.getPixels()); + _currentBuffer.free(); } void GfxFrameout::run() { @@ -119,10 +119,10 @@ void GfxFrameout::run() { ScreenItem::init(); GfxText32::init(); - // NOTE: This happens in SCI::InitPlane in the actual engine, - // and is a background fill plane to ensure hidden planes - // (planes with a priority of -1) are never drawn - Plane *initPlane = new Plane(Common::Rect(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight)); + // This plane is created in SCI::InitPlane in SSCI, and is a background fill + // plane to ensure "hidden" planes (planes with negative priority) are never + // drawn + Plane *initPlane = new Plane(Common::Rect(_scriptWidth, _scriptHeight)); initPlane->_priority = 0; _planes.add(initPlane); } @@ -133,7 +133,7 @@ void GfxFrameout::clear() { _showList.clear(); } -bool GfxFrameout::gameIsHiRes() const { +bool GfxFrameout::detectHiRes() const { // QFG4 is always low resolution if (g_sci->getGameId() == GID_QFG4) { return false; @@ -296,8 +296,8 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) { } if (plane->_created) { - // NOTE: The original engine calls some `AbortPlane` function that - // just ends up doing this anyway so we skip the extra indirection + // SSCI calls some `AbortPlane` function that just ends up doing this + // anyway, so we skip the extra indirection _planes.erase(plane); } else { plane->_created = 0; @@ -331,8 +331,8 @@ void GfxFrameout::kernelMovePlaneItems(const reg_t object, const int16 deltaX, c for (ScreenItemList::iterator it = plane->_screenItemList.begin(); it != plane->_screenItemList.end(); ++it) { ScreenItem &screenItem = **it; - // If object is a number, the screen item from the - // engine, not a script, and should be ignored + // If object is a number, the screen item from the engine, not a script, + // and should be ignored if (screenItem._object.isNumber()) { continue; } @@ -360,19 +360,19 @@ void GfxFrameout::addPlane(Plane *plane) { error("Plane %04x:%04x already exists", PRINT_REG(plane->_object)); } - plane->clipScreenRect(_screenRect); + plane->clipScreenRect(Common::Rect(_currentBuffer.w, _currentBuffer.h)); _planes.add(plane); } void GfxFrameout::updatePlane(Plane &plane) { - // NOTE: This assertion comes from SCI engine code. + // This assertion comes from SSCI assert(_planes.findByObject(plane._object) == &plane); Plane *visiblePlane = _visiblePlanes.findByObject(plane._object); - plane.sync(visiblePlane, _screenRect); - // NOTE: updateScreenRect was originally called a second time here, - // but it is already called at the end of the Plane::Update call - // in the original engine anyway. + plane.sync(visiblePlane, Common::Rect(_currentBuffer.w, _currentBuffer.h)); + // updateScreenRect was called a second time here in SSCI, but it is already + // called at the end of the sync call (also in SSCI) so there is no reason + // to do it again _planes.sort(); } @@ -401,8 +401,8 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR robotPlayer.doRobot(); } - // NOTE: The original engine allocated these as static arrays of 100 - // pointers to ScreenItemList / RectList + // SSCI allocated these as static arrays of 100 pointers to + // ScreenItemList / RectList ScreenItemListList screenItemLists; EraseListList eraseLists; @@ -455,12 +455,12 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][kGlobalVarPreviousRoomNo].toSint16(); - Common::Rect rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); + Common::Rect rect(_currentBuffer.w, _currentBuffer.h); _showList.add(rect); showBits(); - // NOTE: The original engine allocated these as static arrays of 100 - // pointers to ScreenItemList / RectList + // SSCI allocated these as static arrays of 100 pointers to + // ScreenItemList / RectList ScreenItemListList screenItemLists; EraseListList eraseLists; @@ -557,24 +557,39 @@ void GfxFrameout::directFrameOut(const Common::Rect &showRect) { } #ifdef USE_RGB_COLOR +void GfxFrameout::redrawGameScreen(const Common::Rect &skipRect) const { + Common::ScopedPtr<Graphics::Surface> game(_currentBuffer.convertTo(g_system->getScreenFormat(), _palette->getHardwarePalette())); + assert(game); + + Common::Rect rects[4]; + int splitCount = splitRects(Common::Rect(game->w, game->h), skipRect, rects); + if (splitCount != -1) { + while (splitCount--) { + const Common::Rect &drawRect = rects[splitCount]; + g_system->copyRectToScreen(game->getBasePtr(drawRect.left, drawRect.top), game->pitch, drawRect.left, drawRect.top, drawRect.width(), drawRect.height()); + } + } + + game->free(); +} + void GfxFrameout::resetHardware() { updateMousePositionForRendering(); - _showList.add(Common::Rect(getCurrentBuffer().screenWidth, getCurrentBuffer().screenHeight)); + _showList.add(Common::Rect(_currentBuffer.w, _currentBuffer.h)); g_system->getPaletteManager()->setPalette(_palette->getHardwarePalette(), 0, 256); showBits(); } #endif /** - * Determines the parts of `middleRect` that aren't overlapped - * by `showRect`, optimised for contiguous memory writes. - * Returns -1 if `middleRect` and `showRect` have no intersection. - * Returns number of returned parts (in `outRects`) otherwise. - * (In particular, this returns 0 if `middleRect` is contained - * in `other`.) + * Determines the parts of `middleRect` that aren't overlapped by `showRect`, + * optimised for contiguous memory writes. + * + * `middleRect` is modified directly to extend into the upper and lower rects. * - * `middleRect` is modified directly to extend into the upper - * and lower rects. + * @returns -1 if `middleRect` and `showRect` have no intersection, or the + * number of returned parts (in `outRects`) otherwise. (In particular, this + * returns 0 if `middleRect` is contained in `showRect`.) */ int splitRectsForRender(Common::Rect &middleRect, const Common::Rect &showRect, Common::Rect(&outRects)[2]) { if (!middleRect.intersects(showRect)) { @@ -651,8 +666,7 @@ int splitRectsForRender(Common::Rect &middleRect, const Common::Rect &showRect, return splitCount; } -// NOTE: The third rectangle parameter is only ever given a non-empty rect -// by VMD code, via `frameOut` +// The third rectangle parameter is only ever passed by VMD code void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect) { RectList eraseList; Common::Rect outRects[4]; @@ -670,8 +684,8 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL const Plane *outerPlane = _planes[outerPlaneIndex]; const Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); - // NOTE: SSCI only ever checks for kPlaneTypeTransparent here, even - // though kPlaneTypeTransparentPicture is also a transparent plane + // SSCI only ever checks for kPlaneTypeTransparent here, even though + // kPlaneTypeTransparentPicture is also a transparent plane if (outerPlane->_type == kPlaneTypeTransparent) { foundTransparentPlane = true; } @@ -741,7 +755,6 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } - // clean up deleted planes if (deletedPlaneCount) { for (int planeIndex = planeCount - 1; planeIndex >= 0; --planeIndex) { Plane *plane = _planes[planeIndex]; @@ -866,7 +879,7 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } - // NOTE: SSCI only looks for kPlaneTypeTransparent, not + // SSCI really only looks for kPlaneTypeTransparent, not // kPlaneTypeTransparentPicture if (foundTransparentPlane) { for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { @@ -913,8 +926,6 @@ void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { const DrawItem &drawItem = *screenItemList[i]; mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); const ScreenItem &screenItem = *drawItem.screenItem; - // TODO: Remove -// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect)); CelObj &celObj = *screenItem._celObj; celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX); } @@ -986,9 +997,8 @@ void GfxFrameout::showBits() { for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); - // NOTE: SCI engine used BR-inclusive rects so used slightly - // different masking here to ensure that the width of rects - // was always even. + // SSCI uses BR-inclusive rects so has slightly different masking here + // to ensure that the width of rects is always even rounded.left &= ~1; rounded.right = (rounded.right + 1) & ~1; _cursor->gonnaPaint(rounded); @@ -998,13 +1008,12 @@ void GfxFrameout::showBits() { for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); - // NOTE: SCI engine used BR-inclusive rects so used slightly - // different masking here to ensure that the width of rects - // was always even. + // SSCI uses BR-inclusive rects so has slightly different masking here + // to ensure that the width of rects is always even rounded.left &= ~1; rounded.right = (rounded.right + 1) & ~1; - byte *sourceBuffer = (byte *)_currentBuffer.getPixels() + rounded.top * _currentBuffer.screenWidth + rounded.left; + byte *sourceBuffer = (byte *)_currentBuffer.getPixels() + rounded.top * _currentBuffer.w + rounded.left; // Sometimes screen items (especially from SCI2.1early transitions, like // in the asteroids minigame in PQ4) generate zero-dimension show @@ -1029,7 +1038,7 @@ void GfxFrameout::showBits() { #else { #endif - g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height()); + g_system->copyRectToScreen(sourceBuffer, _currentBuffer.w, rounded.left, rounded.top, rounded.width(), rounded.height()); } } @@ -1089,14 +1098,14 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co byte *pixels = (byte *)_currentBuffer.getPixels(); - for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) { + for (int pixelIndex = 0, numPixels = _currentBuffer.w * _currentBuffer.h; pixelIndex < numPixels; ++pixelIndex) { byte currentValue = pixels[pixelIndex]; int8 styleRangeValue = styleRanges[currentValue]; if (styleRangeValue == -1 && styleRangeValue == style) { currentValue = pixels[pixelIndex] = clut[currentValue]; - // NOTE: In original engine this assignment happens outside of the - // condition, but if the branch is not followed the value is just - // going to be the same as it was before + // In SSCI this assignment happens outside of the condition, but if + // the branch is not followed the value is just going to be the same + // as it was before, so we do it here instead styleRangeValue = styleRanges[currentValue]; } @@ -1110,7 +1119,7 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co } void GfxFrameout::updateScreen(const int delta) { - // using OSystem::getMillis instead of Sci::getTickCount because these + // Using OSystem::getMillis instead of Sci::getTickCount here because these // values need to be monotonically increasing for the duration of the // GfxFrameout object or else the screen will stop updating const uint32 now = g_system->getMillis() * 60 / 1000; @@ -1199,20 +1208,19 @@ reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &positio return make_reg(0, 0); } - // NOTE: The original engine passed a copy of the ScreenItem into isOnMe - // as a hack around the fact that the screen items in `_visiblePlanes` - // did not have their `_celObj` pointers cleared when their CelInfo was - // updated by `Plane::decrementScreenItemArrayCounts`. We handle this - // this more intelligently by clearing `_celObj` in the copy assignment - // operator, which is only ever called by `decrementScreenItemArrayCounts` - // anyway. + // SSCI passed a copy of the ScreenItem into isOnMe as a hack around the + // fact that the screen items in `_visiblePlanes` did not have their + // `_celObj` pointers cleared when their CelInfo was updated by + // `Plane::decrementScreenItemArrayCounts`. We handle this this more + // intelligently by clearing `_celObj` in the copy assignment operator, + // which is only ever called by `decrementScreenItemArrayCounts` anyway. return make_reg(0, isOnMe(*screenItem, *plane, position, checkPixel)); } bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const { Common::Point scaledPosition(position); - mulru(scaledPosition, Ratio(_currentBuffer.screenWidth, _currentBuffer.scriptWidth), Ratio(_currentBuffer.screenHeight, _currentBuffer.scriptHeight)); + mulru(scaledPosition, Ratio(_currentBuffer.w, _scriptWidth), Ratio(_currentBuffer.h, _scriptHeight)); scaledPosition.x += plane._planeRect.left; scaledPosition.y += plane._planeRect.top; @@ -1229,7 +1237,7 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const scaledPosition.y -= screenItem._scaledPosition.y; if (getSciVersion() < SCI_VERSION_2_1_LATE) { - mulru(scaledPosition, Ratio(celObj._xResolution, _currentBuffer.screenWidth), Ratio(celObj._yResolution, _currentBuffer.screenHeight)); + mulru(scaledPosition, Ratio(celObj._xResolution, _currentBuffer.w), Ratio(celObj._yResolution, _currentBuffer.h)); } if (screenItem._scale.signal != kScaleSignalNone && screenItem._scale.x && screenItem._scale.y) { @@ -1271,9 +1279,9 @@ bool GfxFrameout::getNowSeenRect(const reg_t screenItemObject, Common::Rect &res const ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject); if (screenItem == nullptr) { - // NOTE: MGDX is assumed to use the older getNowSeenRect since it was - // released before SQ6, but this has not been verified since it cannot - // be disassembled at the moment (Phar Lap Windows-only release) + // MGDX is assumed to use the older getNowSeenRect since it was released + // before SQ6, but this has not been verified since it cannot be + // disassembled at the moment (Phar Lap Windows-only release) // (See also kSetNowSeen32) if (getSciVersion() <= SCI_VERSION_2_1_EARLY || g_sci->getGameId() == GID_SQ6 || diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index a7b529fe90..eddf88f523 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -37,37 +37,70 @@ class GfxTransitions32; struct PlaneShowStyle; /** - * Frameout class, kFrameout and relevant functions for SCI32 games. - * Roughly equivalent to GraphicsMgr in the actual SCI engine. + * Frameout class, kFrameOut and relevant functions for SCI32 games. + * Roughly equivalent to GraphicsMgr in SSCI. */ class GfxFrameout { friend class GfxTransitions32; + +public: + GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor); + ~GfxFrameout(); + + void clear(); + void run(); + + /** + * Returns true if the game should render at a resolution greater than + * 320x240. + */ + inline bool isHiRes() const { return _isHiRes; } + + /** + * Gets the x-resolution used by game scripts. + */ + inline int16 getScriptWidth() const { return _scriptWidth; } + + /** + * Gets the y-resolution used by game scripts. + */ + inline int16 getScriptHeight() const { return _scriptHeight; } + + /** + * Gets the x-resolution of the output buffer. + */ + inline int16 getScreenWidth() const { return _currentBuffer.w; } + + /** + * Gets the y-resolution of the output buffer. + */ + inline int16 getScreenHeight() const { return _currentBuffer.h; } + private: GfxCursor32 *_cursor; GfxPalette32 *_palette; + GfxTransitions32 *_transitions; SegManager *_segMan; /** - * Determines whether the current game should be rendered in - * high resolution. + * Whether or not the game should render at a resolution above 320x240. */ - bool gameIsHiRes() const; - -public: - GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor); - ~GfxFrameout(); - bool _isHiRes; - void clear(); - void run(); + /** + * The resolution used by game scripts. + * @see celobj32.h comments on kLowResX/kLowResY. + */ + int16 _scriptWidth, _scriptHeight; + + /** + * Determines whether the current game should be rendered in high + * resolution. + */ + bool detectHiRes() const; #pragma mark - #pragma mark Screen items -private: - void remapMarkRedraw(); - bool getNowSeenRect(const reg_t screenItemObject, Common::Rect &result) const; - public: /** * Adds a screen item. @@ -100,39 +133,27 @@ public: bool kernelSetNowSeen(const reg_t screenItemObject) const; int16 kernelObjectIntersect(const reg_t object1, const reg_t object2) const; -#pragma mark - -#pragma mark Planes private: - /** - * The list of planes (i.e. layers) that have been added - * to the screen. - * - * @note This field is on `GraphicsMgr.screen` in SCI - * engine. - */ - PlaneList _planes; - - /** - * Updates an existing plane with properties from the - * given VM object. - */ - void updatePlane(Plane &plane); + void remapMarkRedraw(); + bool getNowSeenRect(const reg_t screenItemObject, Common::Rect &result) const; +#pragma mark - +#pragma mark Planes public: /** * Creates and adds a new plane to the plane list. Ownership of the passed * object is transferred to GfxFrameout. * - * @note This method is on Screen in SCI engine, but it - * is only ever called on `GraphicsMgr.screen`. + * @note This method is on Screen in SSCI, but it is only ever called on + * `GraphicsMgr.screen`. */ void addPlane(Plane *plane); /** * Deletes a plane within the current plane list. * - * @note This method is on Screen in SCI engine, but it - * is only ever called on `GraphicsMgr.screen`. + * @note This method is on Screen in SSCI, but it is only ever called on + * `GraphicsMgr.screen`. */ void deletePlane(Plane &plane); @@ -148,148 +169,27 @@ public: void kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics); int16 kernelGetHighPlanePri(); -#pragma mark - -#pragma mark Pics -public: - void kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 pictureX, const int16 pictureY, const bool mirrorX, const bool deleteDuplicate); - -#pragma mark - -#pragma mark Rendering private: /** - * The last time the hardware screen was updated. - */ - uint32 _lastScreenUpdateTick; - - GfxTransitions32 *_transitions; - - /** - * State tracker to provide more accurate 60fps - * video throttling. - */ - uint8 _throttleState; - - /** - * The internal display pixel buffer. During frameOut, - * this buffer is drawn into according to the draw and - * erase rects calculated by `calcLists`, then drawn out - * to the hardware surface according to the `_showList` - * rects (which are also calculated by `calcLists`). - */ - Buffer _currentBuffer; - - /** - * When true, a change to the remap zone in the palette - * has occurred and screen items with remap data need to - * be redrawn. - */ - bool _remapOccurred; - - /** - * TODO: Document - * TODO: Depending upon if the engine ever modifies this - * rect, it may be stupid to store it separately instead - * of just getting width/height from GfxScreen. - * - * @note This field is on `GraphicsMgr.screen` in SCI - * engine. - */ - Common::Rect _screenRect; - - /** - * A list of rectangles, in display coordinates, that - * represent portions of the internal screen buffer that - * should be drawn to the hardware display surface. + * The list of planes (i.e. layers) that will be rendered to the screen on + * the next call to `frameOut`. * - * @note This field is on `GraphicsMgr.screen` in SCI - * engine. - */ - RectList _showList; - - /** - * The amount of extra overdraw that is acceptable when - * merging two show list rectangles together into a - * single larger rectangle. - * - * @note This field is on `GraphicsMgr.screen` in SCI - * engine. - */ - int _overdrawThreshold; - - /** - * A list of planes that are currently drawn to the - * hardware display surface. Used to calculate - * differences in plane properties between the last - * frame and current frame. - * - * @note This field is on `GraphicsMgr.visibleScreen` in - * SCI engine. - */ - PlaneList _visiblePlanes; - - /** - * Calculates the location and dimensions of dirty rects - * over the entire screen for rendering the next frame. - * The draw and erase lists in `drawLists` and - * `eraseLists` each represent one plane on the screen. - * The optional `eraseRect` argument allows a specific - * area of the screen to be erased. - */ - void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect = Common::Rect()); - - /** - * Erases the areas in the given erase list from the - * visible screen buffer by filling them with the color - * from the corresponding plane. This is an optimisation - * for colored-type planes only; other plane types have - * to be redrawn from pixel data. - */ - void drawEraseList(const RectList &eraseList, const Plane &plane); - - /** - * Draws all screen items from the given draw list to - * the visible screen buffer. - */ - void drawScreenItemList(const DrawList &screenItemList); - - /** - * Adds a new rectangle to the list of regions to write - * out to the hardware. The provided rect may be merged - * into an existing rectangle to reduce the number of - * blit operations. - */ - void mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold); - - /** - * Sends all dirty rects from the internal frame buffer to the backend, - * then updates the hardware screen. + * @note This field is on `GraphicsMgr.screen` in SSCI. */ - void showBits(); + PlaneList _planes; /** - * Validates whether the given palette index in the - * style range should copy a color from the next - * palette to the source palette during a palette - * morph operation. + * Updates an existing plane with properties from the given VM object. */ - inline bool validZeroStyle(const uint8 style, const int i) const { - if (style != 0) { - return false; - } + void updatePlane(Plane &plane); - // TODO: Cannot check Shivers or MGDX until those executables can be - // unwrapped - switch (g_sci->getGameId()) { - case GID_KQ7: - case GID_PHANTASMAGORIA: - case GID_SQ6: - return (i > 71 && i < 104); - break; - default: - return true; - } - } +#pragma mark - +#pragma mark Pics +public: + void kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 pictureX, const int16 pictureY, const bool mirrorX, const bool deleteDuplicate); +#pragma mark - +#pragma mark Rendering public: /** * Updates the hardware screen, no more than once per tick. @@ -307,7 +207,7 @@ public: * Resets the pixel format of the hardware surface to the given format. */ void setPixelFormat(const Graphics::PixelFormat &format) const { - initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes, &format); + initGraphics(_currentBuffer.w, _currentBuffer.h, &format); } /** @@ -316,9 +216,9 @@ public: bool _throttleKernelFrameOut; /** - * Whether palMorphFrameOut should be used instead of - * frameOut for rendering. Used by kMorphOn to - * explicitly enable palMorphFrameOut for one frame. + * Whether `palMorphFrameOut` should be used instead of `frameOut` for + * rendering. Used by `kMorphOn` to explicitly enable `palMorphFrameOut` for + * one frame. */ bool _palMorphIsOn; @@ -329,16 +229,14 @@ public: void kernelFrameOut(const bool showBits); /** - * Throttles the engine as necessary to maintain - * 60fps output. + * Throttles the engine as necessary to maintain 60fps output. */ void throttle(); /** - * Updates the internal screen buffer for the next - * frame. If `shouldShowBits` is true, also sends the - * buffer to hardware. If `eraseRect` is non-empty, - * it is added to the erase list for this frame. + * Updates the internal screen buffer for the next frame. If + * `shouldShowBits` is true, also sends the buffer to hardware. If + * `eraseRect` is non-empty, it is added to the erase list for this frame. */ void frameOut(const bool shouldShowBits, const Common::Rect &eraseRect = Common::Rect()); @@ -353,6 +251,14 @@ public: */ void directFrameOut(const Common::Rect &showRect); + /** + * Redraws the game screen from the internal frame buffer to the system. + * Used after pixel format changes. + * + * @param skipRect An area of the screen that does not need to be redrawn. + */ + void redrawGameScreen(const Common::Rect &skipRect) const; + #ifdef USE_RGB_COLOR /** * Sends the entire internal screen buffer and palette to hardware. @@ -361,19 +267,18 @@ public: #endif /** - * Modifies the raw pixel data for the next frame with - * new palette indexes based on matched style ranges. + * Modifies the raw pixel data for the next frame with new palette indexes + * based on matched style ranges. */ void alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges); - // NOTE: This function is used within ScreenItem subsystem and assigned - // to various booleanish fields that seem to represent the state of the - // screen item (created, updated, deleted). In GK1/DOS, Phant1/m68k, - // SQ6/DOS, SQ6/Win, and Phant2/Win, this function simply returns 1. If - // you know of any game/environment where this function returns some - // value other than 1, or if you used to work at Sierra and can explain - // why this is a thing (and if anyone needs to care about it), please - // open a ticket!! + // This function is used within ScreenItem subsystem and assigned to various + // booleanish fields that seem to represent the state of the screen item + // (created, updated, deleted). In GK1/DOS, Phant1/m68k, SQ6/DOS, SQ6/Win, + // and Phant2/Win, this function simply returns 1. If you know of any + // game/environment where this function returns some value other than 1, or + // if you used to work at Sierra and can explain why this is a thing (and if + // anyone needs to care about it), please open a ticket!! inline int getScreenCount() const { return 1; }; @@ -383,8 +288,121 @@ public: */ void shakeScreen(const int16 numShakes, const ShakeDirection direction); +private: + /** + * The last time the hardware screen was updated. + */ + uint32 _lastScreenUpdateTick; + + /** + * State tracker to provide more accurate 60fps video throttling. + */ + uint8 _throttleState; + + /** + * The internal display pixel buffer. During `frameOut`, this buffer is + * drawn into according to the draw and erase rects calculated by + * `calcLists`, then drawn out to the hardware surface according to the + * `_showList` rects (which are also calculated by `calcLists`). + */ + Buffer _currentBuffer; + + /** + * When true, a change to the remap zone in the palette has occurred and + * screen items with remap data need to be redrawn. + */ + bool _remapOccurred; + + /** + * A list of rectangles, in screen coordinates, that represent portions of + * the internal screen buffer that are dirty and should be drawn to the + * hardware display surface. + * + * @note This field is on `GraphicsMgr.screen` in SSCI. + */ + RectList _showList; + + /** + * The amount of extra overdraw that is acceptable when merging two show + * list rectangles together into a single larger rectangle. + * + * @note This field is on `GraphicsMgr.screen` in SSCI. + */ + int _overdrawThreshold; + + /** + * The list of planes that are currently drawn to the hardware display + * surface. Used to calculate differences in plane properties between the + * last frame and current frame. + * + * @note This field is on `GraphicsMgr.visibleScreen` in SSCI. + */ + PlaneList _visiblePlanes; + + /** + * Calculates the location and dimensions of dirty rects over the entire + * screen for rendering the next frame. The draw and erase lists in + * `drawLists` and `eraseLists` each represent one plane on the screen. + * The optional `eraseRect` argument allows a specific area of the screen to + * be explicitly erased. + */ + void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect = Common::Rect()); + + /** + * Erases the areas in the given erase list from the visible screen buffer + * by filling them with the color from the corresponding plane. This is an + * optimisation for colored-type planes only; other plane types have to be + * redrawn from pixel data. + */ + void drawEraseList(const RectList &eraseList, const Plane &plane); + + /** + * Draws all screen items from the given draw list to the visible screen + * buffer. + */ + void drawScreenItemList(const DrawList &screenItemList); + + /** + * Adds a new rectangle to the list of regions to write out to the hardware. + * The provided rect may be merged into an existing rectangle to reduce the + * number of blit operations. + */ + void mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold); + + /** + * Sends all dirty rects from the internal frame buffer to the backend, then + * updates the hardware screen. + */ + void showBits(); + + /** + * Validates whether the given palette index in the style range should copy + * a color from the next palette to the source palette during a palette + * morph operation. + */ + inline bool validZeroStyle(const uint8 style, const int i) const { + if (style != 0) { + return false; + } + + // TODO: Cannot check Shivers or MGDX until those executables can be + // unwrapped + switch (g_sci->getGameId()) { + case GID_KQ7: + case GID_PHANTASMAGORIA: + case GID_SQ6: + return (i > 71 && i < 104); + break; + default: + return true; + } + } + #pragma mark - #pragma mark Mouse cursor +public: + reg_t kernelIsOnMe(const reg_t object, const Common::Point &position, const bool checkPixel) const; + private: void updateMousePositionForRendering() const { // In SSCI, mouse events were received via hardware interrupt, so the @@ -398,14 +416,11 @@ private: } /** - * Determines whether or not the point given by - * `position` is inside of the given screen item. + * Determines whether or not the point given by `position` is inside of the + * given screen item. */ bool isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const; -public: - reg_t kernelIsOnMe(const reg_t object, const Common::Point &position, const bool checkPixel) const; - #pragma mark - #pragma mark Debugging public: diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index 38da280701..5842bf0887 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -225,47 +225,7 @@ inline int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&o return splitCount; } -struct Buffer : public Graphics::Surface { - uint16 screenWidth; - uint16 screenHeight; - uint16 scriptWidth; - uint16 scriptHeight; - - Buffer() : - screenWidth(0), - screenHeight(0), - scriptWidth(320), - scriptHeight(200) {} - - Buffer(const uint16 width, const uint16 height, uint8 *const pix) : - screenWidth(width), - screenHeight(height), - // TODO: These values are not correct for all games. Script - // dimensions were hard-coded per game in the original - // interpreter. Search all games for their internal script - // dimensions and set appropriately. (This code does not - // appear to exist at all in SCI3, which uses 640x480.) - scriptWidth(320), - scriptHeight(200) { - init(width, height, width, pix, Graphics::PixelFormat::createFormatCLUT8()); - } - - void clear(const uint8 value) { - memset(pixels, value, w * h); - } - - inline uint8 *getAddress(const uint16 x, const uint16 y) { - return (uint8 *)getBasePtr(x, y); - } - - inline uint8 *getAddressSimRes(const uint16 x, const uint16 y) { - return (uint8*)pixels + (y * w * screenHeight / scriptHeight) + (x * screenWidth / scriptWidth); - } - - bool isNull() { - return pixels == nullptr; - } -}; +typedef Graphics::Surface Buffer; #endif struct Color { diff --git a/engines/sci/graphics/lists32.h b/engines/sci/graphics/lists32.h index ff0bc6ceae..82e9e58b36 100644 --- a/engines/sci/graphics/lists32.h +++ b/engines/sci/graphics/lists32.h @@ -28,13 +28,11 @@ namespace Sci { /** - * StablePointerArray holds pointers in a fixed-size array - * that maintains position of erased items until `pack` is - * called. It is used by DrawList, RectList, and - * ScreenItemList. StablePointerArray takes ownership of - * all pointers that are passed to it and deletes them when - * calling `erase` or when destroying the - * StablePointerArray. + * StablePointerArray holds pointers in a fixed-size array that maintains + * position of erased items until `pack` is called. It is used by DrawList, + * RectList, and ScreenItemList. StablePointerArray takes ownership of all + * pointers that are passed to it and deletes them when calling `erase` or when + * destroying the StablePointerArray. */ template<class T, uint N> class StablePointerArray { diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp index 8a63365f03..f80f115e1b 100644 --- a/engines/sci/graphics/paint32.cpp +++ b/engines/sci/graphics/paint32.cpp @@ -42,9 +42,9 @@ reg_t GfxPaint32::kernelAddLine(const reg_t planeObject, const Common::Point &st CelInfo32 celInfo; celInfo.type = kCelTypeMem; celInfo.bitmap = bitmapId; - // SSCI stores the line color on `celInfo`, even though - // this is not a `kCelTypeColor`, as a hack so that - // `kUpdateLine` can get the originally used color + // SSCI stores the line color on `celInfo`, even though this is not a + // `kCelTypeColor`, as a hack so that `kUpdateLine` can get the originally + // used color celInfo.color = color; ScreenItem *screenItem = new ScreenItem(planeObject, celInfo, gameRect); @@ -93,7 +93,8 @@ void GfxPaint32::plotter(int x, int y, int color, void *data) { const uint32 index = bitmapWidth * y + x; // Only draw the points in the bitmap, and ignore the rest. SSCI scripts - // can draw lines ending outside the visible area (e.g. negative coordinates) + // can draw lines ending outside the visible area (e.g. negative + // coordinates) if (x >= 0 && x < bitmapWidth && y >= 0 && y < bitmapHeight) { if (properties.solid) { pixels[index] = (uint8)color; @@ -125,8 +126,8 @@ reg_t GfxPaint32::makeLineBitmap(const Common::Point &startPoint, const Common:: thickness = (MAX<uint8>(1, thickness) - 1) | 1; const uint8 halfThickness = thickness >> 1; - const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const uint16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const uint16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); outRect.left = MIN<int16>(startPoint.x, endPoint.x); outRect.top = MIN<int16>(startPoint.y, endPoint.y); diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index d8a2ead66d..7a3b396da8 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -39,11 +39,10 @@ namespace Sci { HunkPalette::HunkPalette(const SciSpan<const byte> &rawPalette) : _version(0), - // NOTE: The header size in palettes is garbage. In at least KQ7 2.00b and - // Phant1, the 999.pal sets this value to 0. In most other palettes it is - // set to 14, but the *actual* size of the header structure used in SSCI is - // 13, which is reflected by `kHunkPaletteHeaderSize`. - // _headerSize(rawPalette[0]), + // The header size in palettes is garbage. In at least KQ7 2.00b and Phant1, + // the 999.pal sets this value to 0. In most other palettes it is set to 14, + // but the *actual* size of the header structure used in SSCI is 13, which + // is reflected by `kHunkPaletteHeaderSize`. _numPalettes(rawPalette.getUint8At(kNumPaletteEntriesOffset)), _data() { assert(_numPalettes == 0 || _numPalettes == 1); @@ -371,18 +370,17 @@ static const uint8 gammaTables[GfxPalette32::numGammaTables][256] = { _varyLastTick(0), _varyTime(0), _varyDirection(0), + _varyPercent(0), _varyTargetPercent(0), _varyNumTimesPaused(0), // Palette cycling - _cyclers(), _cycleMap(), // Gamma correction _gammaLevel(-1), _gammaChanged(false) { - _varyPercent = _varyTargetPercent; for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) { _fadeTable[i] = 100; } @@ -390,11 +388,6 @@ static const uint8 gammaTables[GfxPalette32::numGammaTables][256] = { loadPalette(999); } -GfxPalette32::~GfxPalette32() { - varyOff(); - cycleAllOff(); -} - bool GfxPalette32::loadPalette(const GuiResourceId resourceId) { Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false); @@ -604,16 +597,16 @@ void GfxPalette32::setVary(const Palette &target, const int16 percent, const int } void GfxPalette32::setVaryPercent(const int16 percent, const int32 ticks) { - if (_varyTargetPalette != nullptr) { + if (_varyTargetPalette) { setVaryTime(percent, ticks); } - // NOTE: SSCI had two additional parameters for this function to change the + // SSCI had two additional parameters for this function to change the // `_varyFromColor`, but they were always hardcoded to be ignored } void GfxPalette32::setVaryTime(const int32 time) { - if (_varyTargetPalette != nullptr) { + if (_varyTargetPalette) { setVaryTime(_varyTargetPercent, time); } } @@ -645,16 +638,8 @@ void GfxPalette32::varyOff() { _varyFromColor = 0; _varyToColor = 255; _varyDirection = 0; - - if (_varyTargetPalette != nullptr) { - delete _varyTargetPalette; - _varyTargetPalette = nullptr; - } - - if (_varyStartPalette != nullptr) { - delete _varyStartPalette; - _varyStartPalette = nullptr; - } + _varyTargetPalette.reset(); + _varyStartPalette.reset(); } void GfxPalette32::varyPause() { @@ -667,7 +652,7 @@ void GfxPalette32::varyOn() { --_varyNumTimesPaused; } - if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0) { + if (_varyTargetPalette && _varyNumTimesPaused == 0) { if (_varyPercent != _varyTargetPercent && _varyTime != 0) { _varyDirection = (_varyTargetPercent - _varyPercent > 0) ? 1 : -1; } else { @@ -677,28 +662,26 @@ void GfxPalette32::varyOn() { } void GfxPalette32::setTarget(const Palette &palette) { - delete _varyTargetPalette; - _varyTargetPalette = new Palette(palette); + _varyTargetPalette.reset(new Palette(palette)); } void GfxPalette32::setStart(const Palette &palette) { - delete _varyStartPalette; - _varyStartPalette = new Palette(palette); + _varyStartPalette.reset(new Palette(palette)); } void GfxPalette32::mergeStart(const Palette &palette) { - if (_varyStartPalette != nullptr) { + if (_varyStartPalette) { mergePalette(*_varyStartPalette, palette); } else { - _varyStartPalette = new Palette(palette); + _varyStartPalette.reset(new Palette(palette)); } } void GfxPalette32::mergeTarget(const Palette &palette) { - if (_varyTargetPalette != nullptr) { + if (_varyTargetPalette) { mergePalette(*_varyTargetPalette, palette); } else { - _varyTargetPalette = new Palette(palette); + _varyTargetPalette.reset(new Palette(palette)); } } @@ -714,9 +697,9 @@ void GfxPalette32::applyVary() { _varyPercent += _varyDirection; } - if (_varyPercent == 0 || _varyTargetPalette == nullptr) { + if (_varyPercent == 0 || !_varyTargetPalette) { for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) { - if (_varyStartPalette != nullptr && i >= _varyFromColor && i <= _varyToColor) { + if (_varyStartPalette && i >= _varyFromColor && i <= _varyToColor) { _nextPalette.colors[i] = _varyStartPalette->colors[i]; } else { _nextPalette.colors[i] = _sourcePalette.colors[i]; @@ -728,7 +711,7 @@ void GfxPalette32::applyVary() { Color targetColor = _varyTargetPalette->colors[i]; Color sourceColor; - if (_varyStartPalette != nullptr) { + if (_varyStartPalette) { sourceColor = _varyStartPalette->colors[i]; } else { sourceColor = _sourcePalette.colors[i]; @@ -816,27 +799,28 @@ void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const in clearCycleMap(fromColor, cycler->numColorsToCycle); } else { for (int i = 0; i < kNumCyclers; ++i) { - if (_cyclers[i] == nullptr) { - _cyclers[i] = cycler = new PalCycler; + if (!_cyclers[i]) { + cycler = new PalCycler; + _cyclers[i].reset(cycler); break; } } } - // If there are no free cycler slots, SCI engine overrides the first oldest - // cycler that it finds, where "oldest" is determined by the difference - // between the tick and now + // If there are no free cycler slots, SSCI overrides the first oldest cycler + // that it finds, where "oldest" is determined by the difference between the + // tick and now if (cycler == nullptr) { const uint32 now = g_sci->getTickCount(); uint32 minUpdateDelta = 0xFFFFFFFF; for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const candidate = _cyclers[i]; + PalCyclerOwner &candidate = _cyclers[i]; const uint32 updateDelta = now - candidate->lastUpdateTick; if (updateDelta < minUpdateDelta) { minUpdateDelta = updateDelta; - cycler = candidate; + cycler = candidate.get(); } } @@ -882,19 +866,18 @@ void GfxPalette32::cyclePause(const uint8 fromColor) { void GfxPalette32::cycleAllOn() { for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler != nullptr && cycler->numTimesPaused > 0) { + PalCyclerOwner &cycler = _cyclers[i]; + if (cycler && cycler->numTimesPaused > 0) { --cycler->numTimesPaused; } } } void GfxPalette32::cycleAllPause() { - // NOTE: The original engine did not check for null pointers in the - // palette cyclers pointer array. + // SSCI did not check for null pointers in the palette cyclers pointer array for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler != nullptr) { + PalCyclerOwner &cycler = _cyclers[i]; + if (cycler) { // This seems odd, because currentCycle is 0..numColorsPerCycle, // but fromColor is 0..255. When applyAllCycles runs, the values // end up back in range @@ -905,8 +888,8 @@ void GfxPalette32::cycleAllPause() { applyAllCycles(); for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler != nullptr) { + PalCyclerOwner &cycler = _cyclers[i]; + if (cycler) { ++cycler->numTimesPaused; } } @@ -914,11 +897,10 @@ void GfxPalette32::cycleAllPause() { void GfxPalette32::cycleOff(const uint8 fromColor) { for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler != nullptr && cycler->fromColor == fromColor) { + PalCyclerOwner &cycler = _cyclers[i]; + if (cycler && cycler->fromColor == fromColor) { clearCycleMap(fromColor, cycler->numColorsToCycle); - delete cycler; - _cyclers[i] = nullptr; + _cyclers[i].reset(); break; } } @@ -926,11 +908,10 @@ void GfxPalette32::cycleOff(const uint8 fromColor) { void GfxPalette32::cycleAllOff() { for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler != nullptr) { + PalCyclerOwner &cycler = _cyclers[i]; + if (cycler) { clearCycleMap(cycler->fromColor, cycler->numColorsToCycle); - delete cycler; - _cyclers[i] = nullptr; + _cyclers[i].reset(); } } } @@ -969,9 +950,9 @@ void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToS PalCycler *GfxPalette32::getCycler(const uint16 fromColor) { for (int cyclerIndex = 0; cyclerIndex < kNumCyclers; ++cyclerIndex) { - PalCycler *cycler = _cyclers[cyclerIndex]; - if (cycler != nullptr && cycler->fromColor == fromColor) { - return cycler; + PalCyclerOwner &cycler = _cyclers[cyclerIndex]; + if (cycler && cycler->fromColor == fromColor) { + return cycler.get(); } } @@ -980,12 +961,12 @@ PalCycler *GfxPalette32::getCycler(const uint16 fromColor) { void GfxPalette32::applyAllCycles() { Color paletteCopy[256]; - memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256); + memcpy(paletteCopy, _nextPalette.colors, sizeof(paletteCopy)); for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler != nullptr) { - cycler->currentCycle = (((int) cycler->currentCycle) + 1) % cycler->numColorsToCycle; + PalCyclerOwner &cycler = _cyclers[i]; + if (cycler) { + cycler->currentCycle = (cycler->currentCycle + 1) % cycler->numColorsToCycle; for (int j = 0; j < cycler->numColorsToCycle; j++) { _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle]; } @@ -995,12 +976,12 @@ void GfxPalette32::applyAllCycles() { void GfxPalette32::applyCycles() { Color paletteCopy[256]; - memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256); + memcpy(paletteCopy, _nextPalette.colors, sizeof(paletteCopy)); const uint32 now = g_sci->getTickCount(); for (int i = 0; i < kNumCyclers; ++i) { - PalCycler *const cycler = _cyclers[i]; - if (cycler == nullptr) { + PalCyclerOwner &cycler = _cyclers[i]; + if (!cycler) { continue; } diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index b8ba85eb4a..2d00b86501 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -23,6 +23,8 @@ #ifndef SCI_GRAPHICS_PALETTE32_H #define SCI_GRAPHICS_PALETTE32_H +#include "common/ptr.h" + namespace Sci { #pragma mark HunkPalette @@ -233,7 +235,6 @@ struct PalCycler { class GfxPalette32 { public: GfxPalette32(ResourceManager *resMan); - ~GfxPalette32(); void saveLoadWithSerializer(Common::Serializer &s); @@ -440,13 +441,13 @@ private: * operation. If this palette is not specified, `_sourcePalette` is used * instead. */ - Palette *_varyStartPalette; + Common::ScopedPtr<Palette> _varyStartPalette; /** * An optional palette used to provide target colors for a palette vary * operation. */ - Palette *_varyTargetPalette; + Common::ScopedPtr<Palette> _varyTargetPalette; /** * The minimum palette index that has been varied from the source palette. @@ -553,7 +554,8 @@ private: kNumCyclers = 10 }; - PalCycler *_cyclers[kNumCyclers]; + typedef Common::ScopedPtr<PalCycler> PalCyclerOwner; + PalCyclerOwner _cyclers[kNumCyclers]; /** * Updates the `currentCycle` of the given `cycler` by `speed` entries. @@ -564,8 +566,8 @@ private: * The cycle map is used to detect overlapping cyclers, and to avoid * remapping to palette entries that are being cycled. * - * According to SCI engine code, when two cyclers overlap, a fatal error has - * occurred and the engine will display an error and then exit. + * According to SSCI, when two cyclers overlap, a fatal error has occurred + * and the engine will display an error and then exit. * * The color remapping system avoids attempts to remap to palette entries * that are cycling because they won't be the expected color once the cycler diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index d9e0d2d9ff..46a8aedbb0 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -137,10 +137,10 @@ void Plane::init() { } void Plane::convertGameRectToPlaneRect() { - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); const Ratio ratioX = Ratio(screenWidth, scriptWidth); const Ratio ratioY = Ratio(screenHeight, scriptHeight); @@ -201,10 +201,9 @@ void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *p } else { screenItem->_position = celObj->_relativePosition; } - _screenItemList.add(screenItem); + screenItem->_celObj.reset(celObj); - delete screenItem->_celObj; - screenItem->_celObj = celObj; + _screenItemList.add(screenItem); } _type = (g_sci->_features->hasTransparentPicturePlanes() && transparent) ? kPlaneTypeTransparentPicture : kPlaneTypePicture; } @@ -321,9 +320,9 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList for (ScreenItemList::size_type i = 0; i < screenItemCount; ++i) { // Items can be added to ScreenItemList and we don't want to process - // those new items, but the list also can grow smaller, so we need - // to check that we are still within the upper bound of the list and - // quit if we aren't any more + // those new items, but the list also can grow smaller, so we need to + // check that we are still within the upper bound of the list and quit + // if we aren't any more if (i >= _screenItemList.size()) { break; } @@ -333,18 +332,17 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList continue; } - // NOTE: The original engine used an array without bounds checking - // so could just get the visible screen item directly; we need to - // verify that the index is actually within the valid range for - // the visible plane before accessing the item to avoid a range - // error. + // SSCI used an array without bounds checking so could just get the + // visible screen item directly; we need to verify that the index is + // actually within the valid range for the visible plane before + // accessing the item to avoid a range error. const ScreenItem *visibleItem = nullptr; if (i < visiblePlaneItemCount) { visibleItem = visiblePlane._screenItemList[i]; } - // Keep erase rects for this screen item from drawing outside - // of its owner plane + // Keep erase rects for this screen item from drawing outside of its + // owner plane Common::Rect visibleItemScreenRect; if (visibleItem != nullptr) { visibleItemScreenRect = visibleItem->_screenRect; @@ -428,8 +426,8 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList breakEraseListByPlanes(eraseList, planeList); breakDrawListByPlanes(drawList, planeList); - // We store the current size of the drawlist, as we want to loop - // over the currently inserted entries later. + // The current size of the draw list is stored here, as we need to loop over + // only the already-inserted entries later. DrawList::size_type drawListSizePrimary = drawList.size(); const RectList::size_type eraseListCount = eraseList.size(); @@ -824,8 +822,8 @@ void Plane::sync(const Plane *other, const Common::Rect &screenRect) { convertGameRectToPlaneRect(); _screenRect = _planeRect; - // NOTE: screenRect originally was retrieved through globals - // instead of being passed into the function + // screenRect was retrieved through globals in SSCI instead of being passed + // into the function. We don't do that just to avoid the extra indirection clipScreenRect(screenRect); } diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index a34df1cc1a..406c3f8a30 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -40,8 +40,7 @@ enum PlaneType { }; enum PlanePictureCodes { - // NOTE: Any value at or below 65531 means the plane - // is a kPlaneTypePicture. + // Any value at or below 65531 means the plane is a kPlaneTypePicture kPlanePic = 65531, kPlanePicTransparentPicture = 65532, kPlanePicOpaque = 65533, @@ -104,61 +103,52 @@ class PlaneList; class Plane { private: /** - * A serial used for planes that are generated inside - * the graphics engine, rather than the interpreter. + * A serial used for planes that are generated inside the graphics engine, + * rather than the interpreter. */ static uint16 _nextObjectId; /** - * A serial used to identify the creation order of - * planes, to ensure a stable sort order for planes - * with identical priorities. + * A serial used to identify the creation order of planes, to ensure a + * stable sort order for planes with identical priorities. */ static uint32 _nextCreationId; /** - * The creation order number, which ensures a stable - * sort when planes with identical priorities are added - * to the plane list. + * The creation order number, which ensures a stable sort when planes with + * identical priorities are added to the plane list. */ uint32 _creationId; /** - * For planes that are used to render picture data, the - * resource ID of the picture to be displayed. This - * value may also be one of the special - * PlanePictureCodes, in which case the plane becomes a - * non-picture plane. + * For planes that are used to render picture data, the resource ID of the + * picture to be displayed. This value may also be one of the special + * PlanePictureCodes, in which case the plane becomes a non-picture plane. */ GuiResourceId _pictureId; /** - * Whether or not the contents of picture planes should - * be drawn horizontally mirrored. Only applies to - * planes of type kPlaneTypePicture. + * Whether or not the contents of picture planes should be drawn + * horizontally mirrored. Only applies to planes of type kPlaneTypePicture. */ bool _mirrored; /** - * Whether the picture ID for this plane has changed. - * This flag is set when the plane is created or updated - * from a VM object, and is cleared when the plane is - * synchronised to another plane (which calls - * changePic). + * Whether the picture ID for this plane has changed. This flag is set when + * the plane is created or updated from a VM object, and is cleared when the + * plane is synchronised to another plane (which calls changePic). */ bool _pictureChanged; /** - * Converts the dimensions of the game rect used by - * scripts to the dimensions of the plane rect used to - * render content to the screen. Coordinates with - * remainders are rounded up to the next whole pixel. + * Converts the dimensions of the game rect used by scripts to the + * dimensions of the plane rect used to render content to the screen. + * Coordinates with remainders are rounded up to the next whole pixel. */ void convertGameRectToPlaneRect(); /** - * Sets the type of the plane according to its assigned - * picture resource ID. + * Sets the type of the plane according to its assigned picture resource ID. */ void setType(); @@ -169,80 +159,73 @@ public: PlaneType _type; /** - * The color to use when erasing the plane. Only - * applies to planes of type kPlaneTypeColored. + * The color to use when erasing the plane. Only applies to planes of type + * kPlaneTypeColored. */ byte _back; /** - * Whether the priority of this plane has changed. - * This flag is set when the plane is updated from - * another plane and cleared when draw list calculation - * occurs. + * Whether the priority of this plane has changed. This flag is set when the + * plane is updated from another plane and cleared when draw list + * calculation occurs. */ int _priorityChanged; /** - * A handle to the VM object corresponding to this - * plane. Some planes are generated purely within the - * graphics engine and have a numeric object value. + * A handle to the VM object corresponding to this plane. Some planes are + * generated purely within the graphics engine and have a numeric object + * value. */ reg_t _object; /** - * The rendering priority of the plane. Higher - * priorities are drawn above lower priorities. + * The rendering priority of the plane. Higher priorities are drawn above + * lower priorities. */ int16 _priority; /** - * Whether or not all screen items in this plane should - * be redrawn on the next frameout, instead of just - * the screen items marked as updated. This is set when - * visual changes to the plane itself are made that - * affect the rendering of the entire plane, and cleared - * once those changes are rendered by `redrawAll`. + * Whether or not all screen items in this plane should be redrawn on the + * next frameout, instead of just the screen items marked as updated. This + * is set when visual changes to the plane itself are made that affect the + * rendering of the entire plane, and cleared once those changes are + * rendered by `redrawAll`. */ int _redrawAllCount; /** * Flags indicating the state of the plane. - * - `created` is set when the plane is first created, - * either from a VM object or from within the engine - * itself - * - `updated` is set when the plane is updated from - * another plane and the two planes' `planeRect`s do - * not match - * - `deleted` is set when the plane is deleted by a - * kernel call - * - `moved` is set when the plane has been moved or - * resized + * - `created` is set when the plane is first created, either from a VM + * object or from within the engine itself + * - `updated` is set when the plane is updated from another plane and the + * two planes' `planeRect`s do not match + * - `deleted` is set when the plane is deleted by a kernel call + * - `moved` is set when the plane has been moved or resized */ int _created, _updated, _deleted, _moved; /** - * The vanishing point for the plane. Used when - * automatically calculating the correct scaling of the - * plane's screen items according to their position. + * The vanishing point for the plane. Used when automatically calculating + * the correct scaling of the plane's screen items according to their + * position. */ Common::Point _vanishingPoint; /** - * The position & dimensions of the plane in screen - * coordinates. This rect is not clipped to the screen, - * so may include coordinates that are offscreen. + * The position & dimensions of the plane in screen coordinates. This rect + * is not clipped to the screen, so may include coordinates that are + * offscreen. */ Common::Rect _planeRect; /** - * The position & dimensions of the plane in game script - * coordinates. + * The position & dimensions of the plane in game script coordinates. */ Common::Rect _gameRect; /** - * The position & dimensions of the plane in screen - * coordinates. This rect is clipped to the screen. + * The position & dimensions of the plane in screen coordinates. This rect + * is clipped to the screen. */ Common::Rect _screenRect; @@ -257,10 +240,10 @@ public: */ static void init(); - // NOTE: This constructor signature originally did not accept a - // picture ID, but some calls to construct planes with this signature - // immediately set the picture ID and then called setType again, so - // it made more sense to just make the picture ID a parameter instead. + // In SSCI this constructor signature did not accept a picture ID, but some + // calls to construct planes with this signature immediately set the picture + // ID and then called setType again, so it made more sense to just make the + // picture ID a parameter instead. Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored); Plane(const reg_t object); @@ -284,14 +267,13 @@ public: } /** - * Clips the screen rect of this plane to fit within the - * given screen rect. + * Clips the screen rect of this plane to fit within the given screen rect. */ inline void clipScreenRect(const Common::Rect &screenRect) { - // LSL6 hires creates planes with invalid rects; SSCI does not - // care about this, but `Common::Rect::clip` does, so we need to - // check whether or not the rect is actually valid before clipping - // and only clip valid rects + // LSL6 hires creates planes with invalid rects; SSCI does not care + // about this, but `Common::Rect::clip` does, so we need to check + // whether or not the rect is actually valid before clipping and only + // clip valid rects if (_screenRect.isValidRect() && _screenRect.intersects(screenRect)) { _screenRect.clip(screenRect); } else { @@ -305,30 +287,25 @@ public: void printDebugInfo(Console *con) const; /** - * Compares the properties of the current plane against - * the properties of the `other` plane (which is the - * corresponding plane from the visible plane list) to - * discover which properties have been changed on this - * plane by a call to `update(reg_t)`. + * Compares the properties of the current plane against the properties of + * the `other` plane (which is the corresponding plane from the visible + * plane list) to discover which properties have been changed on this plane + * by a call to `update(reg_t)`. * - * @note This method was originally called UpdatePlane - * in SCI engine. + * @note This method was called UpdatePlane in SSCI. */ void sync(const Plane *other, const Common::Rect &screenRect); /** - * Updates the plane to match the state of the plane - * object from the virtual machine. + * Updates the plane to match the state of the plane object from the VM. * - * @note This method was originally called UpdatePlane - * in SCI engine. + * @note This method was called UpdatePlane in SSCI. */ void update(const reg_t object); /** - * Modifies the position of all non-pic screen items - * by the given delta. If `scrollPics` is true, pic - * items are also repositioned. + * Modifies the position of all non-pic screen items by the given delta. If + * `scrollPics` is true, pic items are also repositioned. */ void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics); @@ -336,47 +313,43 @@ public: #pragma mark Plane - Pic private: /** - * Adds all cels from the specified picture resource to - * the plane as screen items. If a position is provided, - * the screen items will be given that position; - * otherwise, the default relative positions for each - * cel will be taken from the picture resource data. + * Adds all cels from the specified picture resource to the plane as screen + * items. If a position is provided, the screen items will be given that + * position; otherwise, the default relative positions for each cel will be + * taken from the picture resource data. */ inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX); /** - * Marks all screen items to be deleted that are within - * this plane and match the given picture ID. + * Marks all screen items to be deleted that are within this plane and match + * the given picture ID. */ void deletePic(const GuiResourceId pictureId); /** - * Marks all screen items to be deleted that are within - * this plane and are picture cels. + * Marks all screen items to be deleted that are within this plane and are + * picture cels. */ void deleteAllPics(); public: /** - * Marks all existing screen items matching the current - * picture to be deleted, then adds all cels from the - * new picture resource to the plane at the given - * position. + * Marks all existing screen items matching the current picture to be + * deleted, then adds all cels from the new picture resource to the plane at + * the given position. */ GuiResourceId addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX, const bool deleteDuplicate = true); /** - * If the plane is a picture plane, re-adds all cels - * from its picture resource to the plane. Otherwise, - * just clears the _pictureChanged flag. + * If the plane is a picture plane, re-adds all cels from its picture + * resource to the plane. Otherwise, just clears the _pictureChanged flag. */ void changePic(); /** - * Marks all screen items to be deleted that are within - * this plane and match the given picture ID, then sets - * the picture ID of the plane to the new picture ID - * without adding any screen items. + * Marks all screen items to be deleted that are within this plane and match + * the given picture ID, then sets the picture ID of the plane to the new + * picture ID without adding any screen items. */ void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId); @@ -384,120 +357,111 @@ public: #pragma mark Plane - Rendering private: /** - * Splits all rects in the given draw list at the edges - * of all higher-priority, non-transparent, intersecting - * planes. + * Splits all rects in the given draw list at the edges of all + * higher-priority, non-transparent, intersecting planes. */ void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const; /** - * Splits all rects in the given erase list at the - * edges of higher-priority, non-transparent, - * intersecting planes. + * Splits all rects in the given erase list at the edges of higher-priority, + * non-transparent, intersecting planes. */ void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const; /** - * Adds the screen item at `index` into `drawList`, - * ensuring it is only drawn within the bounds of - * `rect`. If an existing draw list entry exists - * for this screen item, it will be modified. - * Otherwise, a new entry will be added. + * Adds the screen item at `index` into `drawList`, ensuring it is only + * drawn within the bounds of `rect`. If an existing draw list entry exists + * for this screen item, it will be modified. Otherwise, a new entry will be + * added. */ void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const; /** - * Merges `rect` with an existing rect in `eraseList`, - * if possible. Otherwise, adds the rect as a new entry - * to `eraseList`. + * Merges `rect` with an existing rect in `eraseList`, if possible. + * Otherwise, adds the rect as a new entry to `eraseList`. */ void mergeToRectList(const Common::Rect &rect, RectList &eraseList) const; public: /** - * Calculates the location and dimensions of dirty rects - * of the screen items in this plane and adds them to - * the given draw and erase lists, and synchronises this - * plane's list of screen items to the given visible + * Calculates the location and dimensions of dirty rects of the screen items + * in this plane and adds them to the given draw and erase lists, and + * synchronises this plane's list of screen items to the given visible * plane. */ void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); /** - * Synchronises changes to screen items from the current - * plane to the visible plane and deletes screen items - * from the current plane that have been marked as - * deleted. If `forceUpdate` is true, all screen items - * on the visible plane will be updated, even if they - * are not marked as having changed. + * Synchronises changes to screen items from the current plane to the + * visible plane and deletes screen items from the current plane that have + * been marked as deleted. If `forceUpdate` is true, all screen items on the + * visible plane will be updated, even if they are not marked as having + * changed. */ void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate); /** - * This method is called from the highest priority plane - * to the lowest priority plane. + * This method is called from the highest priority plane to the lowest + * priority plane. * - * Adds screen items from this plane to the draw list - * that must be redrawn because they intersect entries - * in the `higherEraseList`. + * Adds screen items from this plane to the draw list that must be redrawn + * because they intersect entries in the `higherEraseList`. * - * If this plane is opaque, all intersecting erase rects - * in `lowerEraseList` are removed, as they would be - * completely overwritten by the contents of this plane. + * If this plane is opaque, all intersecting erase rects in `lowerEraseList` + * are removed, as they would be completely overwritten by the contents of + * this plane. * - * If this plane is transparent, erase rects from the - * `lowerEraseList` are added to the erase list for this - * plane, so that lower planes. + * If this plane is transparent, erase rects from the `lowerEraseList` are + * added to the erase list for this plane, so that lower planes. * * @param drawList The draw list for this plane. * @param eraseList The erase list for this plane. - * @param higherEraseList The erase list for a plane - * above this plane. + * @param higherEraseList The erase list for a plane above this plane. */ void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &higherEraseList) const; /** - * This method is called from the lowest priority plane - * to the highest priority plane. + * This method is called from the lowest priority plane to the highest + * priority plane. * - * Adds screen items from this plane to the draw list - * that must be drawn because the lower plane is being - * redrawn and potentially transparent screen items - * from this plane would draw over the lower priority - * plane's screen items. + * Adds screen items from this plane to the draw list that must be drawn + * because the lower plane is being redrawn and potentially transparent + * screen items from this plane would draw over the lower priority plane's + * screen items. * * This method applies only to transparent planes. * * @param drawList The draw list for this plane. - * @param eraseList The erase list for a plane below - * this plane. + * @param eraseList The erase list for a plane below this plane. */ void filterUpEraseRects(DrawList &drawList, const RectList &lowerEraseList) const; /** - * This method is called from the lowest priority plane - * to the highest priority plane. + * This method is called from the lowest priority plane to the highest + * priority plane. * - * Adds screen items from this plane to the draw list - * that must be drawn because the lower plane is being - * redrawn and potentially transparent screen items - * from this plane would draw over the lower priority - * plane's screen items. + * Adds screen items from this plane to the draw list that must be drawn + * because the lower plane is being redrawn and potentially transparent + * screen items from this plane would draw over the lower priority plane's + * screen items. * * This method applies only to transparent planes. * * @param drawList The draw list for this plane. - * @param lowerDrawList The draw list for a plane below - * this plane. + * @param lowerDrawList The draw list for a plane below this plane. */ void filterUpDrawRects(DrawList &drawList, const DrawList &lowerDrawList) const; /** - * Updates all of the plane's non-deleted screen items - * and adds them to the given draw and erase lists. + * Updates all of the plane's non-deleted screen items and adds them to the + * given draw and erase lists. */ void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); + /** + * Marks all non-deleted remapped screen items within the plane as needing + * to be updated during the next frameout. + */ void remapMarkRedraw(); }; @@ -514,10 +478,9 @@ private: using PlaneListBase::push_back; public: - // A method for finding the index of a plane inside a - // PlaneList is used because entries in the main plane - // list and visible plane list of GfxFrameout are - // synchronised by index + // A method for finding the index of a plane inside a PlaneList is used + // because entries in the main plane list and visible plane list of + // GfxFrameout are synchronised by index int findIndexByObject(const reg_t object) const; Plane *findByObject(const reg_t object) const; @@ -527,8 +490,8 @@ public: int16 getTopPlanePriority() const; /** - * Gets the priority of the top plane in the plane list - * created by a game script. + * Gets the priority of the top plane in the plane list created by a game + * script. */ int16 getTopSciPlanePriority() const; diff --git a/engines/sci/graphics/remap32.cpp b/engines/sci/graphics/remap32.cpp index 9b5ffcd2f7..6fe0a53f71 100644 --- a/engines/sci/graphics/remap32.cpp +++ b/engines/sci/graphics/remap32.cpp @@ -100,9 +100,9 @@ bool SingleRemap::updateBrightness() { } if (_percent != _lastPercent || _originalColorsChanged[i]) { - // NOTE: SSCI checked if percent was over 100 and only - // then clipped values, but we always unconditionally - // ensure the result is in the correct range + // SSCI checked if percent was over 100 and only then clipped + // values, but we always unconditionally ensure the result is in the + // correct range for simplicity's sake color.r = MIN(255, (uint16)color.r * _percent / 100); color.g = MIN(255, (uint16)color.g * _percent / 100); color.b = MIN(255, (uint16)color.b * _percent / 100); @@ -188,8 +188,7 @@ bool SingleRemap::apply() { const GfxRemap32 *const gfxRemap32 = g_sci->_gfxRemap32; const uint8 remapStartColor = gfxRemap32->getStartColor(); - // Blocked colors are not allowed to be used as target - // colors for the remap + // Blocked colors are not allowed to be used as target colors for the remap bool blockedColors[236]; Common::fill(blockedColors, blockedColors + remapStartColor, false); @@ -207,9 +206,8 @@ bool SingleRemap::apply() { } } - // NOTE: SSCI did a loop over colors here to create a - // new array of updated, unblocked colors, but then - // never used it + // SSCI did a loop over colors here to create a new array of updated, + // unblocked colors, but then never used it bool updated = false; for (uint i = 1; i < remapStartColor; ++i) { @@ -280,8 +278,8 @@ int16 SingleRemap::matchColor(const Color &color, const int minimumDistance, int bestIndex = i; } - // This value is only valid if the last index to - // perform a distance calculation was the best index + // This value is only valid if the last index to perform a distance + // calculation was the best index outDistance = distance; return bestIndex; } @@ -295,10 +293,9 @@ GfxRemap32::GfxRemap32() : _blockedRangeCount(0), _remapStartColor(236), _numActiveRemaps(0) { - // The `_remapStartColor` seems to always be 236 in SSCI, - // but if it is ever changed then the various C-style - // member arrays hard-coded to 236 need to be changed to - // match the highest possible value of `_remapStartColor` + // The `_remapStartColor` seems to always be 236 in SSCI, but if it is ever + // changed then the various C-style member arrays hard-coded to 236 need to + // be changed to match the highest possible value of `_remapStartColor` assert(_remapStartColor == 236); if (g_sci->_features->hasMidPaletteCode()) { @@ -316,9 +313,8 @@ void GfxRemap32::remapOff(const uint8 color) { return; } - // NOTE: SSCI simply ignored invalid input values, but - // we at least give a warning so games can be investigated - // for script bugs + // SSCI simply ignored invalid input values, but we at least give a warning + // so games can be investigated for script bugs if (color < _remapStartColor || color > _remapEndColor) { warning("GfxRemap32::remapOff: %d out of remap range", color); return; @@ -341,9 +337,8 @@ void GfxRemap32::remapAllOff() { } void GfxRemap32::remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta) { - // NOTE: SSCI simply ignored invalid input values, but - // we at least give a warning so games can be investigated - // for script bugs + // SSCI simply ignored invalid input values, but we at least give a warning + // so games can be investigated for script bugs if (color < _remapStartColor || color > _remapEndColor) { warning("GfxRemap32::remapByRange: %d out of remap range", color); return; @@ -375,9 +370,8 @@ void GfxRemap32::remapByRange(const uint8 color, const int16 from, const int16 t } void GfxRemap32::remapByPercent(const uint8 color, const int16 percent) { - // NOTE: SSCI simply ignored invalid input values, but - // we at least give a warning so games can be investigated - // for script bugs + // SSCI simply ignored invalid input values, but we at least give a warning + // so games can be investigated for script bugs if (color < _remapStartColor || color > _remapEndColor) { warning("GfxRemap32::remapByPercent: %d out of remap range", color); return; @@ -397,9 +391,8 @@ void GfxRemap32::remapByPercent(const uint8 color, const int16 percent) { } void GfxRemap32::remapToGray(const uint8 color, const int8 gray) { - // NOTE: SSCI simply ignored invalid input values, but - // we at least give a warning so games can be investigated - // for script bugs + // SSCI simply ignored invalid input values, but we at least give a warning + // so games can be investigated for script bugs if (color < _remapStartColor || color > _remapEndColor) { warning("GfxRemap32::remapToGray: %d out of remap range", color); return; @@ -423,9 +416,8 @@ void GfxRemap32::remapToGray(const uint8 color, const int8 gray) { } void GfxRemap32::remapToPercentGray(const uint8 color, const int16 gray, const int16 percent) { - // NOTE: SSCI simply ignored invalid input values, but - // we at least give a warning so games can be investigated - // for script bugs + // SSCI simply ignored invalid input values, but we at least give a warning + // so games can be investigated for script bugs if (color < _remapStartColor || color > _remapEndColor) { warning("GfxRemap32::remapToPercentGray: %d out of remap range", color); return; diff --git a/engines/sci/graphics/remap32.h b/engines/sci/graphics/remap32.h index 407e879e7c..9fc7ef637d 100644 --- a/engines/sci/graphics/remap32.h +++ b/engines/sci/graphics/remap32.h @@ -55,8 +55,7 @@ public: RemapType _type; /** - * The first color that should be shifted by a range - * remap. + * The first color that should be shifted by a range remap. */ uint8 _from; @@ -66,158 +65,140 @@ public: uint8 _to; /** - * The direction and amount that the colors should be - * shifted in a range remap. + * The direction and amount that the colors should be shifted in a range + * remap. */ int16 _delta; /** - * The difference in brightness that should be - * applied by a brightness (percent) remap. + * The difference in brightness that should be applied by a brightness + * (percent) remap. * - * This value may be be greater than 100, in - * which case the color will be oversaturated. + * This value may be be greater than 100, in which case the color will be + * oversaturated. */ int16 _percent; /** - * The amount of desaturation that should be - * applied by a saturation (gray) remap, where - * 0 is full saturation and 100 is full - * desaturation. + * The amount of desaturation that should be applied by a saturation (gray) + * remap, where 0 is full saturation and 100 is full desaturation. */ uint8 _gray; /** - * The final array used by CelObj renderers to composite - * remapped pixels to the screen buffer. + * The final array used by CelObj renderers to composite remapped pixels to + * the screen buffer. * * Here is how it works: * - * The source bitmap being rendered will have pixels - * within the remap range (236-245 or 236-254), and the - * target buffer will have colors in the non-remapped - * range (0-235). + * The source bitmap being rendered will have pixels within the remap range + * (236-245 or 236-254), and the target buffer will have colors in the + * non-remapped range (0-235). * - * To arrive at the correct color, first the source - * pixel is used to look up the correct SingleRemap for - * that pixel. Then, the final composited color is - * looked up in this array using the target's pixel - * color. In other words, + * To arrive at the correct color, first the source pixel is used to look up + * the correct SingleRemap for that pixel. Then, the final composited color + * is looked up in this array using the target's pixel color. In other + * words, * `target = _remaps[remapEndColor - source].remapColors[target]`. */ uint8 _remapColors[236]; /** - * Resets this SingleRemap's color information to - * default values. + * Resets this SingleRemap's color information to default values. */ void reset(); /** - * Recalculates and reapplies remap colors to the - * `_remapColors` array. + * Recalculates and reapplies remap colors to the `_remapColors` array. */ bool update(); private: /** - * The previous brightness value. Used to - * determine whether or not targetColors needs - * to be updated. + * The previous brightness value. Used to determine whether or not + * `_idealColors` needs to be updated. */ int16 _lastPercent; /** - * The previous saturation value. Used to - * determine whether or not targetColors needs - * to be updated. + * The previous saturation value. Used to determine whether or not + * `_idealColors` needs to be updated. */ uint8 _lastGray; /** - * The colors from the current GfxPalette32 palette - * before this SingleRemap is applied. + * The colors from the current GfxPalette32 palette before this SingleRemap + * is applied. */ Color _originalColors[236]; /** - * Map of colors that changed in `_originalColors` - * when this SingleRemap was updated. This map is - * transient and gets reset to `false` after the + * Map of colors that changed in `_originalColors` when this SingleRemap was + * updated. This map is transient and gets reset to `false` after the * SingleRemap finishes updating. */ bool _originalColorsChanged[236]; /** - * The ideal target RGB color values for each generated - * remap color. + * The ideal target RGB color values for each generated remap color. */ Color _idealColors[236]; /** - * Map of colors that changed in `_idealColors` when - * this SingleRemap was updated. This map is transient - * and gets reset to `false` after the SingleRemap - * finishes applying. + * Map of colors that changed in `_idealColors` when this SingleRemap was + * updated. This map is transient and gets reset to `false` after the + * SingleRemap finishes applying. */ bool _idealColorsChanged[236]; /** - * When applying a SingleRemap, finding an appropriate - * color in the palette is the responsibility of a - * distance function. Once a match is found, the - * distance of that match is stored here so that the - * next time the SingleRemap is applied, it can check - * the distance from the previous application and avoid - * triggering an expensive redraw of the entire screen + * When applying a SingleRemap, finding an appropriate color in the palette + * is the responsibility of a distance function. Once a match is found, the + * distance of that match is stored here so that the next time the + * SingleRemap is applied, it can check the distance from the previous + * application and avoid triggering an expensive redraw of the entire screen * if the new palette value only changed slightly. */ int _matchDistances[236]; /** - * Computes the final target values for a range remap - * and applies them directly to the `_remaps` map. + * Computes the final target values for a range remap and applies them + * directly to the `_remaps` map. * * @note Was ByRange in SSCI. */ bool updateRange(); /** - * Computes the intermediate target values for a - * brightness remap and applies them indirectly via - * the `apply` method. + * Computes the intermediate target values for a brightness remap and + * applies them indirectly via the `apply` method. * * @note Was ByPercent in SSCI. */ bool updateBrightness(); /** - * Computes the intermediate target values for a - * saturation remap and applies them indirectly via - * the `apply` method. + * Computes the intermediate target values for a saturation remap and + * applies them indirectly via the `apply` method. * * @note Was ToGray in SSCI. */ bool updateSaturation(); /** - * Computes the intermediate target values for a - * saturation + brightness bitmap and applies them - * indirectly via the `apply` method. + * Computes the intermediate target values for a saturation + brightness + * bitmap and applies them indirectly via the `apply` method. * * @note Was ToPercentGray in SSCI. */ bool updateSaturationAndBrightness(); /** - * Computes and applies the final values to the - * `_remaps` map. + * Computes and applies the final values to the `_remaps` map. * - * @note In SSCI, a boolean array of changed values - * was passed into this method, but this was done by - * creating arrays on the stack in the caller. Instead - * of doing this, we simply add another member property + * @note In SSCI, a boolean array of changed values was passed into this + * method, but this was done by creating arrays on the stack in the caller. + * Instead of doing this, we simply add another member property * `_idealColorsChanged` and use that instead. */ bool apply(); @@ -225,19 +206,18 @@ private: /** * Calculates the square distance of two colors. * - * @note In SSCI this method is Rgb24::Dist, but it is - * only used by SingleRemap. + * @note In SSCI this method is Rgb24::Dist, but it is only used by + * SingleRemap. */ int colorDistance(const Color &a, const Color &b) const; /** - * Finds the closest index in the next palette matching - * the given RGB color. Returns -1 if no match can be - * found that is closer than `minimumDistance`. + * Finds the closest index in the next palette matching the given RGB color. + * Returns -1 if no match can be found that is closer than + * `minimumDistance`. * - * @note In SSCI, this method is SOLPalette::Match, but - * this particular signature is only used by - * SingleRemap. + * @note In SSCI, this method is SOLPalette::Match, but this particular + * signature is only used by SingleRemap. */ int16 matchColor(const Color &color, const int minimumDistance, int &outDistance, const bool *const blockedIndexes) const; }; @@ -246,8 +226,7 @@ private: #pragma mark GfxRemap32 /** - * This class provides color remapping support for SCI32 - * games. + * This class provides color remapping support for SCI32 games. */ class GfxRemap32 : public Common::Serializable { public: @@ -262,8 +241,8 @@ public: inline int16 getBlockedRangeCount() const { return _blockedRangeCount; } /** - * Turns off remapping of the given color. If `color` is - * 0, all remaps are turned off. + * Turns off remapping of the given color. If `color` is 0, all remaps are + * turned off. */ void remapOff(const uint8 color); @@ -273,55 +252,48 @@ public: void remapAllOff(); /** - * Configures a SingleRemap for the remap color `color`. - * The SingleRemap will shift palette colors between - * `from` and `to` (inclusive) by `delta` palette - * entries when the remap is applied. + * Configures a SingleRemap for the remap color `color`. The SingleRemap + * will shift palette colors between `from` and `to` (inclusive) by `delta` + * palette entries when the remap is applied. */ void remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta); /** - * Configures a SingleRemap for the remap color `color` - * to modify the brightness of remapped colors by - * `percent`. + * Configures a SingleRemap for the remap color `color` to modify the + * brightness of remapped colors by `percent`. */ void remapByPercent(const uint8 color, const int16 percent); /** - * Configures a SingleRemap for the remap color `color` - * to modify the saturation of remapped colors by - * `gray`. + * Configures a SingleRemap for the remap color `color` to modify the + * saturation of remapped colors by `gray`. */ void remapToGray(const uint8 color, const int8 gray); /** - * Configures a SingleRemap for the remap color `color` - * to modify the brightness of remapped colors by - * `percent`, and saturation of remapped colors by - * `gray`. + * Configures a SingleRemap for the remap color `color` to modify the + * brightness of remapped colors by `percent`, and saturation of remapped + * colors by `gray`. */ void remapToPercentGray(const uint8 color, const int16 gray, const int16 percent); /** - * Prevents GfxRemap32 from using the given range of - * palette entries as potential remap targets. + * Prevents GfxRemap32 from using the given range of palette entries as + * potential remap targets. * * @NOTE Was DontMapToRange in SSCI. */ void blockRange(const uint8 from, const int16 count); /** - * Determines whether or not the given color has an - * active remapper. If it does not, it is treated as a - * skip color and the pixel is not drawn. + * Determines whether or not the given color has an active remapper. If it + * does not, it is treated as a skip color and the pixel is not drawn. * - * @note SSCI uses a boolean array to decide whether a - * a pixel is remapped, but it is possible to get the - * same information from `_remaps`, as this function - * does. - * Presumably, the separate array was created for - * performance reasons, since this is called a lot in - * the most critical section of the renderer. + * @note SSCI uses a boolean array to decide whether a pixel is remapped, + * but it is possible to get the same information from `_remaps`, as this + * function does. Presumably, the separate array was created for performance + * reasons, since this is called a lot in the most critical section of the + * renderer. */ inline bool remapEnabled(uint8 color) const { const uint8 index = _remapEndColor - color; @@ -335,10 +307,9 @@ public: } /** - * Calculates the correct color for a target by looking - * up the target color in the SingleRemap that controls - * the given sourceColor. If there is no remap for the - * given color, it will be treated as a skip color. + * Calculates the correct color for a target by looking up the target color + * in the SingleRemap that controls the given sourceColor. If there is no + * remap for the given color, it will be treated as a skip color. */ inline uint8 remapColor(const uint8 sourceColor, const uint8 targetColor) const { const uint8 index = _remapEndColor - sourceColor; @@ -358,12 +329,11 @@ public: } /** - * Updates all active remaps in response to a palette - * change or a remap settings change. + * Updates all active remaps in response to a palette change or a remap + * settings change. * - * `paletteChanged` is true if the next palette in - * GfxPalette32 has been previously modified by other - * palette operations. + * `paletteChanged` is true if the next palette in GfxPalette32 has been + * previously modified by other palette operations. */ bool remapAllTables(const bool paletteUpdated); @@ -371,14 +341,12 @@ private: typedef Common::Array<SingleRemap> SingleRemapsList; /** - * The first index of the remap area in the system - * palette. + * The first index of the remap area in the system palette. */ const uint8 _remapStartColor; /** - * The last index of the remap area in the system - * palette. + * The last index of the remap area in the system palette. */ uint8 _remapEndColor; @@ -393,20 +361,19 @@ private: SingleRemapsList _remaps; /** - * If true, indicates that one or more SingleRemaps were - * reconfigured and all remaps need to be recalculated. + * If true, indicates that one or more SingleRemaps were reconfigured and + * all remaps need to be recalculated. */ bool _needsUpdate; /** - * The first color that is blocked from being used as a - * remap target color. + * The first color that is blocked from being used as a remap target color. */ uint8 _blockedRangeStart; /** - * The size of the range of blocked colors. If zero, - * all colors are potential targets for remapping. + * The size of the range of blocked colors. If zero, all colors are + * potential targets for remapping. */ int16 _blockedRangeCount; }; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 1af36677b3..b9cef360dc 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -179,13 +179,13 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { // We add 2 to the height of the icon bar to add a buffer between the screen and the // icon bar (as did the original interpreter). if (g_sci->getGameId() == GID_KQ6) - initGraphics(_displayWidth, _displayHeight + 26 + 2, _displayWidth > 320); + initGraphics(_displayWidth, _displayHeight + 26 + 2); else if (g_sci->getGameId() == GID_FREDDYPHARKAS) - initGraphics(_displayWidth, _displayHeight + 28 + 2, _displayWidth > 320); + initGraphics(_displayWidth, _displayHeight + 28 + 2); else error("Unknown SCI1.1 Mac game"); } else - initGraphics(_displayWidth, _displayHeight, _displayWidth > 320); + initGraphics(_displayWidth, _displayHeight); } GfxScreen::~GfxScreen() { diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index 14e71735fb..0addd773de 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -39,7 +39,6 @@ uint32 ScreenItem::_nextCreationId = 0; ScreenItem::ScreenItem(const reg_t object) : _creationId(_nextCreationId++), -_celObj(nullptr), _object(object), _pictureId(-1), _created(g_sci->_gfxFrameout->getScreenCount()), @@ -59,7 +58,6 @@ _plane(plane), _useInsetRect(false), _z(0), _celInfo(celInfo), -_celObj(nullptr), _fixedPriority(false), _position(0, 0), _object(make_reg(0, _nextObjectId++)), @@ -76,7 +74,6 @@ _plane(plane), _useInsetRect(false), _z(0), _celInfo(celInfo), -_celObj(nullptr), _fixedPriority(false), _position(rect.left, rect.top), _object(make_reg(0, _nextObjectId++)), @@ -98,7 +95,6 @@ _scale(scaleInfo), _useInsetRect(false), _z(0), _celInfo(celInfo), -_celObj(nullptr), _fixedPriority(false), _position(position), _object(make_reg(0, _nextObjectId++)), @@ -115,7 +111,6 @@ _plane(other._plane), _scale(other._scale), _useInsetRect(other._useInsetRect), _celInfo(other._celInfo), -_celObj(nullptr), _object(other._object), _mirrorX(other._mirrorX), _scaledPosition(other._scaledPosition), @@ -127,18 +122,18 @@ _drawBlackLines(other._drawBlackLines) { } void ScreenItem::operator=(const ScreenItem &other) { - // NOTE: The original engine did not check for differences in `_celInfo` - // to clear `_celObj` here; instead, it unconditionally set `_celInfo`, - // didn't clear `_celObj`, and did hacky stuff in `kIsOnMe` to avoid - // testing a mismatched `_celObj`. See `GfxFrameout::kernelIsOnMe` for - // more detail. kCelTypeMem types are unconditionally invalidated because - // the properties of a CelObjMem can "change" when a game deletes a bitmap - // and then creates a new one that reuses the old bitmap's offset in - // BitmapTable (as happens in the LSL7 About screen when hovering names). + // SSCI did not check for differences in `_celInfo` to clear `_celObj` here; + // instead, it unconditionally set `_celInfo`, didn't clear `_celObj`, and + // did hacky stuff in `kIsOnMe` to avoid testing a mismatched `_celObj`. See + // `GfxFrameout::kernelIsOnMe` for more detail. + // + // kCelTypeMem types are unconditionally invalidated because the properties + // of a CelObjMem can "change" when a game deletes a bitmap and then creates + // a new one that reuses the old bitmap's offset in BitmapTable (as happens + // in the LSL7 About screen when hovering names). if (_celInfo.type == kCelTypeMem || _celInfo != other._celInfo) { _celInfo = other._celInfo; - delete _celObj; - _celObj = nullptr; + _celObj.reset(); } _creationId = other._creationId; @@ -153,10 +148,6 @@ void ScreenItem::operator=(const ScreenItem &other) { _drawBlackLines = other._drawBlackLines; } -ScreenItem::~ScreenItem() { - delete _celObj; -} - void ScreenItem::init() { _nextObjectId = 20000; _nextCreationId = 0; @@ -176,17 +167,12 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo _celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel)); if (_celInfo.resourceId <= kPlanePic) { - // TODO: Enhance GfxView or ResourceManager to allow - // metadata for resources to be retrieved once, from a - // single location Resource *view = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _celInfo.resourceId), false); if (!view) { error("Failed to load %s", _celInfo.toString().c_str()); } - // NOTE: +2 because the header size field itself is excluded from - // the header size in the data - const uint16 headerSize = view->getUint16SEAt(0) + 2; + const uint16 headerSize = view->getUint16SEAt(0) + /* header size field */ sizeof(uint16); const uint8 loopCount = view->getUint8At(2); const uint8 loopSize = view->getUint8At(12); @@ -225,8 +211,7 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo } if (updateCel || updateBitmap) { - delete _celObj; - _celObj = nullptr; + _celObj.reset(); } if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) { @@ -266,10 +251,10 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo } void ScreenItem::calcRects(const Plane &plane) { - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); const CelObj &celObj = getCelObj(); @@ -305,8 +290,8 @@ void ScreenItem::calcRects(const Plane &plane) { } // Cel may use a coordinate system that is not the same size as the - // script coordinate system (usually this means high-resolution - // pictures with low-resolution scripts) + // script coordinate system (usually this means high-resolution pictures + // with low-resolution scripts) if (celObj._xResolution != kLowResX || celObj._yResolution != kLowResY) { // high resolution coordinates @@ -386,7 +371,7 @@ void ScreenItem::calcRects(const Plane &plane) { mulinc(temp, celToScreenX, Ratio()); - CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); + CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj.get()); if (celObjPic == nullptr) { error("Expected a CelObjPic"); } @@ -415,9 +400,8 @@ void ScreenItem::calcRects(const Plane &plane) { if (!scaleX.isOne() || !scaleY.isOne()) { mulinc(_screenItemRect, scaleX, scaleY); - // TODO: This was in the original code, baked into the - // multiplication though it is not immediately clear - // why this is the only one that reduces the BR corner + // TODO: This was in SSCI, baked into the multiplication. It is + // not clear why this is the only one that reduces the BR corner _screenItemRect.right -= 1; _screenItemRect.bottom -= 1; } @@ -434,7 +418,7 @@ void ScreenItem::calcRects(const Plane &plane) { temp.right -= 1; } - CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); + CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj.get()); if (celObjPic == nullptr) { error("Expected a CelObjPic"); } @@ -487,19 +471,19 @@ void ScreenItem::calcRects(const Plane &plane) { } CelObj &ScreenItem::getCelObj() const { - if (_celObj == nullptr) { + if (!_celObj) { switch (_celInfo.type) { case kCelTypeView: - _celObj = new CelObjView(_celInfo.resourceId, _celInfo.loopNo, _celInfo.celNo); + _celObj.reset(new CelObjView(_celInfo.resourceId, _celInfo.loopNo, _celInfo.celNo)); break; case kCelTypePic: error("Internal error, pic screen item with no cel."); break; case kCelTypeMem: - _celObj = new CelObjMem(_celInfo.bitmap); + _celObj.reset(new CelObjMem(_celInfo.bitmap)); break; case kCelTypeColor: - _celObj = new CelObjColor(_celInfo.color, _insetRect.width(), _insetRect.height()); + _celObj.reset(new CelObjColor(_celInfo.color, _insetRect.width(), _insetRect.height())); break; } } @@ -534,7 +518,7 @@ void ScreenItem::printDebugInfo(Console *con) const { con->debugPrintf(" %s\n", _celInfo.toString().c_str()); - if (_celObj != nullptr) { + if (_celObj) { con->debugPrintf(" width %d, height %d, x-resolution %d, y-resolution %d\n", _celObj->_width, _celObj->_height, @@ -583,8 +567,7 @@ void ScreenItem::update() { } _deleted = 0; - delete _celObj; - _celObj = nullptr; + _celObj.reset(); } Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { @@ -604,8 +587,8 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { nsRect = celObjRect; } - const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const uint16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const uint16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); Ratio scaleX, scaleY; if (_scale.signal == kScaleSignalManual) { @@ -647,15 +630,13 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { if (!scaleX.isOne() || !scaleY.isOne()) { // Different games use a different cel scaling mode, but the - // difference isn't consistent across SCI versions; instead, - // it seems to be related to an update that happened during - // SCI2.1mid where games started using hi-resolution game - // scripts + // difference isn't consistent across SCI versions; instead, it + // seems to be related to an update that happened during SCI2.1mid + // where games started using high-resolution game scripts if (scriptWidth == kLowResX) { mulinc(nsRect, scaleX, scaleY); - // TODO: This was in the original code, baked into the - // multiplication though it is not immediately clear - // why this is the only one that reduces the BR corner + // TODO: This was in SSCI, baked into the multiplication. It is + // not clear why this is the only one that reduces the BR corner nsRect.right -= 1; nsRect.bottom -= 1; } else { @@ -693,9 +674,8 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { if (!scaleX.isOne() || !scaleY.isOne()) { mulinc(nsRect, scaleX, scaleY); - // TODO: This was in the original code, baked into the - // multiplication though it is not immediately clear - // why this is the only one that reduces the BR corner + // TODO: This was in SSCI, baked into the multiplication. It is not + // clear why this is the only one that reduces the BR corner nsRect.right -= 1; nsRect.bottom -= 1; } diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index cdde2d3965..5acf759c32 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -49,22 +49,20 @@ class SegManager; #pragma mark ScreenItem /** - * A ScreenItem is the engine-side representation of a - * game script View. + * A ScreenItem is the engine-side representation of a game script View. */ class ScreenItem { private: /** - * A serial used for screen items that are generated - * inside the graphics engine, rather than the - * interpreter. + * A serial used for screen items that are generated inside the graphics + * engine, rather than the interpreter. */ static uint16 _nextObjectId; /** - * A serial used to identify the creation order of - * screen items, to ensure a stable sort order for - * screen items with identical priorities and z-indexes. + * A serial used to identify the creation order of screen items, to ensure a + * stable sort order for screen items with identical priorities and + * z-indexes. */ static uint32 _nextCreationId; @@ -75,160 +73,145 @@ public: reg_t _plane; /** - * Scaling data used to calculate the final screen - * dimensions of the screen item as well as the scaling - * ratios used when drawing the item to screen. + * Scaling data used to calculate the final screen dimensions of the screen + * item as well as the scaling ratios used when drawing the item to screen. */ ScaleInfo _scale; private: /** - * The position & dimensions of the screen item in - * screen coordinates. This rect includes the offset - * of the parent plane, but is not clipped to the - * screen, so may include coordinates that are - * offscreen. + * The position & dimensions of the screen item in screen coordinates. This + * rect includes the offset of the parent plane, but is not clipped to the + * screen, so may include coordinates that are offscreen. */ Common::Rect _screenItemRect; /** - * If true, the `_insetRect` rectangle will be used - * when calculating the dimensions of the screen item - * instead of the cel's intrinsic width and height. + * If true, the `_insetRect` rectangle will be used when calculating the + * dimensions of the screen item instead of the cel's intrinsic width and + * height. * - * In other words, using an inset rect means that - * the cel is cropped to the dimensions given in - * `_insetRect`. + * In other words, using an inset rect means that the cel is cropped to the + * dimensions given in `_insetRect`. */ bool _useInsetRect; /** - * The cropping rectangle used when `_useInsetRect` - * is true. + * The cropping rectangle used when `_useInsetRect` is true. * - * `_insetRect` is also used to describe the fill - * rectangle of a screen item with a CelObjColor - * cel. + * `_insetRect` is also used to describe the fill rectangle of a screen item + * with a CelObjColor cel. */ Common::Rect _insetRect; /** - * The z-index of the screen item in pseudo-3D space. - * Higher values are drawn on top of lower values. + * The z-index of the screen item in pseudo-3D space. Higher values are + * drawn on top of lower values. */ int _z; /** - * Sets the common properties of a screen item that must - * be set both during creation and update of a screen - * item. + * Sets the common properties of a screen item that must be set both during + * creation and update of a screen item. */ void setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap); public: /** - * The creation order number, which ensures a stable - * sort when screen items with identical priorities and - * z-indexes are added to the screen item list. + * The creation order number, which ensures a stable sort when screen items + * with identical priorities and z-indexes are added to the screen item + * list. */ uint32 _creationId; /** - * A descriptor for the cel object represented by the - * screen item. + * A descriptor for the cel object represented by the screen item. */ CelInfo32 _celInfo; /** - * The cel object used to actually render the screen - * item. This member is populated by calling - * `getCelObj`. + * The cel object used to actually render the screen item. This member is + * populated by calling `getCelObj`. */ - mutable CelObj *_celObj; + mutable Common::ScopedPtr<CelObj> _celObj; /** - * If set, the priority for this screen item is fixed - * in place. Otherwise, the priority of the screen item - * is calculated from its y-position + z-index. + * If set, the priority for this screen item is fixed in place. Otherwise, + * the priority of the screen item is calculated from its y-position + + * z-index. */ bool _fixedPriority; /** - * The rendering priority of the screen item, relative - * only to the other screen items within the same plane. - * Higher priorities are drawn above lower priorities. + * The rendering priority of the screen item, relative only to the other + * screen items within the same plane. Higher priorities are drawn above + * lower priorities. */ int16 _priority; /** - * The top-left corner of the screen item, in game - * script coordinates, relative to the parent plane. + * The top-left corner of the screen item, in game script coordinates, + * relative to the parent plane. */ Common::Point _position; /** - * The associated View script object that was - * used to create the ScreenItem, or a numeric - * value in the case of a ScreenItem that was - * generated outside of the VM. + * The associated View script object that was used to create the ScreenItem, + * or a numeric value in the case of a ScreenItem that was generated by the + * kernel. */ reg_t _object; /** - * For screen items representing picture resources, - * the resource ID of the picture. + * For screen items representing picture resources, the resource ID of the + * picture. */ GuiResourceId _pictureId; /** * Flags indicating the state of the screen item. - * - `created` is set when the screen item is first - * created, either from a VM object or from within the - * engine itself - * - `updated` is set when `created` is not already set - * and the screen item is updated from a VM object - * - `deleted` is set by the parent plane, if the parent - * plane is a pic type and its picture resource ID has - * changed + * - `created` is set when the screen item is first created, either from a + * VM object or from within the kernel + * - `updated` is set when `created` is not already set and the screen item + * is updated from a VM object + * - `deleted` is set by the parent plane, if the parent plane is a pic type + * and its picture resource ID has changed */ int _created, _updated, _deleted; /** - * For screen items that represent picture cels, this - * value is set to match the `_mirrorX` property of the - * parent plane and indicates that the cel should be - * drawn horizontally mirrored. For final drawing, it is - * XORed with the `_mirrorX` property of the cel object. - * The cel object's `_mirrorX` property comes from the - * resource data itself. + * For screen items that represent picture cels, this value is set to match + * the `_mirrorX` property of the parent plane and indicates that the cel + * should be + * drawn horizontally mirrored. For final drawing, it is XORed with the + * `_mirrorX` property of the cel object. The cel object's `_mirrorX` + * property comes from the resource data. */ bool _mirrorX; /** - * The scaling ratios to use when drawing this screen - * item. These values are calculated according to the - * scale info whenever the screen item is updated. + * The scaling ratios to use when drawing this screen item. These values are + * calculated according to the scale info whenever the screen item is + * updated. */ Ratio _ratioX, _ratioY; /** - * The top-left corner of the screen item, in screen - * coordinates. + * The top-left corner of the screen item, in screen coordinates. */ Common::Point _scaledPosition; /** - * The position & dimensions of the screen item in - * screen coordinates. This rect includes the offset of - * the parent plane and is clipped to the screen. + * The position & dimensions of the screen item in screen coordinates. This + * rect includes the offset of the parent plane and is clipped to the + * screen. */ Common::Rect _screenRect; /** - * Whether or not the screen item should be drawn - * with black lines drawn every second line. This is - * used when pixel doubling videos to improve apparent - * sharpness at the cost of your eyesight. + * Whether or not the screen item should be drawn with black lines drawn + * every second line. This is used when pixel doubling videos to improve + * apparent sharpness at the cost of your eyesight. */ bool _drawBlackLines; @@ -242,7 +225,6 @@ public: ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect); ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo); ScreenItem(const ScreenItem &other); - ~ScreenItem(); void operator=(const ScreenItem &); inline bool operator<(const ScreenItem &other) const { @@ -337,39 +319,36 @@ public: } /** - * Calculates the dimensions and scaling parameters for - * the screen item, using the given plane as the parent - * plane for screen rect positioning. + * Calculates the dimensions and scaling parameters for the screen item, + * using the given plane as the parent plane for screen rect positioning. * - * @note This method was called Update in SCI engine. + * @note This method was called Update in SSCI. */ void calcRects(const Plane &plane); /** - * Retrieves the corresponding cel object for this - * screen item. If a cel object does not already exist, - * one will be created and assigned. + * Retrieves the corresponding cel object for this screen item. If a cel + * object does not already exist, one will be created and assigned. */ CelObj &getCelObj() const; void printDebugInfo(Console *con) const; /** - * Updates the properties of the screen item from a - * VM object. + * Updates the properties of the screen item from a VM object. */ void update(const reg_t object); /** - * Updates the properties of the screen item for one not belonging - * to a VM object. Originally GraphicsMgr::UpdateScreenItem. + * Updates the properties of the screen item for one not belonging to a VM + * object. Originally GraphicsMgr::UpdateScreenItem. */ void update(); /** - * Gets the "now seen" rect for the screen item, which - * represents the current size and position of the - * screen item on the screen in script coordinates. + * Gets the "now seen" rect for the screen item, which represents the + * current size and position of the screen item on the screen in script + * coordinates. */ Common::Rect getNowSeenRect(const Plane &plane) const; }; diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index 7399fe0268..6ebbcc0f0e 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -44,7 +44,7 @@ int16 GfxText32::_yResolution = 0; GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : _segMan(segMan), _cache(fonts), - // Not a typo, the original engine did not initialise height, only width + // SSCI did not initialise height, so we intentionally do not do so also _width(0), _text(""), _bitmap(NULL_REG) { @@ -53,8 +53,8 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : } void GfxText32::init() { - _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + _xResolution = g_sci->_gfxFrameout->getScriptWidth(); + _yResolution = g_sci->_gfxFrameout->getScriptHeight(); } reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling, const bool gc) { @@ -73,8 +73,8 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect setFont(fontId); if (doScaling) { - int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); Ratio scaleX(_xResolution, scriptWidth); Ratio scaleY(_yResolution, scriptHeight); @@ -84,8 +84,8 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect mulinc(_textRect, scaleX, scaleY); } - // _textRect represents where text is drawn inside the - // bitmap; clipRect is the entire bitmap + // `_textRect` represents where text is drawn inside the bitmap; `clipRect` + // is the entire bitmap Common::Rect bitmapRect(_width, _height); if (_textRect.intersects(bitmapRect)) { @@ -115,8 +115,8 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & setFont(fontId); - int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); mulinc(_textRect, Ratio(_xResolution, scriptWidth), Ratio(_yResolution, scriptHeight)); @@ -134,9 +134,9 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & SciBitmap &bitmap = *_segMan->allocateBitmap(&_bitmap, _width, _height, _skipColor, 0, 0, _xResolution, _yResolution, 0, false, gc); - // NOTE: The engine filled the bitmap pixels with 11 here, which is silly - // because then it just erased the bitmap using the skip color. So we don't - // fill the bitmap redundantly here. + // SSCI filled the bitmap pixels with 11 here, which is silly because then + // it just erased the bitmap using the skip color. So we don't fill the + // bitmap redundantly here. _backColor = _skipColor; erase(bitmapRect, false); @@ -164,9 +164,10 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & } void GfxText32::setFont(const GuiResourceId fontId) { - // NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font - // table is built on the FontMgr directly; instead, because we already have - // font resources, this code just grabs a font out of GfxCache. + // In SSCI, this calls FontMgr::BuildFontTable, and then a font table is + // built on the FontMgr directly; instead, because we already have GfxFont + // resources from SCI16 and those resources did not change in SCI32, this + // code just grabs those out of GfxCache if (fontId != _fontId) { _fontId = fontId; _font = _cache->getFont(_fontId); @@ -179,7 +180,7 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint SciBitmap &bitmap = *_segMan->lookupBitmap(_bitmap); byte *pixels = bitmap.getPixels() + rect.top * _width + rect.left; - // NOTE: Not fully disassembled, but this should be right + // Not fully disassembled, but appears to be correct in all cases int16 rectWidth = targetRect.width(); int16 heightRemaining = targetRect.height(); int16 sidesHeight = heightRemaining - size * 2; @@ -217,7 +218,7 @@ void GfxText32::drawChar(const char charIndex) { } int16 GfxText32::getScaledFontHeight() const { - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); return (_font->getHeight() * scriptHeight + _yResolution - 1) / _yResolution; } @@ -275,10 +276,9 @@ void GfxText32::drawTextBox(const Common::String &text) { void GfxText32::drawText(const uint index, uint length) { assert(index + length <= _text.size()); - // NOTE: This draw loop implementation is somewhat different than the - // implementation in the actual engine, but should be accurate. Primarily - // the changes revolve around eliminating some extra temporaries and - // fixing the logic to match. + // This draw loop implementation is somewhat different than the + // implementation in SSCI, but is accurate. Primarily the changes revolve + // around eliminating some extra temporaries and fixing the logic to match. const char *text = _text.c_str() + index; while (length-- > 0) { char currentChar = *text++; @@ -335,16 +335,16 @@ void GfxText32::drawText(const uint index, uint length) { void GfxText32::invertRect(const reg_t bitmapId, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) { Common::Rect targetRect = rect; if (doScaling) { - bitmapStride = bitmapStride * _xResolution / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + bitmapStride = bitmapStride * _xResolution / g_sci->_gfxFrameout->getScriptWidth(); targetRect = scaleRect(rect); } SciBitmap &bitmap = *_segMan->lookupBitmap(bitmapId); - // NOTE: SCI code is super weird here; it seems to be trying to look at the - // entire size of the bitmap including the header, instead of just the pixel - // data size. We just look at the pixel size. This function generally is an - // odd duck since the stride dimension for a bitmap is built in to the bitmap + // SSCI is super weird here; it seems to be trying to look at the entire + // size of the bitmap including the header, instead of just the pixel data + // size. We just look at the pixel size. This function generally is an odd + // duck since the stride dimension for a bitmap is built in to the bitmap // header, so perhaps it was once an unheadered bitmap format and this // function was never updated to match? Or maybe they exploit the // configurable stride length somewhere else to do stair stepping inverts... @@ -355,7 +355,7 @@ void GfxText32::invertRect(const reg_t bitmapId, int16 bitmapStride, const Commo error("InvertRect too big: %u >= %u", invertSize, bitmapSize); } - // NOTE: Actual engine just added the bitmap header size hardcoded here + // SSCI just added a hardcoded bitmap header size here byte *pixel = bitmap.getPixels() + bitmapStride * targetRect.top + targetRect.left; int16 stride = bitmapStride - targetRect.width(); @@ -392,16 +392,17 @@ uint GfxText32::getLongest(uint *charIndex, const int16 width) { char currentChar; while ((currentChar = *text++) != '\0') { - // NOTE: In the original engine, the font, color, and alignment were - // reset here to their initial values + // In SSCI, the font, color, and alignment were reset here to their + // initial values; this does not seem to be necessary and really + // complicates the font system, so we do not do it // The text to render contains a line break; stop at the line break if (currentChar == '\r' || currentChar == '\n') { - // Skip the rest of the line break if it is a Windows-style - // \r\n or non-standard \n\r - // NOTE: In the original engine, the `text` pointer had not been - // advanced yet so the indexes used to access characters were - // one higher + // Skip the rest of the line break if it is a Windows-style \r\n (or + // non-standard \n\r) + + // In SSCI, the `text` pointer had not been advanced yet here, so + // the indexes used to access characters were one higher there if ( (currentChar == '\r' && text[0] == '\n') || (currentChar == '\n' && text[0] == '\r' && text[1] != '\n') @@ -418,16 +419,20 @@ uint GfxText32::getLongest(uint *charIndex, const int16 width) { } // Skip the line break and return all text seen up to now - // NOTE: In original engine, the font, color, and alignment were - // reset, then getTextWidth was called to use its side-effects to - // set font, color, and alignment according to the text from - // `initialCharIndex` to `testLength` + // In SSCI, the font, color, and alignment were reset, then + // getTextWidth was called to use its side-effects to set font, + // color, and alignment according to the text from + // `initialCharIndex` to `testLength`. This is complicated and + // apparently not necessary for correct operation, so we do not do + // it + ++*charIndex; return testLength; } else if (currentChar == ' ') { - // The last word in the line made it too wide to fit in the text area; - // return up to the previous word, then collapse the whitespace - // between that word and its next sibling word into the line break + // The last word in the line made it too wide to fit in the text + // area; return up to the previous word, then collapse the + // whitespace between that word and its next sibling word into the + // line break if (getTextWidth(initialCharIndex, testLength) > width) { *charIndex = lastWordBreakIndex; const char *nextChar = _text.c_str() + lastWordBreakIndex; @@ -435,14 +440,17 @@ uint GfxText32::getLongest(uint *charIndex, const int16 width) { ++*charIndex; } - // NOTE: In original engine, the font, color, and alignment were - // set here to the values that were seen at the last space character + // In SSCI, the font, color, and alignment were set here to the + // values that were seen at the last space character, but this + // is complicated and unnecessary so we do not do it + return length; } - // NOTE: In the original engine, the values of _fontId, _foreColor, - // and _alignment were stored for use in the return path mentioned - // just above here + // In SSCI, the values of `_fontId`, `_foreColor`, and `_alignment` + // were stored for use in the return path mentioned just above here, + // but we do not need to do this because we do not cause + // side-effects when calculating text dimensions // We found a word break that was within the text area, memorise it // and continue processing. +1 on the character index because it has @@ -456,8 +464,9 @@ uint GfxText32::getLongest(uint *charIndex, const int16 width) { ++*charIndex; ++testLength; - // NOTE: In the original engine, the font, color, and alignment were - // reset here to their initial values + // In SSCI, the font, color, and alignment were reset here to their + // initial values, but we do not need to do this because we do not cause + // side-effects when calculating text dimensions // The text to render contained no word breaks yet but is already too // wide for the text area; just split the word in half at the point @@ -471,10 +480,11 @@ uint GfxText32::getLongest(uint *charIndex, const int16 width) { // The complete text to render was a single word, or was narrower than // the text area, so return the entire line if (length == 0 || getTextWidth(initialCharIndex, testLength) <= width) { - // NOTE: In original engine, the font, color, and alignment were - // reset, then getTextWidth was called to use its side-effects to - // set font, color, and alignment according to the text from - // `initialCharIndex` to `testLength` + // In SSCI, the font, color, and alignment were reset, then getTextWidth + // was called to use its side-effects to set font, color, and alignment + // according to the text from `initialCharIndex` to `testLength`. This + // is not necessary because we do not cause side-effects when + // calculating text dimensions return testLength; } @@ -495,14 +505,13 @@ int16 GfxText32::getTextWidth(const uint index, uint length) const { while (length > 0 && currentChar != '\0') { // Control codes are in the format `|<code><value>|` if (currentChar == '|') { - // NOTE: Original engine code changed the global state of the - // FontMgr here upon encountering any color, alignment, or - // font control code. - // To avoid requiring all callers to manually restore these - // values on every call, we ignore control codes other than - // font change (since alignment and color do not change the - // width of characters), and simply update the font pointer - // on stack instead of the member property font. + // SSCI changed the global state of the FontMgr here upon + // encountering any color, alignment, or font control code. To avoid + // requiring all callers to manually restore these values on every + // call, we ignore control codes other than font change (since + // alignment and color do not change the width of characters), and + // simply update the font pointer on stack instead of the member + // property font to avoid these unnecessary side-effects. currentChar = *text++; --length; @@ -548,17 +557,17 @@ int16 GfxText32::getTextWidth(const Common::String &text, const uint index, cons } Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, bool doScaling) { - // NOTE: Like most of the text rendering code, this function was pretty - // weird in the original engine. The initial result rectangle was actually - // a 1x1 rectangle (0, 0, 0, 0), which was then "fixed" after the main - // text size loop finished running by subtracting 1 from the right and - // bottom edges. Like other functions in SCI32, this has been converted - // to use exclusive rects with inclusive rounding. + // Like most of the text rendering code, this function was pretty weird in + // SSCI. The initial result rectangle was actually a 1x1 rectangle + // (0, 0, 0, 0), which was then "fixed" after the main text size loop + // finished running by subtracting 1 from the right and bottom edges. Like + // other functions in SCI32, this has been converted to use exclusive rects + // with inclusive rounding. Common::Rect result; - int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); maxWidth = maxWidth * _xResolution / scriptWidth; @@ -598,17 +607,18 @@ Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { result.bottom = 0; } else { - // NOTE: In the original engine code, the bottom was not decremented - // by 1, which means that the rect was actually a pixel taller than - // the height of the font. This was not the case in the other branch, - // which decremented the bottom by 1 at the end of the loop. + // In SSCI, the bottom was not decremented by 1, which means that + // the rect was actually a pixel taller than the height of the font. + // This was not the case in the other branch, which decremented the + // bottom by 1 at the end of the loop. For accuracy, we do what SSCI + // did, even though this means the result is a pixel off result.bottom = _font->getHeight() + 1; } } if (doScaling) { - // NOTE: The original engine code also scaled top/left but these are - // always zero so there is no reason to do that. + // SSCI also scaled top/left but these are always zero so there is no + // reason to do that result.right = ((result.right - 1) * scriptWidth + _xResolution - 1) / _xResolution + 1; result.bottom = ((result.bottom - 1) * scriptHeight + _yResolution - 1) / _yResolution + 1; } @@ -628,8 +638,8 @@ int16 GfxText32::getStringWidth(const Common::String &text) { } int16 GfxText32::getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling) { - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); Common::Rect scaledRect(textRect); if (doScaling) { @@ -700,7 +710,10 @@ void GfxText32::scrollLine(const Common::String &lineText, int numLines, uint8 c _foreColor = color; _alignment = align; - //int fc = _foreColor; + + // As with other font functions, SSCI saved _foreColor here so it could be + // restored after the getTextWidth call, but this call is side-effect-free + // in our implementation so this is not necessary setFont(fontId); @@ -713,8 +726,8 @@ void GfxText32::scrollLine(const Common::String &lineText, int numLines, uint8 c _drawPosition.x += _textRect.width() - textWidth; } - //_foreColor = fc; - //setFont(fontId); + // _foreColor and font were restored here in SSCI due to side-effects of + // getTextWidth which do not exist in our implementation drawText(0, lineText.size()); } diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index c8fafb287f..f9de3ad5fb 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -45,11 +45,11 @@ enum ScrollDirection { class GfxFont; /** - * This class handles text calculation and rendering for - * SCI32 games. The text calculation system in SCI32 is - * nearly the same as SCI16, which means this class behaves - * similarly. Notably, GfxText32 maintains drawing - * parameters across multiple calls. + * This class handles text calculation and rendering for SCI32 games. The text + * calculation system in SCI32 is nearly the same as SCI16, which means this + * class behaves similarly. Notably, GfxText32 maintains drawing parameters + * across multiple calls, instead of requiring all text parameters to be + * provided on every draw call. */ class GfxText32 { private: @@ -57,10 +57,10 @@ private: GfxCache *_cache; /** - * The width and height of the currently active text - * bitmap, in text-system coordinates. + * The width and height of the currently active text bitmap, in text-system + * coordinates. * - * @note These are unsigned in the actual engine. + * @note These are unsigned in SSCI. */ int16 _width, _height; @@ -75,20 +75,19 @@ private: uint8 _backColor; /** - * The transparent color of the text box. Used when - * compositing the bitmap onto the screen. + * The transparent color of the text box. Used when compositing the bitmap + * onto the screen. */ uint8 _skipColor; /** - * The rect where the text is drawn within the bitmap. - * This rect is clipped to the dimensions of the bitmap. + * The rect where the text is drawn within the bitmap. This rect is clipped + * to the dimensions of the bitmap. */ Common::Rect _textRect; /** - * The text being drawn to the currently active text - * bitmap. + * The text being drawn to the currently active text bitmap. */ Common::String _text; @@ -103,7 +102,8 @@ private: int16 _borderColor; /** - * TODO: Document + * If true, text will be drawn using a dither that draws only every other + * pixel of the text. */ bool _dimmed; @@ -123,28 +123,26 @@ private: void drawText(const uint index, uint length); /** - * Gets the length of the longest run of text available - * within the currently loaded text, starting from the - * given `charIndex` and running for up to `maxWidth` - * pixels. Returns the number of characters that can be - * written, and mutates the value pointed to by - * `charIndex` to point to the index of the next - * character to render. + * Gets the length of the longest run of text available within the currently + * loaded text, starting from the given `charIndex` and running for up to + * `maxWidth` pixels. Returns the number of characters that can be written, + * and mutates the value pointed to by `charIndex` to point to the index of + * the next character to render. */ uint getLongest(uint *charIndex, const int16 maxWidth); /** - * Gets the pixel width of a substring of the currently - * loaded text, without scaling. + * Gets the pixel width of a substring of the currently loaded text, without + * scaling. */ int16 getTextWidth(const uint index, uint length) const; inline Common::Rect scaleRect(const Common::Rect &rect) { Common::Rect scaledRect(rect); - int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - Ratio scaleX(_xResolution, scriptWidth); - Ratio scaleY(_yResolution, scriptHeight); + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); + const Ratio scaleX(_xResolution, scriptWidth); + const Ratio scaleY(_yResolution, scriptHeight); mulinc(scaledRect, scaleX, scaleY); return scaledRect; } @@ -163,29 +161,27 @@ public: reg_t _bitmap; /** - * The size of the x-dimension of the coordinate system - * used by the text renderer. Static since it was global in SSCI. + * The size of the x-dimension of the coordinate system used by the text + * renderer. Static since it was global in SSCI. */ static int16 _xResolution; /** - * The size of the y-dimension of the coordinate system - * used by the text renderer. Static since it was global in SSCI. + * The size of the y-dimension of the coordinate system used by the text + * renderer. Static since it was global in SSCI. */ static int16 _yResolution; /** - * The currently active font resource used to write text - * into the bitmap. + * The currently active font resource used to write text into the bitmap. * - * @note SCI engine builds the font table directly - * inside of FontMgr; we use GfxFont instead. + * @note SSCI builds the font table directly inside of FontMgr; we use + * GfxFont instead. */ GfxFont *_font; /** - * Creates a plain font bitmap with a flat color - * background. + * Creates a plain font bitmap with a flat color background. */ reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling, const bool gc); @@ -195,12 +191,12 @@ public: reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed, const bool gc); inline int scaleUpWidth(int value) const { - const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); return (value * scriptWidth + _xResolution - 1) / _xResolution; } inline int scaleUpHeight(int value) const { - const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); return (value * scriptHeight + _yResolution - 1) / _yResolution; } @@ -212,26 +208,22 @@ public: /** * Draws the given text to the bitmap. * - * @note The original engine holds a reference to a - * shared string which lets the text be updated from - * outside of the font manager. Instead, we give this - * extra signature to send the text to draw. - * - * TODO: Use shared string instead? + * @note SSCI holds a reference to a shared string which lets the text be + * updated from outside of the font manager. Instead, we use this extra + * signature to send the text to draw. */ void drawTextBox(const Common::String &text); /** - * Erases the given rect by filling with the background - * color. + * Erases the given rect by filling with the background color. */ void erase(const Common::Rect &rect, const bool doScaling); void invertRect(const reg_t bitmap, const int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling); /** - * Sets the font to be used for rendering and - * calculation of text dimensions. + * Sets the font to be used for rendering and calculation of text + * dimensions. */ void setFont(const GuiResourceId fontId); @@ -251,8 +243,8 @@ public: Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling); /** - * Gets the pixel width of a substring of the currently - * loaded text, with scaling. + * Gets the pixel width of a substring of the currently loaded text, with + * scaling. */ int16 getTextWidth(const Common::String &text, const uint index, const uint length); @@ -262,23 +254,21 @@ public: int16 getStringWidth(const Common::String &text); /** - * Gets the number of characters of `text`, starting - * from `index`, that can be safely rendered into - * `textRect`. + * Gets the number of characters of `text`, starting from `index`, that can + * be safely rendered into `textRect`. */ int16 getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling); /** - * Gets the number of characters of `text`, starting - * from `index`, that can be safely rendered into - * `textRect` using the given font. + * Gets the number of characters of `text`, starting from `index`, that can + * be safely rendered into `textRect` using the given font. */ int16 getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling); /** * Scroll up/down one line. `numLines` is the number of the lines in the - * textarea, and `textLine` contains the text to draw as the newly - * visible line. Originally FontMgr::DrawOneLine and FontMgr::UpOneLine. + * textarea, and `textLine` contains the text to draw as the newly visible + * line. Originally FontMgr::DrawOneLine and FontMgr::UpOneLine. */ void scrollLine(const Common::String &textLine, int numLines, uint8 color, TextAlign align, GuiResourceId fontId, ScrollDirection dir); }; diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp index df9c2da82b..43479a2505 100644 --- a/engines/sci/graphics/transitions32.cpp +++ b/engines/sci/graphics/transitions32.cpp @@ -126,7 +126,7 @@ void GfxTransitions32::processShowStyles() { g_sci->_gfxFrameout->frameOut(true); throttle(); } - } while(continueProcessing && doFrameOut); + } while (continueProcessing && doFrameOut); } void GfxTransitions32::processEffects(PlaneShowStyle &showStyle) { @@ -249,14 +249,13 @@ void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeOb if (createNewEntry) { entry = new PlaneShowStyle; - // NOTE: SCI2.1 engine tests if allocation returned a null pointer - // but then only avoids setting currentStep if this is so. Since - // this is a nonsensical approach, we do not do that here + // SSCI2.1 tests if allocation returned a null pointer but then only + // avoids setting currentStep if this is so. Since this nonsensical, we + // do not do that here entry->currentStep = 0; entry->processed = false; entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; entry->plane = planeObj; - entry->fadeColorRangesCount = 0; if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { // for pixel dissolve @@ -267,32 +266,26 @@ void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeOb entry->screenItems.clear(); entry->width = plane->_gameRect.width(); entry->height = plane->_gameRect.height(); - } else { - entry->fadeColorRanges = nullptr; - if (hasFadeArray) { - // NOTE: SCI2.1mid engine does no check to verify that an array is - // successfully retrieved, and SegMan will cause a fatal error - // if we try to use a memory segment that is not an array - SciArray &table = *_segMan->lookupArray(pFadeArray); - - uint32 rangeCount = table.size(); - entry->fadeColorRangesCount = rangeCount; - - // NOTE: SCI engine code always allocates memory even if the range - // table has no entries, but this does not really make sense, so - // we avoid the allocation call in this case - if (rangeCount > 0) { - entry->fadeColorRanges = new uint16[rangeCount]; - for (size_t i = 0; i < rangeCount; ++i) { - entry->fadeColorRanges[i] = table.getAsInt16(i); - } + } else if (hasFadeArray) { + // SSCI2.1mid does no check to verify that an array is successfully + // retrieved + SciArray &table = *_segMan->lookupArray(pFadeArray); + + const uint32 rangeCount = table.size(); + + // SSCI always allocates memory even if the range table has no + // entries, but this does not really make sense, so we avoid the + // allocation call in this case + if (rangeCount > 0) { + entry->fadeColorRanges.reserve(rangeCount); + for (uint32 i = 0; i < rangeCount; ++i) { + entry->fadeColorRanges.push_back(table.getAsInt16(i)); } } } } - // NOTE: The original engine had no nullptr check and would just crash - // if it got to here + // SSCI had no nullptr check and would just crash if it got to here if (entry == nullptr) { error("Cannot edit non-existing ShowStyle entry"); } @@ -397,10 +390,9 @@ ShowStyleList::iterator GfxTransitions32::deleteShowStyle(const ShowStyleList::i break; case kShowStyleFadeIn: case kShowStyleFadeOut: - if (getSciVersion() > SCI_VERSION_2_1_EARLY && showStyle->fadeColorRangesCount > 0) { - delete[] showStyle->fadeColorRanges; - } - break; + // SSCI manually allocated the color ranges for fades and deleted that + // memory here, but we use a container so there is no extra cleanup + // needed case kShowStyleNone: case kShowStyleMorph: case kShowStyleHShutterIn: @@ -552,7 +544,8 @@ void GfxTransitions32::configure21EarlyDissolve(PlaneShowStyle &showStyle, const showStyle.bitmap = bitmapId; const Buffer &source = g_sci->_gfxFrameout->getCurrentBuffer(); - Buffer target(showStyle.width, showStyle.height, bitmap.getPixels()); + Buffer target; + target.init(showStyle.width, showStyle.height, showStyle.width, bitmap.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); target.fillRect(Common::Rect(bitmap.getWidth(), bitmap.getHeight()), kDefaultSkipColor); target.copyRectToSurface(source, 0, 0, gameRect); @@ -762,7 +755,8 @@ bool GfxTransitions32::processPixelDissolve21Early(PlaneShowStyle &showStyle) { bool unchanged = true; SciBitmap &bitmap = *_segMan->lookupBitmap(showStyle.bitmap); - Buffer buffer(showStyle.width, showStyle.height, bitmap.getPixels()); + Buffer buffer; + buffer.init(showStyle.width, showStyle.height, showStyle.width, bitmap.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); uint32 numPixels = showStyle.width * showStyle.height; uint32 numPixelsPerDivision = (numPixels + showStyle.divisions) / showStyle.divisions; @@ -944,8 +938,8 @@ bool GfxTransitions32::processFade(const int8 direction, PlaneShowStyle &showSty percent *= 100; percent /= showStyle.divisions - 1; - if (showStyle.fadeColorRangesCount > 0) { - for (int i = 0, len = showStyle.fadeColorRangesCount; i < len; i += 2) { + if (showStyle.fadeColorRanges.size()) { + for (uint i = 0, len = showStyle.fadeColorRanges.size(); i < len; i += 2) { g_sci->_gfxPalette32->setFade(percent, showStyle.fadeColorRanges[i], showStyle.fadeColorRanges[i + 1]); } } else { diff --git a/engines/sci/graphics/transitions32.h b/engines/sci/graphics/transitions32.h index c4c52283d1..4dbacf41b9 100644 --- a/engines/sci/graphics/transitions32.h +++ b/engines/sci/graphics/transitions32.h @@ -28,7 +28,7 @@ #include "sci/engine/vm_types.h" namespace Sci { -enum ShowStyleType /* : uint8 */ { +enum ShowStyleType { kShowStyleNone = 0, kShowStyleHShutterOut = 1, kShowStyleHShutterIn = 2, @@ -48,14 +48,12 @@ enum ShowStyleType /* : uint8 */ { }; /** - * Show styles represent transitions applied to draw planes. - * One show style per plane can be active at a time. + * A show style represents a transition applied to a Plane. One show style per + * plane can be active at a time. */ struct PlaneShowStyle { /** - * The ID of the plane this show style belongs to. - * In SCI2.1mid (at least SQ6), per-plane transitions - * were removed and a single plane ID is used. + * The ID of the plane this transition applies to. */ reg_t plane; @@ -65,9 +63,8 @@ struct PlaneShowStyle { ShowStyleType type; /** - * When true, the show style is an entry transition - * to a new room. When false, it is an exit - * transition away from an old room. + * When true, the show style is an entry transition to a new room. When + * false, it is an exit transition away from an old room. */ bool fadeUp; @@ -77,38 +74,35 @@ struct PlaneShowStyle { int16 divisions; /** - * The color used by transitions that draw CelObjColor - * screen items. -1 for transitions that do not draw - * screen items. + * The color used by transitions that draw CelObjColor screen items. -1 for + * transitions that do not draw screen items. */ int16 color; - // TODO: Probably uint32 - // TODO: This field probably should be used in order to - // provide time-accurate processing of show styles. In the - // actual SCI engine (at least 2–2.1mid) it appears that - // style transitions are drawn “as fast as possibleâ€, one - // step per loop, even though this delay field exists + /** + * The amount of time, in ticks, between each cycle of the animation. + */ int delay; - // TODO: Probably bool, but never seems to be true? + /** + * If true, GfxTransitions32 will yield back to the main game loop after + * calculating the next frame. Otherwise, GfxTransitions32 takes exclusive + * control over the game loop until the transition has completed. + */ bool animate; /** - * The wall time at which the next step of the animation - * should execute. + * The time at which the next step of the animation should execute. */ uint32 nextTick; /** - * During playback of the show style, the current step - * (out of divisions). + * During playback of the show style, the current step (out of `divisions`). */ int currentStep; /** - * Whether or not this style has finished running and - * is ready for disposal. + * Whether or not this style has finished running and is ready for disposal. */ bool processed; @@ -117,32 +111,30 @@ struct PlaneShowStyle { // /** - * A list of screen items, each representing one - * block of a wipe transition. + * A list of screen items, each representing one block of a wipe transition. + * These screen items are owned by GfxFrameout. */ Common::Array<ScreenItem *> screenItems; /** - * For wipe transitions, the number of edges with a - * moving wipe (1, 2, or 4). + * For wipe transitions, the number of edges with a moving wipe (1, 2, or + * 4). */ uint8 numEdges; /** - * The dimensions of the plane, in game script - * coordinates. + * The dimensions of the plane, in game script coordinates. */ int16 width, height; /** - * For pixel dissolve transitions, the screen item - * used to render the transition. + * For pixel dissolve transitions, the screen item used to render the + * transition. This screen item is owned by GfxFrameout. */ ScreenItem *bitmapScreenItem; /** - * For pixel dissolve transitions, the bitmap used - * to render the transition. + * For pixel dissolve transitions, the bitmap used to render the transition. */ reg_t bitmap; @@ -152,15 +144,13 @@ struct PlaneShowStyle { uint32 dissolveMask; /** - * The first pixel that was dissolved in a pixel - * dissolve transition. + * The first pixel that was dissolved in a pixel dissolve transition. */ uint32 firstPixel; /** - * The last pixel that was dissolved. Once all - * pixels have been dissolved, `pixel` will once - * again equal `firstPixel`. + * The last pixel that was dissolved. Once all pixels have been dissolved, + * `pixel` will once again equal `firstPixel`. */ uint32 pixel; @@ -169,21 +159,15 @@ struct PlaneShowStyle { // /** - * The number of entries in the fadeColorRanges array. - */ - uint8 fadeColorRangesCount; - - /** - * A pointer to an dynamically sized array of palette - * indexes, in the order [ fromColor, toColor, ... ]. + * An array of palette indexes, in the order [ fromColor, toColor, ... ]. * Only colors within this range are transitioned. */ - uint16 *fadeColorRanges; + Common::Array<uint16> fadeColorRanges; }; /** - * PlaneScroll describes a transition between two different - * pictures within a single plane. + * PlaneScroll describes a transition between two different pictures within a + * single plane. */ struct PlaneScroll { /** @@ -197,28 +181,26 @@ struct PlaneScroll { int16 x, y; /** - * The distance that should be scrolled. Only one of - * `deltaX` or `deltaY` may be set. + * The distance that should be scrolled. Only one of `deltaX` or `deltaY` + * may be set. */ int16 deltaX, deltaY; /** - * The pic that should be created and scrolled into - * view inside the plane. + * The pic that should be created and scrolled into view inside the plane. */ GuiResourceId newPictureId; /** - * The picture that should be scrolled out of view - * and deleted from the plane. + * The picture that should be scrolled out of view and deleted from the + * plane. */ GuiResourceId oldPictureId; /** - * If true, the scroll animation is interleaved - * with other updates to the graphics. If false, - * the scroll will be exclusively animated until - * it is finished. + * If true, GfxTransitions32 will yield back to the main game loop after + * calculating the next frame. Otherwise, GfxTransitions32 takes exclusive + * control over the game loop until the transition has completed. */ bool animate; @@ -235,6 +217,7 @@ class GfxTransitions32 { public: GfxTransitions32(SegManager *_segMan); ~GfxTransitions32(); + private: SegManager *_segMan; @@ -264,209 +247,185 @@ public: inline bool hasShowStyles() const { return !_showStyles.empty(); } /** - * Processes all active show styles in a loop - * until they are finished. + * Processes all active show styles in a loop until they are finished. */ void processShowStyles(); /** - * Processes show styles that are applied - * through `GfxFrameout::palMorphFrameOut`. + * Processes show styles that are applied through + * `GfxFrameout::palMorphFrameOut`. */ void processEffects(PlaneShowStyle &showStyle); - // NOTE: This signature is taken from SCI3 Phantasmagoria 2 - // and is valid for all implementations of SCI32 void kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen); /** - * Sets the range that will be used by - * `GfxFrameout::palMorphFrameOut` to alter - * palette entries. + * Sets the range that will be used by `GfxFrameout::palMorphFrameOut` to + * alter palette entries. */ void kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor); /** - * A map of palette entries that can be morphed - * by the Morph show style. + * A map of palette entries that can be morphed by the Morph show style. */ int8 _styleRanges[256]; private: /** - * Default sequence values for pixel dissolve - * transition bit masks. + * Default sequence values for pixel dissolve transition bit masks. */ int *_dissolveSequenceSeeds; /** - * Default values for `PlaneShowStyle::divisions` - * for the current SCI version. + * Default values for `PlaneShowStyle::divisions` for the current SCI + * version. */ int16 *_defaultDivisions; /** - * The list of PlaneShowStyles that are - * currently active. + * The list of PlaneShowStyles that are currently active. */ ShowStyleList _showStyles; /** - * Finds a show style that applies to the given - * plane. + * Finds a show style that applies to the given plane. */ PlaneShowStyle *findShowStyleForPlane(const reg_t planeObj); /** - * Finds the iterator for a show style that - * applies to the given plane. + * Finds the iterator for a show style that applies to the given plane. */ ShowStyleList::iterator findIteratorForPlane(const reg_t planeObj); /** - * Deletes the given PlaneShowStyle and returns - * the next PlaneShowStyle from the list of - * styles. + * Deletes the given PlaneShowStyle and returns the next PlaneShowStyle from + * the list of styles. */ ShowStyleList::iterator deleteShowStyle(const ShowStyleList::iterator &showStyle); /** - * Initializes the given PlaneShowStyle for a - * horizontal wipe effect for SCI2 to 2.1early. + * Initializes the given PlaneShowStyle for a horizontal wipe effect for + * SCI2 to 2.1early. */ void configure21EarlyHorizontalWipe(PlaneShowStyle &showStyle, const int16 priority); /** - * Initializes the given PlaneShowStyle for a - * horizontal shutter effect for SCI2 to 2.1early. + * Initializes the given PlaneShowStyle for a horizontal shutter effect for + * SCI2 to 2.1early. */ void configure21EarlyHorizontalShutter(PlaneShowStyle &showStyle, const int16 priority); /** - * Initializes the given PlaneShowStyle for an - * iris effect for SCI2 to 2.1early. + * Initializes the given PlaneShowStyle for an iris effect for SCI2 to + * 2.1early. */ void configure21EarlyIris(PlaneShowStyle &showStyle, const int16 priority); /** - * Initializes the given PlaneShowStyle for a - * pixel dissolve effect for SCI2 to 2.1early. + * Initializes the given PlaneShowStyle for a pixel dissolve effect for SCI2 + * to 2.1early. */ void configure21EarlyDissolve(PlaneShowStyle &showStyle, const int16 priority, const Common::Rect &gameRect); /** - * Processes one tick of the given - * PlaneShowStyle. + * Processes one tick of the given PlaneShowStyle. */ bool processShowStyle(PlaneShowStyle &showStyle, uint32 now); /** - * Performs an instant transition between two - * rooms. + * Performs an instant transition between two rooms. */ bool processNone(PlaneShowStyle &showStyle); /** - * Performs a transition that renders into a room - * with a horizontal shutter effect. + * Performs a transition that renders into a room with a horizontal shutter + * effect. */ bool processHShutterOut(PlaneShowStyle &showStyle); /** - * Performs a transition that renders to black - * with a horizontal shutter effect. + * Performs a transition that renders to black with a horizontal shutter + * effect. */ void processHShutterIn(const PlaneShowStyle &showStyle); /** - * Performs a transition that renders into a room - * with a vertical shutter effect. + * Performs a transition that renders into a room with a vertical shutter + * effect. */ void processVShutterOut(PlaneShowStyle &showStyle); /** - * Performs a transition that renders to black - * with a vertical shutter effect. + * Performs a transition that renders to black with a vertical shutter + * effect. */ void processVShutterIn(PlaneShowStyle &showStyle); /** - * Performs a transition that renders into a room - * with a wipe to the left. + * Performs a transition that renders into a room with a wipe to the left. */ void processWipeLeft(PlaneShowStyle &showStyle); /** - * Performs a transition that renders to black - * with a wipe to the right. + * Performs a transition that renders to black with a wipe to the right. */ void processWipeRight(PlaneShowStyle &showStyle); /** - * Performs a transition that renders into a room - * with a wipe upwards. + * Performs a transition that renders into a room with a wipe upwards. */ void processWipeUp(PlaneShowStyle &showStyle); /** - * Performs a transition that renders to black - * with a wipe downwards. + * Performs a transition that renders to black with a wipe downwards. */ void processWipeDown(PlaneShowStyle &showStyle); /** - * Performs a transition that renders into a room - * with an iris effect. + * Performs a transition that renders into a room with an iris effect. */ bool processIrisOut(PlaneShowStyle &showStyle); /** - * Performs a transition that renders to black - * with an iris effect. + * Performs a transition that renders to black with an iris effect. */ bool processIrisIn(PlaneShowStyle &showStyle); /** - * Performs a transition that renders between - * rooms using a block dissolve effect. + * Performs a transition that renders between rooms using a block dissolve + * effect. */ void processDissolveNoMorph(PlaneShowStyle &showStyle); /** - * Performs a transition that renders between - * rooms with a pixel dissolve effect. + * Performs a transition that renders between rooms with a pixel dissolve + * effect. */ bool processPixelDissolve(PlaneShowStyle &showStyle); /** - * SCI2 to 2.1early implementation of pixel - * dissolve. + * SCI2 to 2.1early implementation of pixel dissolve. */ bool processPixelDissolve21Early(PlaneShowStyle &showStyle); /** - * SCI2.1mid and later implementation of - * pixel dissolve. + * SCI2.1mid and later implementation of pixel dissolve. */ bool processPixelDissolve21Mid(const PlaneShowStyle &showStyle); /** - * Performs a transition that fades to black - * between rooms. + * Performs a transition that fades to black between rooms. */ bool processFade(const int8 direction, PlaneShowStyle &showStyle); /** - * Morph transition calls back into the - * transition system's `processEffects` - * method, which then applies transitions - * other than None, Fade, or Morph. + * Morph transition calls back into the transition system's `processEffects` + * method, which then applies transitions other than None, Fade, or Morph. */ bool processMorph(PlaneShowStyle &showStyle); /** - * Performs a generic transition for any of - * the wipe/shutter/iris effects. + * Performs a generic transition for any of the wipe/shutter/iris effects. */ bool processWipe(const int8 direction, PlaneShowStyle &showStyle); @@ -476,8 +435,7 @@ public: inline bool hasScrolls() const { return !_scrolls.empty(); } /** - * Processes all active plane scrolls - * in a loop until they are finished. + * Processes all active plane scrolls in a loop until they are finished. */ void processScrolls(); @@ -490,8 +448,7 @@ private: ScrollList _scrolls; /** - * Performs a scroll of the content of - * a plane. + * Performs a scroll of the content of a plane. */ bool processScroll(PlaneScroll &scroll); }; diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index e837cecb49..8d68c8e3bb 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -63,8 +63,8 @@ bool VideoPlayer::open(const Common::String &fileName) { } #ifndef USE_RGB_COLOR - // KQ7 2.00b videos are compressed in 24bpp Cinepak, so cannot play on - // a system with no RGB support + // KQ7 2.00b videos are compressed in 24bpp Cinepak, so cannot play on a + // system with no RGB support if (_decoder->getPixelFormat().bytesPerPixel != 1) { void showScummVMDialog(const Common::String &message); showScummVMDialog(Common::String::format(_("Cannot play back %dbpp video on a system with maximum color depth of 8bpp"), _decoder->getPixelFormat().bpp())); @@ -117,8 +117,8 @@ bool VideoPlayer::endHQVideo() { } VideoPlayer::EventFlags VideoPlayer::playUntilEvent(const EventFlags flags, const uint32 maxSleepMs) { - // Flushing all the keyboard and mouse events out of the event manager - // keeps events queued from before the start of playback from accidentally + // Flushing all the keyboard and mouse events out of the event manager keeps + // events queued from before the start of playback from accidentally // activating a video stop flag _eventMan->flushEvents(); @@ -201,11 +201,10 @@ void VideoPlayer::submitPalette(const uint8 palette[256 * 3]) const { g_system->getPaletteManager()->setPalette(palette, 0, 256); // KQ7 1.x has videos encoded using Microsoft Video 1 where palette 0 is - // white and 255 is black, which is basically the opposite of DOS/Win - // SCI palettes. So, when drawing to an 8bpp hwscreen, whenever a new - // palette is seen, the screen must be re-filled with the new black - // entry to ensure areas outside the video are always black and not some - // other color + // white and 255 is black, which is basically the opposite of DOS/Win SCI + // palettes. So, when drawing to an 8bpp hwscreen, whenever a new palette is + // seen, the screen must be re-filled with the new black entry to ensure + // areas outside the video are always black and not some other color for (int color = 0; color < 256; ++color) { if (palette[0] == 0 && palette[1] == 0 && palette[2] == 0) { g_system->fillScreen(color); @@ -231,7 +230,8 @@ void VideoPlayer::renderFrame(const Graphics::Surface &nextFrame) const { if (_decoder->getWidth() != _drawRect.width() || _decoder->getHeight() != _drawRect.height()) { Graphics::Surface *const unscaledFrame(convertedFrame); // TODO: The only reason TransparentSurface is used here because it is - // where common scaler code is right now. + // where common scaler code is right now, which should just be part of + // Graphics::Surface (or some free functions). const Graphics::TransparentSurface tsUnscaledFrame(*unscaledFrame); #ifdef USE_RGB_COLOR if (_hqVideoMode) { @@ -307,17 +307,17 @@ void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const return; } - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); const int16 scaledWidth = (_decoder->getWidth() * Ratio(screenWidth, scriptWidth)).toInt(); const int16 scaledHeight = (_decoder->getHeight() * Ratio(screenHeight, scriptHeight)).toInt(); - // Normally we would use the coordinates passed into the play function - // to position the video, but since we are scaling the video (which SSCI - // did not do), the coordinates are not correct. Since videos are always + // Normally we would use the coordinates passed into the play function to + // position the video, but since we are scaling the video (which SSCI did + // not do), the coordinates are not correct. Since videos are always // intended to play in the center of the screen, we just recalculate the // origin here. _drawRect.left = (screenWidth - scaledWidth) / 2; @@ -392,8 +392,8 @@ AVIPlayer::IOStatus AVIPlayer::init(const bool doublePixels) { height *= 2; } - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); // When scaling videos, they must not grow larger than the hardware screen // or else the engine will crash. This is particularly important for the GK1 @@ -449,9 +449,9 @@ AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int1 } AVIPlayer::EventFlags AVIPlayer::playUntilEvent(const EventFlags flags, const uint32 maxSleepMs) { - // NOTE: In SSCI, whether or not a video could be skipped was controlled by - // game scripts; here, we always allow skipping video with the mouse or - // escape key, to improve the user experience + // In SSCI, whether or not a video could be skipped was controlled by game + // scripts; here, we always allow skipping video with the mouse or escape + // key, to improve the user experience return VideoPlayer::playUntilEvent(flags | kEventFlagMouseDown | kEventFlagEscapeKey, maxSleepMs); } @@ -681,8 +681,8 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags, const uint32) { if (flags & kEventFlagReverse) { - // NOTE: This flag may not work properly since SSCI does not care - // if a video has audio, but the VMD decoder does. + // This flag may not work properly since SSCI does not care if a video + // has audio, but the VMD decoder does. warning("VMD reverse playback flag was set. Please report this event to the bug tracker"); const bool success = _decoder->setReverse(true); assert(success); @@ -783,20 +783,7 @@ void VMDPlayer::redrawGameScreen() const { return; } - Graphics::Surface *game = g_sci->_gfxFrameout->getCurrentBuffer().convertTo(g_system->getScreenFormat(), g_sci->_gfxPalette32->getHardwarePalette()); - assert(game); - - Common::Rect rects[4]; - int splitCount = splitRects(Common::Rect(game->w, game->h), _drawRect, rects); - if (splitCount != -1) { - while (splitCount--) { - const Common::Rect &drawRect = rects[splitCount]; - g_system->copyRectToScreen(game->getBasePtr(drawRect.left, drawRect.top), game->pitch, drawRect.left, drawRect.top, drawRect.width(), drawRect.height()); - } - } - - game->free(); - delete game; + g_sci->_gfxFrameout->redrawGameScreen(_drawRect); } #endif @@ -838,10 +825,10 @@ void VMDPlayer::submitPalette(const uint8 rawPalette[256 * 3]) const { if (_isComposited) { SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId); bitmap->setPalette(palette); - // NOTE: SSCI calls updateScreenItem and frameOut here, but this should - // not be necessary in ScummVM since the new palette gets submitted - // before the next frame is rendered, and the frame rendering call will - // perform the same operations. + // SSCI calls updateScreenItem and frameOut here, but this is not + // necessary in ScummVM since the new palette gets submitted before the + // next frame is rendered, and the frame rendering call will perform the + // same operations. } else { g_sci->_gfxPalette32->submit(palette); g_sci->_gfxPalette32->updateForFrame(); @@ -900,8 +887,8 @@ void VMDPlayer::initComposited() { } const uint32 hunkPaletteSize = HunkPalette::calculateHunkPaletteSize(256, false); - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); SciBitmap &vmdBitmap = *_segMan->allocateBitmap(&_bitmapId, _drawRect.width(), _drawRect.height(), 255, 0, 0, screenWidth, screenHeight, hunkPaletteSize, false, false); vmdBitmap.getBuffer().fillRect(Common::Rect(_drawRect.width(), _drawRect.height()), 0); @@ -931,7 +918,7 @@ void VMDPlayer::initComposited() { _screenItem->_drawBlackLines = true; } - // NOTE: There was code for positioning the screen item using insetRect + // In SSCI, there was code for positioning the screen item using insetRect // here, but none of the game scripts seem to use this functionality. g_sci->_gfxFrameout->addScreenItem(*_screenItem); @@ -1016,9 +1003,8 @@ void VMDPlayer::setPlane(const int16 priority, const reg_t planeId) { void VMDPlayer::restrictPalette(const uint8 startColor, const int16 endColor) { _startColor = startColor; - // At least GK2 sends 256 as the end color, which is wrong, - // but works in the original engine as the storage size is 4 bytes - // and used values are clamped to 0-255 + // At least GK2 sends 256 as the end color, which is wrong, but works in + // SSCI as the storage size is 4 bytes and used values are clamped to 0-255 _endColor = MIN<int16>(255, endColor); } diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 7b2cffa2ae..ce5e4ba883 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -331,7 +331,7 @@ public: */ VMDStatus getStatus() const; - // NOTE: Was WaitForEvent in SSCI + // Was WaitForEvent in SSCI EventFlags kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval); private: @@ -347,7 +347,8 @@ private: /** * The Resource object for VMDs that are read out of a resource bundle - * instead of being streamed from the filesystem. + * instead of being streamed from the filesystem. The resource is owned by + * ResourceManager. */ Resource *_bundledVmd; @@ -399,12 +400,13 @@ protected: private: /** - * The plane where the VMD will be drawn. + * The plane where the VMD will be drawn. The plane is owned by GfxFrameout. */ Plane *_plane; /** - * The screen item representing the VMD surface. + * The screen item representing the VMD surface. The screen item is owned by + * GfxFrameout. */ ScreenItem *_screenItem; @@ -538,7 +540,7 @@ private: /** * An optional plane that will be used to black out areas of the screen - * outside of the VMD surface. + * outside of the VMD surface. The plane is owned by GfxFrameout. */ Plane *_blackoutPlane; @@ -675,6 +677,7 @@ protected: private: /** * An empty plane drawn behind the video when the doFrameOut flag is true. + * The plane is owned by GfxFrameout. */ Plane *_plane; diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp index 59ebc86bf3..f9a0140323 100644 --- a/engines/sci/sound/audio32.cpp +++ b/engines/sci/sound/audio32.cpp @@ -153,6 +153,7 @@ Audio32::Audio32(ResourceManager *resMan) : _handle(), _mutex(), + _channels(getSciVersion() < SCI_VERSION_2_1_EARLY ? 10 : getSciVersion() < SCI_VERSION_3 ? 5 : 8), _numActiveChannels(0), _inAudioThread(false), @@ -170,22 +171,10 @@ Audio32::Audio32(ResourceManager *resMan) : _startedAtTick(0), _attenuatedMixing(true), + _useModifiedAttenuation(g_sci->_features->usesModifiedAudioAttenuation()), _monitoredChannelIndex(-1), - _monitoredBuffer(nullptr), - _monitoredBufferSize(0), _numMonitoredSamples(0) { - - if (getSciVersion() < SCI_VERSION_2_1_EARLY) { - _channels.resize(10); - } else if (getSciVersion() < SCI_VERSION_3) { - _channels.resize(5); - } else { - _channels.resize(8); - } - - _useModifiedAttenuation = g_sci->_features->usesModifiedAudioAttenuation(); - // In games where scripts premultiply master audio volumes into the volumes // of the individual audio channels sent to the mixer, Audio32 needs to use // the kPlainSoundType so that the master SFX volume is not applied twice. @@ -202,15 +191,14 @@ Audio32::Audio32(ResourceManager *resMan) : Audio32::~Audio32() { stop(kAllChannels); _mixer->stopHandle(_handle); - free(_monitoredBuffer); } #pragma mark - #pragma mark AudioStream implementation -int Audio32::writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) { +int Audio32::writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume) { const int samplePairsToRead = numSamples >> 1; - const int samplePairsWritten = converter->flow(*sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume); + const int samplePairsWritten = converter.flow(sourceStream, targetBuffer, samplePairsToRead, leftVolume, rightVolume); return samplePairsWritten << 1; } @@ -243,34 +231,28 @@ bool Audio32::channelShouldMix(const AudioChannel &channel) const { return true; } -// In earlier versions of SCI32 engine, audio mixing is -// split into three different functions. +// In earlier versions of SCI32 engine, audio mixing is split into three +// different functions. // -// The first function is called from the main game thread in -// AsyncEventCheck; later versions of SSCI also call it when -// getting the playback position. This function is -// responsible for cleaning up finished channels and -// filling active channel buffers with decompressed audio -// matching the hardware output audio format so they can -// just be copied into the main DAC buffer directly later. +// The first function is called from the main game thread in AsyncEventCheck; +// later versions of SSCI also call it when getting the playback position. This +// function is responsible for cleaning up finished channels and filling active +// channel buffers with decompressed audio matching the hardware output audio +// format so they can just be copied into the main DAC buffer directly later. // -// The second function is called by the audio hardware when -// the DAC buffer needs to be filled, and by `play` when -// there is only one active sample (so it can just blow away -// whatever was already in the DAC buffer). It merges all -// active channels into the DAC buffer and then updates the -// offset into the DAC buffer. +// The second function is called by the audio hardware when the DAC buffer needs +// to be filled, and by `play` when there is only one active sample (so it can +// just blow away whatever was already in the DAC buffer). It merges all active +// channels into the DAC buffer and then updates the offset into the DAC buffer. // -// Finally, a third function is called by the second -// function, and it actually puts data into the DAC buffer, -// performing volume, distortion, and balance adjustments. +// Finally, a third function is called by the second function, and it actually +// puts data into the DAC buffer, performing volume, distortion, and balance +// adjustments. // -// Since we only have one callback from the audio thread, -// and should be able to do all audio processing in -// real time, and we have streams, and we do not need to -// completely fill the audio buffer, the functionality of -// all these original functions is combined here and -// simplified. +// Since we only have one callback from the audio thread, and should be able to +// do all audio processing in real time, and we have streams, and we do not need +// to completely fill the audio buffer, the functionality of all these original +// functions is combined here and simplified. int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) { Common::StackLock lock(_mutex); @@ -278,20 +260,17 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) return 0; } - // ResourceManager is not thread-safe so we need to - // avoid calling into it from the audio thread, but at - // the same time we need to be able to clear out any - // finished channels on a regular basis + // ResourceManager is not thread-safe so we need to avoid calling into it + // from the audio thread, but at the same time we need to be able to clear + // out any finished channels on a regular basis _inAudioThread = true; freeUnusedChannels(); const bool playOnlyMonitoredChannel = getSciVersion() != SCI_VERSION_3 && _monitoredChannelIndex != -1; - // The caller of `readBuffer` is a rate converter, - // which reuses (without clearing) an intermediate - // buffer, so we need to zero the intermediate buffer - // to prevent mixing into audio data from the last - // callback. + // The caller of `readBuffer` is a rate converter, which reuses (without + // clearing) an intermediate buffer, so we need to zero the intermediate + // buffer to prevent mixing into audio data from the last callback. memset(buffer, 0, numSamples * sizeof(Audio::st_sample_t)); // This emulates the attenuated mixing mode of SSCI engine, which reduces @@ -340,8 +319,8 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) continue; } - // Channel finished fading and had the - // stopChannelOnFade flag set, so no longer exists + // Channel finished fading and had the stopChannelOnFade flag set, so no + // longer exists if (channel.fadeStartTick && processFade(channelIndex)) { --channelIndex; continue; @@ -352,10 +331,10 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) if (channel.pan == -1 || !isStereo()) { int volume = channel.volume; if (getSciVersion() == SCI_VERSION_2) { - // NOTE: In SSCI, audio is decompressed into a temporary - // buffer, then the samples in that buffer are looped over, - // shifting each sample right 3, 2, or 1 bits to reduce the - // volume. + // In SSCI, audio is decompressed into a temporary buffer, then + // the samples in that buffer are looped over, shifting each + // sample right 3, 2, or 1 bits to reduce the volume within the + // ranges given here if (volume > 0 && volume <= 42) { volume = 15; } else if (volume > 42 && volume <= 84) { @@ -367,20 +346,18 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) // In SCI3, granularity of the non-maximum volumes is 1/32 volume &= ~4; - // NOTE: In the SSCI DOS interpreter, non-maximum volumes are - // divided by 8 which puts them in a range of [0, 16). That - // reduced volume range gets passed into a volume function which - // expects values [0, 32). So, effectively, all non-maximum - // volumes are half-volume in DOS in SCI3. In Windows, volumes - // [120, 124) are the same as 127 due to a programming bug. - // We do not emulate either of these incorrect behaviors. + // In the SSCI DOS interpreter, non-maximum volumes are divided + // by 8 which puts them in a range of [0, 16). That reduced + // volume range gets passed into a volume function which expects + // values [0, 32). So, effectively, all non-maximum volumes are + // half-volume in DOS in SCI3 due to a programming bug. In + // Windows, volumes [120, 124) are the same as 127 due to + // another programming bug. We do not emulate either of these + // incorrect behaviors. } leftVolume = rightVolume = volume * Audio::Mixer::kMaxChannelVolume / kMaxVolume; } else { - // TODO: This should match the SCI3 algorithm, - // which seems to halve the volume of each - // channel when centered; is this intended? leftVolume = channel.volume * (100 - channel.pan) / 100 * Audio::Mixer::kMaxChannelVolume / kMaxVolume; rightVolume = channel.volume * channel.pan / 100 * Audio::Mixer::kMaxChannelVolume / kMaxVolume; } @@ -397,19 +374,15 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) } if (channelIndex == _monitoredChannelIndex) { - const size_t bufferSize = numSamples * sizeof(Audio::st_sample_t); - if (_monitoredBufferSize < bufferSize) { - _monitoredBuffer = (Audio::st_sample_t *)realloc(_monitoredBuffer, bufferSize); - _monitoredBufferSize = bufferSize; + if (numSamples > (int)_monitoredBuffer.size()) { + _monitoredBuffer.resize(numSamples); } + memset(_monitoredBuffer.data(), 0, _monitoredBuffer.size() * sizeof(Audio::st_sample_t)); + _numMonitoredSamples = writeAudioInternal(*channel.stream, *channel.converter, _monitoredBuffer.data(), numSamples, leftVolume, rightVolume); - memset(_monitoredBuffer, 0, _monitoredBufferSize); - - _numMonitoredSamples = writeAudioInternal(channel.stream, channel.converter, _monitoredBuffer, numSamples, leftVolume, rightVolume); - - Audio::st_sample_t *sourceBuffer = _monitoredBuffer; + Audio::st_sample_t *sourceBuffer = _monitoredBuffer.data(); Audio::st_sample_t *targetBuffer = buffer; - const Audio::st_sample_t *const end = _monitoredBuffer + _numMonitoredSamples; + const Audio::st_sample_t *const end = _monitoredBuffer.data() + _numMonitoredSamples; while (sourceBuffer != end) { Audio::clampedAdd(*targetBuffer++, *sourceBuffer++); } @@ -428,7 +401,7 @@ int Audio32::readBuffer(Audio::st_sample_t *const buffer, const int numSamples) leftVolume = rightVolume = 0; } - const int channelSamplesWritten = writeAudioInternal(channel.stream, channel.converter, buffer, numSamples, leftVolume, rightVolume); + const int channelSamplesWritten = writeAudioInternal(*channel.stream, *channel.converter, buffer, numSamples, leftVolume, rightVolume); if (channelSamplesWritten > maxSamplesWritten) { maxSamplesWritten = channelSamplesWritten; } @@ -458,10 +431,9 @@ uint8 Audio32::getNumUnlockedChannels() const { } int16 Audio32::findChannelByArgs(int argc, const reg_t *argv, const int startIndex, const reg_t soundNode) const { - // NOTE: argc/argv are already reduced by one in our engine because - // this call is always made from a subop, so no reduction for the - // subop is made in this function. SSCI takes extra steps to skip - // the subop argument. + // SSCI takes extra steps to skip the subop argument here, but argc/argv are + // already reduced by one in our engine by the kernel since these calls are + // always subops so we do not need to do anything extra argc -= startIndex; if (argc <= 0) { @@ -501,10 +473,10 @@ int16 Audio32::findChannelById(const ResourceId resourceId, const reg_t soundNod if (resourceId.getType() == kResourceTypeAudio) { for (int16 i = 0; i < _numActiveChannels; ++i) { - const AudioChannel channel = _channels[i]; + const AudioChannel &candidate = _channels[i]; if ( - channel.id == resourceId && - (soundNode.isNull() || soundNode == channel.soundNode) + candidate.id == resourceId && + (soundNode.isNull() || soundNode == candidate.soundNode) ) { return i; } @@ -553,7 +525,7 @@ void Audio32::freeUnusedChannels() { } void Audio32::freeChannel(const int16 channelIndex) { - // The original engine did this: + // SSCI did this: // 1. Unlock memory-cached resource, if one existed // 2. Close patched audio file descriptor, if one existed // 3. Free decompression memory buffer, if one existed @@ -563,14 +535,13 @@ void Audio32::freeChannel(const int16 channelIndex) { // Robots have no corresponding resource to free if (channel.robot) { - delete channel.stream; - channel.stream = nullptr; + channel.stream.reset(); channel.robot = false; } else { - // We cannot unlock resources from the audio thread - // because ResourceManager is not thread-safe; instead, - // we just record that the resource needs unlocking and - // unlock it whenever we are on the main thread again + // We cannot unlock resources from the audio thread because + // ResourceManager is not thread-safe; instead, we just record that the + // resource needs unlocking and unlock it whenever we are on the main + // thread again if (_inAudioThread) { _resourcesToUnlock.push_back(channel.resource); } else { @@ -578,12 +549,10 @@ void Audio32::freeChannel(const int16 channelIndex) { } channel.resource = nullptr; - delete channel.stream; - channel.stream = nullptr; + channel.stream.reset(); } - delete channel.converter; - channel.converter = nullptr; + channel.converter.reset(); if (_monitoredChannelIndex == channelIndex) { _monitoredChannelIndex = -1; @@ -677,13 +646,12 @@ bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) { channel.pausedAtTick = 0; channel.soundNode = NULL_REG; channel.volume = kMaxVolume; - // TODO: SCI3 introduces stereo audio channel.pan = -1; - channel.converter = Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false); + channel.converter.reset(Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false)); // The RobotAudioStream buffer size is // ((bytesPerSample * channels * sampleRate * 2000ms) / 1000ms) & ~3 // where bytesPerSample = 2, channels = 1, and sampleRate = 22050 - channel.stream = new RobotAudioStream(88200); + channel.stream.reset(new RobotAudioStream(88200)); _robotAudioPaused = false; if (_numActiveChannels == 1) { @@ -691,7 +659,7 @@ bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) { } } - return static_cast<RobotAudioStream *>(channel.stream)->addPacket(packet); + return static_cast<RobotAudioStream *>(channel.stream.get())->addPacket(packet); } bool Audio32::queryRobotAudio(RobotAudioStream::StreamState &status) const { @@ -703,7 +671,7 @@ bool Audio32::queryRobotAudio(RobotAudioStream::StreamState &status) const { return false; } - status = static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->getStatus(); + status = static_cast<RobotAudioStream *>(getChannel(channelIndex).stream.get())->getStatus(); return true; } @@ -715,7 +683,7 @@ bool Audio32::finishRobotAudio() { return false; } - static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->finish(); + static_cast<RobotAudioStream *>(getChannel(channelIndex).stream.get())->finish(); return true; } @@ -741,7 +709,7 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool if (channelIndex != kNoExistingChannel) { AudioChannel &channel = getChannel(channelIndex); - MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream); + MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream.get()); if (stream == nullptr) { error("[Audio32::play]: Unable to cast stream for resource %s", resourceId.toString().c_str()); } @@ -760,43 +728,42 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool return 0; } - // NOTE: SCI engine itself normally searches in this order: + // SSCI normally searches in this order: // // For Audio36: // - // 1. First, request a FD using Audio36 name and use it as the - // source FD for reading the audio resource data. - // 2a. If the returned FD is -1, or equals the audio map, or - // equals the audio bundle, try to get the offset of the - // data from the audio map, using the Audio36 name. + // 1. First, request a FD using Audio36 name and use it as the source FD for + // reading the audio resource data. + // 2a. If the returned FD is -1, or equals the audio map, or equals the + // audio bundle, try to get the offset of the data from the audio map, + // using the Audio36 name. // - // If the returned offset is -1, this is not a valid resource; - // return 0. Otherwise, set the read offset for the FD to the - // returned offset. - // 2b. Otherwise, use the FD as-is (it is a patch file), with zero - // offset, and record it separately so it can be closed later. + // If the returned offset is -1, this is not a valid resource; return 0. + // Otherwise, set the read offset for the FD to the returned offset. + // 2b. Otherwise, use the FD as-is (it is a patch file), with zero offset, + // and record it separately so it can be closed later. // // For plain audio: // - // 1. First, request an Audio resource from the resource cache. If - // one does not exist, make the same request for a Wave resource. - // 2a. If an audio resource was discovered, record its memory ID - // and clear the streaming FD - // 2b. Otherwise, request an Audio FD. If one does not exist, make - // the same request for a Wave FD. If neither exist, this is not - // a valid resource; return 0. Otherwise, use the returned FD as - // the streaming ID and set the memory ID to null. + // 1. First, request an Audio resource from the resource cache. If one does + // not exist, make the same request for a Wave resource. + // 2a. If an audio resource was discovered, record its memory ID and clear + // the streaming FD + // 2b. Otherwise, request an Audio FD. If one does not exist, make the same + // request for a Wave FD. If neither exist, this is not a valid + // resource; return 0. Otherwise, use the returned FD as the streaming + // ID and set the memory ID to null. // // Once these steps are complete, the audio engine either has a file - // descriptor + offset that it can use to read streamed audio, or it - // has a memory ID that it can use to read cached audio. + // descriptor + offset that it can use to read streamed audio, or it has a + // memory ID that it can use to read cached audio. // - // Here in ScummVM we just ask the resource manager to give us the - // resource and we get a seekable stream. + // Here in ScummVM we just ask the resource manager to give us the resource + // and we get a seekable stream. - // TODO: This should be fixed to use streaming, which means - // fixing the resource manager to allow streaming, which means - // probably rewriting a bunch of the resource manager. + // TODO: This should be fixed to use streaming, which means fixing the + // resource manager to allow streaming, which means probably rewriting a + // bunch of the resource manager. Resource *resource = _resMan->findResource(resourceId, true); if (resource == nullptr) { warning("[Audio32::play]: %s could not be found", resourceId.toString().c_str()); @@ -812,7 +779,6 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool channel.fadeStartTick = 0; channel.soundNode = soundNode; channel.volume = volume < 0 || volume > kMaxVolume ? (int)kMaxVolume : volume; - // TODO: SCI3 introduces stereo audio channel.pan = -1; if (monitor) { @@ -842,18 +808,17 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool audioStream = Audio::makeRawStream(dataStream, _globalSampleRate, flags, DisposeAfterUse::YES); } - channel.stream = new MutableLoopAudioStream(audioStream, loop); - channel.converter = Audio::makeRateConverter(channel.stream->getRate(), getRate(), channel.stream->isStereo(), false); + channel.stream.reset(new MutableLoopAudioStream(audioStream, loop)); + channel.converter.reset(Audio::makeRateConverter(channel.stream->getRate(), getRate(), channel.stream->isStereo(), false)); - // NOTE: SCI engine sets up a decompression buffer here for the audio - // stream, plus writes information about the sample to the channel to - // convert to the correct hardware output format, and allocates the - // monitoring buffer to match the bitrate/samplerate/channels of the - // original stream. We do not need to do any of these things since we - // use audio streams, and allocate and fill the monitoring buffer - // when reading audio data from the stream. + // SSCI sets up a decompression buffer here for the audio stream, plus + // writes information about the sample to the channel to convert to the + // correct hardware output format, and allocates the monitoring buffer to + // match the bitrate/samplerate/channels of the original stream. We do not + // need to do any of these things since we use audio streams, and allocate + // and fill the monitoring buffer when reading audio data from the stream. - MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream); + MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream.get()); if (stream == nullptr) { error("[Audio32::play]: Unable to cast stream for resource %s", resourceId.toString().c_str()); } @@ -958,8 +923,8 @@ bool Audio32::pause(const int16 channelIndex) { } } - // NOTE: The actual engine returns false here regardless of whether - // or not channels were paused + // SSCI returns false here regardless of whether or not channels were + // paused, so we emulate this behaviour } else { AudioChannel &channel = getChannel(channelIndex); @@ -996,9 +961,10 @@ int16 Audio32::stop(const int16 channelIndex) { } } - // NOTE: SSCI stops the DSP interrupt and frees the - // global decompression buffer here if there are no - // more active channels + // SSCI stops the DSP interrupt and frees the global decompression buffer + // here if there are no more active channels, which we do not need to do + // since the system manages audio callbacks and we have no static + // decompression buffer return oldNumChannels; } @@ -1015,13 +981,14 @@ int16 Audio32::getPosition(const int16 channelIndex) const { return -1; } - // NOTE: SSCI treats this as an unsigned short except for - // when the value is 65535, then it treats it as signed + // SSCI treats this as an unsigned short except for when the value is 65535, + // then it treats it as signed int position = -1; const uint32 now = g_sci->getTickCount(); - // NOTE: The original engine also queried the audio driver to see whether - // it thought that there was audio playback occurring via driver opcode 9 + // SSCI also queried the audio driver to see whether it thought that there + // was audio playback occurring via driver opcode 9, but we have no analogue + // to query if (channelIndex == kAllChannels) { if (_pausedAtTick) { position = _pausedAtTick - _startedAtTick; @@ -1052,7 +1019,7 @@ void Audio32::setLoop(const int16 channelIndex, const bool loop) { AudioChannel &channel = getChannel(channelIndex); - MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream); + MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream.get()); assert(stream); stream->loop() = loop; } @@ -1161,8 +1128,8 @@ bool Audio32::hasSignal() const { return false; } - const Audio::st_sample_t *buffer = _monitoredBuffer; - const Audio::st_sample_t *const end = _monitoredBuffer + _numMonitoredSamples; + const Audio::st_sample_t *buffer = _monitoredBuffer.data(); + const Audio::st_sample_t *const end = _monitoredBuffer.data() + _numMonitoredSamples; while (buffer != end) { const Audio::st_sample_t sample = *buffer++; @@ -1193,9 +1160,9 @@ reg_t Audio32::kernelPlay(const bool autoPlay, const int argc, const reg_t *cons if (argc < 6 || argv[5].toSint16() == 1) { loop = false; } else { - // NOTE: Uses -1 for infinite loop. Presumably the - // engine was supposed to allow counter loops at one - // point, but ended up only using loop as a boolean. + // SSCI uses -1 for infinite loop. Presumably the engine was + // supposed to allow counter loops at one point, but ended up only + // using loop as a boolean. loop = (bool)argv[5].toSint16(); } @@ -1311,8 +1278,10 @@ reg_t Audio32::kernelFade(const int argc, const reg_t *const argv) { Common::StackLock lock(_mutex); - // NOTE: In SSCI, this call to find the channel is hacked up; argc is - // set to 2 before the call, and then restored after the call. + // In SSCI, this call to find the channel is hacked up; argc is set to 2 + // before the call, and then restored after the call. We just implemented + // findChannelByArgs in a manner that allows us to pass this information + // without messing with argc/argv instead const int16 channelIndex = findChannelByArgs(2, argv, 0, argc > 5 ? argv[5] : NULL_REG); const int16 volume = argv[1].toSint16(); const int16 speed = argv[2].toSint16(); @@ -1359,7 +1328,7 @@ void Audio32::printAudioList(Console *con) const { Common::StackLock lock(_mutex); for (int i = 0; i < _numActiveChannels; ++i) { const AudioChannel &channel = _channels[i]; - const MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream); + const MutableLoopAudioStream *stream = dynamic_cast<MutableLoopAudioStream *>(channel.stream.get()); con->debugPrintf(" %d[%04x:%04x]: %s, started at %d, pos %d/%d, vol %d, pan %d%s%s\n", i, PRINT_REG(channel.soundNode), diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h index 71f5883541..a0167f3d2d 100644 --- a/engines/sci/sound/audio32.h +++ b/engines/sci/sound/audio32.h @@ -50,22 +50,23 @@ struct AudioChannel { ResourceId id; /** - * The resource loaded into this channel. + * The resource loaded into this channel. The resource is owned by + * ResourceManager. */ Resource *resource; /** - * The audio stream loaded into this channel. Can cast - * to `SeekableAudioStream` for normal channels and - * `RobotAudioStream` for robot channels. + * The audio stream loaded into this channel. Can cast to + * `SeekableAudioStream` for normal channels and `RobotAudioStream` for + * robot channels. */ - Audio::AudioStream *stream; + Common::ScopedPtr<Audio::AudioStream> stream; /** - * The converter used to transform and merge the input - * stream into the mixer's output buffer. + * The converter used to transform and merge the input stream into the + * mixer's output buffer. */ - Audio::RateConverter *converter; + Common::ScopedPtr<Audio::RateConverter> converter; /** * Duration of the channel, in ticks. @@ -83,8 +84,8 @@ struct AudioChannel { uint32 pausedAtTick; /** - * The time, in ticks, that the channel fade began. - * If 0, the channel is not being faded. + * The time, in ticks, that the channel fade began. If 0, the channel is not + * being faded. */ uint32 fadeStartTick; @@ -104,20 +105,19 @@ struct AudioChannel { int fadeTargetVolume; /** - * Whether or not the channel should be stopped and - * freed when the fade is complete. + * Whether or not the channel should be stopped and freed when the fade is + * complete. */ bool stopChannelOnFade; /** - * Whether or not this channel contains a Robot - * audio block. + * Whether or not this channel contains a Robot audio block. */ bool robot; /** - * For digital sound effects, the related VM - * Sound::nodePtr object for the sound. + * For digital sound effects, the related VM Sound::nodePtr object for the + * sound. */ reg_t soundNode; @@ -127,17 +127,37 @@ struct AudioChannel { int volume; /** - * The amount to pan to the right, from 0 to 100. - * 50 is centered, -1 is not panned. + * The amount to pan to the right, from 0 to 100. 50 is centered, -1 is not + * panned. */ int pan; + + AudioChannel &operator=(AudioChannel &other) { + id = other.id; + resource = other.resource; + stream.reset(other.stream.release()); + converter.reset(other.converter.release()); + duration = other.duration; + startedAtTick = other.startedAtTick; + pausedAtTick = other.pausedAtTick; + fadeStartTick = other.fadeStartTick; + fadeStartVolume = other.fadeStartVolume; + fadeDuration = other.fadeDuration; + fadeTargetVolume = other.fadeTargetVolume; + stopChannelOnFade = other.stopChannelOnFade; + robot = other.robot; + soundNode = other.soundNode; + volume = other.volume; + pan = other.pan; + return *this; + } }; #pragma mark - /** - * Special audio channel indexes used to select a channel - * for digital audio playback. + * Special audio channel indexes used to select a channel for digital audio + * playback. */ enum AudioChannelIndex { kRobotChannel = -3, @@ -146,10 +166,9 @@ enum AudioChannelIndex { }; /** - * Audio32 acts as a permanent audio stream into the system - * mixer and provides digital audio services for the SCI32 - * engine, since the system mixer does not support all the - * features of SCI. + * Audio32 acts as a permanent audio stream into the system mixer and provides + * digital audio services for the SCI32 engine, since the system mixer does not + * support all the features of SCI. */ class Audio32 : public Audio::AudioStream, public Common::Serializable { public: @@ -196,10 +215,10 @@ private: bool channelShouldMix(const AudioChannel &channel) const; /** - * Mixes audio from the given source stream into the - * target buffer using the given rate converter. + * Mixes audio from the given source stream into the target buffer using the + * given rate converter. */ - int writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume); + int writeAudioInternal(Audio::AudioStream &sourceStream, Audio::RateConverter &converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume); #pragma mark - #pragma mark Channel management @@ -226,17 +245,15 @@ public: uint8 getNumUnlockedChannels() const; /** - * Finds a channel that is already configured for the - * given audio sample. + * Finds a channel that is already configured for the given audio sample. * - * @param startIndex The location of the audio resource - * information in the arguments list. + * @param startIndex The location of the audio resource information in the + * arguments list. */ int16 findChannelByArgs(int argc, const reg_t *argv, const int startIndex, const reg_t soundNode) const; /** - * Finds a channel that is already configured for the - * given audio sample. + * Finds a channel that is already configured for the given audio sample. */ int16 findChannelById(const ResourceId resourceId, const reg_t soundNode = NULL_REG) const; @@ -255,26 +272,24 @@ private: Common::Array<AudioChannel> _channels; /** - * The number of active audio channels in the mixer. - * Being active is not the same as playing; active - * channels may be paused. + * The number of active audio channels in the mixer. Being active is not the + * same as playing; active channels may be paused. */ uint8 _numActiveChannels; /** * Whether or not we are in the audio thread. * - * This flag is used instead of passing a parameter to - * `freeUnusedChannels` because a parameter would - * require forwarding through the public method `stop`, - * and there is not currently any reason for this - * implementation detail to be exposed. + * This flag is used instead of passing a parameter to `freeUnusedChannels` + * because a parameter would require forwarding through the public method + * `stop`, and there is not currently any reason for this implementation + * detail to be exposed. */ bool _inAudioThread; /** - * The list of resources from freed channels that need - * to be unlocked from the main thread. + * The list of resources from freed channels that need to be unlocked from + * the main thread. */ UnlockList _resourcesToUnlock; @@ -302,8 +317,7 @@ private: } /** - * Frees all non-looping channels that have reached the - * end of their stream. + * Frees all non-looping channels that have reached the end of their stream. */ void freeUnusedChannels(); @@ -313,8 +327,7 @@ private: void freeChannel(const int16 channelIndex); /** - * Unlocks all resources that were freed by the audio - * thread. + * Unlocks all resources that were freed by the audio thread. */ void unlockResources(); @@ -322,58 +335,58 @@ private: #pragma mark Script compatibility public: /** - * Gets the (fake) sample rate of the hardware DAC. - * For script compatibility only. + * Gets the (fake) sample rate of the hardware DAC. For script compatibility + * only. */ inline uint16 getSampleRate() const { return _globalSampleRate; } /** - * Sets the (fake) sample rate of the hardware DAC. - * For script compatibility only. + * Sets the (fake) sample rate of the hardware DAC. For script compatibility + * only. */ void setSampleRate(uint16 rate); /** - * Gets the (fake) bit depth of the hardware DAC. - * For script compatibility only. + * Gets the (fake) bit depth of the hardware DAC. For script compatibility + * only. */ inline uint8 getBitDepth() const { return _globalBitDepth; } /** - * Sets the (fake) sample rate of the hardware DAC. - * For script compatibility only. + * Sets the (fake) sample rate of the hardware DAC. For script compatibility + * only. */ void setBitDepth(uint8 depth); /** - * Gets the (fake) number of output (speaker) channels - * of the hardware DAC. For script compatibility only. + * Gets the (fake) number of output (speaker) channels of the hardware DAC. + * For script compatibility only. */ inline uint8 getNumOutputChannels() const { return _globalNumOutputChannels; } /** - * Sets the (fake) number of output (speaker) channels - * of the hardware DAC. For script compatibility only. + * Sets the (fake) number of output (speaker) channels of the hardware DAC. + * For script compatibility only. */ void setNumOutputChannels(int16 numChannels); /** - * Gets the (fake) number of preloaded channels. - * For script compatibility only. + * Gets the (fake) number of preloaded channels. For script compatibility + * only. */ inline uint8 getPreload() const { return _preload; } /** - * Sets the (fake) number of preloaded channels. - * For script compatibility only. + * Sets the (fake) number of preloaded channels. For script compatibility + * only. */ inline void setPreload(uint8 preload) { _preload = preload; @@ -381,49 +394,43 @@ public: private: /** - * The hardware DAC sample rate. Stored only for script - * compatibility. + * The hardware DAC sample rate. Stored only for script compatibility. */ uint16 _globalSampleRate; /** - * The maximum allowed sample rate of the system mixer. - * Stored only for script compatibility. + * The maximum allowed sample rate of the system mixer. Stored only for + * script compatibility. */ uint16 _maxAllowedSampleRate; /** - * The hardware DAC bit depth. Stored only for script - * compatibility. + * The hardware DAC bit depth. Stored only for script compatibility. */ uint8 _globalBitDepth; /** - * The maximum allowed bit depth of the system mixer. - * Stored only for script compatibility. + * The maximum allowed bit depth of the system mixer. Stored only for script + * compatibility. */ uint8 _maxAllowedBitDepth; /** - * The hardware DAC output (speaker) channel - * configuration. Stored only for script compatibility. + * The hardware DAC output (speaker) channel configuration. Stored only for + * script compatibility. */ uint8 _globalNumOutputChannels; /** - * The maximum allowed number of output (speaker) - * channels of the system mixer. Stored only for script - * compatibility. + * The maximum allowed number of output (speaker) channels of the system + * mixer. Stored only for script compatibility. */ uint8 _maxAllowedOutputChannels; /** - * The number of audio channels that should have their - * data preloaded into memory instead of streaming from - * disk. - * 1 = all channels, 2 = 2nd active channel and above, - * etc. - * Stored only for script compatibility. + * The number of audio channels that should have their data preloaded into + * memory instead of streaming from disk. 1 = all channels, 2 = 2nd active + * channel and above, etc. Stored only for script compatibility. */ uint8 _preload; @@ -442,8 +449,7 @@ private: int16 findRobotChannel() const; /** - * When true, channels marked as robot audio will not be - * played. + * When true, channels marked as robot audio will not be played. */ bool _robotAudioPaused; @@ -456,8 +462,8 @@ public: uint16 play(int16 channelIndex, const ResourceId resourceId, const bool autoPlay, const bool loop, const int16 volume, const reg_t soundNode, const bool monitor); /** - * Resumes playback of a paused audio channel, or of - * the entire audio player. + * Resumes playback of a paused audio channel, or of the entire audio + * player. */ bool resume(const int16 channelIndex); bool resume(const ResourceId resourceId, const reg_t soundNode = NULL_REG) { @@ -475,8 +481,7 @@ public: } /** - * Stops and unloads an audio channel, or the entire - * audio player. + * Stops and unloads an audio channel, or the entire audio player. */ int16 stop(const int16 channelIndex); int16 stop(const ResourceId resourceId, const reg_t soundNode = NULL_REG) { @@ -490,8 +495,7 @@ public: uint16 restart(const ResourceId resourceId, const bool autoPlay, const bool loop, const int16 volume, const reg_t soundNode, const bool monitor); /** - * Returns the playback position for the given channel - * number, in ticks. + * Returns the playback position for the given channel number, in ticks. */ int16 getPosition(const int16 channelIndex) const; int16 getPosition(const ResourceId resourceId, const reg_t soundNode = NULL_REG) { @@ -531,8 +535,8 @@ private: #pragma mark Effects public: /** - * Gets the volume for a given channel. Passing - * `kAllChannels` will get the global volume. + * Gets the volume for a given channel. Passing `kAllChannels` will get the + * global volume. */ int16 getVolume(const int16 channelIndex) const; int16 getVolume(const ResourceId resourceId, const reg_t soundNode) const { @@ -541,8 +545,8 @@ public: } /** - * Sets the volume of an audio channel. Passing - * `kAllChannels` will set the global volume. + * Sets the volume of an audio channel. Passing `kAllChannels` will set the + * global volume. */ void setVolume(const int16 channelIndex, int16 volume); void setVolume(const ResourceId resourceId, const reg_t soundNode, const int16 volume) { @@ -583,24 +587,21 @@ public: private: /** - * If true, audio will be mixed by reducing the target - * buffer by half every time a new channel is mixed in. - * The final channel is not attenuated. + * If true, audio will be mixed by reducing the target buffer by half every + * time a new channel is mixed in. The final channel is not attenuated. */ bool _attenuatedMixing; /** - * When true, a modified attenuation algorithm is used - * (`A/4 + B`) instead of standard linear attenuation - * (`A/2 + B/2`). + * When true, a modified attenuation algorithm is used (`A/4 + B`) instead + * of standard linear attenuation (`A/2 + B/2`). */ bool _useModifiedAttenuation; /** * Processes an audio fade for the given channel. * - * @returns true if the fade was completed and the - * channel was stopped. + * @returns true if the fade was completed and the channel was stopped. */ bool processFade(const int16 channelIndex); @@ -608,35 +609,27 @@ private: #pragma mark Signal monitoring public: /** - * Returns whether the currently monitored audio channel - * contains any signal within the next audio frame. + * Returns whether the currently monitored audio channel contains any signal + * within the next audio frame. */ bool hasSignal() const; private: /** - * The index of the channel being monitored for signal, - * or -1 if no channel is monitored. When a channel is - * monitored, it also causes the engine to play only the - * monitored channel. + * The index of the channel being monitored for signal, or -1 if no channel + * is monitored. When a channel is monitored, it also causes the engine to + * play only the monitored channel. */ int16 _monitoredChannelIndex; /** - * The data buffer holding decompressed audio data for - * the channel that will be monitored for an audio - * signal. - */ - Audio::st_sample_t *_monitoredBuffer; - - /** - * The size of the buffer, in bytes. + * The data buffer holding decompressed audio data for the channel that will + * be monitored for an audio signal. */ - size_t _monitoredBufferSize; + Common::Array<Audio::st_sample_t> _monitoredBuffer; /** - * The number of valid audio samples in the signal - * monitoring buffer. + * The number of valid audio samples in the signal monitoring buffer. */ int _numMonitoredSamples; diff --git a/engines/sci/sound/decoders/sol.cpp b/engines/sci/sound/decoders/sol.cpp index 50d353cf18..a6eb8a47b8 100644 --- a/engines/sci/sound/decoders/sol.cpp +++ b/engines/sci/sound/decoders/sol.cpp @@ -157,11 +157,10 @@ SOLStream<STEREO, S16BIT, OLDDPCM8>::SOLStream(Common::SeekableReadStream *strea template <bool STEREO, bool S16BIT, bool OLDDPCM8> bool SOLStream<STEREO, S16BIT, OLDDPCM8>::seek(const Audio::Timestamp &where) { if (where != 0) { - // In order to seek in compressed SOL files, all - // previous bytes must be known since it uses - // differential compression. Therefore, only seeking - // to the beginning is supported now (SSCI does not - // offer seeking anyway) + // In order to seek in compressed SOL files, all previous bytes must be + // known since it uses differential compression. Therefore, only seeking + // to the beginning is supported now (SSCI does not offer seeking + // anyway) return false; } diff --git a/engines/sci/sound/decoders/sol.h b/engines/sci/sound/decoders/sol.h index 1141132365..2b84b8bc35 100644 --- a/engines/sci/sound/decoders/sol.h +++ b/engines/sci/sound/decoders/sol.h @@ -47,8 +47,7 @@ private: uint16 _sampleRate; /** - * The raw (possibly-compressed) size of audio data in - * the stream. + * The raw (possibly-compressed) size of audio data in the stream. */ int32 _rawDataSize; diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 5530952dcf..b2bd72e669 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -626,13 +626,7 @@ void SciMusic::soundStop(MusicEntry *pSnd) { void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) { assert(volume <= MUSIC_VOLUME_MAX); - if (pSnd->isSample) { -#ifdef ENABLE_SCI32 - if (_soundVersion >= SCI_VERSION_2_1_EARLY) { - g_sci->_audio32->setVolume(ResourceId(kResourceTypeAudio, pSnd->resourceId), pSnd->soundObj, volume); - } -#endif - } else if (pSnd->pMidiParser) { + if (!pSnd->isSample && pSnd->pMidiParser) { Common::StackLock lock(_mutex); pSnd->pMidiParser->mainThreadBegin(); pSnd->pMidiParser->setVolume(volume); diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 0bb295d41e..54fdcf5f8f 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -350,11 +350,9 @@ reg_t SoundCommandParser::kDoSoundPause(EngineState *s, int argc, reg_t *argv) { } #ifdef ENABLE_SCI32 - // NOTE: The original engine also expected a global - // "kernel call" flag to be true in order to perform - // this action, but the architecture of the ScummVM - // implementation is so different that it doesn't - // matter here + // SSCI also expected a global "kernel call" flag to be true in order to + // perform this action, but the architecture of the ScummVM + // implementation is so different that it doesn't matter here if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) { if (shouldPause) { g_sci->_audio32->pause(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj); @@ -741,8 +739,8 @@ reg_t SoundCommandParser::kDoSoundSetVolume(EngineState *s, int argc, reg_t *arg #ifdef ENABLE_SCI32 // SSCI unconditionally sets volume if it is digital audio if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) { - _music->soundSetVolume(musicSlot, value); - } else + g_sci->_audio32->setVolume(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, value); + } #endif if (musicSlot->volume != value) { musicSlot->volume = value; diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index 79079edfae..4711a971a4 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -112,8 +112,8 @@ bool RobotAudioStream::addPacket(const RobotAudioPacket &packet) { // 4 (odd). const int8 bufferIndex = packet.position % 4 ? 1 : 0; - // Packet 0 is the first primer, packet 2 is the second primer, - // packet 4+ are regular audio data + // Packet 0 is the first primer, packet 2 is the second primer, packet 4+ + // are regular audio data if (packet.position <= 2 && _firstPacketPosition == -1) { _readHead = 0; _readHeadAbs = 0; @@ -445,10 +445,10 @@ void RobotDecoder::initVideo(const int16 x, const int16 y, const int16 scale, co _maxFrameRate = _frameRate + kMaxFrameRateDrift; if (_xResolution == 0 || _yResolution == 0) { - // TODO: Default values were taken from RESOURCE.CFG hires property - // if it exists, so need to check games' configuration files for those - _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + // In SSCI, default values were taken from RESOURCE.CFG hires property + // if it exists, but no games seem to take advantage of this + _xResolution = g_sci->_gfxFrameout->getScreenWidth(); + _yResolution = g_sci->_gfxFrameout->getScreenHeight(); } if (hasPalette) { @@ -677,10 +677,10 @@ void RobotDecoder::showFrame(const uint16 frameNo, const uint16 newX, const uint if (_isHiRes) { SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[i].bitmapId); - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); if (scriptWidth == kLowResX && scriptHeight == kLowResY) { const Ratio lowResToScreenX(screenWidth, kLowResX); @@ -1386,12 +1386,8 @@ void RobotDecoder::doVersion5(const bool shouldSubmitAudio) { for (RobotScreenItemList::size_type i = 0; i < screenItemCount; ++i) { Common::Point position(_screenItemX[i], _screenItemY[i]); -// TODO: Version 6 robot? -// int scaleXRemainder; if (_scaleInfo.signal == kScaleSignalManual) { position.x = (position.x * _scaleInfo.x) / 128; -// TODO: Version 6 robot? -// scaleXRemainder = (position.x * _scaleInfo.x) % 128; position.y = (position.y * _scaleInfo.y) / 128; } @@ -1400,8 +1396,6 @@ void RobotDecoder::doVersion5(const bool shouldSubmitAudio) { celInfo.bitmap = _celHandles[i].bitmapId; ScreenItem *screenItem = new ScreenItem(_planeId, celInfo, position, _scaleInfo); _screenItemList[i] = screenItem; - // TODO: Version 6 robot? - // screenItem->_field_30 = scaleXRemainder; if (_priority == -1) { screenItem->_fixedPriority = false; @@ -1414,8 +1408,6 @@ void RobotDecoder::doVersion5(const bool shouldSubmitAudio) { ScreenItem *screenItem = _screenItemList[i]; screenItem->_celInfo.bitmap = _celHandles[i].bitmapId; screenItem->_position = position; - // TODO: Version 6 robot? - // screenItem->_field_30 = scaleXRemainder; if (_priority == -1) { screenItem->_fixedPriority = false; @@ -1461,10 +1453,10 @@ uint32 RobotDecoder::createCel5(const byte *rawVideoData, const int16 screenItem rawVideoData += kCelHeaderSize; - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getScriptWidth(); + const int16 scriptHeight = g_sci->_gfxFrameout->getScriptHeight(); + const int16 screenWidth = g_sci->_gfxFrameout->getScreenWidth(); + const int16 screenHeight = g_sci->_gfxFrameout->getScreenHeight(); Common::Point origin; if (scriptWidth == kLowResX && scriptHeight == kLowResY) { @@ -1570,7 +1562,7 @@ void RobotDecoder::preallocateCelMemory(const byte *rawVideoData, const int16 nu for (int i = 0; i < numFixedCels; ++i) { CelHandleInfo &celHandle = _celHandles[i]; - // NOTE: There was a check to see if the cel handle was not allocated + // In SSCI, there was a check to see if the cel handle was not allocated // here, for some reason, which would mean that nothing was ever // allocated from fixed cels, because the _celHandles array just got // deleted and recreated... diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index aa1d9da75c..314628ee01 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -52,7 +52,10 @@ class SegManager; // Unlike traditional AV formats, Robot videos almost always require playback // within the game engine because certain information (like the resolution of // the Robot coordinates and the background for the video) is dependent on data -// that does not exist within the Robot file itself. +// that does not exist within the Robot file itself. In version 6, robots could +// also participate in palette remapping by drawing remap pixels, and the +// information for processing these pixels is also not stored within the Robot +// file. // // The Robot container consists of a file header, an optional primer audio // section, an optional colour palette, a frame seek index, a set of cuepoints, @@ -66,9 +69,9 @@ class SegManager; // contiguous data blocks, each of which can be independently compressed with // LZS or left uncompressed. An entire cel can also be line decimated, where // lines are deleted from the source bitmap at compression time and are -// reconstructed by decompression using line doubling. Each cel also includes -// coordinates where it should be placed within the video frame, relative to the -// top-left corner of the frame. +// reconstructed by decompression using line interpolation. Each cel also +// includes coordinates where it should be placed within the video frame, +// relative to the top-left corner of the frame. // // Audio coding is fixed-length, and all audio blocks except for the primer // audio are the same size. Audio is encoded with Sierra SOL DPCM16 compression, @@ -254,8 +257,8 @@ class SegManager; #pragma mark RobotAudioStream /** - * A Robot audio stream is a simple loop buffer - * that accepts audio blocks from the Robot engine. + * A Robot audio stream is a simple loop buffer that accepts audio blocks from + * the Robot engine. */ class RobotAudioStream : public Audio::AudioStream { public: @@ -266,40 +269,34 @@ public: kRobotSampleRate = 22050, /** - * Multiplier for the size of a packet that - * is being expanded by writing to every other - * byte of the target buffer. + * Multiplier for the size of a packet that is being expanded by writing + * to every other byte of the target buffer. */ kEOSExpansion = 2 }; /** - * Playback state information. Used for framerate - * calculation. + * Playback state information. Used for framerate calculation. */ struct StreamState { /** - * The current position of the read head of - * the audio stream. + * The current position of the read head of the audio stream. */ int bytesPlaying; /** - * The sample rate of the audio stream. - * Always 22050. + * The sample rate of the audio stream. Always 22050. */ uint16 rate; /** - * The bit depth of the audio stream. - * Always 16. + * The bit depth of the audio stream. Always 16. */ uint8 bits; }; /** - * A single packet of compressed audio from a - * Robot data stream. + * A single packet of compressed audio from a Robot data stream. */ struct RobotAudioPacket { /** @@ -314,8 +311,7 @@ public: int dataSize; /** - * The uncompressed, file-relative position - * of this audio packet. + * The uncompressed, file-relative position of this audio packet. */ int position; @@ -328,20 +324,19 @@ public: /** * Adds a new audio packet to the stream. - * @returns `true` if the audio packet was fully - * consumed, otherwise `false`. + * @returns `true` if the audio packet was fully consumed, otherwise + * `false`. */ bool addPacket(const RobotAudioPacket &packet); /** - * Prevents any additional audio packets from - * being added to the audio stream. + * Prevents any additional audio packets from being added to the audio + * stream. */ void finish(); /** - * Returns the current status of the audio - * stream. + * Returns the current status of the audio stream. */ StreamState getStatus() const; @@ -349,8 +344,7 @@ private: Common::Mutex _mutex; /** - * Loop buffer for playback. Contains decompressed - * 16-bit PCM samples. + * Loop buffer for playback. Contains decompressed 16-bit PCM samples. */ byte *_loopBuffer; @@ -360,66 +354,57 @@ private: int32 _loopBufferSize; /** - * The position of the read head within the loop - * buffer, in bytes. + * The position of the read head within the loop buffer, in bytes. */ int32 _readHead; /** - * The lowest file position that can be buffered, - * in uncompressed bytes. + * The lowest file position that can be buffered, in uncompressed bytes. */ int32 _readHeadAbs; /** - * The highest file position that can be buffered, - * in uncompressed bytes. + * The highest file position that can be buffered, in uncompressed bytes. */ int32 _maxWriteAbs; /** - * The highest file position, in uncompressed bytes, - * that has been written to the stream. - * Different from `_maxWriteAbs`, which is the highest - * uncompressed position which *can* be written right - * now. + * The highest file position, in uncompressed bytes, that has been written + * to the stream. This is different from `_maxWriteAbs`, which is the + * highest uncompressed position which *can* be written right now. */ int32 _writeHeadAbs; /** - * The highest file position, in uncompressed bytes, - * that has been written to the even & odd sides of - * the stream. + * The highest file position, in uncompressed bytes, that has been written + * to the even & odd sides of the stream. * - * Index 0 corresponds to the 'even' side; index - * 1 correspond to the 'odd' side. + * Index 0 corresponds to the 'even' side; index 1 corresponds to the 'odd' + * side. */ int32 _jointMin[2]; /** - * When `true`, the stream is waiting for all primer - * blocks to be received before allowing playback to - * begin. + * When `true`, the stream is waiting for all primer blocks to be received + * before allowing playback to begin. */ bool _waiting; /** - * When `true`, the stream will accept no more audio - * blocks. + * When `true`, the stream will accept no more audio blocks. */ bool _finished; /** - * The uncompressed position of the first packet of - * robot data. Used to decide whether all primer - * blocks have been received and the stream should + * The uncompressed position of the first packet of robot data. Used to + * decide whether all primer blocks have been received and the stream should * be started. */ int32 _firstPacketPosition; /** - * Decompression buffer, used to temporarily store - * an uncompressed block of audio data. + * Decompression buffer, used to temporarily store an uncompressed block of + * audio data. */ byte *_decompressionBuffer; @@ -429,23 +414,20 @@ private: int32 _decompressionBufferSize; /** - * The position of the packet currently in the - * decompression buffer. Used to avoid - * re-decompressing audio data that has already - * been decompressed during a partial packet read. + * The position of the packet currently in the decompression buffer. Used to + * avoid re-decompressing audio data that has already been decompressed + * during a partial packet read. */ int32 _decompressionBufferPosition; /** - * Calculates the absolute ranges for new fills - * into the loop buffer. + * Calculates the absolute ranges for new fills into the loop buffer. */ void fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex); /** - * Interpolates `numSamples` samples from the read - * head, if no true samples were written for one - * (or both) of the joint channels. + * Interpolates `numSamples` samples from the read head, if no true samples + * were written for one (or both) of the joint channels. */ void interpolateMissingSamples(const int32 numSamples); @@ -501,9 +483,8 @@ public: }; enum { - // Special high value used to represent - // parameters that should be left unchanged - // when calling `showFrame` + // Special high value used to represent parameters that should be left + // unchanged when calling `showFrame` kUnspecified = 50000 }; @@ -530,8 +511,8 @@ private: kCueListSize = 256, /** - * Maximum number of 'fixed' cels that never - * change for the duration of a robot. + * Maximum number of 'fixed' cels that never change for the duration of + * a robot. */ kFixedCelListSize = 4, @@ -541,27 +522,22 @@ private: kRawPaletteSize = 1200, /** - * The size of a frame of Robot data. This - * value was used to align the first block of - * data after the main Robot header to the next - * CD sector. + * The size of a frame of Robot data. This value was used to align the + * first block of data after the main Robot header to the next CD + * sector. */ kRobotFrameSize = 2048, /** - * The size of a block of zero-compressed - * audio. Used to fill audio when the size of - * an audio packet does not match the expected - * packet size. + * The size of a block of zero-compressed audio. Used to fill audio when + * the size of an audio packet does not match the expected packet size. */ kRobotZeroCompressSize = 2048, /** - * The size of the audio block header, in bytes. - * The audio block header consists of the - * compressed size of the audio in the record, - * plus the position of the audio in the - * compressed data stream. + * The size of the audio block header, in bytes. The audio block header + * consists of the compressed size of the audio in the record, plus the + * position of the audio in the compressed data stream. */ kAudioBlockHeaderSize = 8, @@ -571,23 +547,20 @@ private: kCelHeaderSize = 22, /** - * The maximum amount that the frame rate is - * allowed to drift from the nominal frame rate - * in order to correct for AV drift or slow - * playback. + * The maximum amount that the frame rate is allowed to drift from the + * nominal frame rate in order to correct for AV drift or slow playback. */ kMaxFrameRateDrift = 1 }; /** - * The version number for the currently loaded - * robot. + * The version number for the currently loaded robot. * * There are several known versions of robot: * * v2: before Nov 1994; no known examples * v3: before Nov 1994; no known examples - * v4: Jan 1995; PQ:SWAT demo + * v4: Jan 1995; KQ7 1.65, PQ:SWAT demo * v5: Mar 1995; SCI2.1 and SCI3 games * v6: SCI3 games */ @@ -625,8 +598,8 @@ private: #pragma mark Playback public: /** - * Opens a robot file for playback. - * Newly opened robots are paused by default. + * Opens a robot file for playback. Newly opened robots are paused by + * default. */ void open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale); @@ -636,8 +609,8 @@ public: void close(); /** - * Pauses the robot. Once paused, the audio for a robot - * is disabled until the end of playback. + * Pauses the robot. Once paused, the audio for a robot is disabled until + * the end of playback. */ void pause(); @@ -654,8 +627,7 @@ public: void showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority); /** - * Retrieves the value associated with the - * current cue point. + * Retrieves the value associated with the current cue point. */ int16 getCue() const; @@ -688,27 +660,23 @@ private: PositionList _recordPositions; /** - * The offset of the Robot file within a - * resource bundle. + * The offset of the Robot file within a resource bundle. */ int32 _fileOffset; /** - * A list of cue times that is updated to - * prevent earlier cue values from being - * given to the game more than once. + * A list of cue times that is updated to prevent earlier cue values from + * being given to the game more than once. */ mutable int32 _cueTimes[kCueListSize]; /** - * The original list of cue times from the - * raw Robot data. + * The original list of cue times from the raw Robot data. */ int32 _masterCueTimes[kCueListSize]; /** - * The list of values to provide to a game - * when a cue value is requested. + * The list of values to provide to a game when a cue value is requested. */ int32 _cueValues[kCueListSize]; @@ -723,23 +691,20 @@ private: int16 _normalFrameRate; /** - * The minimal playback frame rate. Used to - * correct for AV sync drift when the video - * is more than one frame ahead of the audio. + * The minimal playback frame rate. Used to correct for AV sync drift when + * the video is more than one frame ahead of the audio. */ int16 _minFrameRate; /** - * The maximum playback frame rate. Used to - * correct for AV sync drift when the video - * is more than one frame behind the audio. + * The maximum playback frame rate. Used to correct for AV sync drift when + * the video is more than one frame behind the audio. */ int16 _maxFrameRate; /** - * The maximum number of record blocks that - * can be skipped without causing audio to - * drop out. + * The maximum number of record blocks that can be skipped without causing + * audio to drop out. */ int16 _maxSkippablePackets; @@ -754,32 +719,28 @@ private: int _previousFrameNo; /** - * The time, in ticks, when the robot was - * last started or resumed. + * The time, in ticks, when the robot was last started or resumed. */ int32 _startTime; /** - * The first frame displayed when the - * robot was resumed. + * The first frame displayed when the robot was resumed. */ int32 _startFrameNo; /** - * The last frame displayed when the robot - * was resumed. + * The last frame displayed when the robot was resumed. */ int32 _startingFrameNo; /** - * Seeks the raw data stream to the record for - * the given frame number. + * Seeks the raw data stream to the record for the given frame number. */ bool seekToFrame(const int frameNo); /** - * Sets the start time and frame of the robot - * when the robot is started or resumed. + * Sets the start time and frame of the robot when the robot is started or + * resumed. */ void setRobotTime(const int frameNo); @@ -787,11 +748,10 @@ private: #pragma mark Timing private: /** - * This class tracks the amount of time it takes for - * a frame of robot animation to be rendered. This - * information is used by the player to speculatively - * skip rendering of future frames to keep the - * animation in sync with the robot audio. + * This class tracks the amount of time it takes for a frame of robot + * animation to be rendered. This information is used by the player to + * speculatively skip rendering of future frames to keep the animation in + * sync with the robot audio. */ class DelayTime { public: @@ -808,14 +768,13 @@ private: void endTiming(); /** - * Returns whether or not timing is currently in - * progress. + * Returns whether or not timing is currently in progress. */ bool timingInProgress() const; /** - * Returns the median time, in ticks, of the - * currently stored timing samples. + * Returns the median time, in ticks, of the currently stored timing + * samples. */ int predictedTicks() const; @@ -823,24 +782,23 @@ private: RobotDecoder *_decoder; /** - * The start time, in ticks, of the current timing - * loop. If no loop is in progress, the value is 0. + * The start time, in ticks, of the current timing loop. If no loop is + * in progress, the value is 0. * - * @note This is slightly different than SSCI where - * the not-timing value was -1. + * @note This is slightly different than SSCI where the not-timing value + * was -1. */ uint32 _startTime; /** - * A sorted list containing the timing data for - * the last `kDelayListSize` frames, in ticks. + * A sorted list containing the timing data for the last + * `kDelayListSize` frames, in ticks. */ int _delays[kDelayListSize]; /** - * A list of monotonically increasing identifiers - * used to identify and replace the oldest sample - * in the `_delays` array when finishing the + * A list of monotonically increasing identifiers used to identify and + * replace the oldest sample in the `_delays` array when finishing the * next timing operation. */ uint _timestamps[kDelayListSize]; @@ -862,17 +820,14 @@ private: }; /** - * Calculates the next frame number that needs - * to be rendered, using the timing data - * collected by DelayTime. + * Calculates the next frame number that needs to be rendered, using the + * timing data collected by DelayTime. */ uint16 calculateNextFrameNo(const uint32 extraTicks = 0) const; /** - * Calculates and returns the number of frames - * that should be rendered in `ticks` time, - * according to the current target frame rate - * of the robot. + * Calculates and returns the number of frames that should be rendered in + * `ticks` time, according to the current target frame rate of the robot. */ uint32 ticksToFrames(const uint32 ticks) const; @@ -891,15 +846,13 @@ private: private: enum { /** - * The number of ticks that should elapse - * between each AV sync check. + * The number of ticks that should elapse between each AV sync check. */ kAudioSyncCheckInterval = 5 * 60 /* 5 seconds */ }; /** - * The status of the audio track of a Robot - * animation. + * The status of the audio track of a Robot animation. */ enum RobotAudioStatus { kRobotAudioReady = 1, @@ -913,8 +866,7 @@ private: #pragma mark Audio - AudioList private: /** - * This class manages packetized audio playback - * for robots. + * This class manages packetized audio playback for robots. */ class AudioList { public: @@ -926,8 +878,8 @@ private: void startAudioNow(); /** - * Stops playback of robot audio, allowing - * any queued audio to finish playing back. + * Stops playback of robot audio, allowing any queued audio to finish + * playing back. */ void stopAudio(); @@ -937,40 +889,36 @@ private: void stopAudioNow(); /** - * Submits as many blocks of audio as possible - * to the audio engine. + * Submits as many blocks of audio as possible to the audio engine. */ void submitDriverMax(); /** * Adds a new AudioBlock to the queue. * - * @param position The absolute position of the - * audio for the block, in compressed bytes. + * @param position The absolute position of the audio for the block, in + * compressed bytes. * @param size The size of the buffer. - * @param buffer A pointer to compressed audio - * data that will be copied into the new - * AudioBlock. + * @param buffer A pointer to compressed audio data that will be copied + * into the new AudioBlock. */ void addBlock(const int position, const int size, const byte *buffer); /** - * Immediately stops any active playback and - * purges all audio data in the audio list. + * Immediately stops any active playback and purges all audio data in + * the audio list. */ void reset(); /** - * Pauses the robot audio channel in - * preparation for the first block of audio - * data to be read. + * Pauses the robot audio channel in preparation for the first block of + * audio data to be read. */ void prepareForPrimer(); /** - * Sets the audio offset which is used to - * offset the position of audio packets - * sent to the audio stream. + * Sets the audio offset which is used to offset the position of audio + * packets sent to the audio stream. */ void setAudioOffset(const int offset); @@ -979,8 +927,7 @@ private: private: /** - * AudioBlock represents a block of audio - * from the Robot's audio track. + * AudioBlock represents a block of audio from the Robot's audio track. */ class AudioBlock { public: @@ -988,44 +935,37 @@ private: ~AudioBlock(); /** - * Submits the block of audio to the - * audio manager. - * @returns true if the block was fully - * read, or false if the block was not - * read or only partially read. + * Submits the block of audio to the audio manager. + * @returns true if the block was fully read, or false if the block + * was not read or only partially read. */ bool submit(const int startOffset); private: /** - * The absolute position, in compressed - * bytes, of this audio block's audio - * data in the audio stream. + * The absolute position, in compressed bytes, of this audio block's + * audio data in the audio stream. */ int _position; /** - * The compressed size, in bytes, of - * this audio block's audio data. + * The compressed size, in bytes, of this audio block's audio data. */ int _size; /** - * A buffer containing raw - * SOL-compressed audio data. + * A buffer containing raw SOL-compressed audio data. */ byte *_data; }; /** - * The list of compressed audio blocks - * submitted for playback. + * The list of compressed audio blocks submitted for playback. */ AudioBlock *_blocks[kAudioListSize]; /** - * The number of blocks in `_blocks` that are - * ready to be submitted. + * The number of blocks in `_blocks` that are ready to be submitted. */ uint8 _blocksSize; @@ -1040,8 +980,7 @@ private: uint8 _newestBlockIndex; /** - * The offset used when sending packets to the - * audio stream. + * The offset used when sending packets to the audio stream. */ int _startOffset; @@ -1068,46 +1007,42 @@ private: AudioList _audioList; /** - * The size, in bytes, of a block of audio data, - * excluding the audio block header. + * The size, in bytes, of a block of audio data, excluding the audio block + * header. */ uint16 _audioBlockSize; /** - * The expected size of a block of audio data, - * in bytes, excluding the audio block header. + * The expected size of a block of audio data, in bytes, excluding the audio + * block header. */ int16 _expectedAudioBlockSize; /** - * The number of compressed audio bytes that are - * needed per frame to fill the audio buffer - * without causing audio to drop out. + * The number of compressed audio bytes that are needed per frame to fill + * the audio buffer without causing audio to drop out. */ int16 _audioRecordInterval; /** - * If true, primer audio buffers should be filled - * with silence instead of trying to read buffers - * from the Robot data. + * If true, primer audio buffers should be filled with silence instead of + * trying to read buffers from the Robot data. */ uint16 _primerZeroCompressFlag; /** - * The size, in bytes, of the primer audio in the - * Robot, including any extra alignment padding. + * The size, in bytes, of the primer audio in the Robot, including any extra + * alignment padding. */ uint16 _primerReservedSize; /** - * The combined size, in bytes, of the even and odd - * primer channels. + * The combined size, in bytes, of the even and odd primer channels. */ int32 _totalPrimerSize; /** - * The absolute offset of the primer audio data in - * the robot data stream. + * The absolute offset of the primer audio data in the robot data stream. */ int32 _primerPosition; @@ -1122,54 +1057,47 @@ private: int32 _oddPrimerSize; /** - * The absolute position in the audio stream of - * the first audio packet. + * The absolute position in the audio stream of the first audio packet. */ int32 _firstAudioRecordPosition; /** - * A temporary buffer used to hold one frame of - * raw (DPCM-compressed) audio when reading audio - * records from the robot stream. + * A temporary buffer used to hold one frame of raw (DPCM-compressed) audio + * when reading audio records from the robot stream. */ byte *_audioBuffer; /** - * The next tick count when AV sync should be - * checked and framerate adjustments made, if - * necessary. + * The next tick count when AV sync should be checked and framerate + * adjustments made, if necessary. */ uint32 _checkAudioSyncTime; /** - * Primes the audio buffer with the first frame - * of audio data. + * Primes the audio buffer with the first frame of audio data. * * @note `primeAudio` was `InitAudio` in SSCI */ bool primeAudio(const uint32 startTick); /** - * Reads primer data from the robot data stream - * and puts it into the given buffers. + * Reads primer data from the robot data stream and puts it into the given + * buffers. */ bool readPrimerData(byte *outEvenBuffer, byte *outOddBuffer); /** - * Reads audio data for the given frame number - * into the given buffer. + * Reads audio data for the given frame number into the given buffer. * - * @param outAudioPosition The position of the - * audio, in compressed bytes, in the data stream. - * @param outAudioSize The size of the audio data, - * in compressed bytes. + * @param outAudioPosition The position of the audio, in compressed bytes, + * in the data stream. + * @param outAudioSize The size of the audio data, in compressed bytes. */ bool readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize); /** - * Submits part of the audio packet of the given - * frame to the audio list, starting `startPosition` - * bytes into the audio. + * Submits part of the audio packet of the given frame to the audio list, + * starting `startPosition` bytes into the audio. */ bool readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition); @@ -1198,35 +1126,33 @@ public: } /** - * Puts the current dimensions of the robot, in game script - * coordinates, into the given rect, and returns the total - * number of frames in the robot animation. + * Puts the current dimensions of the robot, in game script coordinates, + * into the given rect, and returns the total number of frames in the robot + * animation. */ uint16 getFrameSize(Common::Rect &outRect) const; /** - * Pumps the robot player for the next frame of video. - * This is the main rendering function. + * Pumps the robot player for the next frame of video. This is the main + * rendering function. */ void doRobot(); /** - * Submits any outstanding audio blocks that should - * be added to the queue before the robot frame - * becomes visible. + * Submits any outstanding audio blocks that should be added to the queue + * before the robot frame becomes visible. */ void frameAlmostVisible(); /** - * Evaluates frame drift and makes modifications to - * the player in order to ensure that future frames - * will arrive on time. + * Evaluates frame drift and makes modifications to the player in order to + * ensure that future frames will arrive on time. */ void frameNowVisible(); /** - * Scales a vertically compressed cel to its original - * uncompressed dimensions. + * Scales a vertically compressed cel to its original uncompressed + * dimensions. */ void expandCel(byte *target, const byte* source, const int16 celWidth, const int16 celHeight) const; @@ -1258,21 +1184,18 @@ private: }; /** - * A reg_t pointer to an in-memory - * bitmap containing the cel. + * A reg_t pointer to an in-memory bitmap containing the cel. */ reg_t bitmapId; /** - * The lifetime of the cel, either just - * for this frame or for the entire + * The lifetime of the cel, either just for this frame or for the entire * duration of the robot playback. */ CelHandleLifetime status; /** - * The size, in pixels, of the decompressed - * cel. + * The size, in pixels, of the decompressed cel. */ int area; @@ -1298,16 +1221,14 @@ private: void createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette); /** - * Creates a single screen item for a cel in a - * version 5/6 robot. + * Creates a single screen item for a cel in a version 5/6 robot. * * Returns the size, in bytes, of the raw cel data. */ uint32 createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette); /** - * Preallocates memory for the next `numCels` cels - * in the robot data stream. + * Preallocates memory for the next `numCels` cels in the robot data stream. */ void preallocateCelMemory(const byte *rawVideoData, const int16 numCels); @@ -1322,8 +1243,7 @@ private: reg_t _planeId; /** - * The origin of the robot animation, in screen - * coordinates. + * The origin of the robot animation, in screen coordinates. */ Common::Point _position; @@ -1338,80 +1258,72 @@ private: int16 _xResolution, _yResolution; /** - * Whether or not the coordinates read from robot - * data are high resolution. + * Whether or not the coordinates read from robot data are high resolution. */ bool _isHiRes; /** - * The maximum number of cels that will be rendered - * on any given frame in this robot. Used for - * preallocation of cel memory. + * The maximum number of cels that will be rendered on any given frame in + * this robot. Used for preallocation of cel memory. */ int16 _maxCelsPerFrame; /** - * The maximum areas, in pixels, for each of - * the fixed cels in the robot. Used for - * preallocation of cel memory. + * The maximum areas, in pixels, for each of the fixed cels in the robot. + * Used for preallocation of cel memory. */ MaxCelAreaList _maxCelArea; /** - * The hunk palette to use when rendering the - * current frame, if the `usePalette` flag was set - * in the robot header. + * The hunk palette to use when rendering the current frame, if the + * `usePalette` flag was set in the robot header. */ uint8 *_rawPalette; /** - * A list of the raw video data sizes, in bytes, - * for each frame of the robot. + * A list of the raw video data sizes, in bytes, for each frame of the + * robot. */ VideoSizeList _videoSizes; /** - * A list of cels that will be present for the - * entire duration of the robot animation. + * A list of cels that will be present for the entire duration of the robot + * animation. */ FixedCelsList _fixedCels; /** - * A list of handles for each cel in the current - * frame. + * A list of handles for each cel in the current frame. */ CelHandleList _celHandles; /** - * Scratch memory used to temporarily store - * decompressed cel data for vertically squashed - * cels. + * Scratch memory used to temporarily store decompressed cel data for + * vertically squashed cels. */ ScratchMemory _celDecompressionBuffer; /** - * The size, in bytes, of the squashed cel - * decompression buffer. + * The size, in bytes, of the squashed cel decompression buffer. */ int _celDecompressionArea; /** - * If true, the robot just started playing and - * is awaiting output for the first frame. + * If true, the robot just started playing and is awaiting output for the + * first frame. */ bool _syncFrame; /** - * Scratch memory used to store the compressed robot - * video data for the current frame. + * Scratch memory used to store the compressed robot video data for the + * current frame. */ ScratchMemory _doVersion5Scratch; /** - * When set to a non-negative value, forces the next - * call to doRobot to render the given frame number - * instead of whatever frame would have normally been - * rendered. + * When set to a non-negative value, forces the next call to doRobot to + * render the given frame number instead of whatever frame would have + * normally been rendered. */ mutable int _cueForceShowFrame; @@ -1426,14 +1338,14 @@ private: RobotScreenItemList _screenItemList; /** - * The positions of the various screen items in this - * robot, in screen coordinates. + * The positions of the various screen items in this robot, in screen + * coordinates. */ Common::Array<int16> _screenItemX, _screenItemY; /** - * The raw position values from the cel header for - * each screen item currently on-screen. + * The raw position values from the cel header for each screen item + * currently on-screen. */ Common::Array<int16> _originalScreenItemX, _originalScreenItemY; @@ -1449,11 +1361,10 @@ private: int16 _priority; /** - * The amount of visual vertical compression applied - * to the current cel. A value of 100 means no - * compression; a value above 100 indicates how much - * the cel needs to be scaled along the y-axis to - * return to its original dimensions. + * The amount of visual vertical compression applied to the current cel. A + * value of 100 means no compression; a value above 100 indicates how much + * the cel needs to be scaled along the y-axis to return to its original + * dimensions. */ uint8 _verticalScaleFactor; }; diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index cd2055e017..0aa993a53a 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -660,6 +660,12 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul if (d.md5Entry) continue; + // Prevent executables being detected as Steam variant. If we don't + // know the md5, then it's just the regular executable. Otherwise we + // will most likely fail on trying read the index from the executable. + // Fixes bug #10290 + if (gfp->genMethod == kGenRoomNumSteam || gfp->genMethod == kGenDiskNumSteam) + continue; // ____ _ ____ // | _ \ __ _ _ __| |_ |___ \ * diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index aec5773a3b..77d82c88a6 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -952,7 +952,7 @@ ScummEngine_v100he::~ScummEngine_v100he() { ScummEngine_vCUPhe::ScummEngine_vCUPhe(OSystem *syst, const DetectorResult &dr) : Engine(syst){ _syst = syst; _game = dr.game; - _filenamePattern = dr.fp, + _filenamePattern = dr.fp; _cupPlayer = new CUP_Player(syst, this, _mixer); } @@ -962,7 +962,7 @@ ScummEngine_vCUPhe::~ScummEngine_vCUPhe() { } Common::Error ScummEngine_vCUPhe::run() { - initGraphics(CUP_Player::kDefaultVideoWidth, CUP_Player::kDefaultVideoHeight, true); + initGraphics(CUP_Player::kDefaultVideoWidth, CUP_Player::kDefaultVideoHeight); if (_cupPlayer->open(_filenamePattern.pattern)) { _cupPlayer->play(); @@ -1247,7 +1247,7 @@ Common::Error ScummEngine::init() { // Initialize backend if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) { - initGraphics(kHercWidth, kHercHeight, true); + initGraphics(kHercWidth, kHercHeight); } else { int screenWidth = _screenWidth; int screenHeight = _screenHeight; @@ -1266,7 +1266,7 @@ Common::Error ScummEngine::init() { _outputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); if (_game.platform != Common::kPlatformFMTowns && _game.platform != Common::kPlatformPCEngine) { - initGraphics(screenWidth, screenHeight, screenWidth > 320, &_outputPixelFormat); + initGraphics(screenWidth, screenHeight, &_outputPixelFormat); if (_outputPixelFormat != _system->getScreenFormat()) return Common::kUnsupportedColorMode; } else { @@ -1281,14 +1281,14 @@ Common::Error ScummEngine::init() { } } - initGraphics(screenWidth, screenHeight, screenWidth > 320, tryModes); + initGraphics(screenWidth, screenHeight, tryModes); if (_system->getScreenFormat().bytesPerPixel != 2) return Common::kUnsupportedColorMode; } #else if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) { warning("Starting game without the required 16bit color support.\nYou may experience color glitches"); - initGraphics(screenWidth, screenHeight, (screenWidth > 320)); + initGraphics(screenWidth, screenHeight); } else { return Common::Error(Common::kUnsupportedColorMode, "16bit color support is required for this game"); } @@ -1298,7 +1298,7 @@ Common::Error ScummEngine::init() { if (_game.platform == Common::kPlatformFMTowns && _game.version == 5) return Common::Error(Common::kUnsupportedColorMode, "This game requires dual graphics layer support which is disabled in this build"); #endif - initGraphics(screenWidth, screenHeight, (screenWidth > 320)); + initGraphics(screenWidth, screenHeight); } } diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp index 5050062938..2aa6ae8902 100644 --- a/engines/sherlock/scalpel/scalpel.cpp +++ b/engines/sherlock/scalpel/scalpel.cpp @@ -251,14 +251,14 @@ ScalpelEngine::~ScalpelEngine() { void ScalpelEngine::setupGraphics() { if (getPlatform() != Common::kPlatform3DO) { // 320x200 palettized - initGraphics(320, 200, false); + initGraphics(320, 200); } else { // 3DO actually uses RGB555, but some platforms of ours only support RGB565, so we use that const Graphics::PixelFormat pixelFormatRGB565 = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); // First try for a 640x400 mode g_system->beginGFXTransaction(); - initCommonGFX(true); + initCommonGFX(); g_system->initSize(640, 400, &pixelFormatRGB565); OSystem::TransactionError gfxError = g_system->endGFXTransaction(); @@ -266,7 +266,7 @@ void ScalpelEngine::setupGraphics() { _isScreenDoubled = true; } else { // System doesn't support it, so fall back on 320x200 mode - initGraphics(320, 200, false, &pixelFormatRGB565); + initGraphics(320, 200, &pixelFormatRGB565); } } } diff --git a/engines/sherlock/tattoo/tattoo.cpp b/engines/sherlock/tattoo/tattoo.cpp index 31a65783e3..38466a9459 100644 --- a/engines/sherlock/tattoo/tattoo.cpp +++ b/engines/sherlock/tattoo/tattoo.cpp @@ -51,7 +51,7 @@ void TattooEngine::showOpening() { } void TattooEngine::initialize() { - initGraphics(640, 480, true); + initGraphics(640, 480); // Initialize the base engine SherlockEngine::initialize(); diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp index 40b6959a9b..674bfab95b 100644 --- a/engines/sky/sky.cpp +++ b/engines/sky/sky.cpp @@ -259,7 +259,7 @@ Common::Error SkyEngine::go() { } Common::Error SkyEngine::init() { - initGraphics(320, 200, false); + initGraphics(320, 200); _skyDisk = new Disk(); _skySound = new Sound(_mixer, _skyDisk, Audio::Mixer::kMaxChannelVolume); diff --git a/engines/sludge/graphics.cpp b/engines/sludge/graphics.cpp index 3bcb5aa043..578e6f65fe 100644 --- a/engines/sludge/graphics.cpp +++ b/engines/sludge/graphics.cpp @@ -143,7 +143,7 @@ void GraphicsManager::kill() { } bool GraphicsManager::initGfx() { - initGraphics(_winWidth, _winHeight, true, _vm->getScreenPixelFormat()); + initGraphics(_winWidth, _winHeight, _vm->getScreenPixelFormat()); _renderSurface.create(_winWidth, _winHeight, *_vm->getScreenPixelFormat()); if (!killResizeBackdrop(_winWidth, _winHeight)) diff --git a/engines/sludge/transition.cpp b/engines/sludge/transition.cpp index 306a51c300..3a768cb5f4 100644 --- a/engines/sludge/transition.cpp +++ b/engines/sludge/transition.cpp @@ -152,7 +152,8 @@ void resetRandW() { } } - p1 = 0, p2 = 10; + p1 = 0; + p2 = 10; } #if 0 diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index ac358e774b..e58eb97c23 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -191,12 +191,12 @@ bool MoviePlayer::load(uint32 id) { // Need to switch to true color for PSX/MP2 videos if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) - initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0); + initGraphics(g_system->getWidth(), g_system->getHeight(), nullptr); if (!_decoder->loadFile(filename)) { // Go back to 8bpp color if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) - initGraphics(g_system->getWidth(), g_system->getHeight(), true); + initGraphics(g_system->getWidth(), g_system->getHeight()); return false; } @@ -422,7 +422,7 @@ bool MoviePlayer::playVideo() { // Need to jump back to paletted color if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) - initGraphics(g_system->getWidth(), g_system->getHeight(), true); + initGraphics(g_system->getWidth(), g_system->getHeight()); return !_vm->shouldQuit() && !skipped; } diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp index 1e7b6da4f4..8b12fef9aa 100644 --- a/engines/sword1/sword1.cpp +++ b/engines/sword1/sword1.cpp @@ -94,7 +94,7 @@ SwordEngine::~SwordEngine() { Common::Error SwordEngine::init() { - initGraphics(640, 480, true); + initGraphics(640, 480); if (0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1mac") || 0 == scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1macdemo")) diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index e93f27f76c..d25133fe99 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -103,12 +103,12 @@ bool MoviePlayer::load(const char *name) { // Need to switch to true color for PSX/MP2 videos if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) - initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0); + initGraphics(g_system->getWidth(), g_system->getHeight(), nullptr); if (!_decoder->loadFile(filename)) { // Go back to 8bpp color if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) - initGraphics(g_system->getWidth(), g_system->getHeight(), true); + initGraphics(g_system->getWidth(), g_system->getHeight()); return false; } @@ -145,7 +145,7 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI // Need to jump back to paletted color if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) - initGraphics(640, 480, true); + initGraphics(640, 480); } void MoviePlayer::openTextObject(uint32 index) { diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp index 4f3caa437e..a2761eb5ce 100644 --- a/engines/sword2/sword2.cpp +++ b/engines/sword2/sword2.cpp @@ -442,7 +442,7 @@ Common::Error Sword2Engine::run() { _resman = NULL; _memory = NULL; - initGraphics(640, 480, true); + initGraphics(640, 480); _screen = new Screen(this, 640, 480); // Create the debugger as early as possible (but not before the diff --git a/engines/sword25/sword25.cpp b/engines/sword25/sword25.cpp index b6f2641714..9d8d02ef68 100644 --- a/engines/sword25/sword25.cpp +++ b/engines/sword25/sword25.cpp @@ -97,7 +97,7 @@ Common::Error Sword25Engine::run() { Common::Error Sword25Engine::appStart() { // Initialize the graphics mode to RGBA8888 Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); - initGraphics(800, 600, true, &format); + initGraphics(800, 600, &format); if (format != g_system->getScreenFormat()) return Common::kUnsupportedColorMode; diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index 2d10b82f51..d5777bb3dc 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -540,7 +540,7 @@ Common::Error TeenAgentEngine::run() { Common::EventManager *_event = _system->getEventManager(); - initGraphics(kScreenWidth, kScreenHeight, false); + initGraphics(kScreenWidth, kScreenHeight); console = new Console(this); scene = new Scene(this); diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk index be733a5b60..5838751439 100644 --- a/engines/testbed/module.mk +++ b/engines/testbed/module.mk @@ -15,9 +15,11 @@ MODULE_OBJS := \ testsuite.o ifdef USE_CLOUD +ifdef USE_LIBCURL MODULE_OBJS += \ cloud.o endif +endif ifdef USE_SDL_NET MODULE_OBJS += \ diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp index b6364c98ad..f071ab347a 100644 --- a/engines/testbed/testbed.cpp +++ b/engines/testbed/testbed.cpp @@ -140,7 +140,7 @@ TestbedEngine::TestbedEngine(OSystem *syst) // Midi ts = new MidiTestSuite(); _testsuiteList.push_back(ts); -#ifdef USE_CLOUD +#if defined(USE_CLOUD) && defined(USE_LIBCURL) // Cloud ts = new CloudTestSuite(); _testsuiteList.push_back(ts); @@ -185,7 +185,7 @@ void TestbedEngine::invokeTestsuites(TestbedConfigManager &cfMan) { Common::Error TestbedEngine::run() { // Initialize graphics using following: - initGraphics(320, 200, false); + initGraphics(320, 200); // As of now we are using GUI::MessageDialog for interaction, Test if it works. // interactive mode could also be modified by a config parameter "non-interactive=1" diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp index 8ad177abbb..76a8325879 100644 --- a/engines/tinsel/tinsel.cpp +++ b/engines/tinsel/tinsel.cpp @@ -900,13 +900,13 @@ Common::Error TinselEngine::run() { // Initialize backend if (getGameID() == GID_DW2) { #ifndef DW2_EXACT_SIZE - initGraphics(640, 480, true); + initGraphics(640, 480); #else - initGraphics(640, 432, true); + initGraphics(640, 432); #endif _screenSurface.create(640, 432, Graphics::PixelFormat::createFormatCLUT8()); } else { - initGraphics(320, 200, false); + initGraphics(320, 200); _screenSurface.create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); } diff --git a/engines/titanic/configure.engine b/engines/titanic/configure.engine index 23a3b4e599..7c9516cd4a 100644 --- a/engines/titanic/configure.engine +++ b/engines/titanic/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 titanic "Starship Titanic" no "" "" "16bit jpeg highres mad zlib" +add_engine titanic "Starship Titanic" yes "" "" "16bit jpeg highres mad zlib" diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp index b6923c27a4..af34526329 100644 --- a/engines/titanic/core/game_object.cpp +++ b/engines/titanic/core/game_object.cpp @@ -465,7 +465,7 @@ bool CGameObject::isSoundActive(int handle) const { return false; } -void CGameObject::playGlobalSound(const CString &resName, VolumeMode mode, bool initialMute, bool repeated, +void CGameObject::playAmbientSound(const CString &resName, VolumeMode mode, bool initialMute, bool repeated, int handleIndex, Audio::Mixer::SoundType soundType) { if (handleIndex < 0 || handleIndex > 3) return; @@ -524,7 +524,7 @@ void CGameObject::setSoundVolume(int handle, uint percent, uint seconds) { } } -void CGameObject::stopGlobalSound(bool transition, int handleIndex) { +void CGameObject::stopAmbientSound(bool transition, int handleIndex) { CGameManager *gameManager = getGameManager(); if (!gameManager) return; @@ -550,7 +550,7 @@ void CGameObject::stopGlobalSound(bool transition, int handleIndex) { } } -void CGameObject::setGlobalSoundVolume(VolumeMode mode, uint seconds, int handleIndex) { +void CGameObject::setAmbientSoundVolume(VolumeMode mode, uint seconds, int handleIndex) { CGameManager *gameManager = getGameManager(); if (!gameManager) return; @@ -559,7 +559,7 @@ void CGameObject::setGlobalSoundVolume(VolumeMode mode, uint seconds, int handle if (handleIndex == -1) { // Iterate through calling the method for each handle for (int idx = 0; idx < 3; ++idx) - setGlobalSoundVolume(mode, seconds, idx); + setAmbientSoundVolume(mode, seconds, idx); } else if (handleIndex >= 0 && handleIndex <= 3 && _soundHandles[handleIndex] != -1) { uint newVolume = sound._soundManager.getModeVolume(mode); sound.setVolume(_soundHandles[handleIndex], newVolume, seconds); diff --git a/engines/titanic/core/game_object.h b/engines/titanic/core/game_object.h index b592806977..f699d3a66a 100644 --- a/engines/titanic/core/game_object.h +++ b/engines/titanic/core/game_object.h @@ -254,7 +254,7 @@ protected: void setSoundVolume(int handle, uint percent, uint seconds); /** - * Plays a sound, and saves it's handle in the global sound handles list + * Plays an ambient sound, and saves it's handle in the ambient sound handles list * @param resName Filename of sound to play * @param mode Volume mode level * @param initialMute If set, sound transitions in from mute over 2 seconds @@ -262,23 +262,23 @@ protected: * @param handleIndex Slot 0 to 3 in the shared sound handle list to store the sound's handle * @param soundType Specifies whether the sound is a sound effect or music */ - void playGlobalSound(const CString &resName, VolumeMode mode, bool initialMute, bool repeated, + void playAmbientSound(const CString &resName, VolumeMode mode, bool initialMute, bool repeated, int handleIndex, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); /** - * Stops a sound saved in the global sound handle list + * Stops playing an ambient sound * @param transition If set, the sound transitions to silent before stopping - * @param handleIndex Index of sound to stop. If -1, all global sounds are stopped + * @param handleIndex Index of sound to stop. If -1, all ambient sounds are stopped */ - void stopGlobalSound(bool transition, int handleIndex); + void stopAmbientSound(bool transition, int handleIndex); /** - * Updates the volume for a global sound based on the specified mode's volume + * Updates the volume for an ambient sound based on the specified mode's volume * @param mode Volume level mode * @param seconds Number of seconds to transition to new volume - * @param handleIndex Index of global sound to update. If -1, all global sounds are updated + * @param handleIndex Index of ambient sound to update. If -1, all ambient sounds are updated */ - void setGlobalSoundVolume(VolumeMode mode, uint seconds, int handleIndex); + void setAmbientSoundVolume(VolumeMode mode, uint seconds, int handleIndex); /** * Stops sound channel 3 or 0 diff --git a/engines/titanic/detection_tables.h b/engines/titanic/detection_tables.h index bff065cf1c..1d68537430 100644 --- a/engines/titanic/detection_tables.h +++ b/engines/titanic/detection_tables.h @@ -31,7 +31,7 @@ static const TitanicGameDescription gameDescriptions[] = { AD_ENTRY1s("newgame.st", "c276f2661f0d0a547445a65db78b2292", 87227), Common::EN_ANY, Common::kPlatformWindows, - ADGF_TESTING, + 0, GUIO1(GUIO_NONE) }, }, @@ -44,7 +44,7 @@ static const TitanicGameDescription gameDescriptions[] = { AD_ENTRY1s("newgame.st", "db22924adfd6730f4b79f4e51b25e779", 87608), Common::DE_DEU, Common::kPlatformWindows, - ADGF_UNSTABLE, + 0, GUIO1(GUIO_NONE) }, }, diff --git a/engines/titanic/game/bridge_view.cpp b/engines/titanic/game/bridge_view.cpp index 608724c16c..d98cee13c0 100644 --- a/engines/titanic/game/bridge_view.cpp +++ b/engines/titanic/game/bridge_view.cpp @@ -77,7 +77,7 @@ bool CBridgeView::ActMsg(CActMsg *msg) { hideMouse(); CChangeMusicMsg musicMsg; - musicMsg._flags = 1; + musicMsg._action = MUSIC_STOP; musicMsg.execute("BridgeAutoMusicPlayer"); playSound(TRANSLATE("a#42.wav", "a#35.wav")); playMovie(MOVIE_NOTIFY_OBJECT); diff --git a/engines/titanic/game/code_wheel.cpp b/engines/titanic/game/code_wheel.cpp index 441c481dfe..29ae8e683f 100644 --- a/engines/titanic/game/code_wheel.cpp +++ b/engines/titanic/game/code_wheel.cpp @@ -21,8 +21,6 @@ */ #include "titanic/game/code_wheel.h" -#include "titanic/titanic.h" -#include "titanic/translation.h" #include "titanic/translation.h" namespace Titanic { @@ -35,15 +33,52 @@ BEGIN_MESSAGE_MAP(CodeWheel, CBomb) ON_MESSAGE(CheckCodeWheelsMsg) END_MESSAGE_MAP() -static const int START_FRAMES[15] = { +static const int START_FRAMES_EN[15] = { 0, 5, 10, 15, 19, 24, 28, 33, 38, 42, 47, 52, 57, 61, 66 }; -static const int END_FRAMES[15] = { +static const int END_FRAMES_EN[15] = { 5, 10, 15, 19, 24, 28, 33, 38, 42, 47, 52, 57, 61, 66, 70 }; +static const int START_FRAMES_DE2[28] = { + 390, 383, 375, 368, 361, 353, 346, 339, 331, 324, + 317, 309, 302, 295, 287, 280, 272, 265, 258, 251, + 244, 236, 229, 221, 214, 207, 199, 0 +}; +static const int END_FRAMES_DE2[28] = { + 397, 390, 383, 375, 368, 361, 353, 346, 339, 331, + 324, 317, 309, 302, 295, 287, 280, 272, 265, 258, + 251, 244, 236, 229, 221, 214, 207, 0 +}; + +static const int CORRECT_VALUES_DE[3][8] = { + { 2, 7, 4, 8, 18, 18, 4, 17 }, + { 12, 0, 6, 10, 11, 20, 6, 18 }, + { 13, 8, 4, 12, 0, 13, 3, 26 } +}; +static const int START_FRAMES_DE[28] = { + 0, 7, 15, 22, 29, 37, 44, 51, 58, 66, + 73, 80, 88, 95, 102, 110, 117, 125, 132, 139, + 146, 154, 161, 168, 175, 183, 190, 0 +}; +static const int END_FRAMES_DE[28] = { + 7, 15, 22, 29, 37, 44, 51, 58, 66, 73, + 80, 88, 95, 102, 110, 117, 125, 132, 139, 146, + 154, 161, 168, 175, 183, 190, 198, 0 +}; +static const int START_FRAMES_REV_DE[28] = { + 390, 383, 375, 368, 361, 353, 346, 339, 331, 324, + 317, 309, 302, 295, 287, 280, 272, 265, 258, 251, + 244, 236, 229, 221, 214, 207, 199, 0 +}; +static const int END_FRAMES_REV_DE[28] = { + 397, 390, 383, 375, 368, 361, 353, 346, 339, 331, + 324, 317, 309, 302, 295, 287, 280, 272, 265, 258, + 251, 244, 236, 229, 221, 214, 207, 0 +}; + CodeWheel::CodeWheel() : CBomb(), _correctValue(0), _value(4), - _matched(false), _field114(0), _field118(0) { + _matched(false), _column(0), _row(0) { } void CodeWheel::save(SimpleFile *file, int indent) { @@ -51,9 +86,10 @@ void CodeWheel::save(SimpleFile *file, int indent) { file->writeNumberLine(_correctValue, indent); file->writeNumberLine(_value, indent); file->writeNumberLine(_matched, indent); - if (g_vm->isGerman()) { - file->writeNumberLine(_field114, indent); - file->writeNumberLine(_field118, indent); + + if (g_language == Common::DE_DEU) { + file->writeNumberLine(_row, indent); + file->writeNumberLine(_column, indent); } CBomb::save(file, indent); @@ -64,9 +100,14 @@ void CodeWheel::load(SimpleFile *file) { _correctValue = file->readNumber(); _value = file->readNumber(); _matched = file->readNumber(); - if (g_vm->isGerman()) { - _field114 = file->readNumber(); - _field118 = file->readNumber(); + + if (g_language == Common::DE_DEU) { + _row = file->readNumber(); + _column = file->readNumber(); + + assert(_column >= 1 && _column <= 8); + assert(_row >= 0 && _row <= 2); + _correctValue = CORRECT_VALUES_DE[_row][_column - 1]; } CBomb::load(file); @@ -77,29 +118,30 @@ bool CodeWheel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { _matched = false; if (msg->_mousePos.y > yp) { - if (_value == _correctValue) - _matched = true; + _value = (_value + 1) % TRANSLATE(15, 27); - _value = (_value + 1) % 15; - playMovie(START_FRAMES[_value], END_FRAMES[_value], + playMovie(TRANSLATE(START_FRAMES_EN[_value], START_FRAMES_DE[_value]), + TRANSLATE(END_FRAMES_EN[_value], END_FRAMES_DE[_value]), MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); - } else { - if (_value == _correctValue) - _matched = true; - playMovie(START_FRAMES[14 - _value] + 68, END_FRAMES[14 - _value] + 68, + } else { + playMovie(TRANSLATE(START_FRAMES_EN[14 - _value] + 68, START_FRAMES_REV_DE[_value]), + TRANSLATE(END_FRAMES_EN[14 - _value] + 68, END_FRAMES_REV_DE[_value]), MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); - _value = (_value <= 0) ? 14 : _value - 1; + _value = (_value <= 0) ? TRANSLATE(14, 26) : _value - 1; } + if (_value == _correctValue) + _matched = true; + playSound(TRANSLATE("z#59.wav", "z#590.wav")); return true; } bool CodeWheel::EnterViewMsg(CEnterViewMsg *msg) { // WORKAROUND: Don't keep resetting code wheels back to default - loadFrame(END_FRAMES[_value]); + loadFrame(TRANSLATE(END_FRAMES_EN[_value], END_FRAMES_DE[_value])); return true; } @@ -123,4 +165,8 @@ bool CodeWheel::CheckCodeWheelsMsg(CCheckCodeWheelsMsg *msg) { return true; } +void CodeWheel::reset() { + _value = TRANSLATE(4, 14); +} + } // End of namespace Titanic diff --git a/engines/titanic/game/code_wheel.h b/engines/titanic/game/code_wheel.h index 3d19eeb53c..4af1c6f388 100644 --- a/engines/titanic/game/code_wheel.h +++ b/engines/titanic/game/code_wheel.h @@ -39,8 +39,7 @@ private: int _value; bool _matched; // German specific fields - int _field114; - int _field118; + int _row, _column; public: CLASSDEF; CodeWheel(); @@ -58,7 +57,7 @@ public: /** * Resets a code wheel back to the default 'O' value */ - void reset() { _value = 4; } + void reset(); }; } // End of namespace Titanic diff --git a/engines/titanic/game/computer_screen.cpp b/engines/titanic/game/computer_screen.cpp index dee469af1c..8150448be7 100644 --- a/engines/titanic/game/computer_screen.cpp +++ b/engines/titanic/game/computer_screen.cpp @@ -116,7 +116,7 @@ bool CComputerScreen::TimerMsg(CTimerMsg *msg) { break; case 2: { - CChangeMusicMsg musicMsg(CString(), 1); + CChangeMusicMsg musicMsg(CString(), MUSIC_STOP); musicMsg.execute("HomeMusicPlayer"); playSound(TRANSLATE("a#33.wav", "a#28.wav")); playSound(TRANSLATE("a#31.wav", "a#26.wav")); diff --git a/engines/titanic/game/credits.cpp b/engines/titanic/game/credits.cpp index ddb44f127c..016245dbb0 100644 --- a/engines/titanic/game/credits.cpp +++ b/engines/titanic/game/credits.cpp @@ -57,13 +57,13 @@ bool CCredits::SignalObject(CSignalObject *msg) { } bool CCredits::TimerMsg(CTimerMsg *msg) { - stopGlobalSound(true, -1); + stopAmbientSound(true, -1); setVisible(true); loadSound(TRANSLATE("a#16.wav", "a#11.wav")); loadSound(TRANSLATE("a#24.wav", "a#19.wav")); if (playCutscene(0, 18)) { - playGlobalSound(TRANSLATE("a#16.wav", "a#11.wav"), VOL_NORMAL, false, false, 0); + playAmbientSound(TRANSLATE("a#16.wav", "a#11.wav"), VOL_NORMAL, false, false, 0); if (playCutscene(19, 642)) { playSound(TRANSLATE("a#24.wav", "a#19.wav")); playCutscene(643, 750); @@ -77,7 +77,7 @@ bool CCredits::TimerMsg(CTimerMsg *msg) { setVisible(false); petShow(); enableMouse(); - stopGlobalSound(true, -1); + stopAmbientSound(true, -1); return true; } diff --git a/engines/titanic/game/ear_sweet_bowl.cpp b/engines/titanic/game/ear_sweet_bowl.cpp index 387392b26d..703dfde261 100644 --- a/engines/titanic/game/ear_sweet_bowl.cpp +++ b/engines/titanic/game/ear_sweet_bowl.cpp @@ -47,10 +47,6 @@ bool CEarSweetBowl::MovieEndMsg(CMovieEndMsg *msg) { doneMsg.execute(findRoom()); if (!doneMsg._value) { - CPetControl *pet = getPetControl(); - if (pet) - pet->hasRoomFlags(); - CIsParrotPresentMsg parrotMsg; parrotMsg.execute(findRoom()); diff --git a/engines/titanic/game/emma_control.cpp b/engines/titanic/game/emma_control.cpp index e3ba7cc42c..46cde9bfcd 100644 --- a/engines/titanic/game/emma_control.cpp +++ b/engines/titanic/game/emma_control.cpp @@ -57,7 +57,7 @@ bool CEmmaControl::EnterViewMsg(CEnterViewMsg *msg) { bool CEmmaControl::StatusChangeMsg(CStatusChangeMsg *msg) { _flag = !_flag; setVisible(_flag); - CChangeMusicMsg changeMsg(_flag ? _visibleSoundName : _hiddenSoundName, 0); + CChangeMusicMsg changeMsg(_flag ? _visibleSoundName : _hiddenSoundName, MUSIC_NONE); changeMsg.execute(findRoom(), CAutoMusicPlayer::_type, MSGFLAG_SCAN | MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_CLASS_DEF); return true; diff --git a/engines/titanic/game/end_credit_text.cpp b/engines/titanic/game/end_credit_text.cpp index 8809799a27..2392f9d321 100644 --- a/engines/titanic/game/end_credit_text.cpp +++ b/engines/titanic/game/end_credit_text.cpp @@ -44,7 +44,7 @@ void CEndCreditText::load(SimpleFile *file) { } bool CEndCreditText::ActMsg(CActMsg *msg) { - playGlobalSound(TRANSLATE("z#41.wav", "z#573.wav"), VOL_NORMAL, false, false, 0); + playAmbientSound(TRANSLATE("z#41.wav", "z#573.wav"), VOL_NORMAL, false, false, 0); createCredits(); _flag = true; return true; @@ -64,7 +64,7 @@ bool CEndCreditText::FrameMsg(CFrameMsg *msg) { } bool CEndCreditText::TimerMsg(CTimerMsg *msg) { - setGlobalSoundVolume(VOL_MUTE, 2, -1); + setAmbientSoundVolume(VOL_MUTE, 2, -1); sleep(1000); quitGame(); return true; diff --git a/engines/titanic/game/end_credits.cpp b/engines/titanic/game/end_credits.cpp index 4ea54a3b76..84ac020a2c 100644 --- a/engines/titanic/game/end_credits.cpp +++ b/engines/titanic/game/end_credits.cpp @@ -45,11 +45,11 @@ void CEndCredits::load(SimpleFile *file) { bool CEndCredits::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { if (_flag) { deinit(); - stopGlobalSound(true, -1); + stopAmbientSound(true, -1); _flag = false; } else { loadSound(TRANSLATE("z#41.wav", "z#573.wav")); - playGlobalSound(TRANSLATE("z#41.wav", "z#573.wav"), VOL_NORMAL, false, false, 0); + playAmbientSound(TRANSLATE("z#41.wav", "z#573.wav"), VOL_NORMAL, false, false, 0); _flag = true; } diff --git a/engines/titanic/game/end_explode_ship.cpp b/engines/titanic/game/end_explode_ship.cpp index 5f90604fd7..5ad046b5ad 100644 --- a/engines/titanic/game/end_explode_ship.cpp +++ b/engines/titanic/game/end_explode_ship.cpp @@ -56,7 +56,7 @@ bool CEndExplodeShip::ActMsg(CActMsg *msg) { } else if (msg->_action == "TakeOff") { loadSound(TRANSLATE("a#31.wav", "a#26.wav")); loadSound(TRANSLATE("a#14.wav", "a#7.wav")); - playGlobalSound(TRANSLATE("a#13.wav", "a#6.wav"), VOL_NORMAL, true, true, 0); + playAmbientSound(TRANSLATE("a#13.wav", "a#6.wav"), VOL_NORMAL, true, true, 0); addTimer(1, 10212, 0); } @@ -72,7 +72,7 @@ bool CEndExplodeShip::TimerMsg(CTimerMsg *msg) { } if (msg->_actionVal == 3) { - setGlobalSoundVolume(VOL_MUTE, 2, -1); + setAmbientSoundVolume(VOL_MUTE, 2, -1); CActMsg actMsg(_isExploding ? "ExplodeCredits" : "Credits"); actMsg.execute("EndGameCredits"); } diff --git a/engines/titanic/game/end_sequence_control.cpp b/engines/titanic/game/end_sequence_control.cpp index e8bc04aa85..a7abf01da0 100644 --- a/engines/titanic/game/end_sequence_control.cpp +++ b/engines/titanic/game/end_sequence_control.cpp @@ -62,7 +62,7 @@ bool CEndSequenceControl::TimerMsg(CTimerMsg *msg) { } bool CEndSequenceControl::MovieEndMsg(CMovieEndMsg *msg) { - setGlobalSoundVolume(VOL_MUTE, 2, -1); + setAmbientSoundVolume(VOL_MUTE, 2, -1); changeView("TheEnd.Node 3.N"); addTimer(2, 1000, 0); return true; @@ -72,7 +72,7 @@ bool CEndSequenceControl::EnterRoomMsg(CEnterRoomMsg *msg) { petHide(); disableMouse(); addTimer(1, 1000, 0); - playGlobalSound(TRANSLATE("a#15.wav", "a#8.wav"), VOL_NORMAL, true, true, 0, Audio::Mixer::kSpeechSoundType); + playAmbientSound(TRANSLATE("a#15.wav", "a#8.wav"), VOL_NORMAL, true, true, 0, Audio::Mixer::kSpeechSoundType); return true; } diff --git a/engines/titanic/game/phonograph.cpp b/engines/titanic/game/phonograph.cpp index 71fd42861a..71b3df768b 100644 --- a/engines/titanic/game/phonograph.cpp +++ b/engines/titanic/game/phonograph.cpp @@ -83,8 +83,8 @@ bool CPhonograph::PhonographPlayMsg(CPhonographPlayMsg *msg) { _isPlaying = true; msg->_value = 1; } else { - stopGlobalSound(false, -1); - playGlobalSound(cylinderMsg._name, VOL_QUIET, true, true, 0); + stopAmbientSound(false, -1); + playAmbientSound(cylinderMsg._name, VOL_QUIET, true, true, 0); _isPlaying = true; msg->_value = 1; } @@ -107,7 +107,7 @@ bool CPhonograph::PhonographStopMsg(CPhonographStopMsg *msg) { CStopMusicMsg stopMsg; stopMsg.execute(this); } else { - stopGlobalSound(msg->_leavingRoom, -1); + stopAmbientSound(msg->_leavingRoom, -1); } msg->_cylinderPresent = true; } diff --git a/engines/titanic/game/sgt/sgt_nav.cpp b/engines/titanic/game/sgt/sgt_nav.cpp index 3be92b4828..904ce64217 100644 --- a/engines/titanic/game/sgt/sgt_nav.cpp +++ b/engines/titanic/game/sgt/sgt_nav.cpp @@ -21,6 +21,7 @@ */ #include "titanic/game/sgt/sgt_nav.h" +#include "titanic/pet_control/pet_control.h" namespace Titanic { @@ -43,7 +44,9 @@ bool SGTNav::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { CTurnOn onMsg; CTurnOff offMsg; - if (_statics->_chestOfDrawers == "Open" && _statics->_bedhead == "Open") { + CPetControl *pet = getPetControl(); + if (_statics->_chestOfDrawers == "Open" && _statics->_bedhead == "Open" + && pet->isInAssignedRoom()) { if (_statics->_vase == "Open") offMsg.execute("Vase"); if (_statics->_tv == "Closed") @@ -68,10 +71,14 @@ bool SGTNav::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { } bool SGTNav::MouseMoveMsg(CMouseMoveMsg *msg) { - if (_statics->_chestOfDrawers == "Open" && _statics->_bedhead == "Open") - _cursorId = CURSOR_MOVE_FORWARD; - else - _cursorId = CURSOR_ARROW; + _cursorId = CURSOR_ARROW; + + if (_statics->_chestOfDrawers == "Open" && _statics->_bedhead == "Open") { + CPetControl *pet = getPetControl(); + if (pet->isInAssignedRoom()) { + _cursorId = CURSOR_MOVE_FORWARD; + } + } return true; } diff --git a/engines/titanic/game/sgt/sgt_state_room.cpp b/engines/titanic/game/sgt/sgt_state_room.cpp index ca6acaef16..b4927e6828 100644 --- a/engines/titanic/game/sgt/sgt_state_room.cpp +++ b/engines/titanic/game/sgt/sgt_state_room.cpp @@ -125,10 +125,10 @@ bool CSGTStateRoom::VisibleMsg(CVisibleMsg *msg) { bool CSGTStateRoom::EnterRoomMsg(CEnterRoomMsg *msg) { CPetControl *pet = getPetControl(); - uint roomFlags = pet->getRoomFlags(); - uint assignedRoom = pet->getAssignedRoomFlags(); - if (roomFlags == assignedRoom) { + // WORKAROUND: Correctly show SGT furniture states in assigned stateroom + // even when the user has already upgraded to 2nd or 1st class + if (pet->isInAssignedRoom()) { loadFrame(_savedFrame); _isClosed = _savedIsClosed; setVisible(_savedVisible); @@ -139,7 +139,7 @@ bool CSGTStateRoom::EnterRoomMsg(CEnterRoomMsg *msg) { if (isEquals("Drawer")) { petSetArea(PET_REMOTE); - if (roomFlags == assignedRoom && getPassengerClass() == 3 + if (pet->isInAssignedRoom() && getPassengerClass() == 3 && _statics->_announcementFlag) { // Congratulations, you may have won an upgrade playSound(TRANSLATE("b#21.wav", "b#2.wav")); @@ -149,7 +149,7 @@ bool CSGTStateRoom::EnterRoomMsg(CEnterRoomMsg *msg) { _statics->_drawer = "Closed"; setVisible(false); _isClosed = true; - } else if (roomFlags != assignedRoom) { + } else if (!pet->isInAssignedRoom()) { loadFrame(0); if (_displayFlag) { setVisible(true); diff --git a/engines/titanic/game/transport/lift.cpp b/engines/titanic/game/transport/lift.cpp index 568e5becaf..95a4a8ebb9 100644 --- a/engines/titanic/game/transport/lift.cpp +++ b/engines/titanic/game/transport/lift.cpp @@ -190,23 +190,23 @@ bool CLift::StatusChangeMsg(CStatusChangeMsg *msg) { bool CLift::MovieEndMsg(CMovieEndMsg *msg) { switch (msg->_endFrame) { case 108: - setGlobalSoundVolume(VOL_MUTE, 1, 0); - setGlobalSoundVolume(VOL_QUIET, 1, 1); + setAmbientSoundVolume(VOL_MUTE, 1, 0); + setAmbientSoundVolume(VOL_QUIET, 1, 1); break; case 190: - setGlobalSoundVolume(VOL_MUTE, 1, 1); - setGlobalSoundVolume(VOL_QUIET, 1, 2); + setAmbientSoundVolume(VOL_MUTE, 1, 1); + setAmbientSoundVolume(VOL_QUIET, 1, 2); break; case 407: - setGlobalSoundVolume(VOL_MUTE, 1, 2); - setGlobalSoundVolume(VOL_QUIET, 1, 1); + setAmbientSoundVolume(VOL_MUTE, 1, 2); + setAmbientSoundVolume(VOL_QUIET, 1, 1); break; case 489: - setGlobalSoundVolume(VOL_MUTE, 1, 1); - setGlobalSoundVolume(VOL_QUIET, 1, 0); + setAmbientSoundVolume(VOL_MUTE, 1, 1); + setAmbientSoundVolume(VOL_QUIET, 1, 0); break; default: { @@ -252,17 +252,17 @@ bool CLift::EnterRoomMsg(CEnterRoomMsg *msg) { } if (floorNum < 20) { - playGlobalSound(TRANSLATE("z#520.wav", "z#259.wav"), VOL_QUIET, true, true, 0); - playGlobalSound(TRANSLATE("z#519.wav", "z#258.wav"), VOL_MUTE, false, true, 1); - playGlobalSound(TRANSLATE("z#518.wav", "z#257.wav"), VOL_MUTE, false, true, 2); + playAmbientSound(TRANSLATE("z#520.wav", "z#259.wav"), VOL_QUIET, true, true, 0); + playAmbientSound(TRANSLATE("z#519.wav", "z#258.wav"), VOL_MUTE, false, true, 1); + playAmbientSound(TRANSLATE("z#518.wav", "z#257.wav"), VOL_MUTE, false, true, 2); } else if (floorNum < 28) { - playGlobalSound(TRANSLATE("z#520.wav", "z#259.wav"), VOL_MUTE, false, true, 0); - playGlobalSound(TRANSLATE("z#519.wav", "z#258.wav"), VOL_QUIET, true, true, 1); - playGlobalSound(TRANSLATE("z#518.wav", "z#257.wav"), VOL_MUTE, false, true, 2); + playAmbientSound(TRANSLATE("z#520.wav", "z#259.wav"), VOL_MUTE, false, true, 0); + playAmbientSound(TRANSLATE("z#519.wav", "z#258.wav"), VOL_QUIET, true, true, 1); + playAmbientSound(TRANSLATE("z#518.wav", "z#257.wav"), VOL_MUTE, false, true, 2); } else { - playGlobalSound(TRANSLATE("z#520.wav", "z#259.wav"), VOL_MUTE, false, true, 0); - playGlobalSound(TRANSLATE("z#519.wav", "z#258.wav"), VOL_MUTE, false, true, 1); - playGlobalSound(TRANSLATE("z#518.wav", "z#257.wav"), VOL_QUIET, true, true, 2); + playAmbientSound(TRANSLATE("z#520.wav", "z#259.wav"), VOL_MUTE, false, true, 0); + playAmbientSound(TRANSLATE("z#519.wav", "z#258.wav"), VOL_MUTE, false, true, 1); + playAmbientSound(TRANSLATE("z#518.wav", "z#257.wav"), VOL_QUIET, true, true, 2); } } @@ -270,7 +270,7 @@ bool CLift::EnterRoomMsg(CEnterRoomMsg *msg) { } bool CLift::LeaveRoomMsg(CLeaveRoomMsg *msg) { - stopGlobalSound(true, -1); + stopAmbientSound(true, -1); CPetControl *pet = getPetControl(); if (pet->getRoomsElevatorNum() == 4 && _hasHead && !_hasCorrectHead) { diff --git a/engines/titanic/game/transport/pellerator.cpp b/engines/titanic/game/transport/pellerator.cpp index 9958226c6b..55f701f0f0 100644 --- a/engines/titanic/game/transport/pellerator.cpp +++ b/engines/titanic/game/transport/pellerator.cpp @@ -69,7 +69,6 @@ void CPellerator::load(SimpleFile *file) { bool CPellerator::StatusChangeMsg(CStatusChangeMsg *msg) { setVisible(true); - playGlobalSound(TRANSLATE("z#74.wav", "z#605.wav"), VOL_QUIET, true, true, 0); int classNum = getPassengerClass(); int newDest = msg->_newStatus; @@ -78,6 +77,8 @@ bool CPellerator::StatusChangeMsg(CStatusChangeMsg *msg) { } else if (classNum == 3 || (msg->_newStatus > 4 && classNum != 1)) { petDisplayMessage(1, CLASS_NOT_ALLOWED_AT_DEST); } else if (newDest > _destination) { + playAmbientSound(TRANSLATE("z#74.wav", "z#605.wav"), VOL_QUIET, true, true, 0); + CString name = getName(); changeView(name == "PelleratorObject2" ? "Pellerator.Node 1.N" : "Pellerator.Node 1.S"); @@ -179,6 +180,8 @@ bool CPellerator::StatusChangeMsg(CStatusChangeMsg *msg) { playMovie(264, 264, MOVIE_NOTIFY_OBJECT); _destination = newDest; } else if (newDest < _destination) { + playAmbientSound(TRANSLATE("z#74.wav", "z#605.wav"), VOL_QUIET, true, true, 0); + CString name = getName(); changeView(name == "PelleratorObject2" ? "Pellerator.Node 1.N" : "Pellerator.Node 1.S"); @@ -338,7 +341,7 @@ bool CPellerator::EnterRoomMsg(CEnterRoomMsg *msg) { bool CPellerator::MovieEndMsg(CMovieEndMsg *msg) { setVisible(false); - stopGlobalSound(true, -1); + stopAmbientSound(true, -1); switch (_destination) { case 0: diff --git a/engines/titanic/game/transport/service_elevator.cpp b/engines/titanic/game/transport/service_elevator.cpp index 1e76c63e01..e3e6306e3a 100644 --- a/engines/titanic/game/transport/service_elevator.cpp +++ b/engines/titanic/game/transport/service_elevator.cpp @@ -127,8 +127,8 @@ bool CServiceElevator::ServiceElevatorMsg(CServiceElevatorMsg *msg) { switch (_fieldDC) { case 0: _string1 = "DeepSpace"; - _string2 = "a#2.wav"; - queueSound("z#416.wav", _soundHandle2, 50); + _string2 = TRANSLATE("a#2.wav", "a#54.wav"); + queueSound(TRANSLATE("z#416.wav", "z#160.wav"), _soundHandle2, 50); break; case 1: diff --git a/engines/titanic/game_manager.h b/engines/titanic/game_manager.h index 89c40d0356..c319e18e22 100644 --- a/engines/titanic/game_manager.h +++ b/engines/titanic/game_manager.h @@ -81,8 +81,8 @@ public: CInputHandler _inputHandler; CInputTranslator _inputTranslator; CTreeItem *_dragItem; - CMusicRoom _musicRoom; CSound _sound; + CMusicRoom _musicRoom; public: CGameManager(CProjectItem *project, CGameView *gameView, Audio::Mixer *mixer); ~CGameManager(); diff --git a/engines/titanic/messages/messages.h b/engines/titanic/messages/messages.h index 115976a70d..925898acc3 100644 --- a/engines/titanic/messages/messages.h +++ b/engines/titanic/messages/messages.h @@ -226,6 +226,9 @@ enum Movement { MOVE_NONE = 0, MOVE_FORWARDS, MOVE_BACKWARDS, TURN_LEFT, TURN_RIGHT }; +enum ChangeMusicAction { + MUSIC_NONE = 0, MUSIC_STOP = 1, MUSIC_START = 2 +}; class CMovementMsg : public CMessage { public: @@ -258,7 +261,7 @@ MESSAGE0(CArmPickedUpFromTableMsg); MESSAGE0(CBodyInBilgeRoomMsg); MESSAGE1(CBowlStateChangeMsg, int, state, 0); MESSAGE2(CCarryObjectArrivedMsg, CString, strValue, "", int, numValue, 0); -MESSAGE2(CChangeMusicMsg, CString, filename, "", int, flags, 0); +MESSAGE2(CChangeMusicMsg, CString, filename, "", ChangeMusicAction, action, MUSIC_NONE); MESSAGE1(CChangeSeasonMsg, CString, season, "Summer"); MESSAGE0(CCheckAllPossibleCodes); MESSAGE2(CCheckChevCode, int, classNum, 0, uint, chevCode, 0); diff --git a/engines/titanic/npcs/bilge_succubus.cpp b/engines/titanic/npcs/bilge_succubus.cpp index 827e39fb4b..49c4190fa5 100644 --- a/engines/titanic/npcs/bilge_succubus.cpp +++ b/engines/titanic/npcs/bilge_succubus.cpp @@ -144,7 +144,7 @@ bool CBilgeSuccUBus::PETDeliverMsg(CPETDeliverMsg *msg) { playMovie(_trayOutStartFrame, _trayOutEndFrame, MOVIE_WAIT_FOR_FINISH); playMovie(_sneezing1StartFrame, _sneezing1EndFrame, MOVIE_NOTIFY_OBJECT | MOVIE_WAIT_FOR_FINISH); playMovie(_sneezing2StartFrame, _sneezing2EndFrame, MOVIE_NOTIFY_OBJECT | MOVIE_WAIT_FOR_FINISH); - incTransitions(); + lockMouse(); } } else { startTalking(this, 230012); @@ -271,7 +271,7 @@ bool CBilgeSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { startTalking(this, 150); CBodyInBilgeRoomMsg bodyMsg; bodyMsg.execute("Service Elevator Entity"); - decTransitions(); + unlockMouse(); _sendAction = SA_SENT; } else { diff --git a/engines/titanic/npcs/succubus.cpp b/engines/titanic/npcs/succubus.cpp index b8fddf2079..8cc2e99878 100644 --- a/engines/titanic/npcs/succubus.cpp +++ b/engines/titanic/npcs/succubus.cpp @@ -426,7 +426,7 @@ bool CSuccUBus::PETDeliverMsg(CPETDeliverMsg *msg) { _sendAction = SA_SENT; _mailPresent = false; _inProgress = true; - incTransitions(); + lockMouse(); if (_isFeathers) { // The feather has special handling to be rejected by the SuccUBus @@ -521,7 +521,7 @@ bool CSuccUBus::PETReceiveMsg(CPETReceiveMsg *msg) { if (_receiveStartFrame >= 0) { _sendAction = SA_FEATHERS; _inProgress = true; - incTransitions(); + lockMouse(); playMovie(_receiveStartFrame, _receiveEndFrame, MOVIE_NOTIFY_OBJECT); } } @@ -625,7 +625,7 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { if (_inProgress) { _inProgress = false; - decTransitions(); + unlockMouse(); } CSUBTransition transMsg; @@ -642,7 +642,7 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { _mailP = nullptr; if (_inProgress) { _inProgress = false; - decTransitions(); + unlockMouse(); } CSUBTransition transMsg; diff --git a/engines/titanic/pet_control/pet_control.h b/engines/titanic/pet_control/pet_control.h index 6408c8a29b..a95b896159 100644 --- a/engines/titanic/pet_control/pet_control.h +++ b/engines/titanic/pet_control/pet_control.h @@ -438,10 +438,10 @@ public: } /** - * Returns true if the Rooms list has a room with the given flags + * Returns true if the player is in the current or previously assigned rooms */ - bool hasRoomFlags() const { - return _rooms.hasRoomFlags(getRoomFlags()); + bool isInAssignedRoom() const { + return _rooms.isAssignedRoom(getRoomFlags()); } uint getRoomFlags() const { diff --git a/engines/titanic/pet_control/pet_rooms.cpp b/engines/titanic/pet_control/pet_rooms.cpp index 45ed209d5c..8fa0f6b4f8 100644 --- a/engines/titanic/pet_control/pet_rooms.cpp +++ b/engines/titanic/pet_control/pet_rooms.cpp @@ -339,7 +339,7 @@ bool CPetRooms::changeLocationClass(PassengerClass newClassNum) { return true; } -bool CPetRooms::hasRoomFlags(uint roomFlags) const { +bool CPetRooms::isAssignedRoom(uint roomFlags) const { for (CPetRoomsGlyphs::const_iterator i = _glyphs.begin(); i != _glyphs.end(); ++i) { const CPetRoomsGlyph *glyph = static_cast<const CPetRoomsGlyph *>(*i); if (glyph->isAssigned() && glyph->getRoomFlags() == roomFlags) diff --git a/engines/titanic/pet_control/pet_rooms.h b/engines/titanic/pet_control/pet_rooms.h index ecfeadf2b9..a893e9a49d 100644 --- a/engines/titanic/pet_control/pet_rooms.h +++ b/engines/titanic/pet_control/pet_rooms.h @@ -164,9 +164,10 @@ public: bool changeLocationClass(PassengerClass newClassNum); /** - * Returns true if a room glyph exists with the given flags + * Returns true if the specified location is the current or + * previously assigned room */ - bool hasRoomFlags(uint roomFlags) const; + bool isAssignedRoom(uint roomFlags) const; /** * Returns the room flags for the player's currently assigned room diff --git a/engines/titanic/pet_control/pet_rooms_glyphs.cpp b/engines/titanic/pet_control/pet_rooms_glyphs.cpp index af73cb803c..02f6860d31 100644 --- a/engines/titanic/pet_control/pet_rooms_glyphs.cpp +++ b/engines/titanic/pet_control/pet_rooms_glyphs.cpp @@ -28,6 +28,7 @@ #include "titanic/support/screen_manager.h" #include "titanic/support/simple_file.h" #include "titanic/titanic.h" +#include "titanic/translation.h" namespace Titanic { @@ -161,12 +162,12 @@ void CPetRoomsGlyph::getTooltip(CTextControl *text) { // Get the room description CString roomStr = roomFlags.getRoomDesc(); - if (roomStr == "The Elevator") { + if (roomStr == TRANSLATE("The Elevator", "Der Aufzug")) { int elevNum = owner->getElevatorNum(); - roomStr = CString::format("Elevator %d", elevNum); + roomStr = CString::format(g_vm->_strings[ELEVATOR_NUM].c_str(), elevNum); } - roomStr += " (shift-click edits)"; + roomStr += g_vm->_strings[SHIFT_CLICK_TO_EDIT]; text->setText(prefix + roomStr); } diff --git a/engines/titanic/room_flags.cpp b/engines/titanic/room_flags.cpp index a28a4bd68c..8ca1a9e07d 100644 --- a/engines/titanic/room_flags.cpp +++ b/engines/titanic/room_flags.cpp @@ -22,6 +22,7 @@ #include "titanic/room_flags.h" #include "titanic/titanic.h" +#include "titanic/support/strings.h" namespace Titanic { @@ -440,6 +441,18 @@ void CRoomFlags::changeClass(PassengerClass newClassNum) { setRoomBits(roomNum); } +CString CRoomFlags::getElevatorDesc() const { + return CString::format(g_vm->_strings[ELEVATOR_NUM].c_str(), getElevatorNum()); +} + +CString CRoomFlags::getFloorDesc() const { + return CString::format(g_vm->_strings[FLOOR_NUM].c_str(), getFloorNum()); +} + +CString CRoomFlags::getRoomNumDesc() const { + return CString::format(g_vm->_strings[ROOM_NUM].c_str(), getRoomNum()); +} + bool CRoomFlags::compareClassElevator(uint flags1, uint flags2) { CRoomFlags f1(flags1); CRoomFlags f2(flags2); diff --git a/engines/titanic/room_flags.h b/engines/titanic/room_flags.h index 3ce86bbddd..95b65330cf 100644 --- a/engines/titanic/room_flags.h +++ b/engines/titanic/room_flags.h @@ -124,13 +124,6 @@ public: uint getElevatorNum() const { return getElevatorBits() + 1; } /** - * Get a description for the elevator number - */ - CString getElevatorDesc() const { - return CString::format("Elevator %d", getElevatorNum()); - } - - /** * Gets the bits for the passenger class */ uint getPassengerClassBits() const; @@ -168,13 +161,6 @@ public: uint getFloorNum() const; /** - * Get a description for the floor number - */ - CString getFloorDesc() const { - return CString::format("Floor %d", getFloorNum()); - } - - /** * Sets the bits for the room number */ void setRoomBits(uint roomBits); @@ -190,11 +176,19 @@ public: uint getRoomNum() const { return getRoomBits(); } /** + * Get a description for the elevator number + */ + CString getElevatorDesc() const; + + /** + * Get a description for the floor number + */ + CString getFloorDesc() const; + + /** * Gets a string for the room number */ - CString getRoomNumDesc() const { - return CString::format("Room %d", getRoomNum()); - } + CString getRoomNumDesc() const; bool getBit0() const; diff --git a/engines/titanic/sound/audio_buffer.h b/engines/titanic/sound/audio_buffer.h index c775c5bb35..6d074a356d 100644 --- a/engines/titanic/sound/audio_buffer.h +++ b/engines/titanic/sound/audio_buffer.h @@ -44,7 +44,7 @@ private: * Leave a critical section */ void leaveCriticalSection(); -public: +private: bool _finished; public: CAudioBuffer(int maxSize); @@ -75,6 +75,11 @@ public: bool full() const { return _data.full(); } /** + * Returns true if the audio buffering is finished + */ + bool isFinished() const { return _finished && empty(); } + + /** * Adds a value to the buffer */ void push(int16 value); @@ -93,6 +98,11 @@ public: * Reads out a specified number of samples */ int read(int16 *values, int count); + + /** + * Marks the buffer as finishing, and that no more new data will arrive + */ + void finalize() { _finished = true; } }; } // End of namespace Titanic diff --git a/engines/titanic/sound/auto_music_player.cpp b/engines/titanic/sound/auto_music_player.cpp index ce20c33765..d77424c97f 100644 --- a/engines/titanic/sound/auto_music_player.cpp +++ b/engines/titanic/sound/auto_music_player.cpp @@ -47,7 +47,7 @@ void CAutoMusicPlayer::load(SimpleFile *file) { } bool CAutoMusicPlayer::EnterRoomMsg(CEnterRoomMsg *msg) { - if (!_isRepeated) { + if (!_isEnabled) { CRoomItem *room = findRoom(); if (msg->_newRoom == room) addTimer(2000); @@ -57,11 +57,11 @@ bool CAutoMusicPlayer::EnterRoomMsg(CEnterRoomMsg *msg) { } bool CAutoMusicPlayer::LeaveRoomMsg(CLeaveRoomMsg *msg) { - if (_isRepeated) { + if (_isEnabled) { CRoomItem *room = findRoom(); if (msg->_oldRoom == room) { CChangeMusicMsg changeMsg; - changeMsg._flags = 1; + changeMsg._action = MUSIC_STOP; changeMsg.execute(this); } } diff --git a/engines/titanic/sound/auto_music_player_base.cpp b/engines/titanic/sound/auto_music_player_base.cpp index 96874768de..a22a113329 100644 --- a/engines/titanic/sound/auto_music_player_base.cpp +++ b/engines/titanic/sound/auto_music_player_base.cpp @@ -21,6 +21,7 @@ */ #include "titanic/sound/auto_music_player_base.h" +#include "titanic/game_manager.h" namespace Titanic { @@ -32,13 +33,13 @@ BEGIN_MESSAGE_MAP(CAutoMusicPlayerBase, CGameObject) END_MESSAGE_MAP() CAutoMusicPlayerBase::CAutoMusicPlayerBase() : CGameObject(), - _initialMute(true), _isRepeated(false), _volumeMode(VOL_NORMAL), _transition(1) { + _initialMute(true), _isEnabled(false), _volumeMode(VOL_NORMAL), _transition(1) { } void CAutoMusicPlayerBase::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeQuotedLine(_filename, indent); file->writeNumberLine(_initialMute, indent); - file->writeNumberLine(_isRepeated, indent); + file->writeNumberLine(_isEnabled, indent); file->writeNumberLine(_volumeMode, indent); file->writeNumberLine(_transition, indent); @@ -49,7 +50,7 @@ void CAutoMusicPlayerBase::load(SimpleFile *file) { file->readNumber(); _filename = file->readString(); _initialMute = file->readNumber(); - _isRepeated = file->readNumber(); + _isEnabled = file->readNumber(); _volumeMode = (VolumeMode)file->readNumber(); _transition = file->readNumber(); @@ -62,39 +63,49 @@ bool CAutoMusicPlayerBase::StatusChangeMsg(CStatusChangeMsg *msg) { bool CAutoMusicPlayerBase::TimerMsg(CTimerMsg *msg) { CChangeMusicMsg musicMsg; - musicMsg._flags = 2; + musicMsg._action = MUSIC_START; musicMsg.execute(this); return true; } bool CAutoMusicPlayerBase::LoadSuccessMsg(CLoadSuccessMsg *msg) { - if (_isRepeated) - playGlobalSound(_filename, _volumeMode, _initialMute, true, 0, + if (_isEnabled) { + // WORKAROUND: A problem was encountered with the EmbLobby music player + // not getting turned off when room was left, so was turned on again + // when loading a savegame elsewhere. This guards against it + CRoomItem *newRoom = getGameManager()->getRoom(); + if (findRoom() != newRoom) { + _isEnabled = false; + return true; + } + + playAmbientSound(_filename, _volumeMode, _initialMute, true, 0, Audio::Mixer::kMusicSoundType); + } return true; } bool CAutoMusicPlayerBase::ChangeMusicMsg(CChangeMusicMsg *msg) { - if (_isRepeated && msg->_flags == 1) { - _isRepeated = false; - stopGlobalSound(_transition, -1); + if (_isEnabled && msg->_action == MUSIC_STOP) { + _isEnabled = false; + stopAmbientSound(_transition, -1); } if (!msg->_filename.empty()) { _filename = msg->_filename; - if (_isRepeated) { - stopGlobalSound(_transition, -1); - playGlobalSound(_filename, _volumeMode, _initialMute, true, 0, + if (_isEnabled) { + stopAmbientSound(_transition, -1); + playAmbientSound(_filename, _volumeMode, _initialMute, true, 0, Audio::Mixer::kMusicSoundType); } } - if (!_isRepeated && msg->_flags == 2) { - _isRepeated = true; - playGlobalSound(_filename, _volumeMode, _initialMute, true, 0, + if (!_isEnabled && msg->_action == MUSIC_START) { + _isEnabled = true; + playAmbientSound(_filename, _volumeMode, _initialMute, true, 0, Audio::Mixer::kMusicSoundType); } diff --git a/engines/titanic/sound/auto_music_player_base.h b/engines/titanic/sound/auto_music_player_base.h index 4dd0665488..d0392951bc 100644 --- a/engines/titanic/sound/auto_music_player_base.h +++ b/engines/titanic/sound/auto_music_player_base.h @@ -36,7 +36,7 @@ class CAutoMusicPlayerBase : public CGameObject { protected: CString _filename; bool _initialMute; - bool _isRepeated; + bool _isEnabled; VolumeMode _volumeMode; int _transition; public: diff --git a/engines/titanic/sound/music_room_handler.cpp b/engines/titanic/sound/music_room_handler.cpp index 5c1cd6012d..2265e46bb2 100644 --- a/engines/titanic/sound/music_room_handler.cpp +++ b/engines/titanic/sound/music_room_handler.cpp @@ -38,7 +38,7 @@ CMusicRoomHandler::CMusicRoomHandler(CProjectItem *project, CSoundManager *sound for (int idx = 0; idx < 4; ++idx) _songs[idx] = new CMusicSong(idx); Common::fill(&_startPos[0], &_startPos[4], 0); - Common::fill(&_animTime[0], &_animTime[4], 0.0); + Common::fill(&_animExpiryTime[0], &_animExpiryTime[4], 0.0); Common::fill(&_position[0], &_position[4], 0); _audioBuffer = new CAudioBuffer(88200); @@ -89,7 +89,7 @@ void CMusicRoomHandler::setup(int volume) { } _position[idx] = _startPos[idx]; - _animTime[idx] = 0.0; + _animExpiryTime[idx] = 0.0; } _instrumentsActive = 4; @@ -180,7 +180,7 @@ void CMusicRoomHandler::start() { } bool CMusicRoomHandler::update() { - uint currentTicks = g_vm->_events->getTicksCount(); + uint currentTicks = g_system->getMillis(); if (!_startTicks) { start(); @@ -195,10 +195,12 @@ bool CMusicRoomHandler::update() { _soundStartTicks = currentTicks; } - updateAudio(); - updateInstruments(); + if (_instrumentsActive > 0) { + updateAudio(); + updateInstruments(); + } - return _instrumentsActive > 0; + return !_audioBuffer->isFinished(); } void CMusicRoomHandler::updateAudio() { @@ -232,9 +234,15 @@ void CMusicRoomHandler::updateAudio() { _audioBuffer->push(audioData, size); delete[] audioData; } + + if (_instrumentsActive == 0) + // Reaching end of music + _audioBuffer->finalize(); } void CMusicRoomHandler::updateInstruments() { + uint currentTicks = g_system->getMillis(); + if (_active && _soundStartTicks) { for (MusicInstrument instrument = BELLS; instrument <= BASS; instrument = (MusicInstrument)((int)instrument + 1)) { @@ -248,12 +256,11 @@ void CMusicRoomHandler::updateInstruments() { continue; } - uint ticks = g_vm->_events->getTicksCount() - _soundStartTicks; - double time = (double)ticks * 0.001 - 0.6; - double threshold = _animTime[instrument] - ins->_animTime; + double time = (double)(currentTicks - _soundStartTicks) / 1000.0 - 0.6; + double threshold = _animExpiryTime[instrument] - ins->_insStartTime; if (time >= threshold) { - _animTime[instrument] += getAnimDuration(instrument, _position[instrument]); + _animExpiryTime[instrument] += getAnimDuration(instrument, _position[instrument]); const CValuePair &vp = (*_songs[instrument])[_position[instrument]]; if (vp._data != 0x7FFFFFFF) { diff --git a/engines/titanic/sound/music_room_handler.h b/engines/titanic/sound/music_room_handler.h index ec117e3098..0eed315614 100644 --- a/engines/titanic/sound/music_room_handler.h +++ b/engines/titanic/sound/music_room_handler.h @@ -55,7 +55,7 @@ private: CMusicSong *_songs[4]; int _startPos[4]; int _position[4]; - double _animTime[4]; + double _animExpiryTime[4]; bool _active; CWaveFile *_waveFile; diff --git a/engines/titanic/sound/music_room_instrument.cpp b/engines/titanic/sound/music_room_instrument.cpp index 15ac2cd192..882325c08a 100644 --- a/engines/titanic/sound/music_room_instrument.cpp +++ b/engines/titanic/sound/music_room_instrument.cpp @@ -52,7 +52,7 @@ void CMusicRoomInstrument::deinit() { CMusicRoomInstrument::CMusicRoomInstrument(CProjectItem *project, CSoundManager *soundManager, MusicWaveInstrument instrument) : _project(project), _soundManager(soundManager), _instrument(instrument) { Common::fill(&_gameObjects[0], &_gameObjects[4], (CGameObject *)nullptr); - _animTime = 0.0; + _insStartTime = 0.0; _waveIndex = -1; _readPos = 0; _readIncrement = 0; @@ -66,7 +66,7 @@ CMusicRoomInstrument::CMusicRoomInstrument(CProjectItem *project, CSoundManager _gameObjects[1] = static_cast<CGameObject *>(_project->findByName("Piano Mouth")); _gameObjects[2] = static_cast<CGameObject *>(_project->findByName("Piano Left Arm")); _gameObjects[3] = static_cast<CGameObject *>(_project->findByName("Piano Right Arm")); - _animTime = 0.45; + _insStartTime = 0.45; break; case MV_BASS: @@ -75,14 +75,14 @@ CMusicRoomInstrument::CMusicRoomInstrument(CProjectItem *project, CSoundManager case MV_BELLS: _gameObjects[0] = static_cast<CGameObject *>(_project->findByName("Tubular Bells")); - _animTime = 0.4; + _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")); _gameObjects[2] = static_cast<CGameObject *>(_project->findByName("Snake_Head")); - _animTime = 0.17; + _insStartTime = 0.17; break; } } @@ -212,12 +212,12 @@ void CMusicRoomInstrument::update(int val) { case 60: _gameObjects[0]->playMovie(0, 512, MOVIE_STOP_PREVIOUS); _gameObjects[0]->movieSetPlaying(true); - _animTime = 0.6; + _insStartTime = 0.6; break; case 62: _gameObjects[0]->playMovie(828, 1023, MOVIE_STOP_PREVIOUS); - _animTime = 0.3; + _insStartTime = 0.3; break; case 63: diff --git a/engines/titanic/sound/music_room_instrument.h b/engines/titanic/sound/music_room_instrument.h index d859fe2a0e..ae9536b370 100644 --- a/engines/titanic/sound/music_room_instrument.h +++ b/engines/titanic/sound/music_room_instrument.h @@ -73,7 +73,7 @@ private: */ void setupArray(int minVal, int maxVal); public: - double _animTime; + double _insStartTime; public: /** * Handles initialization of static fields diff --git a/engines/titanic/sound/node_auto_sound_player.cpp b/engines/titanic/sound/node_auto_sound_player.cpp index 40b3d2ea39..60a93d6b69 100644 --- a/engines/titanic/sound/node_auto_sound_player.cpp +++ b/engines/titanic/sound/node_auto_sound_player.cpp @@ -53,7 +53,7 @@ bool CNodeAutoSoundPlayer::EnterNodeMsg(CEnterNodeMsg *msg) { if (_enabled) { CChangeMusicMsg changeMsg; - changeMsg._flags = 1; + changeMsg._action = MUSIC_STOP; changeMsg.execute(room, CAutoMusicPlayer::_type, MSGFLAG_CLASS_DEF | MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_SCAN); } @@ -72,7 +72,7 @@ bool CNodeAutoSoundPlayer::LeaveNodeMsg(CLeaveNodeMsg *msg) { if (_enabled) { CChangeMusicMsg changeMsg; - changeMsg._flags = 2; + changeMsg._action = MUSIC_START; changeMsg.execute(room, CAutoMusicPlayer::_type, MSGFLAG_CLASS_DEF | MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_SCAN); } diff --git a/engines/titanic/sound/restricted_auto_music_player.cpp b/engines/titanic/sound/restricted_auto_music_player.cpp index 6b01052ebd..a75a55bb15 100644 --- a/engines/titanic/sound/restricted_auto_music_player.cpp +++ b/engines/titanic/sound/restricted_auto_music_player.cpp @@ -58,7 +58,7 @@ bool CRestrictedAutoMusicPlayer::EnterRoomMsg(CEnterRoomMsg *msg) { CString roomName = msg->_oldRoom->getName(); if (!_oldRoomName.compareToIgnoreCase(roomName)) { - _isRepeated = true; + _isEnabled = true; return false; } else { return CAutoMusicPlayer::EnterRoomMsg(msg); @@ -69,7 +69,7 @@ bool CRestrictedAutoMusicPlayer::LeaveRoomMsg(CLeaveRoomMsg *msg) { CString roomName = msg->_newRoom->getName(); if (petCheckNode(_newNodeName) || !_newRoomName.compareToIgnoreCase(roomName)) { - _isRepeated = false; + _isEnabled = false; return true; } else { return CAutoMusicPlayer::LeaveRoomMsg(msg); diff --git a/engines/titanic/sound/seasonal_music_player.cpp b/engines/titanic/sound/seasonal_music_player.cpp index 637a0020ba..92603773ea 100644 --- a/engines/titanic/sound/seasonal_music_player.cpp +++ b/engines/titanic/sound/seasonal_music_player.cpp @@ -90,44 +90,44 @@ bool CSeasonalMusicPlayer::ChangeSeasonMsg(CChangeSeasonMsg *msg) { bool CSeasonalMusicPlayer::ArboretumGateMsg(CArboretumGateMsg *msg) { CChangeMusicMsg changeMsg; - changeMsg._flags = msg->_value ? 2 : 1; + changeMsg._action = msg->_value ? MUSIC_START : MUSIC_STOP; changeMsg.execute(this); return true; } bool CSeasonalMusicPlayer::ChangeMusicMsg(CChangeMusicMsg *msg) { - if (_isRepeated && msg->_flags == 1) { - _isRepeated = false; - stopGlobalSound(_transition, -1); + if (_isEnabled && msg->_action == MUSIC_STOP) { + _isEnabled = false; + stopAmbientSound(_transition, -1); } if (!msg->_filename.empty()) { if (_isSummer) { - setGlobalSoundVolume(VOL_MUTE, 2, 0); - setGlobalSoundVolume(VOL_QUIET, 2, 1); + setAmbientSoundVolume(VOL_MUTE, 2, 0); + setAmbientSoundVolume(VOL_QUIET, 2, 1); } else if (_isAutumn) { - setGlobalSoundVolume(VOL_MUTE, 2, 1); - setGlobalSoundVolume(VOL_QUIET, 2, 2); + setAmbientSoundVolume(VOL_MUTE, 2, 1); + setAmbientSoundVolume(VOL_QUIET, 2, 2); } else if (_isWinter) { - setGlobalSoundVolume(VOL_MUTE, 2, 2); - setGlobalSoundVolume(VOL_QUIET, 2, 3); + setAmbientSoundVolume(VOL_MUTE, 2, 2); + setAmbientSoundVolume(VOL_QUIET, 2, 3); } else if (_isSpring) { - setGlobalSoundVolume(VOL_MUTE, 2, 3); - setGlobalSoundVolume(VOL_QUIET, 2, 0); + setAmbientSoundVolume(VOL_MUTE, 2, 3); + setAmbientSoundVolume(VOL_QUIET, 2, 0); } } - if (!_isRepeated && msg->_flags == 2) { - _isRepeated = true; + if (!_isEnabled && msg->_action == MUSIC_START) { + _isEnabled = true; loadSound(TRANSLATE("c#64.wav", "c#47.wav")); loadSound(TRANSLATE("c#63.wav", "c#46.wav")); loadSound(TRANSLATE("c#65.wav", "c#48.wav")); loadSound(TRANSLATE("c#62.wav", "c#47.wav")); - playGlobalSound(TRANSLATE("c#64.wav", "c#47.wav"), _springMode, _isSpring, true, 0); - playGlobalSound(TRANSLATE("c#63.wav", "c#46.wav"), _summerMode, _isSummer, true, 1); - playGlobalSound(TRANSLATE("c#65.wav", "c#48.wav"), _autumnMode, _isAutumn, true, 2); - playGlobalSound(TRANSLATE("c#62.wav", "c#47.wav"), _winterMode, _isWinter, true, 3); + playAmbientSound(TRANSLATE("c#64.wav", "c#47.wav"), _springMode, _isSpring, true, 0); + playAmbientSound(TRANSLATE("c#63.wav", "c#46.wav"), _summerMode, _isSummer, true, 1); + playAmbientSound(TRANSLATE("c#65.wav", "c#48.wav"), _autumnMode, _isAutumn, true, 2); + playAmbientSound(TRANSLATE("c#62.wav", "c#47.wav"), _winterMode, _isWinter, true, 3); } return true; diff --git a/engines/titanic/sound/titania_speech.cpp b/engines/titanic/sound/titania_speech.cpp index 30446fd992..b227c396fc 100644 --- a/engines/titanic/sound/titania_speech.cpp +++ b/engines/titanic/sound/titania_speech.cpp @@ -60,9 +60,14 @@ bool CTitaniaSpeech::ActMsg(CActMsg *msg) { case 1: loadSound(TRANSLATE("a#12.wav", "a#0.wav")); sleep(1000); - playMovie(0, 187, MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); - movieSetPlaying(true); - movieEvent(0); + playMovie(TRANSLATE(0, 584), TRANSLATE(187, 761), + MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); + if (g_language == Common::EN_ANY) { + movieSetPlaying(true); + movieEvent(0); + } else { + playSound("a#0.wav", prox); + } break; case 2: @@ -73,14 +78,14 @@ bool CTitaniaSpeech::ActMsg(CActMsg *msg) { addTimer(12000); addTimer(18000); addTimer(24000); - startAnimTimer("NextPara", 30000); + startAnimTimer("NextPara", TRANSLATE(30000, 33000)); break; case 3: visibleMsg._visible = false; visibleMsg.execute("TitaniaStillControl"); loadSound(TRANSLATE("a#10.wav", "a#2.wav")); - playMovie(585, 706, MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); + playMovie(585, TRANSLATE(706, 748), MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); playSound(TRANSLATE("a#10.wav", "a#2.wav"), prox); break; @@ -96,7 +101,7 @@ bool CTitaniaSpeech::ActMsg(CActMsg *msg) { visibleMsg._visible = false; visibleMsg.execute("TitaniaStillControl"); loadSound(TRANSLATE("a#8.wav", "a#1.wav")); - playMovie(906, 938, MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); + playMovie(906, TRANSLATE(938, 943), MOVIE_WAIT_FOR_FINISH | MOVIE_NOTIFY_OBJECT); playSound(TRANSLATE("a#8.wav", "a#1.wav"), prox); break; diff --git a/engines/titanic/sound/trigger_auto_music_player.cpp b/engines/titanic/sound/trigger_auto_music_player.cpp index a332570aba..15f1c9f850 100644 --- a/engines/titanic/sound/trigger_auto_music_player.cpp +++ b/engines/titanic/sound/trigger_auto_music_player.cpp @@ -47,11 +47,11 @@ bool CTriggerAutoMusicPlayer::TriggerAutoMusicPlayerMsg(CTriggerAutoMusicPlayerM CRoomItem *room2 = msg->_value == 2 ? locateRoom(_roomName) : findRoom(); CChangeMusicMsg changeMsg; - changeMsg._flags = 1; + changeMsg._action = MUSIC_STOP; changeMsg.execute(room1, CAutoMusicPlayer::_type, MSGFLAG_CLASS_DEF | MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_SCAN); - changeMsg._flags = 2; + changeMsg._action = MUSIC_START; changeMsg.execute(room2, CAutoMusicPlayer::_type, MSGFLAG_CLASS_DEF | MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_SCAN); diff --git a/engines/titanic/sound/view_auto_sound_player.cpp b/engines/titanic/sound/view_auto_sound_player.cpp index 55501fe340..3937a82ad7 100644 --- a/engines/titanic/sound/view_auto_sound_player.cpp +++ b/engines/titanic/sound/view_auto_sound_player.cpp @@ -53,7 +53,7 @@ bool CViewAutoSoundPlayer::EnterViewMsg(CEnterViewMsg *msg) { if (_enabled) { CChangeMusicMsg changeMsg; - changeMsg._flags = 1; + changeMsg._action = MUSIC_STOP; changeMsg.execute(room, CAutoMusicPlayer::_type, MSGFLAG_CLASS_DEF |MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_SCAN); } @@ -72,7 +72,7 @@ bool CViewAutoSoundPlayer::LeaveViewMsg(CLeaveViewMsg *msg) { if (_enabled) { CChangeMusicMsg changeMsg; - changeMsg._flags = 2; + changeMsg._action = MUSIC_START; changeMsg.execute(room, CAutoMusicPlayer::_type, MSGFLAG_CLASS_DEF | MSGFLAG_BREAK_IF_HANDLED | MSGFLAG_SCAN); } diff --git a/engines/titanic/sound/wave_file.cpp b/engines/titanic/sound/wave_file.cpp index ae633a2149..c2a66cfdcb 100644 --- a/engines/titanic/sound/wave_file.cpp +++ b/engines/titanic/sound/wave_file.cpp @@ -52,7 +52,7 @@ int AudioBufferStream::readBuffer(int16 *buffer, const int numSamples) { } bool AudioBufferStream::endOfData() const { - return _audioBuffer->_finished; + return _audioBuffer->isFinished(); } /*------------------------------------------------------------------------*/ diff --git a/engines/titanic/support/avi_surface.cpp b/engines/titanic/support/avi_surface.cpp index 4fad6542fa..3b22a4fee2 100644 --- a/engines/titanic/support/avi_surface.cpp +++ b/engines/titanic/support/avi_surface.cpp @@ -490,6 +490,11 @@ 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") + _currentFrame = -1; + if (_currentFrame != ((int)startFrame - 1) || startFrame == 0) { // Start video playback at the desired starting frame if (startFrame > 0) { diff --git a/engines/titanic/support/direct_draw.cpp b/engines/titanic/support/direct_draw.cpp index 9559480a3b..4fd34f9791 100644 --- a/engines/titanic/support/direct_draw.cpp +++ b/engines/titanic/support/direct_draw.cpp @@ -40,7 +40,7 @@ void DirectDraw::setDisplayMode(int width, int height, int bpp, int refreshRate) assert(bpp == 16); Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); - initGraphics(width, height, true, &pixelFormat); + initGraphics(width, height, &pixelFormat); } void DirectDraw::diagnostics() { diff --git a/engines/titanic/support/files_manager.cpp b/engines/titanic/support/files_manager.cpp index fbfb7e0f61..9a07b68e35 100644 --- a/engines/titanic/support/files_manager.cpp +++ b/engines/titanic/support/files_manager.cpp @@ -50,7 +50,7 @@ bool CFilesManager::loadResourceIndex() { return false; } - if (_version != 3) { + if (_version != 5) { g_vm->GUIError("titanic.dat is out of date"); return false; } diff --git a/engines/titanic/support/mouse_cursor.cpp b/engines/titanic/support/mouse_cursor.cpp index 1c5e0da487..21b425834e 100644 --- a/engines/titanic/support/mouse_cursor.cpp +++ b/engines/titanic/support/mouse_cursor.cpp @@ -54,8 +54,7 @@ static const int CURSOR_DATA[NUM_CURSORS][4] = { }; CMouseCursor::CursorEntry::~CursorEntry() { - delete _videoSurface; - delete _transSurface; + delete _surface; } CMouseCursor::CMouseCursor(CScreenManager *screenManager) : @@ -80,15 +79,33 @@ void CMouseCursor::loadCursorImages() { // Create the surface CVideoSurface *surface = _screenManager->createSurface(CURSOR_SIZE, CURSOR_SIZE); - _cursors[idx]._videoSurface = surface; // Open the cursors video and move to the given frame - OSMovie movie(key, surface); - movie.setFrame(idx); + OSMovie *movie = new OSMovie(key, surface); + movie->setFrame(idx); + Graphics::ManagedSurface *transSurface = movie->duplicateTransparency(); - Graphics::ManagedSurface *transSurface = movie.duplicateTransparency(); - _cursors[idx]._transSurface = transSurface; - surface->setTransparencySurface(transSurface); + // Create a managed surface to hold the RGBA version of the cursor + Graphics::PixelFormat rgbaFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + _cursors[idx]._surface = new Graphics::ManagedSurface(CURSOR_SIZE, CURSOR_SIZE, rgbaFormat); + + // Copy the cursor from the movie's video surface + surface->lock(); + _cursors[idx]._surface->blitFrom(*surface->getRawSurface()); + surface->unlock(); + + // We need to separately merge in the transparency surface + for (int y = 0; y < CURSOR_SIZE; ++y) { + const byte *srcP = (const byte *)transSurface->getBasePtr(0, y); + uint32 *destP = (uint32 *)_cursors[idx]._surface->getBasePtr(0, y); + + for (int x = 0; x < CURSOR_SIZE; ++x, ++srcP, ++destP) + *destP = (*destP & ~0xff) | *srcP; + } + + delete movie; + delete transSurface; + delete surface; } } @@ -131,35 +148,12 @@ void CMouseCursor::setCursor(CursorId cursorId) { ++_setCursorCount; if (cursorId != _cursorId && _busyCount == 0) { - // The original cursors supported partial alpha when rendering the cursor. - // Since we're using the ScummVM CursorMan, we can't do that, so we need - // to build up a surface of the cursor with even partially transparent - // pixels as wholy transparent - CursorEntry &ce = _cursors[cursorId - 1]; - CVideoSurface &srcSurface = *ce._videoSurface; - srcSurface.lock(); - - Graphics::ManagedSurface surface(CURSOR_SIZE, CURSOR_SIZE, g_system->getScreenFormat()); - const uint16 *srcP = srcSurface.getPixels(); - CTransparencySurface transSurface(&ce._transSurface->rawSurface(), TRANS_ALPHA0); - uint16 *destP = (uint16 *)surface.getPixels(); - - for (int y = 0; y < CURSOR_SIZE; ++y) { - transSurface.setRow(y); - transSurface.setCol(0); - - for (int x = 0; x < CURSOR_SIZE; ++x, ++srcP, ++destP) { - *destP = transSurface.isPixelTransparent() ? srcSurface.getTransparencyColor() : *srcP; - transSurface.moveX(); - } - } - - srcSurface.unlock(); + const CursorEntry &ce = _cursors[cursorId - 1]; + _cursorId = cursorId; // Set the cursor - _cursorId = cursorId; - CursorMan.replaceCursor(surface.getPixels(), CURSOR_SIZE, CURSOR_SIZE, - ce._centroid.x, ce._centroid.y, srcSurface.getTransparencyColor(), false, &g_vm->_screen->format); + CursorMan.replaceCursor(ce._surface->getPixels(), CURSOR_SIZE, CURSOR_SIZE, + ce._centroid.x, ce._centroid.y, 0, false, &ce._surface->format); } } diff --git a/engines/titanic/support/mouse_cursor.h b/engines/titanic/support/mouse_cursor.h index aff3bacbc1..1b90a6c6f8 100644 --- a/engines/titanic/support/mouse_cursor.h +++ b/engines/titanic/support/mouse_cursor.h @@ -54,11 +54,10 @@ class CVideoSurface; class CMouseCursor { struct CursorEntry { - CVideoSurface *_videoSurface; - Graphics::ManagedSurface *_transSurface; + Graphics::ManagedSurface *_surface; Common::Point _centroid; - CursorEntry() : _videoSurface(nullptr), _transSurface(nullptr) {} + CursorEntry() : _surface(nullptr) {} ~CursorEntry(); }; private: @@ -72,7 +71,7 @@ private: int _fieldE8; Common::Point _moveStartPos; Common::Point _moveDestPos; - uint _moveStartTime, _moveEndTime; + uint32 _moveStartTime, _moveEndTime; /** * Load the images for each cursor diff --git a/engines/titanic/support/strings.h b/engines/titanic/support/strings.h index b7a775cc99..0fa807d85f 100644 --- a/engines/titanic/support/strings.h +++ b/engines/titanic/support/strings.h @@ -168,6 +168,10 @@ enum StringId { PREVIOUSLY_ASSIGNED_ROOM, SAVED_CHEVRON, CURRENT_LOCATION, + ELEVATOR_NUM, + FLOOR_NUM, + ROOM_NUM, + SHIFT_CLICK_TO_EDIT, A_HOT, A_COLD, LOAD_THE_GAME, diff --git a/engines/titanic/true_talk/barbot_script.cpp b/engines/titanic/true_talk/barbot_script.cpp index 430330a35a..241976f9cf 100644 --- a/engines/titanic/true_talk/barbot_script.cpp +++ b/engines/titanic/true_talk/barbot_script.cpp @@ -872,8 +872,8 @@ ScriptChangedResult BarbotScript::scriptChanged(const TTroomScript *roomScript, } int BarbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { - switch (tagId) { + uint tag1, uint tag2, uint remainder) { + switch (tag2) { case MKTAG('A', 'D', 'V', 'T'): case MKTAG('A', 'R', 'T', 'I'): case MKTAG('A', 'R', 'T', 'Y'): @@ -894,7 +894,7 @@ int BarbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence * case MKTAG('T', 'E', 'A', 'M'): case MKTAG('T', 'U', 'S', 'H'): case MKTAG('W', 'W', 'E', 'B'): - tagId = MKTAG('E', 'N', 'T', 'N'); + tag2 = MKTAG('E', 'N', 'T', 'N'); break; case MKTAG('A', 'U', 'T', 'H'): case MKTAG('B', 'A', 'R', 'K'): @@ -923,61 +923,61 @@ int BarbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence * case MKTAG('T', 'D', 'V', 'P'): case MKTAG('T', 'W', 'A', 'T'): case MKTAG('W', 'E', 'A', 'T'): - tagId = MKTAG('P', 'R', 'S', 'N'); + tag2 = MKTAG('P', 'R', 'S', 'N'); break; case MKTAG('C', 'H', 'S', 'E'): case MKTAG('C', 'M', 'N', 'T'): case MKTAG('F', 'I', 'L', 'M'): case MKTAG('J', 'F', 'O', 'D'): case MKTAG('L', 'I', 'Q', 'D'): - tagId = MKTAG('F', 'O', 'O', 'D'); + tag2 = MKTAG('F', 'O', 'O', 'D'); break; case MKTAG('C', 'R', 'M', 'N'): case MKTAG('C', 'S', 'P', 'Y'): case MKTAG('U', 'B', 'A', 'D'): - tagId = MKTAG('V', 'B', 'A', 'D'); + tag2 = MKTAG('V', 'B', 'A', 'D'); break; case MKTAG('E', 'A', 'R', 'T'): case MKTAG('H', 'O', 'M', 'E'): case MKTAG('N', 'P', 'L', 'C'): case MKTAG('P', 'L', 'A', 'C'): case MKTAG('P', 'L', 'A', 'N'): - tagId = MKTAG('P', 'L', 'A', 'C'); + tag2 = MKTAG('P', 'L', 'A', 'C'); break; case MKTAG('F', 'A', 'U', 'N'): case MKTAG('F', 'I', 'S', 'H'): case MKTAG('F', 'L', 'O', 'R'): - tagId = MKTAG('N', 'A', 'T', 'R'); + tag2 = MKTAG('N', 'A', 'T', 'R'); break; case MKTAG('H', 'H', 'L', 'D'): case MKTAG('T', 'O', 'Y', 'S'): case MKTAG('W', 'E', 'A', 'P'): - tagId = MKTAG('M', 'A', 'C', 'H'); + tag2 = MKTAG('M', 'A', 'C', 'H'); break; case MKTAG('M', 'L', 'T', 'Y'): case MKTAG('P', 'G', 'R', 'P'): case MKTAG('P', 'T', 'I', 'C'): - tagId = MKTAG('G', 'R', 'U', 'P'); + tag2 = MKTAG('G', 'R', 'U', 'P'); break; case MKTAG('P', 'K', 'U', 'P'): case MKTAG('S', 'E', 'X', '1'): case MKTAG('S', 'W', 'E', 'R'): - tagId = MKTAG('R', 'U', 'D', 'E'); + tag2 = MKTAG('R', 'U', 'D', 'E'); break; case MKTAG('P', 'H', 'I', 'L'): case MKTAG('R', 'C', 'K', 'T'): case MKTAG('S', 'C', 'I', 'E'): - tagId = MKTAG('S', 'C', 'I', 'E'); + tag2 = MKTAG('S', 'C', 'I', 'E'); break; case MKTAG('T', 'R', 'A', '2'): case MKTAG('T', 'R', 'A', '3'): - tagId = MKTAG('T', 'R', 'A', 'V'); + tag2 = MKTAG('T', 'R', 'A', 'V'); break; default: break; } - if (val == 36) { + if (tag1 == 36) { switch (getValue(1)) { case 1: return setResponse(getDialogueId(220837), -1); @@ -987,11 +987,11 @@ int BarbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence * default: return setResponse(getDialogueId(220858), -1); } - } else if (val == 61 && getValue(1) > 2) { + } else if (tag1 == 61 && getValue(1) > 2) { return setResponse(getDialogueId(222301), -1); } - return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); + return TTnpcScript::handleQuote(roomScript, sentence, tag1, tag2, remainder); } int BarbotScript::updateState(uint oldId, uint newId, int index) { @@ -1049,124 +1049,136 @@ uint BarbotScript::getDialsBitset() const { int BarbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScript *roomScript, const TTsentence *sentence) { uint id = 0; - if (val1 > 0x200) { - switch (val1 - 0x201) { - case 0: - if (getValue(4) != 2) - id = 250738; - break; - case 1: - if (getValue(4) != 3) - id = 250738; - break; - case 2: - if (getValue(4) != 0) - id = 250738; - break; - default: - break; - } - } else if (val1 == 0x200) { - if (getValue(4) != 1) - id = 250738; - } else { - switch (val1) { - case 2: - if (getValue(1) != 1) - return 1; - break; - case 3: - if (getValue(1) != 2) - return 1; - break; - case 4: - if (getValue(1) != 3) - return 1; - break; - case 5: - if (getValue(1) == 3) - return 1; - break; - case 6: - if (sentence->contains("do not") || sentence->contains("have no") || - sentence->contains("got no")) - return 1; - break; - case 7: - if (!sentence->contains("do not") && !sentence->contains("have no") && - !sentence->contains("got no")) - return 1; - break; - case 8: - if (sentence->_field38 == 2) - return 1; - break; - case 9: { - uint val = CTrueTalkManager::getStateValue(3); - bool lemonFlag = (val & 1) != 0; - bool puretFlag = (val & 4) != 0; - bool tvFlag = (val & 8) != 0; - - if (puretFlag) { - if (!lemonFlag) { - id = tvFlag ? 50369 : 250085; - break; - } else if (!tvFlag) { - id = 250627; - } - } else { - if (lemonFlag) { - id = tvFlag ? 50367 : 50365; - } else if (tvFlag) { - id = 50370; - } - } - break; - } + int index = val1; + if (g_language == Common::DE_DEU && !(val1 >= 512 && val1 <= 515)) + index -= 1000; - case 10: { - uint val = CTrueTalkManager::getStateValue(3); - bool lemonFlag = (val & 1) != 0; - bool puretFlag = (val & 4) != 0; - bool tvFlag = (val & 8) != 0; + switch (index) { + case 2: + if (getValue(1) != 1) + return 1; + break; - if (lemonFlag && puretFlag && tvFlag) { - addResponse(getDialogueId(251027)); - applyResponse(); - CTrueTalkManager::triggerAction(7, 0); - return 2; - } else { - if (getDialRegion(1) == 1) { - if (*srcIdP != 251650) - id = 251651; - } else { - addResponse(getDialRegion(0) != 0 ? 51444 : 51530); - applyResponse(); - return 2; - } + case 3: + if (getValue(1) != 2) + return 1; + break; + + case 4: + if (getValue(1) != 3) + return 1; + break; + + case 5: + if (getValue(1) == 3) + return 1; + break; + + case 6: + if (sentence->contains("do not") || sentence->contains("have no") || + sentence->contains("got no")) + return 1; + break; + + case 7: + if (!sentence->contains(TRANSLATE("do not", "idem")) && !sentence->contains(TRANSLATE("have no", "habe kein")) && + !sentence->contains("got no")) + return 1; + break; + + case 8: + if (sentence->_field38 == 2) + return 1; + break; + + case 9: { + uint val = CTrueTalkManager::getStateValue(3); + bool lemonFlag = (val & 1) != 0; + bool puretFlag = (val & 4) != 0; + bool tvFlag = (val & 8) != 0; + + if (puretFlag) { + if (!lemonFlag) { + id = tvFlag ? 50369 : 250085; + break; + } else if (!tvFlag) { + id = 250627; + } + } else { + if (lemonFlag) { + id = tvFlag ? 50367 : 50365; + } else if (tvFlag) { + id = 50370; } - break; } + break; + } - case 11: - if (CTrueTalkManager::getStateValue(2) != 0) { - CTrueTalkManager::triggerAction(6, 0); - id = 251003; - } - break; + case 10: { + uint val = CTrueTalkManager::getStateValue(3); + bool lemonFlag = (val & 1) != 0; + bool puretFlag = (val & 4) != 0; + bool tvFlag = (val & 8) != 0; - case 12: - if (getDialRegion(1) == 0) { - addResponse(getDialogueId(251871)); + if (lemonFlag && puretFlag && tvFlag) { + addResponse(getDialogueId(251027)); + applyResponse(); + CTrueTalkManager::triggerAction(7, 0); + return 2; + } else { + if (getDialRegion(1) == 1) { + if (*srcIdP != 251650) + id = 251651; + } else { + addResponse(getDialRegion(0) != 0 ? 51444 : 51530); applyResponse(); return 2; - } else if (getRandomNumber(100) > 25 && addRandomResponse(false)) { - return 2; } + } + break; + } - default: - break; + case 11: + if (CTrueTalkManager::getStateValue(2) != 0) { + CTrueTalkManager::triggerAction(6, 0); + id = 251003; } + break; + + case 12: + if (getDialRegion(1) == 0) { + addResponse(getDialogueId(251871)); + applyResponse(); + return 2; + } else if (getRandomNumber(100) > 25 && addRandomResponse(false)) { + return 2; + } + break; + + case 512: + if (getValue(4) != 1) + id = 250738; + break; + + case 513: + if (getValue(4) != 2) + id = 250738; + break; + + case 514: + if (getValue(4) != 3) + id = 250738; + break; + + case 515: + if (getValue(4) != 0) + id = 250738; + break; + + default: + if (g_language == Common::DE_DEU) + return TTnpcScript::doSentenceEntry(val1, srcIdP, roomScript, sentence); + break; } if (id) { diff --git a/engines/titanic/true_talk/barbot_script.h b/engines/titanic/true_talk/barbot_script.h index a8c92b2927..1aea21f744 100644 --- a/engines/titanic/true_talk/barbot_script.h +++ b/engines/titanic/true_talk/barbot_script.h @@ -73,7 +73,12 @@ public: virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); + + /** + * Returns true if the NPC's dial region affects quote responses + */ + virtual bool isQuoteDialled() const { return true; } /** * Handles updating NPC state based on specified dialogue Ids and dial positions diff --git a/engines/titanic/true_talk/bellbot_script.cpp b/engines/titanic/true_talk/bellbot_script.cpp index 84d5c59713..0256b97ef1 100644 --- a/engines/titanic/true_talk/bellbot_script.cpp +++ b/engines/titanic/true_talk/bellbot_script.cpp @@ -483,8 +483,8 @@ ScriptChangedResult BellbotScript::scriptChanged(const TTroomScript *roomScript, } int BellbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { - switch (tagId) { + uint tag1, uint tag2, uint remainder) { + switch (tag2) { case MKTAG('A', 'D', 'V', 'T'): case MKTAG('A', 'R', 'T', 'I'): case MKTAG('A', 'R', 'T', 'Y'): @@ -505,7 +505,7 @@ int BellbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'E', 'A', 'M'): case MKTAG('T', 'V', 'S', 'H'): case MKTAG('W', 'W', 'E', 'B'): - tagId = MKTAG('E', 'N', 'T', 'N'); + tag2 = MKTAG('E', 'N', 'T', 'N'); break; case MKTAG('A', 'C', 'T', 'R'): case MKTAG('A', 'C', 'T', 'S'): @@ -538,59 +538,59 @@ int BellbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'O', 'U', 'P'): case MKTAG('T', 'W', 'A', 'T'): case MKTAG('W', 'E', 'A', 'T'): - tagId = MKTAG('P', 'R', 'S', 'N'); + tag2 = MKTAG('P', 'R', 'S', 'N'); break; case MKTAG('C', 'H', 'S', 'E'): case MKTAG('C', 'M', 'N', 't'): case MKTAG('F', 'I', 'L', 'M'): case MKTAG('J', 'F', 'O', 'D'): case MKTAG('L', 'I', 'Q', 'D'): - tagId = MKTAG('F', 'O', 'O', 'D'); + tag2 = MKTAG('F', 'O', 'O', 'D'); break; case MKTAG('C', 'R', 'I', 'M'): case MKTAG('C', 'S', 'P', 'Y'): case MKTAG('D', 'R', 'U', 'G'): - tagId = MKTAG('V', 'B', 'A', 'D'); + tag2 = MKTAG('V', 'B', 'A', 'D'); break; case MKTAG('E', 'A', 'R', 'T'): case MKTAG('H', 'O', 'M', 'E'): case MKTAG('N', 'P', 'L', 'C'): case MKTAG('P', 'L', 'A', 'N'): - tagId = MKTAG('P', 'L', 'A', 'C'); + tag2 = MKTAG('P', 'L', 'A', 'C'); break; case MKTAG('F', 'A', 'U', 'N'): case MKTAG('F', 'I', 'S', 'H'): case MKTAG('F', 'L', 'O', 'R'): - tagId = MKTAG('N', 'A', 'T', 'R'); + tag2 = MKTAG('N', 'A', 'T', 'R'); break; case MKTAG('H', 'H', 'L', 'D'): case MKTAG('T', 'O', 'Y', 'S'): case MKTAG('W', 'E', 'A', 'P'): - tagId = MKTAG('M', 'A', 'C', 'H'); + tag2 = MKTAG('M', 'A', 'C', 'H'); break; case MKTAG('M', 'L', 'T', 'Y'): case MKTAG('P', 'G', 'R', 'P'): case MKTAG('P', 'T', 'I', 'C'): - tagId = MKTAG('G', 'R', 'U', 'P'); + tag2 = MKTAG('G', 'R', 'U', 'P'); break; case MKTAG('P', 'K', 'U', 'P'): case MKTAG('S', 'E', 'X', '1'): case MKTAG('S', 'W', 'E', 'R'): - tagId = MKTAG('R', 'U', 'D', 'E'); + tag2 = MKTAG('R', 'U', 'D', 'E'); break; case MKTAG('P', 'H', 'I', 'L'): case MKTAG('R', 'C', 'K', 'T'): - tagId = MKTAG('S', 'C', 'I', 'E'); + tag2 = MKTAG('S', 'C', 'I', 'E'); break; case MKTAG('T', 'R', 'A', '2'): case MKTAG('T', 'R', 'A', '3'): - tagId = MKTAG('T', 'R', 'A', 'V'); + tag2 = MKTAG('T', 'R', 'A', 'V'); break; default: break; } - return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); + return TTnpcScript::handleQuote(roomScript, sentence, tag1, tag2, remainder); } int BellbotScript::updateState(uint oldId, uint newId, int index) { @@ -847,13 +847,11 @@ int BellbotScript::doSentenceEntry(int val1, const int *srcIdP, const TTroomScri if (flag) { CTrueTalkManager::triggerAction(29, 1); selectResponse(201771); - } - else { + } else { CTrueTalkManager::triggerAction(29, 2); selectResponse(201554); } - } - else { + } else { selectResponse(21378); } diff --git a/engines/titanic/true_talk/bellbot_script.h b/engines/titanic/true_talk/bellbot_script.h index 7edb182301..17f22ac241 100644 --- a/engines/titanic/true_talk/bellbot_script.h +++ b/engines/titanic/true_talk/bellbot_script.h @@ -98,7 +98,7 @@ public: virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); /** * Handles updating NPC state based on specified dialogue Ids and dial positions diff --git a/engines/titanic/true_talk/deskbot_script.cpp b/engines/titanic/true_talk/deskbot_script.cpp index 123dc3c8a7..3988002f07 100644 --- a/engines/titanic/true_talk/deskbot_script.cpp +++ b/engines/titanic/true_talk/deskbot_script.cpp @@ -181,8 +181,8 @@ ScriptChangedResult DeskbotScript::scriptChanged(const TTroomScript *roomScript, } int DeskbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { - switch (tagId) { + uint tag1, uint tag2, uint remainder) { + switch (tag2) { case MKTAG('A', 'D', 'V', 'T'): case MKTAG('A', 'R', 'T', 'I'): case MKTAG('A', 'R', 'T', 'Y'): @@ -202,7 +202,7 @@ int DeskbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('S', 'P', 'R', 'T'): case MKTAG('T', 'E', 'A', 'M'): case MKTAG('T', 'V', 'S', 'H'): - tagId = MKTAG('E', 'N', 'T', 'N'); + tag2 = MKTAG('E', 'N', 'T', 'N'); break; case MKTAG('A', 'C', 'T', 'R'): case MKTAG('A', 'C', 'T', 'S'): @@ -238,60 +238,59 @@ int DeskbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'W', 'A', 'T'): case MKTAG('W', 'E', 'A', 'T'): case MKTAG('W', 'W', 'E', 'B'): - tagId = MKTAG('P', 'R', 'S', 'N'); + tag2 = MKTAG('P', 'R', 'S', 'N'); break; case MKTAG('C', 'H', 'S', 'E'): case MKTAG('C', 'M', 'N', 'T'): case MKTAG('F', 'I', 'L', 'M'): case MKTAG('J', 'F', 'O', 'D'): case MKTAG('L', 'I', 'Q', 'D'): - tagId = MKTAG('F', 'O', 'O', 'D'); + tag2 = MKTAG('F', 'O', 'O', 'D'); break; case MKTAG('C', 'R', 'I', 'M'): case MKTAG('C', 'S', 'P', 'Y'): case MKTAG('D', 'R', 'U', 'G'): - tagId = MKTAG('V', 'B', 'A', 'D'); + tag2 = MKTAG('V', 'B', 'A', 'D'); break; case MKTAG('E', 'A', 'R', 'T'): case MKTAG('H', 'O', 'M', 'E'): case MKTAG('N', 'P', 'L', 'C'): case MKTAG('P', 'L', 'A', 'N'): - tagId = MKTAG('P', 'L', 'A', 'C'); + tag2 = MKTAG('P', 'L', 'A', 'C'); break; case MKTAG('F', 'A', 'U', 'N'): case MKTAG('F', 'I', 'S', 'H'): case MKTAG('F', 'L', 'O', 'R'): - tagId = MKTAG('N', 'A', 'T', 'R'); + tag2 = MKTAG('N', 'A', 'T', 'R'); break; case MKTAG('H', 'H', 'L', 'D'): case MKTAG('T', 'O', 'Y', 'S'): case MKTAG('W', 'E', 'A', 'P'): - tagId = MKTAG('M', 'A', 'C', 'H'); + tag2 = MKTAG('M', 'A', 'C', 'H'); break; case MKTAG('M', 'L', 'T', 'Y'): case MKTAG('P', 'G', 'R', 'P'): case MKTAG('P', 'T', 'I', 'C'): - tagId = MKTAG('G', 'R', 'U', 'P'); + tag2 = MKTAG('G', 'R', 'U', 'P'); break; case MKTAG('P', 'K', 'U', 'P'): case MKTAG('S', 'E', 'X', '1'): case MKTAG('S', 'W', 'E', 'R'): - tagId = MKTAG('R', 'U', 'D', 'E'); + tag2 = MKTAG('R', 'U', 'D', 'E'); break; case MKTAG('P', 'H', 'I', 'L'): case MKTAG('R', 'C', 'K', 'T'): - tagId = MKTAG('S', 'C', 'I', 'E'); + tag2 = MKTAG('S', 'C', 'I', 'E'); break; case MKTAG('T', 'R', 'A', '2'): case MKTAG('T', 'R', 'A', '3'): - tagId = MKTAG('T', 'R', 'A', 'V'); + tag2 = MKTAG('T', 'R', 'A', 'V'); break; default: break; } - return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); - + return TTnpcScript::handleQuote(roomScript, sentence, tag1, tag2, remainder); } int DeskbotScript::updateState(uint oldId, uint newId, int index) { diff --git a/engines/titanic/true_talk/deskbot_script.h b/engines/titanic/true_talk/deskbot_script.h index 941136d45e..ff07809b08 100644 --- a/engines/titanic/true_talk/deskbot_script.h +++ b/engines/titanic/true_talk/deskbot_script.h @@ -104,7 +104,7 @@ public: virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); /** * Handles updating NPC state based on specified dialogue Ids and dial positions diff --git a/engines/titanic/true_talk/doorbot_script.cpp b/engines/titanic/true_talk/doorbot_script.cpp index 0e07f2ade8..aa0b533727 100644 --- a/engines/titanic/true_talk/doorbot_script.cpp +++ b/engines/titanic/true_talk/doorbot_script.cpp @@ -609,8 +609,8 @@ ScriptChangedResult DoorbotScript::scriptChanged(const TTroomScript *roomScript, } int DoorbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { - switch (tagId) { + uint tag1, uint tag2, uint remainder) { + switch (tag2) { case MKTAG('A', 'D', 'V', 'T'): case MKTAG('A', 'R', 'T', 'I'): case MKTAG('A', 'R', 'T', 'Y'): @@ -631,7 +631,7 @@ int DoorbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'E', 'A', 'M'): case MKTAG('T', 'V', 'S', 'H'): case MKTAG('W', 'W', 'E', 'B'): - tagId = MKTAG('E', 'N', 'T', 'N'); + tag2 = MKTAG('E', 'N', 'T', 'N'); break; case MKTAG('A', 'C', 'T', 'R'): case MKTAG('A', 'C', 'T', 'S'): @@ -663,59 +663,59 @@ int DoorbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'D', 'V', 'P'): case MKTAG('T', 'W', 'A', 'T'): case MKTAG('W', 'E', 'A', 'T'): - tagId = MKTAG('P', 'R', 'S', 'N'); + tag2 = MKTAG('P', 'R', 'S', 'N'); break; case MKTAG('C', 'H', 'S', 'E'): case MKTAG('C', 'M', 'N', 'T'): case MKTAG('F', 'I', 'L', 'M'): case MKTAG('J', 'F', 'O', 'D'): case MKTAG('L', 'I', 'Q', 'D'): - tagId = MKTAG('F', 'O', 'O', 'D'); + tag2 = MKTAG('F', 'O', 'O', 'D'); break; case MKTAG('C', 'R', 'I', 'M'): case MKTAG('C', 'S', 'P', 'Y'): case MKTAG('D', 'R', 'U', 'G'): - tagId = MKTAG('V', 'B', 'A', 'D'); + tag2 = MKTAG('V', 'B', 'A', 'D'); break; case MKTAG('E', 'A', 'R', 'T'): case MKTAG('H', 'O', 'M', 'E'): case MKTAG('N', 'P', 'L', 'C'): case MKTAG('P', 'L', 'A', 'N'): - tagId = MKTAG('P', 'L', 'A', 'C'); + tag2 = MKTAG('P', 'L', 'A', 'C'); break; case MKTAG('F', 'A', 'U', 'N'): case MKTAG('F', 'I', 'S', 'H'): case MKTAG('F', 'L', 'O', 'R'): - tagId = MKTAG('N', 'A', 'T', 'R'); + tag2 = MKTAG('N', 'A', 'T', 'R'); break; case MKTAG('H', 'H', 'L', 'D'): case MKTAG('T', 'O', 'Y', 'S'): case MKTAG('W', 'E', 'A', 'P'): - tagId = MKTAG('M', 'A', 'C', 'H'); + tag2 = MKTAG('M', 'A', 'C', 'H'); break; case MKTAG('M', 'L', 'T', 'Y'): case MKTAG('P', 'G', 'R', 'P'): case MKTAG('P', 'T', 'I', 'C'): - tagId = MKTAG('G', 'R', 'U', 'P'); + tag2 = MKTAG('G', 'R', 'U', 'P'); break; case MKTAG('P', 'K', 'U', 'P'): case MKTAG('S', 'E', 'X', '1'): case MKTAG('S', 'W', 'E', 'R'): - tagId = MKTAG('R', 'U', 'D', 'E'); + tag2 = MKTAG('R', 'U', 'D', 'E'); break; case MKTAG('P', 'H', 'I', 'L'): case MKTAG('R', 'C', 'K', 'T'): - tagId = MKTAG('S', 'C', 'I', 'E'); + tag2 = MKTAG('S', 'C', 'I', 'E'); break; case MKTAG('T', 'R', 'A', '2'): case MKTAG('T', 'R', 'A', '3'): - tagId = MKTAG('T', 'R', 'A', 'V'); + tag2 = MKTAG('T', 'R', 'A', 'V'); break; default: break; } - return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); + return TTnpcScript::handleQuote(roomScript, sentence, tag1, tag2, remainder); } int DoorbotScript::updateState(uint oldId, uint newId, int index) { diff --git a/engines/titanic/true_talk/doorbot_script.h b/engines/titanic/true_talk/doorbot_script.h index 536e9d42ca..50d01dfc57 100644 --- a/engines/titanic/true_talk/doorbot_script.h +++ b/engines/titanic/true_talk/doorbot_script.h @@ -76,7 +76,12 @@ public: virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); + + /** + * Returns true if the NPC's dial region affects quote responses + */ + virtual bool isQuoteDialled() const { return true; } /** * Handles updating NPC state based on specified dialogue Ids and dial positions diff --git a/engines/titanic/true_talk/liftbot_script.cpp b/engines/titanic/true_talk/liftbot_script.cpp index 757b640c76..6dfa8d88db 100644 --- a/engines/titanic/true_talk/liftbot_script.cpp +++ b/engines/titanic/true_talk/liftbot_script.cpp @@ -215,8 +215,8 @@ ScriptChangedResult LiftbotScript::scriptChanged(const TTroomScript *roomScript, } int LiftbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { - switch (tagId) { + uint tag1, uint tag2, uint remainder) { + switch (tag2) { case MKTAG('A', 'D', 'V', 'T'): case MKTAG('A', 'R', 'T', 'I'): case MKTAG('A', 'R', 'T', 'Y'): @@ -237,7 +237,7 @@ int LiftbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'E', 'A', 'M'): case MKTAG('T', 'V', 'S', 'H'): case MKTAG('W', 'W', 'E', 'B'): - tagId = MKTAG('E', 'N', 'T', 'N'); + tag2 = MKTAG('E', 'N', 'T', 'N'); break; case MKTAG('A', 'C', 'T', 'R'): case MKTAG('A', 'C', 'T', 'S'): @@ -272,57 +272,57 @@ int LiftbotScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'D', 'V', 'P'): case MKTAG('T', 'W', 'A', 'T'): case MKTAG('W', 'E', 'A', 'T'): - tagId = MKTAG('P', 'R', 'S', 'N'); + tag2 = MKTAG('P', 'R', 'S', 'N'); break; case MKTAG('C', 'H', 'S', 'E'): case MKTAG('C', 'M', 'N', 'T'): case MKTAG('F', 'I', 'L', 'M'): case MKTAG('J', 'F', 'O', 'D'): case MKTAG('L', 'I', 'Q', 'D'): - tagId = MKTAG('F', 'O', 'O', 'D'); + tag2 = MKTAG('F', 'O', 'O', 'D'); break; case MKTAG('C', 'R', 'I', 'M'): case MKTAG('C', 'S', 'P', 'Y'): case MKTAG('D', 'R', 'U', 'G'): - tagId = MKTAG('V', 'B', 'A', 'D'); + tag2 = MKTAG('V', 'B', 'A', 'D'); break; case MKTAG('E', 'A', 'R', 'T'): case MKTAG('H', 'O', 'M', 'E'): case MKTAG('N', 'P', 'L', 'C'): case MKTAG('P', 'L', 'A', 'N'): - tagId = MKTAG('P', 'L', 'A', 'C'); + tag2 = MKTAG('P', 'L', 'A', 'C'); break; case MKTAG('F', 'A', 'U', 'N'): case MKTAG('F', 'I', 'S', 'H'): case MKTAG('F', 'L', 'O', 'R'): - tagId = MKTAG('N', 'A', 'T', 'R'); + tag2 = MKTAG('N', 'A', 'T', 'R'); break; case MKTAG('H', 'H', 'L', 'D'): case MKTAG('T', 'O', 'Y', 'S'): case MKTAG('W', 'E', 'A', 'P'): - tagId = MKTAG('M', 'A', 'C', 'H'); + tag2 = MKTAG('M', 'A', 'C', 'H'); break; case MKTAG('M', 'L', 'T', 'Y'): case MKTAG('P', 'G', 'R', 'P'): case MKTAG('P', 'T', 'I', 'C'): - tagId = MKTAG('G', 'R', 'U', 'P'); + tag2 = MKTAG('G', 'R', 'U', 'P'); break; case MKTAG('P', 'K', 'U', 'P'): case MKTAG('S', 'E', 'X', '1'): case MKTAG('S', 'W', 'E', 'R'): - tagId = MKTAG('R', 'U', 'D', 'E'); + tag2 = MKTAG('R', 'U', 'D', 'E'); break; case MKTAG('P', 'H', 'I', 'L'): case MKTAG('R', 'C', 'K', 'T'): - tagId = MKTAG('S', 'C', 'I', 'E'); + tag2 = MKTAG('S', 'C', 'I', 'E'); break; case MKTAG('T', 'R', 'A', '2'): case MKTAG('T', 'R', 'A', '3'): - tagId = MKTAG('T', 'R', 'A', 'V'); + tag2 = MKTAG('T', 'R', 'A', 'V'); break; } - return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); + return TTnpcScript::handleQuote(roomScript, sentence, tag1, tag2, remainder); } int LiftbotScript::updateState(uint oldId, uint newId, int index) { diff --git a/engines/titanic/true_talk/liftbot_script.h b/engines/titanic/true_talk/liftbot_script.h index 0a9cdfd6f0..b0efa34416 100644 --- a/engines/titanic/true_talk/liftbot_script.h +++ b/engines/titanic/true_talk/liftbot_script.h @@ -76,7 +76,7 @@ public: virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); /** * Handles updating NPC state based on specified dialogue Ids and dial positions diff --git a/engines/titanic/true_talk/maitred_script.cpp b/engines/titanic/true_talk/maitred_script.cpp index 1033db29e0..99e9c04384 100644 --- a/engines/titanic/true_talk/maitred_script.cpp +++ b/engines/titanic/true_talk/maitred_script.cpp @@ -72,7 +72,7 @@ int MaitreDScript::chooseResponse(const TTroomScript *roomScript, const TTsenten int MaitreDScript::process(const TTroomScript *roomScript, const TTsentence *sentence) { if (roomScript->_scriptId != 132) return 2; - if (preprocess(roomScript, sentence) == 1) + if (preprocess(roomScript, sentence) != 1) return 1; CTrueTalkManager::setFlags(10, 0); @@ -381,8 +381,8 @@ ScriptChangedResult MaitreDScript::scriptChanged(const TTroomScript *roomScript, } int MaitreDScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { - switch (tagId) { + uint tag1, uint tag2, uint remainder) { + switch (tag2) { case MKTAG('A', 'D', 'V', 'T'): case MKTAG('A', 'R', 'T', 'I'): case MKTAG('A', 'R', 'T', 'Y'): @@ -403,7 +403,7 @@ int MaitreDScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'E', 'A', 'M'): case MKTAG('T', 'V', 'S', 'H'): case MKTAG('W', 'W', 'E', 'B'): - tagId = MKTAG('E', 'N', 'T', 'N'); + tag2 = MKTAG('E', 'N', 'T', 'N'); break; case MKTAG('A', 'C', 'T', 'R'): case MKTAG('A', 'C', 'T', 'S'): @@ -437,57 +437,56 @@ int MaitreDScript::handleQuote(const TTroomScript *roomScript, const TTsentence case MKTAG('T', 'D', 'V', 'P'): case MKTAG('T', 'W', 'A', 'T'): case MKTAG('W', 'E', 'A', 'T'): - tagId = MKTAG('P', 'R', 'S', 'N'); + tag2 = MKTAG('P', 'R', 'S', 'N'); break; case MKTAG('C', 'H', 'S', 'E'): case MKTAG('C', 'M', 'N', 'T'): case MKTAG('F', 'I', 'L', 'M'): case MKTAG('L', 'I', 'Q', 'D'): - tagId = MKTAG('F', 'O', 'O', 'D'); + tag2 = MKTAG('F', 'O', 'O', 'D'); break; case MKTAG('C', 'R', 'I', 'M'): case MKTAG('C', 'S', 'P', 'Y'): case MKTAG('D', 'R', 'U', 'G'): - tagId = MKTAG('V', 'B', 'A', 'D'); + tag2 = MKTAG('V', 'B', 'A', 'D'); break; case MKTAG('E', 'A', 'R', 'T'): case MKTAG('H', 'O', 'M', 'E'): case MKTAG('N', 'P', 'L', 'C'): case MKTAG('P', 'L', 'A', 'N'): - tagId = MKTAG('P', 'L', 'A', 'C'); + tag2 = MKTAG('P', 'L', 'A', 'C'); break; case MKTAG('F', 'A', 'U', 'N'): case MKTAG('F', 'I', 'S', 'H'): case MKTAG('F', 'L', 'O', 'R'): - tagId = MKTAG('N', 'A', 'T', 'R'); + tag2 = MKTAG('N', 'A', 'T', 'R'); break; case MKTAG('H', 'H', 'L', 'D'): case MKTAG('T', 'O', 'Y', 'S'): case MKTAG('W', 'E', 'A', 'P'): - tagId = MKTAG('M', 'A', 'C', 'H'); + tag2 = MKTAG('M', 'A', 'C', 'H'); break; case MKTAG('M', 'L', 'T', 'Y'): case MKTAG('P', 'G', 'R', 'P'): case MKTAG('P', 'T', 'I', 'C'): - tagId = MKTAG('G', 'R', 'U', 'P'); + tag2 = MKTAG('G', 'R', 'U', 'P'); break; case MKTAG('P', 'K', 'U', 'P'): case MKTAG('S', 'E', 'X', '1'): case MKTAG('S', 'W', 'E', 'R'): - tagId = MKTAG('R', 'U', 'D', 'E'); + tag2 = MKTAG('R', 'U', 'D', 'E'); break; case MKTAG('P', 'H', 'I', 'L'): case MKTAG('R', 'C', 'K', 'T'): - tagId = MKTAG('S', 'C', 'I', 'E'); + tag2 = MKTAG('S', 'C', 'I', 'E'); break; case MKTAG('T', 'R', 'A', '2'): case MKTAG('T', 'R', 'A', '3'): - tagId = MKTAG('T', 'R', 'A', 'V'); + tag2 = MKTAG('T', 'R', 'A', 'V'); break; - } - return TTnpcScript::handleQuote(roomScript, sentence, val, tagId, remainder); + return TTnpcScript::handleQuote(roomScript, sentence, tag1, tag2, remainder); } int MaitreDScript::updateState(uint oldId, uint newId, int index) { diff --git a/engines/titanic/true_talk/maitred_script.h b/engines/titanic/true_talk/maitred_script.h index bbeee403e7..c34444f5c2 100644 --- a/engines/titanic/true_talk/maitred_script.h +++ b/engines/titanic/true_talk/maitred_script.h @@ -83,7 +83,7 @@ public: virtual ScriptChangedResult scriptChanged(const TTroomScript *roomScript, uint id); virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); /** * Handles updating NPC state based on specified dialogue Ids and dial positions diff --git a/engines/titanic/true_talk/script_support.cpp b/engines/titanic/true_talk/script_support.cpp index bba708fb78..fd1d40b12e 100644 --- a/engines/titanic/true_talk/script_support.cpp +++ b/engines/titanic/true_talk/script_support.cpp @@ -146,16 +146,15 @@ void TTwordEntries::load(const char *name) { void TThandleQuoteEntries::load(const char *name) { Common::SeekableReadStream *r = g_vm->_filesManager->getResource(name); - _tag1 = r->readUint32LE(); - _tag2 = r->readUint32LE(); _rangeStart = r->readUint32LE(); _rangeEnd = r->readUint32LE(); + _incr = r->readUint32LE(); while (r->pos() < r->size()) { TThandleQuoteEntry qe; + qe._tag1 = r->readUint32LE(); + qe._tag2= r->readUint32LE(); qe._index = r->readUint32LE(); - qe._tagId = r->readUint32LE(); - qe._dialogueId = r->readUint32LE(); push_back(qe); } diff --git a/engines/titanic/true_talk/script_support.h b/engines/titanic/true_talk/script_support.h index 3c5b919ccf..25ec713624 100644 --- a/engines/titanic/true_talk/script_support.h +++ b/engines/titanic/true_talk/script_support.h @@ -134,19 +134,19 @@ public: }; struct TThandleQuoteEntry { + uint _tag1; + uint _tag2; uint _index; - uint _tagId; - uint _dialogueId; - TThandleQuoteEntry() : _index(0), _tagId(0), _dialogueId(0) {} + TThandleQuoteEntry() : _tag1(0), _tag2(0), _index(0) {} }; class TThandleQuoteEntries : public Common::Array<TThandleQuoteEntry> { public: - uint _tag1, _tag2; uint _rangeStart, _rangeEnd; + uint _incr; public: - TThandleQuoteEntries() : _tag1(0), _tag2(0), _rangeStart(0), _rangeEnd(0) {} + TThandleQuoteEntries() : _rangeStart(0), _rangeEnd(0), _incr(0) {} void load(const char *name); }; diff --git a/engines/titanic/true_talk/true_talk_manager.cpp b/engines/titanic/true_talk/true_talk_manager.cpp index f8ac185351..39d771c086 100644 --- a/engines/titanic/true_talk/true_talk_manager.cpp +++ b/engines/titanic/true_talk/true_talk_manager.cpp @@ -386,7 +386,7 @@ CString CTrueTalkManager::readDialogueString() { // Strip off any non-printable characters for (byte *p = buffer; *p != '\0'; ++p) { - if (*p < 32 || *p > 127) + if (*p < 32) *p = ' '; } diff --git a/engines/titanic/true_talk/tt_npc_script.cpp b/engines/titanic/true_talk/tt_npc_script.cpp index c9d1beb353..83ce50b83c 100644 --- a/engines/titanic/true_talk/tt_npc_script.cpp +++ b/engines/titanic/true_talk/tt_npc_script.cpp @@ -282,51 +282,41 @@ bool TTnpcScript::handleWord(uint id) const { } int TTnpcScript::handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder) { + uint tag1, uint tag2, uint remainder) { if (_quotes.empty()) return 1; - - int loopCounter = 0; - for (uint idx = 0; idx < _quotes.size() && loopCounter < 2; ++idx) { + for (uint idx = 3; idx < _quotes.size(); ++idx) { const TThandleQuoteEntry *qe = &_quotes[idx]; - if (!qe->_index) { - // End of list; start at beginning again - ++loopCounter; - idx = 0; - qe = &_quotes[0]; - } - - if (qe->_index == val && ( - (tagId == 0 && loopCounter == 2) || - (qe->_tagId < MKTAG('A', 'A', 'A', 'A')) || - (qe->_tagId == tagId) - )) { - uint foundTagId = qe->_tagId; - if (foundTagId > 0 && foundTagId < 100) { - if (!tagId) - foundTagId >>= 1; - if (getRandomNumber(100) < foundTagId) + if (qe->_tag1 == tag1 && + (qe->_tag2 == tag2 || qe->_tag2 < MKTAG('A', 'A', 'A', 'A'))) { + uint threshold = qe->_tag2; + if (threshold > 0 && threshold < 100) { + if (!tag2) + threshold >>= 1; + if (getRandomNumber(100) < threshold) return 1; } - uint dialogueId = qe->_dialogueId; + uint dialogueId = qe->_index; if (dialogueId >= _quotes._rangeStart && dialogueId <= _quotes._rangeEnd) { dialogueId -= _quotes._rangeStart; - if (dialogueId > 3) - error("Invalid dialogue index in BarbotScript"); - - const int RANDOM_LIMITS[4] = { 30, 50, 70, 60 }; - int rangeLimit = RANDOM_LIMITS[dialogueId]; - int dialRegion = getDialRegion(0); - - if (dialRegion != 1) { - rangeLimit = MAX(rangeLimit - 20, 20); + if (dialogueId >= _quotes.size()) + error("Invalid dialogue index in bot script"); + TThandleQuoteEntry "e = _quotes[dialogueId]; + + int rangeLimit = quote._index; + if (isQuoteDialled()) { + // Barbot and Doorbot response is affected by dial region + int dialRegion = getDialRegion(0); + if (dialRegion != 1) { + rangeLimit = MAX((int)quote._tag1 - 20, 20); + } } - dialogueId = (((int)remainder + 25) % 100) >= rangeLimit - ? _quotes._tag1 : _quotes._tag2; + dialogueId = ((remainder + _quotes._incr) % 100) >= (uint)rangeLimit + ? quote._tag2 : quote._tag1; } addResponse(getDialogueId(dialogueId)); @@ -336,7 +326,6 @@ int TTnpcScript::handleQuote(const TTroomScript *roomScript, const TTsentence *s } return 1; - } uint TTnpcScript::getRangeValue(uint id) { diff --git a/engines/titanic/true_talk/tt_npc_script.h b/engines/titanic/true_talk/tt_npc_script.h index c2f001f8cc..6a152ccde1 100644 --- a/engines/titanic/true_talk/tt_npc_script.h +++ b/engines/titanic/true_talk/tt_npc_script.h @@ -269,7 +269,12 @@ public: virtual bool handleWord(uint id) const; virtual int handleQuote(const TTroomScript *roomScript, const TTsentence *sentence, - uint val, uint tagId, uint remainder); + uint tag1, uint tag2, uint remainder); + + /** + * Returns true if the NPC's dial region affects quote responses + */ + virtual bool isQuoteDialled() const { return false; } /** * Given an Id for a previously registered set of random number values, diff --git a/engines/titanic/true_talk/tt_parser.cpp b/engines/titanic/true_talk/tt_parser.cpp index 3481587b8d..5c48de4182 100644 --- a/engines/titanic/true_talk/tt_parser.cpp +++ b/engines/titanic/true_talk/tt_parser.cpp @@ -30,6 +30,7 @@ #include "titanic/true_talk/tt_sentence.h" #include "titanic/true_talk/tt_word.h" #include "titanic/titanic.h" +#include "titanic/translation.h" namespace Titanic { @@ -64,7 +65,7 @@ void TTparser::loadArrays() { loadArray(_replacements1, "TEXT/REPLACEMENTS1"); loadArray(_replacements2, "TEXT/REPLACEMENTS2"); loadArray(_replacements3, "TEXT/REPLACEMENTS3"); - if (g_vm->isGerman()) + if (g_language == Common::DE_DEU) loadArray(_replacements4, "TEXT/REPLACEMENTS4"); loadArray(_phrases, "TEXT/PHRASES"); loadArray(_pronouns, "TEXT/PRONOUNS"); @@ -85,12 +86,13 @@ int TTparser::preprocess(TTsentence *sentence) { if (normalize(sentence)) return 0; - if (g_vm->isGerman()) + if (g_language == Common::DE_DEU) { preprocessGerman(sentence->_normalizedLine); - - // Scan for and replace common slang and contractions with verbose versions - searchAndReplace(sentence->_normalizedLine, _replacements1); - searchAndReplace(sentence->_normalizedLine, _replacements2); + } else { + // Scan for and replace common slang and contractions with verbose versions + searchAndReplace(sentence->_normalizedLine, _replacements1); + searchAndReplace(sentence->_normalizedLine, _replacements2); + } // Check entire normalized line against common phrases to replace for (uint idx = 0; idx < _phrases.size(); idx += 2) { @@ -98,6 +100,12 @@ int TTparser::preprocess(TTsentence *sentence) { sentence->_normalizedLine = _phrases[idx + 1]; } + if (g_language == Common::DE_DEU) { + // Scan for and replace common slang and contractions with verbose versions + searchAndReplace(sentence->_normalizedLine, _replacements1); + searchAndReplace(sentence->_normalizedLine, _replacements2); + } + // Do a further search and replace of roman numerals to decimal searchAndReplace(sentence->_normalizedLine, _replacements3); @@ -371,6 +379,7 @@ int TTparser::searchAndReplace(TTstring &line, int startIndex, const StringArray // Replace the text in the line with it's replacement line = CString(line.c_str(), line.c_str() + startIndex) + replacementStr + CString(line.c_str() + startIndex + origStr.size()); + lineSize = line.size(); startIndex += replacementStr.size(); break; @@ -1741,9 +1750,10 @@ void TTparser::preprocessGerman(TTstring &line) { "et ", "st ", "s ", "e ", "n ", "t " }; - for (uint idx = 0; idx < _replacements4.size(); idx += 3) { - if (!line.hasSuffix(_replacements4[idx + 2])) + for (uint idx = 0; idx < _replacements4.size(); ++idx) { + if (!line.hasSuffix(_replacements4[idx])) continue; + const char *lineP = line.c_str(); const char *p = strstr(lineP, _replacements4[idx].c_str()); if (!p || p == lineP || *(p - 1) != ' ') diff --git a/engines/titanic/true_talk/tt_sentence.cpp b/engines/titanic/true_talk/tt_sentence.cpp index 2c675ba9e5..1bb30d33d6 100644 --- a/engines/titanic/true_talk/tt_sentence.cpp +++ b/engines/titanic/true_talk/tt_sentence.cpp @@ -24,6 +24,7 @@ #include "titanic/true_talk/tt_concept.h" #include "titanic/true_talk/script_handler.h" #include "titanic/titanic.h" +#include "titanic/translation.h" namespace Titanic { @@ -311,6 +312,12 @@ bool TTsentence::isConcept34(int slotIndex, const TTconceptNode *node) const { bool TTsentence::localWord(const char *str) const { CScriptHandler &scriptHandler = *g_vm->_exeResources._owner; bool foundMatch = false; + static const char *const ARTICLES_EN[11] = { + "it", "that", "he", "she", "him", "her", "them", "they", "those", "1", "thing" + }; + static const char *const ARTICLES_DE[9] = { + "es", "das", "er", "ihn", "ihm", "ihnen", "diese", "man", "ding" + }; if (scriptHandler._concept1P) { TTstring s = scriptHandler._concept1P->getText(); @@ -332,15 +339,15 @@ bool TTsentence::localWord(const char *str) const { continue; const TTstring wordStr = nodeP->_wordP->_text; - if (mode == VOCAB_MODE_EN && wordStr == str) { + if ((g_language == Common::DE_DEU || mode == VOCAB_MODE_EN) && wordStr == str) { result = true; } else if (nodeP->_wordP->findSynByName(str, &syn, mode)) { result = true; } else if (foundMatch) { - result = wordStr == "it" || wordStr == "that" || wordStr == "he" - || wordStr == "she" || wordStr == "him" || wordStr == "her" - || wordStr == "them" || wordStr == "they" || wordStr == "those" - || wordStr == "1" || wordStr == "thing"; + result = false; + for (int idx = 0; idx < TRANSLATE(11, 9) && !result; ++idx) { + result = wordStr == TRANSLATE(ARTICLES_EN[idx], ARTICLES_DE[idx]); + } } } diff --git a/engines/titanic/true_talk/tt_sentence.h b/engines/titanic/true_talk/tt_sentence.h index 01a0346be8..dfbf7ae52f 100644 --- a/engines/titanic/true_talk/tt_sentence.h +++ b/engines/titanic/true_talk/tt_sentence.h @@ -119,6 +119,10 @@ public: bool isConcept34(int slotIndex, const TTconceptNode *node = nullptr) const; + /** + * Returns true if the sentence contains the specified word, + * allowing for common synonyms of the desired word + */ bool localWord(const char *str) const; /** diff --git a/engines/titanic/true_talk/tt_vocab.cpp b/engines/titanic/true_talk/tt_vocab.cpp index 861d8f89a0..e9fc098749 100644 --- a/engines/titanic/true_talk/tt_vocab.cpp +++ b/engines/titanic/true_talk/tt_vocab.cpp @@ -182,10 +182,10 @@ TTword *TTvocab::getWord(TTstring &str, TTword **srcWord) const { if (!word) {
TTstring tempStr(str);
if (tempStr.size() > 2) {
- word = getSuffixedWord(tempStr);
+ word = getSuffixedWord(tempStr, srcWord);
if (!word)
- word = getPrefixedWord(tempStr);
+ word = getPrefixedWord(tempStr, srcWord);
}
}
@@ -230,11 +230,11 @@ TTword *TTvocab::getPrimeWord(TTstring &str, TTword **srcWord) const { return newWord;
}
-TTword *TTvocab::getSuffixedWord(TTstring &str) const {
+TTword *TTvocab::getSuffixedWord(TTstring &str, TTword **srcWord) const {
TTstring tempStr(str);
TTword *word = nullptr;
- if (g_vm->isGerman()) {
+ if (g_language == Common::DE_DEU) {
static const char *const SUFFIXES[11] = {
"est", "em", "en", "er", "es", "et", "st",
"s", "e", "n", "t"
@@ -243,7 +243,7 @@ TTword *TTvocab::getSuffixedWord(TTstring &str) const { for (int idx = 0; idx < 11; ++idx) {
if (tempStr.hasSuffix(SUFFIXES[idx])) {
tempStr.deleteSuffix(strlen(SUFFIXES[idx]));
- word = getPrimeWord(tempStr);
+ word = getPrimeWord(tempStr, srcWord);
if (word)
break;
tempStr = str;
@@ -315,7 +315,9 @@ TTword *TTvocab::getSuffixedWord(TTstring &str) const { if (word) {
if (word->_wordClass == WC_ACTION) {
- dynamic_cast<TTaction *>(word)->setVal(1);
+ TTaction *action = dynamic_cast<TTaction *>(word);
+ assert(action);
+ action->setVal(1);
}
} else {
tempStr = str;
@@ -514,7 +516,7 @@ TTword *TTvocab::getSuffixedWord(TTstring &str) const { return word;
}
-TTword *TTvocab::getPrefixedWord(TTstring &str) const {
+TTword *TTvocab::getPrefixedWord(TTstring &str, TTword **srcWord) const {
TTstring tempStr(str);
TTword *word = nullptr;
int prefixLen = 0;
diff --git a/engines/titanic/true_talk/tt_vocab.h b/engines/titanic/true_talk/tt_vocab.h index 7e5cc29bc5..def42ba63d 100644 --- a/engines/titanic/true_talk/tt_vocab.h +++ b/engines/titanic/true_talk/tt_vocab.h @@ -68,7 +68,7 @@ private: * @param str Word to check * @returns New word instance for found match, or nullptr otherwise */ - TTword *getSuffixedWord(TTstring &str) const; + TTword *getSuffixedWord(TTstring &str, TTword **srcWord = nullptr) const; /** * Checks the passed word for common prefixes, and checks for a word @@ -76,7 +76,7 @@ private: * @param str Word to check * @returns New word instance for found match, or nullptr otherwise */ - TTword *getPrefixedWord(TTstring &str) const; + TTword *getPrefixedWord(TTstring &str, TTword **srcWord = nullptr) const; public: TTvocab(VocabMode vocabMode); ~TTvocab(); diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp index c91f51bade..5ec3595196 100644 --- a/engines/toltecs/toltecs.cpp +++ b/engines/toltecs/toltecs.cpp @@ -70,7 +70,7 @@ ToltecsEngine::~ToltecsEngine() { } Common::Error ToltecsEngine::run() { - initGraphics(640, 400, true); + initGraphics(640, 400); _isSaveAllowed = true; diff --git a/engines/tony/window.cpp b/engines/tony/window.cpp index d312d58091..05853e606e 100644 --- a/engines/tony/window.cpp +++ b/engines/tony/window.cpp @@ -54,7 +54,7 @@ RMWindow::~RMWindow() { */ void RMWindow::init() { Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); - initGraphics(RM_SX, RM_SY, true, &pixelFormat); + initGraphics(RM_SX, RM_SY, &pixelFormat); reset(); } diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp index 5e8ff09a86..7a8925b766 100644 --- a/engines/toon/script_func.cpp +++ b/engines/toon/script_func.cpp @@ -48,177 +48,177 @@ ScriptFunc::ScriptFunc(ToonEngine *vm) { Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 Opcode(sys_Cmd_Change_Actor_X_And_Y); // dd offset sub_2A710 Opcode(sys_Cmd_Init_Talking_Character); // dd offset sub_2A4E0 - Opcode(sys_Cmd_Draw_Actor_Standing), // dd offset sub_2A650 - Opcode(sys_Cmd_Get_Actor_X), // dd offset sub_2ADC0 - Opcode(sys_Cmd_Get_Actor_Y), // dd offset sub_2ADD0 - Opcode(sys_Cmd_Get_Actor_Facing), // dd offset sub_2A790 - Opcode(sys_Cmd_Get_Last_Scene), // dd offset sub_29F80 - Opcode(sys_Cmd_Debug_Print), // dd offset sub_2A510 - Opcode(sys_Cmd_Flip_Screens), // dd offset sub_2A180 - Opcode(sys_Cmd_Play_Flic), // dd offset sub_2A080 - Opcode(sys_Cmd_Force_Facing), // dd offset sub_29F90 - Opcode(sys_Cmd_Restart_Thread), // dd offset sub_29F30 - Opcode(sys_Cmd_Walk_Actor_To_Point), // dd offset sub_2A440 - Opcode(sys_Cmd_Set_Sack_Visible), // dd offset sub_29920 - Opcode(sys_Cmd_Set_Actor_Facing), // dd offset sub_2AD60 - Opcode(sys_Cmd_Confiscate_Inventory), // dd offset sub_29EB0 - Opcode(sys_Cmd_Character_Talks), // dd offset sub_29F00 - Opcode(sys_Cmd_Visited_Scene), // dd offset sub_29E80 - Opcode(sys_Cmd_Query_Rif_Flag), // dd offset sub_29D20 - Opcode(sys_Cmd_Query_Scroll), // dd offset sub_29D60 - Opcode(sys_Cmd_Set_Initial_Location), // dd offset sub_2AD80 - Opcode(sys_Cmd_Make_Line_Non_Walkable), // dd offset sub_29FC0 - Opcode(sys_Cmd_Make_Line_Walkable), // dd offset sub_2A050 - Opcode(sys_Cmd_Walk_Actor_On_Condition), // dd offset sub_29D70 - Opcode(sys_Cmd_Set_Actor_Facing_Point), // dd offset sub_29E60 - Opcode(sys_Cmd_Set_Inventory_Slot), // dd offset sub_2B0D0 - Opcode(sys_Cmd_Get_Inventory_Slot), // dd offset sub_2B0F0 - Opcode(sys_Cmd_Add_Item_To_Inventory), // dd offset sub_2AE50 - Opcode(sys_Cmd_Set_Actor_RGB_Modifiers), // dd offset sub_29CA0 - Opcode(sys_Cmd_Init_Conversation_AP), // dd offset sub_2B130 - Opcode(sys_Cmd_Actor_Talks), // dd offset sub_2ADA0 - Opcode(sys_Cmd_Say_Lines), // dd offset sub_29B20 - Opcode(sys_Cmd_Set_Rif_Flag), // dd offset sub_2A320 - Opcode(sys_Cmd_Empty_Inventory), // dd offset sub_2AE10 - Opcode(sys_Cmd_Set_Anim_Scale_Size), // dd offset sub_29BD0 - Opcode(sys_Cmd_Delete_Item_From_Inventory), // dd offset sub_2AE70 - Opcode(sys_Cmd_Specific_Item_In_Inventory), // dd offset sub_2A740 - Opcode(sys_Cmd_Run_Script), // dd offset sub_29AF0 - Opcode(sys_Cmd_Query_Game_Flag), // dd offset sub_2A3E0 - Opcode(sys_Cmd_Reset_Game_Flag), // dd offset sub_2A420 - Opcode(sys_Cmd_Set_Game_Flag), // dd offset sub_2A400 - Opcode(sys_Cmd_Create_Mouse_Item), // dd offset sub_2A4B0 - Opcode(sys_Cmd_Destroy_Mouse_Item), // dd offset sub_2A4D0 - Opcode(sys_Cmd_Get_Mouse_State), // dd offset sub_2A860 - Opcode(sys_Cmd_Hide_Mouse), // dd offset sub_2A5D0 - Opcode(sys_Cmd_Exit_Conversation), // dd offset sub_29AE0 - Opcode(sys_Cmd_Set_Mouse_Pos), // dd offset sub_2A810 - Opcode(sys_Cmd_Show_Mouse), // dd offset sub_2A5F0 - Opcode(sys_Cmd_In_Close_Up), // dd offset sub_29FB0 - Opcode(sys_Cmd_Set_Scroll_Lock), // dd offset sub_298B0 - Opcode(sys_Cmd_Fill_Area_Non_Walkable), // dd offset sub_29FF0 - Opcode(sys_Cmd_Set_Scroll_Coords), // dd offset sub_298D0 - Opcode(sys_Cmd_Hide_Cutaway), // dd offset sub_2A0F0 - Opcode(sys_Cmd_Show_Cutaway), // dd offset sub_2A100 - Opcode(sys_Cmd_Pause_Ticks), // dd offset sub_2A360 - Opcode(sys_Cmd_In_Conversation), // dd offset sub_29C60 - Opcode(sys_Cmd_Character_Talking), // dd offset sub_29C70 - Opcode(sys_Cmd_Set_Flux_Facing_Point), // dd offset sub_29980 - Opcode(sys_Cmd_Set_Flux_Facing), // dd offset sub_299A0 - Opcode(sys_Cmd_Set_Flux_Coords), // dd offset sub_299C0 - Opcode(sys_Cmd_Set_Flux_Visible), // dd offset sub_299F0 - Opcode(sys_Cmd_Get_Flux_X), // dd offset sub_29A40 - Opcode(sys_Cmd_Get_Flux_Y), // dd offset sub_29A50 - Opcode(sys_Cmd_Get_Flux_Facing), // dd offset sub_29A60 - Opcode(sys_Cmd_Get_Flux_Flags), // dd offset sub_29A70 - Opcode(sys_Cmd_Query_Flux_Coords), // dd offset sub_29A90 - Opcode(sys_Cmd_Have_A_Conversation), // dd offset sub_2B110 - Opcode(sys_Cmd_Walk_Flux_To_Point), // dd offset sub_29AC0 - Opcode(sys_Cmd_Get_Actor_Final_X), // dd offset sub_29940 - Opcode(sys_Cmd_Get_Actor_Final_Y), // dd offset sub_29960 - Opcode(sys_Cmd_Query_Scene_Anim_Loaded), // dd offset sub_29870 - Opcode(sys_Cmd_Play_Flux_Anim), // dd offset sub_29820 - Opcode(sys_Cmd_Set_Anim_Priority), // dd offset sub_29790 - Opcode(sys_Cmd_Place_Scene_Anim), // dd offset sub_2A7A0 - Opcode(sys_Cmd_Update_Scene_Animations), // dd offset sub_2AE30 - Opcode(sys_Cmd_Get_Drew_Scale), // dd offset sub_297E0 - Opcode(sys_Cmd_Query_Drew_Flags), // dd offset sub_29800 - Opcode(sys_Cmd_Set_Music), // dd offset sub_29720 - Opcode(sys_Cmd_Query_Speech), // dd offset sub_296D0 - Opcode(sys_Cmd_Enter_New_Scene), // dd offset sub_2A550 - Opcode(sys_Cmd_Enter_Same_Scene), // dd offset sub_2ADE0 - Opcode(sys_Cmd_Is_Pixel_Walkable), // dd offset sub_2A4F0 - Opcode(sys_Cmd_Show_Screen), // dd offset sub_2A0C0 - Opcode(sys_Cmd_Hide_Screen), // dd offset sub_2A0F0 - Opcode(sys_Cmd_Dummy), // dd offset sub_295D0 - Opcode(sys_Cmd_Set_Special_Enter_X_And_Y), // dd offset sub_2A590 - Opcode(sys_Cmd_Get_Mouse_X), // dd offset sub_296B0 - Opcode(sys_Cmd_Get_Mouse_Y), // dd offset sub_296C0 - Opcode(sys_Cmd_Fade_Palette), // dd offset sub_29650 - Opcode(sys_Cmd_Music_Enabled), // dd offset sub_29620 - Opcode(sys_Cmd_Dummy), // dd offset sub_295F0 - Opcode(sys_Cmd_Dummy), // dd offset sub_29610 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Random), // dd offset sub_2A600 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Wait_Key), // dd offset sub_2AE20 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back), // dd offset sub_2A940 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Set_Scene_Anim_Wait), // dd offset sub_2A870 - Opcode(sys_Cmd_Init_Scene_Anim), // dd offset sub_2AC60 - Opcode(sys_Cmd_Set_Scene_Animation_Active_Flag), // dd offset sub_2AB10 - Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame), // dd offset sub_2A8D0 - Opcode(sys_Cmd_Move_Scene_Anim), // dd offset sub_2AA90 - Opcode(sys_Cmd_Run_Actor_Default_Script), // dd offset sub_2A4E0 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Set_Location_Data), // dd offset sub_2AE90 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Set_CountDown_Timer), // dd offset sub_2AFC0 - Opcode(sys_Cmd_Query_CountDown_Timer), // dd offset sub_2AFE0 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Proceed_To_Next_Chapter), // dd offset sub_2AFF0 - Opcode(sys_Cmd_Play_Sfx_Plus), // dd offset sub_2A1D0 - Opcode(sys_Cmd_Play_Sfx), // dd offset sub_2A1A0 - Opcode(sys_Cmd_Set_Ambient_Sfx), // dd offset sub_2A260 - Opcode(sys_Cmd_Kill_Ambient_Sfx), // dd offset sub_2A300 - Opcode(sys_Cmd_Set_Ambient_Sfx_Plus), // dd offset sub_2A290 - Opcode(sys_Cmd_Set_Ambient_Volume), // dd offset sub_2A240 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Freeze_Scene_Animation), // dd offset sub_2AB90 - Opcode(sys_Cmd_Unfreeze_Scene_Animation), // dd offset sub_2ABB0 - Opcode(sys_Cmd_Scene_Animation_Frozen), // dd offset sub_2ABD0 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Set_Script_Game_Data_Global), // dd offset sub_2ABF0 - Opcode(sys_Cmd_Get_Script_Game_Data_Global), // dd offset sub_2AC30 - Opcode(sys_Cmd_Say_Line), // dd offset loc_2A190 - Opcode(sys_Cmd_Knight_Puzzle_Get_Coord), // dd offset sub_2A110 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Add_Scene_Anim), // dd offset sub_2AC60 - Opcode(sys_Cmd_Remove_Scene_Anim), // dd offset sub_2ACE0 - Opcode(sys_Cmd_Disable_Timer), // dd offset sub_2AD00 - Opcode(sys_Cmd_Enable_Timer), // dd offset sub_2AD20 - Opcode(sys_Cmd_Set_Timer), // dd offset sub_2AD40 - Opcode(sys_Cmd_Set_Palette_Color), // dd offset sub_2B020 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Number_Of_NPCs), // dd offset loc_2A190 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 - Opcode(sys_Cmd_Get_Config_Language), // dd offset sub_2B0C0 + Opcode(sys_Cmd_Draw_Actor_Standing); // dd offset sub_2A650 + Opcode(sys_Cmd_Get_Actor_X); // dd offset sub_2ADC0 + Opcode(sys_Cmd_Get_Actor_Y); // dd offset sub_2ADD0 + Opcode(sys_Cmd_Get_Actor_Facing); // dd offset sub_2A790 + Opcode(sys_Cmd_Get_Last_Scene); // dd offset sub_29F80 + Opcode(sys_Cmd_Debug_Print); // dd offset sub_2A510 + Opcode(sys_Cmd_Flip_Screens); // dd offset sub_2A180 + Opcode(sys_Cmd_Play_Flic); // dd offset sub_2A080 + Opcode(sys_Cmd_Force_Facing); // dd offset sub_29F90 + Opcode(sys_Cmd_Restart_Thread); // dd offset sub_29F30 + Opcode(sys_Cmd_Walk_Actor_To_Point); // dd offset sub_2A440 + Opcode(sys_Cmd_Set_Sack_Visible); // dd offset sub_29920 + Opcode(sys_Cmd_Set_Actor_Facing); // dd offset sub_2AD60 + Opcode(sys_Cmd_Confiscate_Inventory); // dd offset sub_29EB0 + Opcode(sys_Cmd_Character_Talks); // dd offset sub_29F00 + Opcode(sys_Cmd_Visited_Scene); // dd offset sub_29E80 + Opcode(sys_Cmd_Query_Rif_Flag); // dd offset sub_29D20 + Opcode(sys_Cmd_Query_Scroll); // dd offset sub_29D60 + Opcode(sys_Cmd_Set_Initial_Location); // dd offset sub_2AD80 + Opcode(sys_Cmd_Make_Line_Non_Walkable); // dd offset sub_29FC0 + Opcode(sys_Cmd_Make_Line_Walkable); // dd offset sub_2A050 + Opcode(sys_Cmd_Walk_Actor_On_Condition); // dd offset sub_29D70 + Opcode(sys_Cmd_Set_Actor_Facing_Point); // dd offset sub_29E60 + Opcode(sys_Cmd_Set_Inventory_Slot); // dd offset sub_2B0D0 + Opcode(sys_Cmd_Get_Inventory_Slot); // dd offset sub_2B0F0 + Opcode(sys_Cmd_Add_Item_To_Inventory); // dd offset sub_2AE50 + Opcode(sys_Cmd_Set_Actor_RGB_Modifiers); // dd offset sub_29CA0 + Opcode(sys_Cmd_Init_Conversation_AP); // dd offset sub_2B130 + Opcode(sys_Cmd_Actor_Talks); // dd offset sub_2ADA0 + Opcode(sys_Cmd_Say_Lines); // dd offset sub_29B20 + Opcode(sys_Cmd_Set_Rif_Flag); // dd offset sub_2A320 + Opcode(sys_Cmd_Empty_Inventory); // dd offset sub_2AE10 + Opcode(sys_Cmd_Set_Anim_Scale_Size); // dd offset sub_29BD0 + Opcode(sys_Cmd_Delete_Item_From_Inventory); // dd offset sub_2AE70 + Opcode(sys_Cmd_Specific_Item_In_Inventory); // dd offset sub_2A740 + Opcode(sys_Cmd_Run_Script); // dd offset sub_29AF0 + Opcode(sys_Cmd_Query_Game_Flag); // dd offset sub_2A3E0 + Opcode(sys_Cmd_Reset_Game_Flag); // dd offset sub_2A420 + Opcode(sys_Cmd_Set_Game_Flag); // dd offset sub_2A400 + Opcode(sys_Cmd_Create_Mouse_Item); // dd offset sub_2A4B0 + Opcode(sys_Cmd_Destroy_Mouse_Item); // dd offset sub_2A4D0 + Opcode(sys_Cmd_Get_Mouse_State); // dd offset sub_2A860 + Opcode(sys_Cmd_Hide_Mouse); // dd offset sub_2A5D0 + Opcode(sys_Cmd_Exit_Conversation); // dd offset sub_29AE0 + Opcode(sys_Cmd_Set_Mouse_Pos); // dd offset sub_2A810 + Opcode(sys_Cmd_Show_Mouse); // dd offset sub_2A5F0 + Opcode(sys_Cmd_In_Close_Up); // dd offset sub_29FB0 + Opcode(sys_Cmd_Set_Scroll_Lock); // dd offset sub_298B0 + Opcode(sys_Cmd_Fill_Area_Non_Walkable); // dd offset sub_29FF0 + Opcode(sys_Cmd_Set_Scroll_Coords); // dd offset sub_298D0 + Opcode(sys_Cmd_Hide_Cutaway); // dd offset sub_2A0F0 + Opcode(sys_Cmd_Show_Cutaway); // dd offset sub_2A100 + Opcode(sys_Cmd_Pause_Ticks); // dd offset sub_2A360 + Opcode(sys_Cmd_In_Conversation); // dd offset sub_29C60 + Opcode(sys_Cmd_Character_Talking); // dd offset sub_29C70 + Opcode(sys_Cmd_Set_Flux_Facing_Point); // dd offset sub_29980 + Opcode(sys_Cmd_Set_Flux_Facing); // dd offset sub_299A0 + Opcode(sys_Cmd_Set_Flux_Coords); // dd offset sub_299C0 + Opcode(sys_Cmd_Set_Flux_Visible); // dd offset sub_299F0 + Opcode(sys_Cmd_Get_Flux_X); // dd offset sub_29A40 + Opcode(sys_Cmd_Get_Flux_Y); // dd offset sub_29A50 + Opcode(sys_Cmd_Get_Flux_Facing); // dd offset sub_29A60 + Opcode(sys_Cmd_Get_Flux_Flags); // dd offset sub_29A70 + Opcode(sys_Cmd_Query_Flux_Coords); // dd offset sub_29A90 + Opcode(sys_Cmd_Have_A_Conversation); // dd offset sub_2B110 + Opcode(sys_Cmd_Walk_Flux_To_Point); // dd offset sub_29AC0 + Opcode(sys_Cmd_Get_Actor_Final_X); // dd offset sub_29940 + Opcode(sys_Cmd_Get_Actor_Final_Y); // dd offset sub_29960 + Opcode(sys_Cmd_Query_Scene_Anim_Loaded); // dd offset sub_29870 + Opcode(sys_Cmd_Play_Flux_Anim); // dd offset sub_29820 + Opcode(sys_Cmd_Set_Anim_Priority); // dd offset sub_29790 + Opcode(sys_Cmd_Place_Scene_Anim); // dd offset sub_2A7A0 + Opcode(sys_Cmd_Update_Scene_Animations); // dd offset sub_2AE30 + Opcode(sys_Cmd_Get_Drew_Scale); // dd offset sub_297E0 + Opcode(sys_Cmd_Query_Drew_Flags); // dd offset sub_29800 + Opcode(sys_Cmd_Set_Music); // dd offset sub_29720 + Opcode(sys_Cmd_Query_Speech); // dd offset sub_296D0 + Opcode(sys_Cmd_Enter_New_Scene); // dd offset sub_2A550 + Opcode(sys_Cmd_Enter_Same_Scene); // dd offset sub_2ADE0 + Opcode(sys_Cmd_Is_Pixel_Walkable); // dd offset sub_2A4F0 + Opcode(sys_Cmd_Show_Screen); // dd offset sub_2A0C0 + Opcode(sys_Cmd_Hide_Screen); // dd offset sub_2A0F0 + Opcode(sys_Cmd_Dummy); // dd offset sub_295D0 + Opcode(sys_Cmd_Set_Special_Enter_X_And_Y); // dd offset sub_2A590 + Opcode(sys_Cmd_Get_Mouse_X); // dd offset sub_296B0 + Opcode(sys_Cmd_Get_Mouse_Y); // dd offset sub_296C0 + Opcode(sys_Cmd_Fade_Palette); // dd offset sub_29650 + Opcode(sys_Cmd_Music_Enabled); // dd offset sub_29620 + Opcode(sys_Cmd_Dummy); // dd offset sub_295F0 + Opcode(sys_Cmd_Dummy); // dd offset sub_29610 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Random); // dd offset sub_2A600 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Wait_Key); // dd offset sub_2AE20 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back); // dd offset sub_2A940 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Set_Scene_Anim_Wait); // dd offset sub_2A870 + Opcode(sys_Cmd_Init_Scene_Anim); // dd offset sub_2AC60 + Opcode(sys_Cmd_Set_Scene_Animation_Active_Flag); // dd offset sub_2AB10 + Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame); // dd offset sub_2A8D0 + Opcode(sys_Cmd_Move_Scene_Anim); // dd offset sub_2AA90 + Opcode(sys_Cmd_Run_Actor_Default_Script); // dd offset sub_2A4E0 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Set_Location_Data); // dd offset sub_2AE90 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Set_CountDown_Timer); // dd offset sub_2AFC0 + Opcode(sys_Cmd_Query_CountDown_Timer); // dd offset sub_2AFE0 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Proceed_To_Next_Chapter); // dd offset sub_2AFF0 + Opcode(sys_Cmd_Play_Sfx_Plus); // dd offset sub_2A1D0 + Opcode(sys_Cmd_Play_Sfx); // dd offset sub_2A1A0 + Opcode(sys_Cmd_Set_Ambient_Sfx); // dd offset sub_2A260 + Opcode(sys_Cmd_Kill_Ambient_Sfx); // dd offset sub_2A300 + Opcode(sys_Cmd_Set_Ambient_Sfx_Plus); // dd offset sub_2A290 + Opcode(sys_Cmd_Set_Ambient_Volume); // dd offset sub_2A240 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Freeze_Scene_Animation); // dd offset sub_2AB90 + Opcode(sys_Cmd_Unfreeze_Scene_Animation); // dd offset sub_2ABB0 + Opcode(sys_Cmd_Scene_Animation_Frozen); // dd offset sub_2ABD0 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Set_Script_Game_Data_Global); // dd offset sub_2ABF0 + Opcode(sys_Cmd_Get_Script_Game_Data_Global); // dd offset sub_2AC30 + Opcode(sys_Cmd_Say_Line); // dd offset loc_2A190 + Opcode(sys_Cmd_Knight_Puzzle_Get_Coord); // dd offset sub_2A110 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Add_Scene_Anim); // dd offset sub_2AC60 + Opcode(sys_Cmd_Remove_Scene_Anim); // dd offset sub_2ACE0 + Opcode(sys_Cmd_Disable_Timer); // dd offset sub_2AD00 + Opcode(sys_Cmd_Enable_Timer); // dd offset sub_2AD20 + Opcode(sys_Cmd_Set_Timer); // dd offset sub_2AD40 + Opcode(sys_Cmd_Set_Palette_Color); // dd offset sub_2B020 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Number_Of_NPCs); // dd offset loc_2A190 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Get_Config_Language); // dd offset sub_2B0C0 Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 } diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index dc1c515e1c..05192ae65e 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -1116,7 +1116,7 @@ Common::Error ToonEngine::run() { if (!loadToonDat()) return Common::kUnknownError; - initGraphics(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT, true); + initGraphics(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT); init(); // do we need to load directly a game? diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp index bb21f399c6..15bcbbc0fc 100644 --- a/engines/touche/touche.cpp +++ b/engines/touche/touche.cpp @@ -199,7 +199,7 @@ ToucheEngine::~ToucheEngine() { } Common::Error ToucheEngine::run() { - initGraphics(kScreenWidth, kScreenHeight, true); + initGraphics(kScreenWidth, kScreenHeight); Graphics::setupFont(_language); diff --git a/engines/tsage/tsage.cpp b/engines/tsage/tsage.cpp index 9b393132d9..417652da50 100644 --- a/engines/tsage/tsage.cpp +++ b/engines/tsage/tsage.cpp @@ -53,7 +53,7 @@ TSageEngine::TSageEngine(OSystem *system, const tSageGameDescription *gameDesc) } Common::Error TSageEngine::init() { - initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false); + initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT); return Common::kNoError; } diff --git a/engines/tucker/tucker.cpp b/engines/tucker/tucker.cpp index ad455c5ded..08ad6a05e7 100644 --- a/engines/tucker/tucker.cpp +++ b/engines/tucker/tucker.cpp @@ -89,7 +89,7 @@ bool TuckerEngine::hasFeature(EngineFeature f) const { } Common::Error TuckerEngine::run() { - initGraphics(kScreenWidth, kScreenHeight, false); + initGraphics(kScreenWidth, kScreenHeight); syncSoundSettings(); _compressedSound.openFile(); if (_startSlot == -1) diff --git a/engines/util.h b/engines/util.h index 0e7b1e118b..ccf28f2e22 100644 --- a/engines/util.h +++ b/engines/util.h @@ -23,17 +23,29 @@ #ifndef ENGINES_UTIL_H #define ENGINES_UTIL_H +#include "common/array.h" #include "common/scummsys.h" #include "common/list.h" #include "graphics/pixelformat.h" +#include "graphics/mode.h" /** * Setup the backend's graphics mode. */ -void initCommonGFX(bool defaultTo1XScaler); +void initCommonGFX(); /** - * Setup the backend's screen size and graphics mode. + * Sends a list of graphics modes to the backend so it can make a decision + * about the best way to set up the display hardware. + * + * Engines that switch between different virtual screen sizes during a game + * should call this function prior to any call to initGraphics. Engines that use + * only a single screen size do not need to call this function. + */ +void initGraphicsModes(const Graphics::ModeList &modes); + +/** + * Sets up the backend's screen size and graphics mode. * * Shows an various warnings on certain backend graphics * transaction failures (aspect switch, fullscreen switch, etc.). @@ -45,8 +57,8 @@ void initCommonGFX(bool defaultTo1XScaler); * Uses the backend's preferred format if graphics format pointer is NULL. * Finds the best compatible format if a list of graphics formats is provided. */ -void initGraphics(int width, int height, bool defaultTo1xScaler); -void initGraphics(int width, int height, bool defaultTo1xScaler, const Graphics::PixelFormat *format); -void initGraphics(int width, int height, bool defaultTo1xScaler, const Common::List<Graphics::PixelFormat> &formatList); +void initGraphics(int width, int height); +void initGraphics(int width, int height, const Graphics::PixelFormat *format); +void initGraphics(int width, int height, const Common::List<Graphics::PixelFormat> &formatList); #endif diff --git a/engines/voyeur/screen.cpp b/engines/voyeur/screen.cpp index 4d34078439..e7aadcc0d8 100644 --- a/engines/voyeur/screen.cpp +++ b/engines/voyeur/screen.cpp @@ -54,7 +54,7 @@ Screen::Screen(VoyeurEngine *vm) : Graphics::Screen(), _vm(vm), _drawPtr(&_defau } void Screen::sInitGraphics() { - initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false); + initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT); create(SCREEN_WIDTH, SCREEN_HEIGHT); clearPalette(); } diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp index c5f3673255..4961feca6e 100644 --- a/engines/wage/wage.cpp +++ b/engines/wage/wage.cpp @@ -106,7 +106,7 @@ WageEngine::~WageEngine() { Common::Error WageEngine::run() { debug("WageEngine::init"); - initGraphics(512, 342, true); + initGraphics(512, 342); // Create debugger console. It requires GFX to be initialized _console = new Console(this); diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index 05dfb961cb..e68004d1e5 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -109,7 +109,7 @@ 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, true, &format); + initGraphics(800, 600, &format); if (g_system->getScreenFormat() != format) { error("Wintermute currently REQUIRES 32bpp"); } diff --git a/engines/xeen/combat.cpp b/engines/xeen/combat.cpp index 784dd18a6e..618b556b6b 100644 --- a/engines/xeen/combat.cpp +++ b/engines/xeen/combat.cpp @@ -69,7 +69,10 @@ static const int ATTACK_TYPE_FX[23] = { 4, 5, 4, 9, 27, 29, 44, 51, 53, 61, 71 }; -static const int MONSTER_SHOOT_POW[7] = { 12, 14, 0, 4, 8, 10, 13 }; +static const PowType MONSTER_SHOOT_POW[7] = { + POW_MAGIC_ARROW, POW_SPARKLES, POW_FIREBALL, + POW_MEGAVOLTS, POW_COLD_RAY, POW_SPRAY, POW_ENERGY_BLAST +}; static const int COMBAT_SHOOTING[4] = { 1, 1, 2, 3 }; @@ -540,7 +543,7 @@ void Combat::monstersAttack() { Map &map = *_vm->_map; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; - int powNum = -1; + PowType powNum = POW_INVALID; MonsterStruct *monsterData = nullptr; OutdoorDrawList &outdoorList = intf._outdoorList; IndoorDrawList &indoorList = intf._indoorList; @@ -549,12 +552,12 @@ void Combat::monstersAttack() { if (_gmonHit[idx] != -1) { monsterData = &map._monsterData[_gmonHit[idx]]; powNum = MONSTER_SHOOT_POW[monsterData->_attackType]; - if (powNum != 12) + if (powNum != POW_MAGIC_ARROW) break; } } - _powSprites.load(Common::String::format("pow%d.icn", powNum)); + _powSprites.load(Common::String::format("pow%d.icn", (int)powNum)); sound.playFX(ATTACK_TYPE_FX[monsterData->_attackType]); for (int charNum = 0; charNum < MAX_PARTY_COUNT; ++charNum) { @@ -1799,7 +1802,7 @@ void Combat::giveExperience(int experience) { } } -void Combat::multiAttack(int powNum) { +void Combat::rangedAttack(PowType powNum) { Interface &intf = *_vm->_interface; Map &map = *_vm->_map; Party &party = *_vm->_party; @@ -1809,7 +1812,7 @@ void Combat::multiAttack(int powNum) { _damageType = DT_POISON; _shootType = ST_1; Common::fill(&_shooting[0], &_shooting[6], 1); - } else if (powNum == 11) { + } else if (powNum == POW_ARROW) { _shootType = ST_1; bool flag = false; @@ -1830,13 +1833,15 @@ void Combat::multiAttack(int powNum) { sound.playFX(21); return; } + + sound.playFX(49); } else { _shooting[0] = 1; _shootType = ST_0; } intf._charsShooting = true; - _powSprites.load(Common::String::format("pow%d.icn", powNum)); + _powSprites.load(Common::String::format("pow%d.icn", (int)powNum)); int monsterIndex = _monsterIndex; int monster2Attack = _monster2Attack; bool attackedFlag = false; @@ -1914,7 +1919,7 @@ void Combat::multiAttack(int powNum) { } } else { int cell = map.getCell(2); - if (cell < map.mazeData()._difficulties._wallNoPass) { + if (cell >= map.mazeData()._difficulties._wallNoPass) { sound.playFX(46); goto finished; } @@ -1964,7 +1969,7 @@ void Combat::multiAttack(int powNum) { } } else { int cell = map.getCell(7); - if (cell < map.mazeData()._difficulties._wallNoPass) { + if (cell >= map.mazeData()._difficulties._wallNoPass) { sound.playFX(46); goto finished; } @@ -2014,7 +2019,7 @@ void Combat::multiAttack(int powNum) { } } else { int cell = map.getCell(14); - if (cell < map.mazeData()._difficulties._wallNoPass) { + if (cell >= map.mazeData()._difficulties._wallNoPass) { sound.playFX(46); goto finished; } @@ -2058,7 +2063,7 @@ done: void Combat::shootRangedWeapon() { _rangeType = RT_ALL; _damageType = DT_PHYSICAL; - multiAttack(11); + rangedAttack(POW_ARROW); } } // End of namespace Xeen diff --git a/engines/xeen/combat.h b/engines/xeen/combat.h index 8a7c676018..3a79a2ea22 100644 --- a/engines/xeen/combat.h +++ b/engines/xeen/combat.h @@ -68,6 +68,15 @@ enum CombatMode { COMBATMODE_0 = 0, COMBATMODE_1 = 1, COMBATMODE_2 = 2 }; +enum PowType { + POW_INVALID = -1, POW_FIREBALL = 0, POW_INCINERATE = 1, + POW_FIERY_FLAIL = 2, POW_LIGHTNING = 3, POW_MEGAVOLTS = 4, + POW_SPARKS = 5, POW_STOPPER = 6, POW_MAGIC_ORB = 7, + POW_COLD_RAY = 8, POW_FROST_WAVE = 9, POW_SPRAY = 10, + POW_ARROW = 11, POW_MAGIC_ARROW = 12, POW_ENERGY_BLAST = 13, + POW_SPARKLES = 14, POW_DEADLY_SWARM = 15 +}; + class XeenEngine; class Character; class XeenItem; @@ -208,7 +217,10 @@ public: int stopAttack(const Common::Point &diffPt); - void multiAttack(int powNum); + /** + * Called to do ranged attacks, both with bows or using a spell + */ + void rangedAttack(PowType powNum); /** * Fires off a ranged attack at all oncoming monsters diff --git a/engines/xeen/dialogs.cpp b/engines/xeen/dialogs.cpp index 3304689416..92082b4e0b 100644 --- a/engines/xeen/dialogs.cpp +++ b/engines/xeen/dialogs.cpp @@ -77,6 +77,11 @@ bool ButtonContainer::checkEvents(XeenEngine *vm) { return true; } } + + if (Common::Rect(8, 8, 224, 135).contains(pt)) { + _buttonValue = 1; + return true; + } } else if (events.isKeyPending()) { Common::KeyState keyState; events.getKey(keyState); diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp index 689a797cce..230bafc695 100644 --- a/engines/xeen/interface.cpp +++ b/engines/xeen/interface.cpp @@ -1247,6 +1247,7 @@ void Interface::draw3d(bool updateFlag, bool skipDelay) { Screen &screen = *_vm->_screen; Scripts &scripts = *_vm->_scripts; + events.updateGameCounter(); if (screen._windows[11]._enabled) return; diff --git a/engines/xeen/interface_map.cpp b/engines/xeen/interface_map.cpp index 1ada1c6e77..dc6cec39ef 100644 --- a/engines/xeen/interface_map.cpp +++ b/engines/xeen/interface_map.cpp @@ -4284,7 +4284,7 @@ void InterfaceMap::drawIndoors() { _indoorList._fwl_1F1R._sprites = &map._wallSprites._fwl2; } else if (_wo[167]) { _indoorList._fwl_1F1R._frame = _overallFrame + 1; - _indoorList._fwl_1F1R._sprites = &map._wallSprites._fwl2; + _indoorList._fwl_1F1R._sprites = &map._wallSprites._fwl1; } else if (_wo[127]) { _indoorList._fwl_1F1R._frame = 1; _indoorList._fwl_1F1R._sprites = &map._wallSprites._fwl2; diff --git a/engines/xeen/spells.cpp b/engines/xeen/spells.cpp index b3471df96c..ff737d9e77 100644 --- a/engines/xeen/spells.cpp +++ b/engines/xeen/spells.cpp @@ -250,7 +250,7 @@ void Spells::acidSpray() { combat._damageType = DT_POISON; combat._rangeType = RT_ALL; sound.playFX(17); - combat.multiAttack(10); + combat.rangedAttack(POW_SPRAY); } void Spells::awaken() { @@ -277,7 +277,7 @@ void Spells::beastMaster() { combat._damageType = DT_BEASTMASTER; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(7); + combat.rangedAttack(POW_MAGIC_ORB); } void Spells::bless() { @@ -302,7 +302,7 @@ void Spells::coldRay() { combat._damageType = DT_COLD; combat._rangeType = RT_ALL; sound.playFX(15); - combat.multiAttack(8); + combat.rangedAttack(POW_COLD_RAY); } void Spells::createFood() { @@ -378,7 +378,7 @@ void Spells::dancingSword() { combat._damageType = DT_PHYSICAL; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(14); + combat.rangedAttack(POW_SPARKLES); } void Spells::dayOfProtection() { @@ -422,7 +422,7 @@ void Spells::deadlySwarm() { combat._damageType = DT_PHYSICAL; combat._rangeType = RT_GROUP; sound.playFX(13); - combat.multiAttack(15); + combat.rangedAttack(POW_DEADLY_SWARM); } void Spells::detectMonster() { @@ -505,20 +505,22 @@ void Spells::dragonSleep() { combat._damageType = DT_DRAGONSLEEP; combat._rangeType = RT_SINGLE; sound.playFX(18); - combat.multiAttack(7); + combat.rangedAttack(POW_MAGIC_ORB); } void Spells::elementalStorm() { Combat &combat = *_vm->_combat; Sound &sound = *_vm->_sound; static const int STORM_FX_LIST[4] = { 13, 14, 15, 17 }; - static const int STORM_MA_LIST[4] = { 0, 5, 9, 10 }; + static const PowType STORM_MA_LIST[4] = { + POW_FIREBALL, POW_SPARKS, POW_FROST_WAVE, POW_SPRAY + }; combat._monsterDamage = 150; combat._damageType = (DamageType)_vm->getRandomNumber(DT_FIRE, DT_POISON); combat._rangeType = RT_ALL; sound.playFX(STORM_FX_LIST[combat._damageType]); - combat.multiAttack(STORM_MA_LIST[combat._damageType]); + combat.rangedAttack(STORM_MA_LIST[combat._damageType]); } void Spells::enchantItem() { @@ -541,7 +543,7 @@ void Spells::energyBlast() { combat._damageType = DT_ENERGY; combat._rangeType = RT_SINGLE; sound.playFX(16); - combat.multiAttack(13); + combat.rangedAttack(POW_ENERGY_BLAST); } void Spells::etherialize() { @@ -570,7 +572,7 @@ void Spells::fantasticFreeze() { combat._damageType = DT_COLD; combat._rangeType = RT_GROUP; sound.playFX(15); - combat.multiAttack(8); + combat.rangedAttack(POW_COLD_RAY); } void Spells::fieryFlail() { @@ -581,7 +583,7 @@ void Spells::fieryFlail() { combat._damageType = DT_FIRE; combat._rangeType = RT_SINGLE; sound.playFX(13); - combat.multiAttack(2); + combat.rangedAttack(POW_FIERY_FLAIL); } void Spells::fingerOfDeath() { @@ -592,7 +594,7 @@ void Spells::fingerOfDeath() { combat._damageType = DT_FINGEROFDEATH; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(14); + combat.rangedAttack(POW_SPARKLES); } void Spells::fireball() { @@ -603,7 +605,7 @@ void Spells::fireball() { combat._damageType = DT_FIRE; combat._rangeType = RT_GROUP; sound.playFX(13); - combat.multiAttack(0); + combat.rangedAttack(POW_FIREBALL); } void Spells::firstAid() { @@ -629,7 +631,7 @@ void Spells::flyingFist() { combat._damageType = DT_PHYSICAL; combat._rangeType = RT_SINGLE; sound.playFX(18); - combat.multiAttack(14); + combat.rangedAttack(POW_SPARKLES); } void Spells::frostbite() { @@ -640,7 +642,7 @@ void Spells::frostbite() { combat._damageType = DT_COLD; combat._rangeType = RT_SINGLE; sound.playFX(8); - combat.multiAttack(8); + combat.rangedAttack(POW_COLD_RAY); } void Spells::golemStopper() { @@ -651,7 +653,7 @@ void Spells::golemStopper() { combat._damageType = DT_GOLEMSTOPPER; combat._rangeType = RT_SINGLE; sound.playFX(16); - combat.multiAttack(6); + combat.rangedAttack(POW_STOPPER); } void Spells::heroism() { @@ -680,7 +682,7 @@ void Spells::holyWord() { combat._damageType = DT_HOLYWORD; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(13); + combat.rangedAttack(POW_ENERGY_BLAST); } void Spells::hypnotize() { @@ -691,7 +693,7 @@ void Spells::hypnotize() { combat._damageType = DT_HYPNOTIZE; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(7); + combat.rangedAttack(POW_MAGIC_ORB); } void Spells::identifyMonster() { @@ -713,7 +715,7 @@ void Spells::implosion() { combat._damageType = DT_ENERGY; combat._rangeType = RT_SINGLE; sound.playFX(18); - combat.multiAttack(6); + combat.rangedAttack(POW_STOPPER); } void Spells::incinerate() { @@ -724,7 +726,7 @@ void Spells::incinerate() { combat._damageType = DT_FIRE; combat._rangeType = RT_SINGLE; sound.playFX(22); - combat.multiAttack(1); + combat.rangedAttack(POW_INCINERATE); } void Spells::inferno() { @@ -735,7 +737,7 @@ void Spells::inferno() { combat._damageType = DT_FIRE; combat._rangeType = RT_GROUP; sound.playFX(13); - combat.multiAttack(1); + combat.rangedAttack(POW_INCINERATE); } void Spells::insectSpray() { @@ -746,7 +748,7 @@ void Spells::insectSpray() { combat._damageType = DT_INSECT_SPRAY; combat._rangeType = RT_GROUP; sound.playFX(17); - combat.multiAttack(10); + combat.rangedAttack(POW_SPRAY); } void Spells::itemToGold() { @@ -825,7 +827,7 @@ void Spells::lightningBolt() { combat._damageType = DT_ELECTRICAL; combat._rangeType = RT_GROUP; sound.playFX(14); - combat.multiAttack(3); + combat.rangedAttack(POW_LIGHTNING); } void Spells::lloydsBeacon() { @@ -842,7 +844,7 @@ void Spells::magicArrow() { combat._monsterDamage = 0; combat._damageType = DT_MAGIC_ARROW; combat._rangeType = RT_SINGLE; - combat.multiAttack(11); + combat.rangedAttack(POW_ARROW); } void Spells::massDistortion() { @@ -853,7 +855,7 @@ void Spells::massDistortion() { combat._damageType = DT_MASS_DISTORTION; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(6); + combat.rangedAttack(POW_STOPPER); } void Spells::megaVolts() { @@ -864,7 +866,7 @@ void Spells::megaVolts() { combat._damageType = DT_ELECTRICAL; combat._rangeType = RT_GROUP; sound.playFX(14); - combat.multiAttack(4); + combat.rangedAttack(POW_MEGAVOLTS); } void Spells::moonRay() { @@ -877,7 +879,7 @@ void Spells::moonRay() { combat._damageType = DT_ENERGY; combat._rangeType = RT_ALL; sound.playFX(16); - combat.multiAttack(13); + combat.rangedAttack(POW_ENERGY_BLAST); for (uint idx = 0; idx < party._activeParty.size(); ++idx) { sound.playFX(30); @@ -910,7 +912,7 @@ void Spells::pain() { combat._damageType = DT_PHYSICAL; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(14); + combat.rangedAttack(POW_SPARKLES); } void Spells::poisonVolley() { @@ -921,7 +923,7 @@ void Spells::poisonVolley() { combat._damageType = DT_POISON_VOLLEY; combat._rangeType = RT_ALL; sound.playFX(49); - combat.multiAttack(11); + combat.rangedAttack(POW_ARROW); } void Spells::powerCure() { @@ -956,7 +958,7 @@ void Spells::prismaticLight() { combat._damageType = (DamageType)_vm->getRandomNumber(DT_PHYSICAL, DT_ENERGY); combat._rangeType = RT_ALL; sound.playFX(18); - combat.multiAttack(14); + combat.rangedAttack(POW_SPARKLES); } void Spells::protectionFromElements() { @@ -1074,7 +1076,7 @@ void Spells::shrapMetal() { combat._damageType = DT_PHYSICAL; combat._rangeType = RT_GROUP; sound.playFX(16); - combat.multiAttack(15); + combat.rangedAttack(POW_DEADLY_SWARM); } void Spells::sleep() { @@ -1085,7 +1087,7 @@ void Spells::sleep() { combat._damageType = DT_SLEEP; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(7); + combat.rangedAttack(POW_MAGIC_ORB); } void Spells::sparks() { @@ -1096,7 +1098,7 @@ void Spells::sparks() { combat._damageType = DT_ELECTRICAL; combat._rangeType = RT_GROUP; sound.playFX(14); - combat.multiAttack(5); + combat.rangedAttack(POW_SPARKS); } void Spells::starBurst() { @@ -1107,7 +1109,7 @@ void Spells::starBurst() { combat._damageType = DT_FIRE; combat._rangeType = RT_ALL; sound.playFX(13); - combat.multiAttack(15); + combat.rangedAttack(POW_DEADLY_SWARM); } void Spells::stoneToFlesh() { @@ -1132,7 +1134,7 @@ void Spells::sunRay() { combat._damageType = DT_ENERGY; combat._rangeType = RT_ALL; sound.playFX(16); - combat.multiAttack(13); + combat.rangedAttack(POW_ENERGY_BLAST); } void Spells::superShelter() { @@ -1284,7 +1286,7 @@ void Spells::toxicCloud() { combat._damageType = DT_POISON; combat._rangeType = RT_GROUP; sound.playFX(17); - combat.multiAttack(10); + combat.rangedAttack(POW_SPRAY); } void Spells::turnUndead() { @@ -1295,7 +1297,7 @@ void Spells::turnUndead() { combat._damageType = DT_UNDEAD; combat._rangeType = RT_GROUP; sound.playFX(18); - combat.multiAttack(13); + combat.rangedAttack(POW_ENERGY_BLAST); } void Spells::walkOnWater() { @@ -1323,7 +1325,7 @@ void Spells::frostbite2() { combat._damageType = DT_COLD; combat._rangeType = RT_SINGLE; sound.playFX(15); - combat.multiAttack(9); + combat.rangedAttack(POW_FROST_WAVE); } } // End of namespace Xeen diff --git a/engines/xeen/town.cpp b/engines/xeen/town.cpp index 13a631aa47..fb5df83303 100644 --- a/engines/xeen/town.cpp +++ b/engines/xeen/town.cpp @@ -1193,6 +1193,9 @@ bool TownMessage::execute(int portrait, const Common::String &name, const Common int confirm) { EventsManager &events = *_vm->_events; Interface &intf = *_vm->_interface; + Map &map = *_vm->_map; + Party &party = *_vm->_party; + Resources &res = *_vm->_resources; Screen &screen = *_vm->_screen; Town &town = *_vm->_town; Window &w = screen._windows[11]; @@ -1205,7 +1208,8 @@ bool TownMessage::execute(int portrait, const Common::String &name, const Common if (!confirm) loadButtons(); - if (town._townSprites[0].empty()) { + if (town._townSprites.empty()) { + town._townSprites.resize(2); town._townSprites[0].load(Common::String::format("face%02d.fac", portrait)); town._townSprites[1].load("frame.fac"); } @@ -1215,25 +1219,32 @@ bool TownMessage::execute(int portrait, const Common::String &name, const Common int result = -1; Common::String msgText = text; - for (;;) { + do { Common::String msg = Common::String::format("\r\v014\x03c\t125%s\t000\v054%s", name.c_str(), msgText.c_str()); + + // Count the number of words const char *msgEnd = w.writeString(msg.c_str()); int wordCount = 0; - for (const char *msgP = msg.c_str(); msgP < msgEnd; ++msgP) { + for (const char *msgP = msg.c_str(); msgP != msgEnd && *msgP; ++msgP) { if (*msgP == ' ') ++wordCount; } - town._drawCtr2 = wordCount * 2; + town._drawCtr2 = wordCount * 2; // Set timeout town._townSprites[1].draw(screen, 0, Common::Point(16, 16)); town._townSprites[0].draw(screen, town._drawFrameIndex, Common::Point(23, 22)); w.update(); - if (!msgEnd) { - // Doesn't look like the code here in original can ever be reached - assert(0); + if (!msgEnd && !confirm) { + res._globalSprites.draw(screen, 7, Common::Point(232, 74)); + res._globalSprites.draw(screen, 0, Common::Point(235, 75)); + res._globalSprites.draw(screen, 2, Common::Point(260, 75)); + screen._windows[34].update(); + + intf._face1State = map._headData[party._mazePosition.y][party._mazePosition.x]._left; + intf._face2State = map._headData[party._mazePosition.y][party._mazePosition.x]._right; } if (confirm == 2) { @@ -1248,28 +1259,31 @@ bool TownMessage::execute(int portrait, const Common::String &name, const Common clearButtons(); do { - events.wait(3); + events.pollEventsAndWait(); checkEvents(_vm); + if (_vm->shouldQuit()) return false; - town.drawTownAnim(false); - events.updateGameCounter(); + while (events.timeElapsed() >= 3) { + town.drawTownAnim(false); + events.updateGameCounter(); + } } while (!_buttonValue); if (msgEnd) + // Another screen of text remaining break; - if (!msgEnd) { - if (confirm || _buttonValue == Common::KEYCODE_ESCAPE || - _buttonValue == Common::KEYCODE_n) - result = 0; - else if (_buttonValue == Common::KEYCODE_y) - result = 1; - } + if (confirm || _buttonValue == Common::KEYCODE_ESCAPE || + _buttonValue == Common::KEYCODE_n) + result = 0; + else if (_buttonValue == Common::KEYCODE_y) + result = 1; } while (result == -1); if (msgEnd) { + // Text remaining, so cut off already displayed page's msgText = Common::String(msgEnd); town._drawCtr2 = wordCount; continue; @@ -1282,6 +1296,7 @@ bool TownMessage::execute(int portrait, const Common::String &name, const Common town._townSprites[0].clear(); town._townSprites[1].clear(); + events.clearEvents(); return result == 1; } diff --git a/engines/xeen/worldofxeen/worldofxeen.cpp b/engines/xeen/worldofxeen/worldofxeen.cpp index 93a0005088..0b19c607ff 100644 --- a/engines/xeen/worldofxeen/worldofxeen.cpp +++ b/engines/xeen/worldofxeen/worldofxeen.cpp @@ -38,6 +38,10 @@ WorldOfXeenEngine::WorldOfXeenEngine(OSystem *syst, const XeenGameDescription *g void WorldOfXeenEngine::outerGameLoop() { //_pendingAction = getGameID() == GType_DarkSide ? WOX_DARKSIDE_INTRO : WOX_CLOUDS_INTRO; _pendingAction = WOX_MENU; + if (gDebugLevel >= 1) + // Skip main menu when starting in debug mode + _pendingAction = WOX_PLAY_GAME; + while (!shouldQuit() && _pendingAction != WOX_QUIT) { switch (_pendingAction) { case WOX_CLOUDS_INTRO: diff --git a/engines/xeen/worldofxeen/worldofxeen.h b/engines/xeen/worldofxeen/worldofxeen.h index 7dbb710430..0177e7cacf 100644 --- a/engines/xeen/worldofxeen/worldofxeen.h +++ b/engines/xeen/worldofxeen/worldofxeen.h @@ -61,6 +61,8 @@ public: void setPendingAction(WOXGameAction action) { _pendingAction = action; } }; +#define WOX_VM (*(::Xeen::WorldOfXeen::WorldOfXeenEngine *)g_vm) + } // End of namespace WorldOfXeen } // End of namespace Xeen diff --git a/engines/xeen/worldofxeen/worldofxeen_menu.cpp b/engines/xeen/worldofxeen/worldofxeen_menu.cpp index ff73cab4bb..fb25ba1757 100644 --- a/engines/xeen/worldofxeen/worldofxeen_menu.cpp +++ b/engines/xeen/worldofxeen/worldofxeen_menu.cpp @@ -23,6 +23,7 @@ #include "common/scummsys.h" #include "xeen/worldofxeen/worldofxeen_menu.h" #include "xeen/resources.h" +#include "xeen/worldofxeen/worldofxeen.h" namespace Xeen { namespace WorldOfXeen { @@ -92,13 +93,17 @@ void WorldOfXeenMenu::execute() { int key = toupper(_buttonValue); _buttonValue = 0; - if (key == 'C' || key == 'V') { + if (key == 27) { + // Hide the options menu + break; + } else if (key == 'C' || key == 'V') { // Show credits CreditsScreen::show(_vm); break; - } else if (key == 27) { - // Hide the options menu - break; + } else if (key == 'S') { + // Start new game + WOX_VM._pendingAction = WOX_PLAY_GAME; + return; } } } diff --git a/engines/xeen/xeen.cpp b/engines/xeen/xeen.cpp index 451afcc198..e2dae4cbf8 100644 --- a/engines/xeen/xeen.cpp +++ b/engines/xeen/xeen.cpp @@ -107,7 +107,7 @@ void XeenEngine::initialize() { _eventData = f.readStream(f.size()); // Set graphics mode - initGraphics(320, 200, false); + initGraphics(320, 200); // If requested, load a savegame instead of showing the intro if (ConfMan.hasKey("save_slot")) { diff --git a/engines/zvision/zvision.cpp b/engines/zvision/zvision.cpp index 6a18cf8e1b..9eb6465947 100644 --- a/engines/zvision/zvision.cpp +++ b/engines/zvision/zvision.cpp @@ -401,13 +401,13 @@ void ZVision::initScreen() { ((WINDOW_HEIGHT - workingWindowHeight) / 2) + workingWindowHeight ); - initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_screenPixelFormat); + initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, &_screenPixelFormat); } void ZVision::initHiresScreen() { _renderManager->upscaleRect(_workingWindow); - initGraphics(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, true, &_screenPixelFormat); + initGraphics(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, &_screenPixelFormat); } } // End of namespace ZVision |