diff options
Diffstat (limited to 'engines')
380 files changed, 37707 insertions, 12385 deletions
diff --git a/engines/agos/detection_tables.h b/engines/agos/detection_tables.h index 77fc88c6bb..2f4709c49e 100644 --- a/engines/agos/detection_tables.h +++ b/engines/agos/detection_tables.h @@ -1259,6 +1259,32 @@ static const AGOSGameDescription gameDescriptions[] = { GF_TALKIE | GF_OLD_BUNDLE | GF_PLANAR }, + // Simon the Sorcerer 1 - English Amiga CD32 demo, from the cover disc of + // issue 5 (October 1994) of Amiga CD32 Gamer + { + { + "simon1", + "CD32 Demo", + + { + { "gameamiga", GAME_BASEFILE, "e243f9229f9728b3476e54d2cf5f18a1", 27998}, + { "icon.pkd", GAME_ICONFILE, "565ef7a98dcc21ef526a2bb10b6f42ed", 18979}, + { "stripped.txt", GAME_STRFILE, "94413c71c86c32ed9baaa1c74a151cb3", 243}, + { "tbllist", GAME_TBLFILE, "f9d5bf2ce09f82289c791c3ca26e1e4b", 696}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformAmiga, + ADGF_CD | ADGF_DEMO, + GUIO2(GUIO_NOSUBTITLES, GUIO_NOMIDI) + }, + + GType_SIMON1, + GID_SIMON1CD32, + GF_TALKIE | GF_OLD_BUNDLE | GF_PLANAR + }, + + // Simon the Sorcerer 1 - English DOS Floppy Demo { { diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp index 33145b7d0d..5a1f9f1917 100644 --- a/engines/agos/gfx.cpp +++ b/engines/agos/gfx.cpp @@ -547,7 +547,7 @@ void AGOSEngine_Simon1::drawMaskedImage(VC10_state *state) { if ((dst[count * 2] & 0xF0) == 0x20) dst[count * 2] = src[count * 2]; if (mask[count + state->x_skip] & 0x0F) - if ((dst[count * 2 + 1] & 0x0F) == 0x20) + if ((dst[count * 2 + 1] & 0xF0) == 0x20) dst[count * 2 + 1] = src[count * 2 + 1]; } else { /* no transparency */ diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp index 8a4e87017a..687a8ef1cf 100644 --- a/engines/agos/input.cpp +++ b/engines/agos/input.cpp @@ -460,7 +460,7 @@ void AGOSEngine_Simon1::handleMouseWheelUp() { _saveLoadEdit = false; listSaveGames(); } - } else { + } else { AGOSEngine::handleMouseWheelUp(); } } @@ -472,11 +472,11 @@ void AGOSEngine_Simon1::handleMouseWheelDown() { _saveLoadRowCurPos += 1; if (_saveLoadRowCurPos >= _numSaveGameRows) _saveLoadRowCurPos = _numSaveGameRows; - + _saveLoadEdit = false; listSaveGames(); } - } else { + } else { AGOSEngine::handleMouseWheelDown(); } } @@ -492,7 +492,7 @@ void AGOSEngine_Elvira2::handleMouseWheelUp() { _saveLoadRowCurPos -= 3; listSaveGames(); - } else { + } else { AGOSEngine::handleMouseWheelUp(); } } @@ -506,7 +506,7 @@ void AGOSEngine_Elvira2::handleMouseWheelDown() { _saveLoadRowCurPos = 1; listSaveGames(); - } else { + } else { AGOSEngine::handleMouseWheelDown(); } } diff --git a/engines/bbvs/bbvs.cpp b/engines/bbvs/bbvs.cpp index d2e13e525c..11e4b6cea7 100644 --- a/engines/bbvs/bbvs.cpp +++ b/engines/bbvs/bbvs.cpp @@ -60,7 +60,7 @@ static const BBPoint kInventorySlotPositions[] = { {135, 93}, {134, 145}, { 96, 224}, {128, 224}, {160, 224}, {192, 224}, {224, 224}, {240, 224}, {256, 224}, { 0, 0} }; - + static const BBRect kVerbRects[6] = { {-32, -2, 19, 27}, {-33, -33, 19, 27}, { 12, -2, 19, 27}, { 13, -33, 19, 27}, {-10, 8, 19, 27}, {-11, -49, 19, 27} @@ -77,7 +77,7 @@ bool WalkArea::contains(const Common::Point &pt) const { BbvsEngine::BbvsEngine(OSystem *syst, const ADGameDescription *gd) : Engine(syst), _gameDescription(gd) { - + _random = new Common::RandomSource("bbvs"); _currActionCommandIndex = -1; _buttheadObject = nullptr; @@ -148,14 +148,14 @@ Common::Error BbvsEngine::run() { _gameModule = new GameModule(); _spriteModule = new SpriteModule(); _sound = new SoundMan(); - + allocSnapshot(); memset(_easterEggInput, 0, sizeof(_easterEggInput)); - + _gameTicks = 0; _playVideoNumber = 0; _bootSaveSlot = -1; - + memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus)); memset(_gameVars, 0, sizeof(_gameVars)); memset(_sceneVisited, 0, sizeof(_sceneVisited)); @@ -189,7 +189,7 @@ Common::Error BbvsEngine::run() { _playVideoNumber = 0; } } - + writeContinueSavegame(); freeSnapshot(); @@ -282,20 +282,20 @@ void BbvsEngine::updateGame() { inputTicks = 1; _gameTicks = _system->getMillis(); } - + if (inputTicks > 20) { inputTicks = 20; _gameTicks = _system->getMillis(); } - + if (inputTicks == 0) return; - + if (_mouseX >= 320 || _mouseY >= 240) { _mouseY = -1; _mouseX = -1; } - + bool done; do { @@ -304,14 +304,14 @@ void BbvsEngine::updateGame() { _mouseButtons &= ~kRightButtonClicked; _keyCode = Common::KEYCODE_INVALID; } while (--inputTicks && _playVideoNumber == 0 && _gameTicks > 0 && !done); - + if (!done && _playVideoNumber == 0 && _gameTicks > 0) { DrawList drawList; buildDrawList(drawList); _screen->drawDrawList(drawList, _spriteModule); drawScreen(); } - + _system->delayMillis(10); } @@ -329,7 +329,7 @@ void BbvsEngine::updateBackgroundSounds() { _backgroundSoundsActive[i] = 0; } } -} +} bool BbvsEngine::update(int mouseX, int mouseY, uint mouseButtons, Common::KeyCode keyCode) { @@ -339,7 +339,7 @@ bool BbvsEngine::update(int mouseX, int mouseY, uint mouseButtons, Common::KeyCo _bootSaveSlot = -1; return false; } - + if (_newSceneNum != 0) { _gameTicks = 0; return changeScene(); @@ -413,7 +413,7 @@ bool BbvsEngine::update(int mouseX, int mouseY, uint mouseButtons, Common::KeyCo } updateInventory(mouseButtons & kLeftButtonClicked); break; - + case kGSVerbs: _isSaveAllowed = false; updateVerbs(); @@ -426,7 +426,7 @@ bool BbvsEngine::update(int mouseX, int mouseY, uint mouseButtons, Common::KeyCo } } break; - + case kGSWait: case kGSWaitDialog: _isSaveAllowed = false; @@ -438,14 +438,14 @@ bool BbvsEngine::update(int mouseX, int mouseY, uint mouseButtons, Common::KeyCo else updateCommon(); break; - + case kGSDialog: _isSaveAllowed = true; saveSnapshot(); updateDialog(mouseButtons & kLeftButtonClicked); updateCommon(); break; - + } return true; @@ -503,7 +503,7 @@ void BbvsEngine::buildDrawList(DrawList &drawList) { // Verbs background drawList.add(_gameModule->getGuiSpriteIndex(13), _verbPos.x - _cameraPos.x, _verbPos.y - _cameraPos.y, 500); - // Selected inventory item + // Selected inventory item if (_currInventoryItem >= 0) { drawList.add(_gameModule->getInventoryItemSpriteIndex(2 * _currInventoryItem), _verbPos.x - _cameraPos.x, _verbPos.y - _cameraPos.y + 27, 500); @@ -538,7 +538,7 @@ void BbvsEngine::updateVerbs() { _mouseCursorSpriteIndex = 0; return; } - + for (int i = 0; i < 6; ++i) { const BBRect &verbRect = kVerbRects[i]; const int16 x = _verbPos.x + verbRect.x; @@ -551,7 +551,7 @@ void BbvsEngine::updateVerbs() { break; } } - + switch (_currVerbNum) { case kVerbLook: case kVerbUse: @@ -585,7 +585,7 @@ void BbvsEngine::updateDialog(bool clicked) { _gameState = kGSScene; return; } - + int slotX = (_mousePos.x - _cameraPos.x) / 32; if (slotX >= _dialogSlotCount) { @@ -597,7 +597,7 @@ void BbvsEngine::updateDialog(bool clicked) { _mouseCursorSpriteIndex = _gameModule->getGuiSpriteIndex(5); _activeItemType = kITDialog; - + // Find the selected dialog item index for (int i = 0; i < 50 && slotX >= 0; ++i) { if (_dialogItemStatus[i]) { @@ -605,7 +605,7 @@ void BbvsEngine::updateDialog(bool clicked) { _activeItemIndex = i; } } - + // Select the dialog item action if it was clicked if (clicked) { for (int i = 0; i < _gameModule->getActionsCount(); ++i) { @@ -622,7 +622,7 @@ void BbvsEngine::updateDialog(bool clicked) { } void BbvsEngine::updateInventory(bool clicked) { - + Common::Rect kInvButtonRects[3] = { Common::Rect(97, 13, 97 + 20, 13 + 26), Common::Rect(135, 15, 135 + 46, 15 + 25), @@ -636,10 +636,10 @@ void BbvsEngine::updateInventory(bool clicked) { if (_currVerbNum != kVerbLook && _currVerbNum != kVerbUse && _currVerbNum != kVerbInvItem) _currVerbNum = kVerbUse; - + const int16 mx = _mousePos.x - _cameraPos.x; const int16 my = _mousePos.y - _cameraPos.y; - + // Check inventory exit left/right edge of screen if (mx < 40 || mx > 280) { _mouseCursorSpriteIndex = _gameModule->getGuiSpriteIndex(10); @@ -652,7 +652,7 @@ void BbvsEngine::updateInventory(bool clicked) { return; } - // Check hovered/clicked inventory button + // Check hovered/clicked inventory button _inventoryButtonIndex = -1; if (kInvButtonRects[0].contains(mx, my)) { _inventoryButtonIndex = 0; @@ -677,10 +677,10 @@ void BbvsEngine::updateInventory(bool clicked) { // Find hovered/clicked inventory item int currItem = -1; - + if (_currVerbNum == kVerbInvItem) currItem = _currInventoryItem; - + _activeItemType = kITEmpty; for (int i = 0; i < 50; ++i) { @@ -769,7 +769,7 @@ void BbvsEngine::updateScene(bool clicked) { } } } - + for (int i = 0; i < _gameModule->getBgObjectsCount(); ++i) { BgObject *bgObject = _gameModule->getBgObject(i); if (lastPriority <= bgObject->rect.bottom && bgObject->rect.contains(_mousePos)) { @@ -921,7 +921,7 @@ bool BbvsEngine::performActionCommand(ActionCommand *actionCommand) { return false; case kActionCmdWalkObject: - { + { 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); @@ -998,14 +998,14 @@ bool BbvsEngine::performActionCommand(ActionCommand *actionCommand) { bool BbvsEngine::processCurrAction() { bool actionsFinished = false; - + if (_sceneObjectActions.size() == 0) { - + for (uint i = 0; i < _currAction->actionCommands.size(); ++i) { ActionCommand *actionCommand = &_currAction->actionCommands[i]; if (actionCommand->timeStamp != 0) break; - + if (actionCommand->cmd == kActionCmdMoveObject || actionCommand->cmd == kActionCmdAnimObject) { SceneObjectAction *sceneObjectAction = 0; // See if there's already an entry for the SceneObject @@ -1027,14 +1027,14 @@ bool BbvsEngine::processCurrAction() { sceneObjectAction->animationIndex = actionCommand->param; } } - + if (actionCommand->cmd == kActionCmdSetCameraPos) { _currCameraNum = actionCommand->param; _newCameraPos = _gameModule->getCameraInit(actionCommand->param)->cameraPos; } } - + // Delete entries for SceneObjects without anim for (uint i = 0; i < _sceneObjectActions.size();) { if (!_sceneObjects[_sceneObjectActions[i].sceneObjectIndex].anim) @@ -1107,7 +1107,7 @@ void BbvsEngine::updateCommon() { if (doActionCommands) { ActionCommand *actionCommand = &_currAction->actionCommands[_currActionCommandIndex]; - + while (actionCommand->timeStamp == _currActionCommandTimeStamp && _currActionCommandIndex < (int)_currAction->actionCommands.size()) { if (!performActionCommand(actionCommand)) { @@ -1161,7 +1161,7 @@ void BbvsEngine::updateCommon() { } updateWalkObject(sceneObject); } - + if (sceneObject->walkCount > 0 && sceneObject->turnCount == 0) { debug(5, "walk step, xIncr: %d, yIncr: %d", sceneObject->xIncr, sceneObject->yIncr); sceneObject->x += sceneObject->xIncr; @@ -1226,7 +1226,7 @@ void BbvsEngine::updateCommon() { } } } - + if (_cameraPos.x < _newCameraPos.x) ++_cameraPos.x; if (_cameraPos.x > _newCameraPos.x) @@ -1235,7 +1235,7 @@ void BbvsEngine::updateCommon() { ++_cameraPos.y; if (_cameraPos.y > _newCameraPos.y) --_cameraPos.y; - + // Check if Butthead is inside a scene exit if (_newSceneNum == 0 && !_currAction && _buttheadObject) { int16 buttheadX = _buttheadObject->x >> 16; @@ -1314,13 +1314,13 @@ void BbvsEngine::stopSounds() { bool BbvsEngine::runMinigame(int minigameNum) { debug(0, "BbvsEngine::runMinigame() minigameNum: %d", minigameNum); - + bool fromMainGame = _currSceneNum != kMainMenu; - + _sound->unloadSounds(); - + Minigame *minigame = 0; - + switch (minigameNum) { case kMinigameBbLoogie: minigame = new MinigameBbLoogie(this); @@ -1338,9 +1338,9 @@ bool BbvsEngine::runMinigame(int minigameNum) { error("Incorrect minigame number %d", minigameNum); break; } - + bool minigameResult = minigame->run(fromMainGame); - + delete minigame; // Check if the principal was hit with a megaloogie in the loogie minigame @@ -1370,11 +1370,11 @@ void BbvsEngine::checkEasterEgg(char key) { "SKCUS", "NAMTAH" }; - + static const int kEasterEggLengths[] = { 7, 5, 5, 6 }; - + if (_currSceneNum == kCredits) { memcpy(&_easterEggInput[1], &_easterEggInput[0], 6); _easterEggInput[0] = key; diff --git a/engines/bbvs/bbvs.h b/engines/bbvs/bbvs.h index 6a9a13905c..bbd8046a8b 100644 --- a/engines/bbvs/bbvs.h +++ b/engines/bbvs/bbvs.h @@ -149,7 +149,7 @@ struct BBPoint { struct BBRect { int16 x, y, width, height; -}; +}; struct BBPolygon { const BBPoint *points; @@ -229,66 +229,66 @@ public: private: const ADGameDescription *_gameDescription; Graphics::PixelFormat _pixelFormat; -public: +public: Common::RandomSource *_random; - + GameModule *_gameModule; SpriteModule *_spriteModule; SoundMan *_sound; - + Screen *_screen; - + int _bootSaveSlot; - + int _mouseX, _mouseY; uint _mouseButtons; Common::KeyCode _keyCode; - + int _mouseCursorSpriteIndex; int _gameState; int _gameTicks; - + Common::Point _mousePos; Common::Point _verbPos; Common::Point _walkMousePos; - + int _activeItemType; int _activeItemIndex; int _currTalkObjectIndex; - + Common::Point _cameraPos, _newCameraPos; - + int _newSceneNum, _prevSceneNum, _currSceneNum; int _playVideoNumber; - + int _dialogSlotCount; byte _dialogItemStatus[kDialogItemStatusCount]; - + byte _gameVars[kGameVarsCount]; byte _sceneVisited[kSceneVisitedCount]; int _currVerbNum; - + int _currInventoryItem; byte _inventoryItemStatus[kInventoryItemStatusCount]; int _inventoryButtonIndex; - + Action *_currAction; uint32 _currActionCommandTimeStamp; int _currActionCommandIndex; - + Common::Array<Action*> _walkAreaActions; - + SceneObject _sceneObjects[kSceneObjectsCount]; Common::Array<SceneObjectAction> _sceneObjectActions; - + SceneObject *_buttheadObject, *_beavisObject; int _currCameraNum; - + byte _backgroundSoundsActive[kSceneSoundsCount]; Audio::SoundHandle _speechSoundHandle; - + int _walkAreasCount; WalkArea _walkAreas[80]; int _walkInfosCount; @@ -298,40 +298,40 @@ public: Common::Rect _tempWalkableRects1[256]; Common::Rect _tempWalkableRects2[256]; WalkInfo *_walkInfoPtrs[256]; - + WalkArea *_sourceWalkArea, *_destWalkArea; Common::Point _sourceWalkAreaPt, _destWalkAreaPt, _finalWalkPt; int _currWalkDistance; bool _walkReachedDestArea; - + bool _hasSnapshot; byte *_snapshot; Common::SeekableMemoryWriteStream *_snapshotStream; - + char _easterEggInput[7]; - + void updateEvents(); int getRandom(int max); void drawDebugInfo(); void drawScreen(); - + void updateGame(); bool evalCondition(Conditions &conditions); bool evalCameraCondition(Conditions &conditions, int value); int evalDialogCondition(Conditions &conditions); void evalActionResults(ActionResults &results); - + void updateBackgroundSounds(); void loadScene(int sceneNum); void initScene(bool sounds); - bool changeScene(); + bool changeScene(); bool update(int mouseX, int mouseY, uint mouseButtons, Common::KeyCode keyCode); - + void buildDrawList(DrawList &drawList); - + void updateVerbs(); void updateDialog(bool clicked); void updateInventory(bool clicked); @@ -342,7 +342,7 @@ public: void skipCurrAction(); void updateCommon(); - + void updateWalkableRects(); void startWalkObject(SceneObject *sceneObject); void updateWalkObject(SceneObject *sceneObject); @@ -360,20 +360,20 @@ public: void walkFindPath(WalkArea *sourceWalkArea, int infoCount); int calcDistance(const Common::Point &pt1, const Common::Point &pt2); void walkFoundPath(int count); - + void updateSceneObjectsTurnValue(); void updateDialogConditions(); - - void playSpeech(int soundNum); + + void playSpeech(int soundNum); void stopSpeech(); - + void playSound(uint soundNum, bool loop = false); void stopSound(uint soundNum); void stopSounds(); - + bool runMinigame(int minigameNum); void playVideo(int videoNum); - + void runMainMenu(); void checkEasterEgg(char key); @@ -409,13 +409,13 @@ public: bool existsSavegame(int num); static Common::String getSavegameFilename(const Common::String &target, int num); static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); - + void allocSnapshot(); void freeSnapshot(); void saveSnapshot(); - + void writeContinueSavegame(); - + }; } // End of namespace Bbvs diff --git a/engines/bbvs/detection.cpp b/engines/bbvs/detection.cpp index e7383163f5..3e247aad99 100644 --- a/engines/bbvs/detection.cpp +++ b/engines/bbvs/detection.cpp @@ -133,7 +133,7 @@ SaveStateDescriptor BbvsMetaEngine::querySaveMetaInfos(const char *target, int s Bbvs::BbvsEngine::kReadSaveHeaderError error; error = Bbvs::BbvsEngine::readSaveHeader(in, true, header); delete in; - if (error == Bbvs::BbvsEngine::kRSHENoError) { + if (error == Bbvs::BbvsEngine::kRSHENoError) { SaveStateDescriptor desc(slot, header.description); // Slot 0 is used for the "Continue" save desc.setDeletableFlag(slot != 0); diff --git a/engines/bbvs/dialogs.cpp b/engines/bbvs/dialogs.cpp index 5247a58ec8..af95f06c1e 100644 --- a/engines/bbvs/dialogs.cpp +++ b/engines/bbvs/dialogs.cpp @@ -34,7 +34,7 @@ struct MenuButton { static const MenuButton kMenuButtons[] = { // Main menu - {"New Game", kCmdNewGame}, + {"New Game", kCmdNewGame}, {"Continue", kCmdContinue}, {"Options", kCmdOptions}, {"Mini Games", kCmdMiniGames}, @@ -61,7 +61,7 @@ MainMenu::~MainMenu() { } void MainMenu::init() { - _buttons[0] = new GUI::ButtonWidget(this, 0, 0, 1, 1, "", 0, 0); + _buttons[0] = new GUI::ButtonWidget(this, 0, 0, 1, 1, "", 0, 0); _buttons[1] = new GUI::ButtonWidget(this, 0, 0, 1, 1, "", 0, 0); _buttons[2] = new GUI::ButtonWidget(this, 0, 0, 1, 1, "", 0, 0); _buttons[3] = new GUI::ButtonWidget(this, 0, 0, 1, 1, "", 0, 0); @@ -76,14 +76,14 @@ void MainMenu::reflowLayout() { const int buttonWidth = screenW * 70 / 320; const int buttonHeight = screenH * 14 / 240; const int buttonPadding = screenW * 3 / 320; - + _w = 2 * buttonWidth + buttonPadding; _h = 3 * buttonHeight + 3 * buttonPadding; _x = (screenW - _w) / 2; _y = screenH - _h; int x = 0, y = 0; - + x = 0; y = 0; _buttons[0]->resize(x, y, buttonWidth, buttonHeight); diff --git a/engines/bbvs/dialogs.h b/engines/bbvs/dialogs.h index 2dce2a110b..7db0b182b7 100644 --- a/engines/bbvs/dialogs.h +++ b/engines/bbvs/dialogs.h @@ -67,13 +67,13 @@ protected: BbvsEngine *_vm; void init(); - + GUI::ButtonWidget *_buttons[5]; - + void gotoMenuScreen(int index); bool canContinue(); void gotoScene(int sceneNum); - + }; } diff --git a/engines/bbvs/gamemodule.cpp b/engines/bbvs/gamemodule.cpp index d6343084ab..80f0e81450 100644 --- a/engines/bbvs/gamemodule.cpp +++ b/engines/bbvs/gamemodule.cpp @@ -39,11 +39,11 @@ GameModule::~GameModule() { void GameModule::load(const char *filename) { debug(0, "GameModule::load()"); - + unload(); Common::File fd; - + if (!fd.open(filename)) error("GameModule::load() Could not open %s", filename); @@ -68,7 +68,7 @@ void GameModule::load(const char *filename) { fd.seek(0x1A8); _buttheadObjectIndex = fd.readUint32LE(); - + fd.close(); debug(0, "GameModule::load() OK"); @@ -177,7 +177,7 @@ int GameModule::getBgSpritePriority(int index) { int GameModule::getSceneSoundsCount() { return _sceneSoundsCount; } - + SceneSound *GameModule::getSceneSound(int index) { assert(index < _sceneSoundsCount); return &_sceneSounds[index]; @@ -257,7 +257,7 @@ void GameModule::unload() { void GameModule::loadBgSprites(Common::SeekableReadStream &s) { debug(0, "GameModule::loadBgSprites()"); - + s.seek(0x14); _bgSpriteCount = s.readUint32LE(); uint32 bgSpriteIndicesOffs = s.readUint32LE(); @@ -275,7 +275,7 @@ void GameModule::loadBgSprites(Common::SeekableReadStream &s) { void GameModule::loadCameraInits(Common::SeekableReadStream &s) { debug(0, "GameModule::loadCameraInits()"); - + s.seek(0x20); for (int i = 0; i < kCameraInitsCount; ++i) { CameraInit &cameraInit = _cameraInits[i]; @@ -283,13 +283,13 @@ void GameModule::loadCameraInits(Common::SeekableReadStream &s) { for (int j = 0; j < 8; ++j) cameraInit.cameraLinks[j] = s.readByte(); for (int j = 0; j < 8; ++j) - cameraInit.rects[j] = readRect(s); + cameraInit.rects[j] = readRect(s); } } void GameModule::loadWalkRects(Common::SeekableReadStream &s) { debug(0, "GameModule::loadWalkRects()"); - + s.seek(0x150); _walkRectsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); @@ -301,7 +301,7 @@ void GameModule::loadWalkRects(Common::SeekableReadStream &s) { void GameModule::loadSceneExits(Common::SeekableReadStream &s) { debug(0, "GameModule::loadSceneExits()"); - + s.seek(0x158); _sceneExitsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); @@ -329,13 +329,13 @@ void GameModule::loadBgObjects(Common::SeekableReadStream &s) { void GameModule::loadAnimations(Common::SeekableReadStream &s) { debug(0, "GameModule::loadAnimations()"); - + s.seek(0x168); _animationsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); _animations = new Animation[_animationsCount]; for (int i = 0; i < _animationsCount; ++i) { - Animation &anim = _animations[i]; + Animation &anim = _animations[i]; s.seek(offs + i * 20); anim.frameCount = s.readUint32LE(); uint32 frameSpriteIndicesOffs = s.readUint32LE(); @@ -363,7 +363,7 @@ void GameModule::loadAnimations(Common::SeekableReadStream &s) { void GameModule::loadSceneObjectDefs(Common::SeekableReadStream &s) { debug(0, "GameModule::loadSceneObjectDefs()"); - + s.seek(0x170); _sceneObjectDefsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); @@ -379,7 +379,7 @@ void GameModule::loadSceneObjectDefs(Common::SeekableReadStream &s) { void GameModule::loadSceneObjectInits(Common::SeekableReadStream &s) { debug(0, "GameModule::loadSceneObjectInits()"); - + s.seek(0x178); _sceneObjectInitsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); @@ -396,7 +396,7 @@ void GameModule::loadSceneObjectInits(Common::SeekableReadStream &s) { void GameModule::loadActions(Common::SeekableReadStream &s) { debug(0, "GameModule::loadActions()"); - + s.seek(0x180); _actionsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); @@ -427,7 +427,7 @@ void GameModule::loadActions(Common::SeekableReadStream &s) { void GameModule::loadGuiSpriteIndices(Common::SeekableReadStream &s) { debug(0, "GameModule::loadGuiSpriteIndices()"); - + s.seek(0x188); uint32 offs = s.readUint32LE(); s.seek(offs); @@ -437,7 +437,7 @@ void GameModule::loadGuiSpriteIndices(Common::SeekableReadStream &s) { void GameModule::loadInventoryItemSpriteIndices(Common::SeekableReadStream &s) { debug(0, "GameModule::loadInventoryItemSpriteIndices()"); - + s.seek(0x18C); uint32 offs = s.readUint32LE(); s.seek(offs); @@ -447,7 +447,7 @@ void GameModule::loadInventoryItemSpriteIndices(Common::SeekableReadStream &s) { void GameModule::loadInventoryItemInfos(Common::SeekableReadStream &s) { debug(0, "GameModule::loadInventoryItemInfos()"); - + s.seek(0x190); uint32 offs = s.readUint32LE(); s.seek(offs); @@ -462,7 +462,7 @@ void GameModule::loadInventoryItemInfos(Common::SeekableReadStream &s) { void GameModule::loadDialogItemSpriteIndices(Common::SeekableReadStream &s) { debug(0, "GameModule::loadDialogItemSpriteIndices()"); - + s.seek(0x194); uint32 offs = s.readUint32LE(); s.seek(offs); @@ -473,7 +473,7 @@ void GameModule::loadDialogItemSpriteIndices(Common::SeekableReadStream &s) { void GameModule::loadSceneSounds(Common::SeekableReadStream &s) { debug(0, "GameModule::loadSceneSounds()"); - + s.seek(0x1A0); _sceneSoundsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); @@ -487,7 +487,7 @@ void GameModule::loadSceneSounds(Common::SeekableReadStream &s) { void GameModule::loadPreloadSounds(Common::SeekableReadStream &s) { debug(0, "GameModule::loadPreloadSounds()"); - + s.seek(0x198); _preloadSoundsCount = s.readUint32LE(); uint32 offs = s.readUint32LE(); diff --git a/engines/bbvs/gamemodule.h b/engines/bbvs/gamemodule.h index 4d4f5b90a1..b31b95dcee 100644 --- a/engines/bbvs/gamemodule.h +++ b/engines/bbvs/gamemodule.h @@ -134,29 +134,29 @@ class GameModule { public: GameModule(); ~GameModule(); - + void load(const char *filename); - + int getFieldC(); int getButtheadObjectIndex(); - + int getGuiSpriteIndex(int index); int getInventoryItemSpriteIndex(int index); int getDialogItemSpriteIndex(int index); - + int getActionsCount(); Action *getAction(int index); - + InventoryItemInfo *getInventoryItemInfo(int index); CameraInit *getCameraInit(int cameraNum); - + int getSceneExitsCount(); SceneExit *getSceneExit(int index); - + int getWalkRectsCount(); Common::Rect *getWalkRects(); - + int getSceneObjectDefsCount(); SceneObjectDef *getSceneObjectDef(int index); @@ -170,44 +170,44 @@ public: int getBgSpriteIndex(int index); int getBgSpritePriority(int index); - int getSceneSoundsCount(); + int getSceneSoundsCount(); SceneSound *getSceneSound(int index); - uint getSceneSoundIndex(uint soundNum); - + uint getSceneSoundIndex(uint soundNum); + uint getPreloadSoundsCount(); uint getPreloadSound(uint index); Animation *getAnimation(int index); - + protected: - + int _bgSpriteCount; int *_bgSpriteIndices; int16 *_bgSpritePriorities; - + CameraInit _cameraInits[kCameraInitsCount]; - + int _walkRectsCount; Common::Rect *_walkRects; - + int _sceneExitsCount; SceneExit *_sceneExits; - + int _bgObjectsCount; BgObject *_bgObjects; - + int _animationsCount; Animation *_animations; - + int _sceneObjectDefsCount; SceneObjectDef *_sceneObjectDefs; - + int _sceneObjectInitsCount; SceneObjectInit *_sceneObjectInits; int _actionsCount; Action *_actions; - + int _sceneSoundsCount; SceneSound *_sceneSounds; @@ -218,16 +218,16 @@ protected: int _inventoryItemSpriteIndices[kInventoryItemSpriteCount]; InventoryItemInfo _inventoryItemInfos[kInventoryItemCount]; int _dialogItemSpriteIndices[kDialogItemSpriteCount]; - + int _fieldC; int _buttheadObjectIndex; Common::Point readPoint(Common::SeekableReadStream &s); Common::Rect readRect(Common::SeekableReadStream &s); Conditions readConditions(Common::SeekableReadStream &s); - + void unload(); - + void loadBgSprites(Common::SeekableReadStream &s); void loadCameraInits(Common::SeekableReadStream &s); void loadWalkRects(Common::SeekableReadStream &s); @@ -243,7 +243,7 @@ protected: void loadDialogItemSpriteIndices(Common::SeekableReadStream &s); void loadSceneSounds(Common::SeekableReadStream &s); void loadPreloadSounds(Common::SeekableReadStream &s); - + }; } // End of namespace Bbvs diff --git a/engines/bbvs/graphics.cpp b/engines/bbvs/graphics.cpp index 810d910abf..43840607c8 100644 --- a/engines/bbvs/graphics.cpp +++ b/engines/bbvs/graphics.cpp @@ -104,9 +104,9 @@ void Screen::drawSprite(Sprite &sprite, int x, int y) { } if (destX + width >= _surface->w) width = _surface->w - destX; - + debug(0, "drawSprite() (%d, %d, %d, %d); skipX: %d; skipY: %d; %d", destX, destY, width, height, skipX, skipY, sprite.type); - + if (sprite.type == 1) { for (int yc = 0; yc < height; ++yc) { byte *source = sprite.getRow(skipY + yc); diff --git a/engines/bbvs/minigames/bbairguitar.cpp b/engines/bbvs/minigames/bbairguitar.cpp index f2e42313e3..1984dbb0fd 100644 --- a/engines/bbvs/minigames/bbairguitar.cpp +++ b/engines/bbvs/minigames/bbairguitar.cpp @@ -198,12 +198,12 @@ void MinigameBbAirGuitar::buildDrawList1(DrawList &drawList) { if (_trackBarX > kTrackBarMaxX) _trackBarX = kTrackBarMaxX; - + _trackBarThumbRect.top = 208; _trackBarThumbRect.bottom = 218; _trackBarThumbRect.left = _trackBarX; _trackBarThumbRect.right = _trackBarX + 6; - + drawList.add(_objects[5].anim->frameIndices[0], _trackBarX, 208, 100); if (_playerMode != 0) { @@ -228,7 +228,7 @@ void MinigameBbAirGuitar::buildDrawList1(DrawList &drawList) { drawList.add(_objects[i].anim->frameIndices[frameIndex], kPointsTbl2[i - 47].x, kPointsTbl2[i - 47].y, 254); } } - + if (_backgroundSpriteIndex > 0) drawList.add(_backgroundSpriteIndex, 0, 0, 0); @@ -394,7 +394,7 @@ void MinigameBbAirGuitar::initObjects1() { _track[0].noteNum = -1; stop(); changePatch(0); - + } bool MinigameBbAirGuitar::updateStatus(int mouseX, int mouseY, uint mouseButtons) { @@ -408,7 +408,7 @@ bool MinigameBbAirGuitar::updateStatus(int mouseX, int mouseY, uint mouseButtons } bool MinigameBbAirGuitar::updateStatus0(int mouseX, int mouseY, uint mouseButtons) { - + if (mouseButtons & kAnyButtonDown) { stopSound(1); _rockTunePlaying = false; @@ -436,14 +436,14 @@ bool MinigameBbAirGuitar::updateStatus0(int mouseX, int mouseY, uint mouseButton } } - + return true; } bool MinigameBbAirGuitar::updateStatus1(int mouseX, int mouseY, uint mouseButtons) { - + int currTicks = _vm->_system->getMillis(); - + if (_playerMode == 1 && _track[_trackIndex].ticks <= currTicks - _noteStartTime) { noteOff(_track[_trackIndex].noteNum); if (_trackIndex < _trackCount && _track[++_trackIndex].noteNum != -1) @@ -481,17 +481,17 @@ bool MinigameBbAirGuitar::updateStatus1(int mouseX, int mouseY, uint mouseButton } else { ++_vuMeterRight2; } - + if (_resetAnims && _vm->_system->getMillis() - _noteStartTime >= 1000) resetObjs(); - + _objects[0].x = mouseX; _objects[0].y = mouseY; - + _trackBarMouseX = CLIP(mouseX, kTrackBarMinX, kTrackBarMaxX); - + bool checkClick = false; - + if (mouseButtons & kAnyButtonClicked) { checkClick = true; } else if (!(mouseButtons & kAnyButtonDown)) { @@ -506,14 +506,14 @@ bool MinigameBbAirGuitar::updateStatus1(int mouseX, int mouseY, uint mouseButton } } else if (!_movingTrackBar) checkClick = true; - + if (checkClick) { afterButtonReleased(); _objects[0].frameIndex = 1; - + if (ptInRect(&kRect2, mouseX, mouseY)) { - + if (_playerMode != 1 && ptInRect(&kPianoRect, mouseX, mouseY)) { for (int i = 0; i <= 12; ++i) { if (ptInPoly(&kPianoKeyAreas[i], mouseX, mouseY)) { @@ -538,7 +538,7 @@ bool MinigameBbAirGuitar::updateStatus1(int mouseX, int mouseY, uint mouseButton break; } } - + if (playerButtonNum >= 0) { _currButtonNum = playerButtonNum; _currPlayerButtonRect = &kPlayerButtonRects[playerButtonNum]; @@ -673,12 +673,12 @@ bool MinigameBbAirGuitar::updateStatus1(int mouseX, int mouseY, uint mouseButton } } } - + if (_buttonClickTicks + 1000 < currTicks) _buttonClickTicks = currTicks; - + int newKind = _buttonClickTicks + 500 < currTicks ? 1 : 0; - + switch (_playerMode) { case 1: @@ -770,20 +770,20 @@ bool MinigameBbAirGuitar::run(bool fromMainGame) { _gameResult = false; _gameDone = false; initObjects(); - + _spriteModule = new SpriteModule(); _spriteModule->load("bbairg/bbairg.000"); Palette palette = _spriteModule->getPalette(); _vm->_screen->setPalette(palette); - + loadSounds(); - + while (!_vm->shouldQuit() &&!_gameDone) { _vm->updateEvents(); update(); } - + _vm->_sound->unloadSounds(); delete _spriteModule; @@ -803,15 +803,15 @@ void MinigameBbAirGuitar::update() { inputTicks = 1; _gameTicks = _vm->_system->getMillis(); } - + if (_vm->_keyCode == Common::KEYCODE_ESCAPE) { _gameDone = true; return; } - + if (inputTicks == 0) return; - + bool done; do { @@ -820,9 +820,9 @@ void MinigameBbAirGuitar::update() { _vm->_mouseButtons &= ~kRightButtonClicked; _vm->_keyCode = Common::KEYCODE_INVALID; } while (--inputTicks && _gameTicks > 0 && !done); - + drawSprites(); - + _vm->_system->delayMillis(10); } @@ -1001,7 +1001,7 @@ void MinigameBbAirGuitar::calcTotalTicks1() { } void MinigameBbAirGuitar::noteOn(int noteNum) { - + if (_currNoteNum != -2) { if (noteNum == _currNoteNum) return; @@ -1087,7 +1087,7 @@ void MinigameBbAirGuitar::noteOn(int noteNum) { } void MinigameBbAirGuitar::noteOff(int noteNum) { - + if (_currNoteNum != noteNum) return; diff --git a/engines/bbvs/minigames/bbairguitar.h b/engines/bbvs/minigames/bbairguitar.h index d4fd6ec30c..40b8a50a03 100644 --- a/engines/bbvs/minigames/bbairguitar.h +++ b/engines/bbvs/minigames/bbairguitar.h @@ -32,7 +32,7 @@ public: MinigameBbAirGuitar(BbvsEngine *vm) : Minigame(vm) {}; bool run(bool fromMainGame); public: - + struct Obj { int kind; int x, y; @@ -44,24 +44,24 @@ public: int16 frameIndexAdd; int16 unk2; }; - + enum { kMaxObjectsCount = 256, kMaxTracks = 2049 }; - + struct PianoKeyInfo { int x, y; int frameIndex; }; - + struct TrackEvt { int8 noteNum; int16 ticks; }; - + Obj _objects[kMaxObjectsCount]; - + int _playerMode; bool _modified; @@ -82,24 +82,24 @@ public: int *_currFrameIndex; int _btn3KindToggle; - + const BBPolygon *_currPianoKeyArea; const Rect *_currPlayerButtonRect; - + bool _movingTrackBar; int _trackBarMouseX; int _trackBarX; Rect _trackBarThumbRect; - + int _currTrackPos, _totalTrackLength; int _ticksDelta; - + int _actionStartTrackPos, _actionTrackPos; int _actionStartTime; int _currNoteNum; int _currPatchNum; - + const ObjAnimation *getAnimation(int animIndex); bool ptInRect(const Rect *r, int x, int y); bool ptInPoly(const BBPolygon *poly, int x, int y); @@ -109,14 +109,14 @@ public: void buildDrawList1(DrawList &drawList); void drawSprites(); - + void initObjs(); Obj *getFreeObject(); - + void initObjects(); void initObjects0(); void initObjects1(); - + bool updateStatus(int mouseX, int mouseY, uint mouseButtons); bool updateStatus0(int mouseX, int mouseY, uint mouseButtons); bool updateStatus1(int mouseX, int mouseY, uint mouseButtons); @@ -124,7 +124,7 @@ public: void updateObjs(); void update(); - + void play(); void record(); void setPlayerMode3(); @@ -136,9 +136,9 @@ public: void noteOn(int noteNum); void noteOff(int noteNum); void resetObjs(); - + void loadSounds(); - void playNote(int noteNum); + void playNote(int noteNum); void stopNote(int noteNum); }; diff --git a/engines/bbvs/minigames/bbant.cpp b/engines/bbvs/minigames/bbant.cpp index 9786682ada..3678934345 100644 --- a/engines/bbvs/minigames/bbant.cpp +++ b/engines/bbvs/minigames/bbant.cpp @@ -94,7 +94,7 @@ void MinigameBbAnt::buildDrawList1(DrawList &drawList) { drawNumber(drawList, _score, 68, 16); drawList.add(getAnimation(166)->frameIndices[0], 230, 2, 2000); drawNumber(drawList, _levelTimeLeft, 280, 16); - + for (int i = 0; i < _stompCount; ++i) drawList.add(getAnimation(130)->frameIndices[0], 20 + i * 30, 230, 2000); @@ -116,7 +116,7 @@ void MinigameBbAnt::buildDrawList3(DrawList &drawList) { drawNumber(drawList, _hiScore, 208, 107); } -void MinigameBbAnt::drawMagnifyingGlass(DrawList &drawList) { +void MinigameBbAnt::drawMagnifyingGlass(DrawList &drawList) { scale2x(_objects[0].x - 28, _objects[0].y - 27); drawList.clear(); drawList.add(_objects[0].anim->frameIndices[0], _objects[0].x, _objects[0].y, _objects[0].priority); @@ -358,7 +358,7 @@ bool MinigameBbAnt::updateStatus0(int mouseX, int mouseY, uint mouseButtons) { _objects[0].x = 0; if (_objects[0].y < 0) _objects[0].y = 0; - + if ((mouseButtons & kLeftButtonDown) || (mouseButtons & kRightButtonDown)) { _gameState = 1; initObjects(); @@ -447,7 +447,7 @@ bool MinigameBbAnt::updateStatus1(int mouseX, int mouseY, uint mouseButtons) { int maxKindCount = 0, objKind = 0; _stompCounter2 = _stompCounter1; - + for (int i = 0; i < 4; ++i) testTbl[i] = _vm->getRandom(_bugsChanceByKind[i] - _bugsCountByKind[i]); @@ -471,7 +471,7 @@ bool MinigameBbAnt::updateStatus1(int mouseX, int mouseY, uint mouseButtons) { if (_stompCounter1 > 20) --_stompCounter1; } - + return true; } @@ -701,7 +701,7 @@ void MinigameBbAnt::updateBugObjAnim(int objIndex) { void MinigameBbAnt::updateObjAnim2(int objIndex) { Obj *obj = &_objects[objIndex]; - + obj->animIndexIncr += _vm->getRandom(3) - 1; if (obj->animIndexIncr < 0) obj->animIndexIncr = 7; @@ -736,7 +736,7 @@ bool MinigameBbAnt::isBugOutOfScreen(int objIndex) { void MinigameBbAnt::updateObjAnim3(int objIndex) { Obj *obj = &_objects[objIndex]; - + obj->animIndexIncr += _vm->getRandom(3) - 1; if (obj->animIndexIncr < 0) obj->animIndexIncr = 7; @@ -752,7 +752,7 @@ void MinigameBbAnt::updateBugObj1(int objIndex) { Obj *obj = &_objects[objIndex]; bool flag1 = false; bool flag2 = false; - + if (--obj->ticks == 0) { ++obj->frameIndex; if (obj->anim->frameCount == obj->frameIndex) { @@ -911,7 +911,7 @@ void MinigameBbAnt::updateStompObj(int objIndex) { void MinigameBbAnt::updateSmokeObj(int objIndex) { Obj *obj = &_objects[objIndex]; - + obj->x += obj->xIncr; obj->y += obj->yIncr; @@ -1047,7 +1047,7 @@ bool MinigameBbAnt::isMagGlassAtBug(int objIndex) { Obj *obj = &_objects[objIndex]; Obj *obj0 = &_objects[0]; bool result = false; - + if (obj->kind >= 1 && obj->kind <= 5) { const BBRect &frameRect1 = obj0->anim->frameRects[0]; const int obj1X1 = obj0->x + frameRect1.x; @@ -1102,7 +1102,7 @@ bool MinigameBbAnt::testObj5(int objIndex) { } void MinigameBbAnt::updateObjs(uint mouseButtons) { - + for (int i = 12; i < kMaxObjectsCount; ++i) { Obj *obj = &_objects[i]; @@ -1162,7 +1162,7 @@ void MinigameBbAnt::updateObjs(uint mouseButtons) { } } - + } } @@ -1170,9 +1170,9 @@ void MinigameBbAnt::updateObjs(uint mouseButtons) { bool MinigameBbAnt::run(bool fromMainGame) { memset(_objects, 0, sizeof(_objects)); - + _numbersAnim = getAnimation(167); - + _backgroundSpriteIndex = 303; _titleScreenSpriteIndex = 304; @@ -1187,23 +1187,23 @@ bool MinigameBbAnt::run(bool fromMainGame) { _gameDone = false; initObjects(); initVars(); - + _spriteModule = new SpriteModule(); _spriteModule->load("bbant/bbant.000"); Palette palette = _spriteModule->getPalette(); _vm->_screen->setPalette(palette); - + loadSounds(); _gameTicks = 0; playSound(12, true); - + while (!_vm->shouldQuit() &&!_gameDone) { _vm->updateEvents(); update(); } - + _vm->_sound->unloadSounds(); if (!_fromMainGame) @@ -1243,19 +1243,19 @@ void MinigameBbAnt::update() { _vm->_mouseButtons &= ~kRightButtonClicked; _vm->_keyCode = Common::KEYCODE_INVALID; } while (--inputTicks && _gameTicks > 0 && !done); - + drawSprites(); - + _vm->_system->delayMillis(10); } void MinigameBbAnt::scale2x(int x, int y) { - Graphics::Surface *surface = _vm->_screen->_surface; - + Graphics::Surface *surface = _vm->_screen->_surface; + int srcX = x + 14, srcY = y + 14; int srcW = kScaleDim, srcH = kScaleDim; - + if (srcX < 0) { srcW += srcX; srcX = 0; @@ -1265,21 +1265,21 @@ void MinigameBbAnt::scale2x(int x, int y) { srcH += srcY; srcY = 0; } - + if (srcX + srcW >= 320) srcW = 320 - srcX - 1; - + if (srcY + srcH >= 240) srcH = 240 - srcY - 1; - + for (int yc = 0; yc < srcH; ++yc) { byte *src = (byte*)surface->getBasePtr(srcX, srcY + yc); memcpy(&_scaleBuf[yc * kScaleDim], src, srcW); } - + int dstX = x, dstY = y; int dstW = 2 * kScaleDim, dstH = 2 * kScaleDim; - + if (dstX < 0) { dstW += dstX; dstX = 0; @@ -1289,15 +1289,15 @@ void MinigameBbAnt::scale2x(int x, int y) { dstH += dstY; dstY = 0; } - + if (dstX + dstW >= 320) dstW = 320 - dstX - 1; - + if (dstY + dstH >= 240) dstH = 240 - dstY - 1; - + int w = MIN(srcW * 2, dstW), h = MIN(srcH * 2, dstH); - + for (int yc = 0; yc < h; ++yc) { byte *src = _scaleBuf + kScaleDim * (yc / 2); byte *dst = (byte*)surface->getBasePtr(dstX, dstY + yc); diff --git a/engines/bbvs/minigames/bbant.h b/engines/bbvs/minigames/bbant.h index be2afe688d..88b4af9c71 100644 --- a/engines/bbvs/minigames/bbant.h +++ b/engines/bbvs/minigames/bbant.h @@ -32,7 +32,7 @@ public: MinigameBbAnt(BbvsEngine *vm) : Minigame(vm) {}; bool run(bool fromMainGame); public: - + struct Obj { int kind; int x, y, priority; @@ -55,21 +55,21 @@ public: int status2; int flag; }; - + enum { kMaxObjectsCount = 256, kScaleDim = 28 }; - + struct ObjInit { const ObjAnimation *anim1; const ObjAnimation *anim2; const ObjAnimation *anim3; int x, y; }; - + Obj _objects[kMaxObjectsCount]; - + int _score, _hiScore; int _totalBugsCount; @@ -96,37 +96,37 @@ public: int _countdown6; int _countdown5; int _countdown7; - + byte _scaleBuf[kScaleDim * kScaleDim]; const ObjAnimation *getAnimation(int animIndex); const ObjInit *getObjInit(int index); const ObjAnimation * const *getObjKindAnimTable(int kind); const ObjAnimation *getObjAnim(int index); - + void buildDrawList0(DrawList &drawList); void buildDrawList1(DrawList &drawList); void buildDrawList2(DrawList &drawList); void buildDrawList3(DrawList &drawList); void drawMagnifyingGlass(DrawList &drawList); - + void drawSprites(); void drawSprites0(); void drawSprites1(); void drawSprites2(); void drawSprites3(); - + Obj *getFreeObject(); - + void initObjects(); void initObjects0(); void initObjects1(); - + void initVars(); void initVars1(); void initVars2(); void initVars3(); - + bool updateStatus(int mouseX, int mouseY, uint mouseButtons); bool updateStatus0(int mouseX, int mouseY, uint mouseButtons); bool updateStatus1(int mouseX, int mouseY, uint mouseButtons); @@ -161,7 +161,7 @@ public: void updateObjs(uint mouseButtons); void update(); - + void scale2x(int x, int y); void loadSounds(); diff --git a/engines/bbvs/minigames/bbloogie.cpp b/engines/bbvs/minigames/bbloogie.cpp index 4601e9ff93..68a3147f9a 100644 --- a/engines/bbvs/minigames/bbloogie.cpp +++ b/engines/bbvs/minigames/bbloogie.cpp @@ -117,7 +117,7 @@ void MinigameBbLoogie::buildDrawList0(DrawList &drawList) { } void MinigameBbLoogie::buildDrawList1(DrawList &drawList) { - + for (int i = 0; i < kMaxObjectsCount; ++i) { Obj *obj = &_objects[i]; switch (obj->kind) { @@ -163,7 +163,7 @@ void MinigameBbLoogie::buildDrawList1(DrawList &drawList) { } void MinigameBbLoogie::buildDrawList2(DrawList &drawList) { - + buildDrawList1(drawList); if (_level > 0 && (_bonusDisplayDelay1 > 0 || _bonusDisplayDelay2 > 0)) { @@ -180,7 +180,7 @@ void MinigameBbLoogie::buildDrawList2(DrawList &drawList) { } void MinigameBbLoogie::buildDrawList3(DrawList &drawList) { - + for (int i = 0; i < kMaxObjectsCount; ++i) { Obj *obj = &_objects[i]; if (obj->kind == 2) @@ -191,7 +191,7 @@ void MinigameBbLoogie::buildDrawList3(DrawList &drawList) { if (_backgroundSpriteIndex) drawList.add(_backgroundSpriteIndex, 0, 0, 0); - + drawList.add(getAnimation(10)->frameIndices[0], 230, 2, 2000); drawNumber(drawList, _levelTimeLeft, 280, 16); @@ -201,7 +201,7 @@ void MinigameBbLoogie::buildDrawList3(DrawList &drawList) { int numberX2 = drawNumber(drawList, _currScore, 68, 16); drawList.add(getAnimation(9)->frameIndices[10], numberX2, 16, 2000); drawNumber(drawList, _dispLevelScore, numberX2 + 10, 16); - + drawList.add(getAnimation(20)->frameIndices[0], 120, 70, 2000); drawList.add(getAnimation(13)->frameIndices[0], 95, 95, 2000); @@ -416,7 +416,7 @@ bool MinigameBbLoogie::updateStatus(int mouseX, int mouseY, uint mouseButtons) { } bool MinigameBbLoogie::updateStatus0(int mouseX, int mouseY, uint mouseButtons) { - + _objects[0].x = mouseX; _objects[0].y = mouseY; @@ -445,7 +445,7 @@ bool MinigameBbLoogie::updateStatus0(int mouseX, int mouseY, uint mouseButtons) _objects[4].kind = 0; _objects[2].kind = 1; } - + for (int i = 0; i < kMaxObjectsCount; ++i) { Obj *obj = &_objects[i]; if (obj->kind == 11) { @@ -487,12 +487,12 @@ bool MinigameBbLoogie::updateStatus0(int mouseX, int mouseY, uint mouseButtons) initVars(); _gameTicks = 0; } - + return true; } bool MinigameBbLoogie::updateStatus1(int mouseX, int mouseY, uint mouseButtons) { - + if (--_levelTimeDelay == 0) { _levelTimeDelay = 58; --_levelTimeLeft; @@ -568,9 +568,9 @@ bool MinigameBbLoogie::updateStatus2(int mouseX, int mouseY, uint mouseButtons) } bool MinigameBbLoogie::updateStatus3(int mouseX, int mouseY, uint mouseButtons) { - + _objects[0].x = mouseX; - + for (int i = 0; i < kMaxObjectsCount; ++i) { Obj *obj = &_objects[i]; if (obj->kind == 2) { @@ -582,7 +582,7 @@ bool MinigameBbLoogie::updateStatus3(int mouseX, int mouseY, uint mouseButtons) } } } - + return true; } @@ -620,7 +620,7 @@ void MinigameBbLoogie::updateObjs(uint mouseButtons) { break; } } - + if (--_carDelay == 0) { // Car Obj *obj = getFreeObject(); @@ -633,7 +633,7 @@ void MinigameBbLoogie::updateObjs(uint mouseButtons) { obj->yIncr = 0; _carDelay = _vm->getRandom(256) + 800; } - + if (--_bikeDelay == 0) { // Bike Obj *obj = getFreeObject(); @@ -646,7 +646,7 @@ void MinigameBbLoogie::updateObjs(uint mouseButtons) { obj->yIncr = 0; _bikeDelay = _vm->getRandom(512) + 500; } - + if (--_squirrelDelay == 0) { // Squirrel Obj *obj = getFreeObject(); @@ -662,7 +662,7 @@ void MinigameBbLoogie::updateObjs(uint mouseButtons) { playSound(9); _squirrelDelay = _vm->getRandom(512) + 300; } - + if (--_paperPlaneDelay == 0) { // Paper plane Obj *obj = getFreeObject(); @@ -685,7 +685,7 @@ void MinigameBbLoogie::updateObjs(uint mouseButtons) { } _paperPlaneDelay = 400; } - + if (_principalDelay >= 0 && --_principalDelay == 0) { // Principal Obj *obj = getFreeObject(); @@ -703,13 +703,13 @@ void MinigameBbLoogie::updateObjs(uint mouseButtons) { _principalFirstFrameIndex = 11; _principalLastFrameIndex = 16; } - + } void MinigameBbLoogie::updatePlayer(int objIndex, uint mouseButtons) { Obj *obj = &_objects[0]; - + switch (obj->status) { case 1: @@ -817,7 +817,7 @@ void MinigameBbLoogie::updateLoogie(int objIndex) { obj->y -= kLoogieOffY[obj->unk2 / 8]; --obj->unk2; } - + if (obj->ticks-- == 0) { obj->ticks = getAnimation(5)->frameTicks[0]; ++obj->frameIndex; @@ -832,9 +832,9 @@ void MinigameBbLoogie::updateLoogie(int objIndex) { void MinigameBbLoogie::updateCar(int objIndex) { Obj *obj = &_objects[objIndex]; - + obj->x += obj->xIncr; - + if (obj->ticks-- == 0) { if (obj->frameIndex++ == 3 || obj->frameIndex == 6) obj->frameIndex = 0; @@ -867,7 +867,7 @@ void MinigameBbLoogie::updateCar(int objIndex) { void MinigameBbLoogie::updateBike(int objIndex) { Obj *obj = &_objects[objIndex]; - + obj->x += obj->xIncr; if (obj->ticks-- == 0) { @@ -965,7 +965,7 @@ void MinigameBbLoogie::updatePaperPlane(int objIndex) { loogieObj = findLoogieObj(loogieObjIndex++); } } - + } void MinigameBbLoogie::updateIndicator(int objIndex) { @@ -995,7 +995,7 @@ void MinigameBbLoogie::updateIndicator(int objIndex) { obj->kind = 0; obj->anim = getAnimation(6); } - + } void MinigameBbLoogie::updatePrincipal(int objIndex) { @@ -1281,22 +1281,22 @@ bool MinigameBbLoogie::run(bool fromMainGame) { _gameDone = false; initObjects(); initVars(); - + _spriteModule = new SpriteModule(); _spriteModule->load("bbloogie/bbloogie.000"); Palette palette = _spriteModule->getPalette(); _vm->_screen->setPalette(palette); - + loadSounds(); playSound(32, true); - + while (!_vm->shouldQuit() &&!_gameDone) { _vm->updateEvents(); update(); } - + _vm->_sound->unloadSounds(); if (!_fromMainGame) @@ -1319,15 +1319,15 @@ void MinigameBbLoogie::update() { inputTicks = 1; _gameTicks = _vm->_system->getMillis(); } - + if (_vm->_keyCode == Common::KEYCODE_ESCAPE) { _gameDone = true; return; } - + if (inputTicks == 0) return; - + bool done; do { @@ -1336,9 +1336,9 @@ void MinigameBbLoogie::update() { _vm->_mouseButtons &= ~kRightButtonClicked; _vm->_keyCode = Common::KEYCODE_INVALID; } while (--inputTicks && _gameTicks > 0 && !done); - + drawSprites(); - + _vm->_system->delayMillis(10); } diff --git a/engines/bbvs/minigames/bbloogie.h b/engines/bbvs/minigames/bbloogie.h index 1dd4049b41..04ead51a1e 100644 --- a/engines/bbvs/minigames/bbloogie.h +++ b/engines/bbvs/minigames/bbloogie.h @@ -32,7 +32,7 @@ public: MinigameBbLoogie(BbvsEngine *vm) : Minigame(vm) {}; bool run(bool fromMainGame); public: - + struct Obj { int kind; int x, y; @@ -44,33 +44,33 @@ public: int16 frameIndexAdd; int16 unk2; }; - + enum { kMaxObjectsCount = 256 }; - + enum { kGSTitleScreen = 0, // Title screen kGSMainGame = 1, // Game when called as part of the main game kGSStandaloneGame = 2, // Game when called as standalone game kGSScoreCountUp = 3 // Score countup and next level text }; - + Obj _objects[kMaxObjectsCount]; - + int _playerKind; const ObjAnimation *_playerAnim; const uint *_playerSounds1, *_playerSounds2; uint _playerSounds1Count, _playerSounds2Count; - + int _level, _levelTimeLeft, _levelTimeDelay; - int _numberOfHits, _currScore, _hiScore; + int _numberOfHits, _currScore, _hiScore; int _doubleScore, _megaLoogieCount; - + int _dispLevelScore, _nextLevelScore; int _timeBonusCtr, _bonusDisplayDelay1, _bonusDisplayDelay2, _bonusDisplayDelay3; - + int _carDelay; int _bikeDelay; int _squirrelDelay; @@ -78,37 +78,37 @@ public: int _paperPlaneDelay; int _principalDelay; - int _prevPrincipalStatus; + int _prevPrincipalStatus; int _principalCtr, _principalFirstFrameIndex, _principalLastFrameIndex; bool _principalAngry; - + const ObjAnimation *getAnimation(int animIndex); - + void buildDrawList(DrawList &drawList); void buildDrawList0(DrawList &drawList); void buildDrawList1(DrawList &drawList); void buildDrawList2(DrawList &drawList); void buildDrawList3(DrawList &drawList); - + void drawSprites(); - + void initObjs(); Obj *getFreeObject(); Obj *findLoogieObj(int startObjIndex); bool isHit(Obj *obj1, Obj *obj2); bool isCursorAtObj(int objIndex); - + void initObjects(); void initObjects0(); void initObjects1(); void initObjects3(); - + void initVars(); void initVars0(); void initVars1(); void initVars2(); void initVars3(); - + bool updateStatus(int mouseX, int mouseY, uint mouseButtons); bool updateStatus0(int mouseX, int mouseY, uint mouseButtons); bool updateStatus1(int mouseX, int mouseY, uint mouseButtons); @@ -129,7 +129,7 @@ public: void incNumberOfHits(); void incScore(int incrAmount); void playRndSound(); - + void update(); void loadSounds(); diff --git a/engines/bbvs/minigames/bbtennis.cpp b/engines/bbvs/minigames/bbtennis.cpp index ddd5cfc804..7763548330 100644 --- a/engines/bbvs/minigames/bbtennis.cpp +++ b/engines/bbvs/minigames/bbtennis.cpp @@ -86,7 +86,7 @@ void MinigameBbTennis::buildDrawList(DrawList &drawList) { } void MinigameBbTennis::buildDrawList0(DrawList &drawList) { - + drawList.add(_objects[0].anim->frameIndices[_objects[0].frameIndex], _objects[0].x, _objects[0].y, 2000); for (int i = 0; i < kMaxObjectsCount; ++i) { @@ -154,7 +154,7 @@ void MinigameBbTennis::buildDrawList1(DrawList &drawList) { break; } - + drawList.add(index, x, y, priority); } @@ -174,16 +174,16 @@ void MinigameBbTennis::buildDrawList1(DrawList &drawList) { drawList.add(getAnimation(9)->frameIndices[0], 256, 52, 500); drawList.add(getAnimation(10)->frameIndices[0], 60, 162, 500); drawList.add(getAnimation(21)->frameIndices[0], 36, 18, 2000); - + drawNumber(drawList, _score, 70, 18); - + for (int i = 0; i < _numHearts; ++i) drawList.add(getAnimation(7)->frameIndices[0], 20 + i * 20, 236, 990); } void MinigameBbTennis::buildDrawList2(DrawList &drawList) { - + for (int i = 0; i < kMaxObjectsCount; ++i) { Obj *obj = &_objects[i]; if (obj->kind) @@ -384,7 +384,7 @@ bool MinigameBbTennis::updateStatus1(int mouseX, int mouseY, uint mouseButtons) _objects[0].x = mouseX; _objects[0].y = mouseY; - + if (_allHeartsGone) { _gameState = 2; initObjects(); @@ -427,13 +427,13 @@ bool MinigameBbTennis::updateStatus1(int mouseX, int mouseY, uint mouseButtons) if (_newBallTimer > 0) --_newBallTimer; - + if (++_delayDecreaseTimer == 30) { _delayDecreaseTimer = 0; if (_playerDecrease < 199) ++_playerDecrease; } - + updateObjs(); if (!_playedThisIsTheCoolest && _score > 3 && _vm->getRandom(10) == 1 && !isAnySoundPlaying(kAllSounds, 11)) { @@ -482,7 +482,7 @@ void MinigameBbTennis::updateObjs() { break; } } - + if (_rapidFireBallsCount == 0) { --_squirrelDelay; if (--_squirrelDelay == 0) { @@ -763,7 +763,7 @@ void MinigameBbTennis::updateTennisPlayer(int objIndex) { } ++_tennisPlayerDelay; break; - + case 2: if (--obj->ticks == 0) { ++obj->frameIndex; @@ -1077,7 +1077,7 @@ void MinigameBbTennis::updateNetPlayer(int objIndex) { void MinigameBbTennis::updateEnemyTennisBall(int objIndex) { Obj *obj = &_objects[objIndex]; - + if (--obj->ticks == 0) { --obj->frameIndex; obj->ticks = getAnimation(6)->frameTicks[obj->frameIndex]; @@ -1103,7 +1103,7 @@ void MinigameBbTennis::updateEnemyTennisBall(int objIndex) { obj->x = (int)obj->fltX; obj->fltY = obj->fltY - obj->fltStepY; obj->y = (int)obj->fltY; - + } void MinigameBbTennis::makeEnemyBall(int x, int y, int frameIndex) { @@ -1184,7 +1184,7 @@ void MinigameBbTennis::hitSomething() { bool MinigameBbTennis::run(bool fromMainGame) { memset(_objects, 0, sizeof(_objects)); - + _numbersAnim = getAnimation(20); _backgroundSpriteIndex = 272; @@ -1201,23 +1201,23 @@ bool MinigameBbTennis::run(bool fromMainGame) { _gameDone = false; initObjects(); initVars(); - + _spriteModule = new SpriteModule(); _spriteModule->load("bbtennis/bbtennis.000"); Palette palette = _spriteModule->getPalette(); _vm->_screen->setPalette(palette); - + loadSounds(); _gameTicks = 0; playSound(29, true); - + while (!_vm->shouldQuit() &&!_gameDone) { _vm->updateEvents(); update(); } - + _vm->_sound->unloadSounds(); if (!_fromMainGame) @@ -1240,15 +1240,15 @@ void MinigameBbTennis::update() { inputTicks = 1; _gameTicks = _vm->_system->getMillis(); } - + if (_vm->_keyCode == Common::KEYCODE_ESCAPE) { _gameDone = true; return; } - + if (inputTicks == 0) return; - + bool done; do { @@ -1257,9 +1257,9 @@ void MinigameBbTennis::update() { _vm->_mouseButtons &= ~kRightButtonClicked; _vm->_keyCode = Common::KEYCODE_INVALID; } while (--inputTicks && _gameTicks > 0 && !done); - + drawSprites(); - + _vm->_system->delayMillis(10); } diff --git a/engines/bbvs/minigames/bbtennis.h b/engines/bbvs/minigames/bbtennis.h index 690bd724a0..7eac904c4d 100644 --- a/engines/bbvs/minigames/bbtennis.h +++ b/engines/bbvs/minigames/bbtennis.h @@ -32,7 +32,7 @@ public: MinigameBbTennis(BbvsEngine *vm) : Minigame(vm) {}; bool run(bool fromMainGame); public: - + struct Obj { int kind; int x, y; @@ -51,20 +51,20 @@ public: int ballStepCtr; int netPlayDirection; }; - + enum { kMaxObjectsCount = 256 }; - + enum { kGSTitleScreen = 0, // Title screen kGSMainGame = 1, // Game when called as part of the main game kGSStandaloneGame = 2, // Game when called as standalone game kGSScoreCountUp = 3 // Score countup and next level text }; - + Obj _objects[kMaxObjectsCount]; - + int _numHearts; int _squirrelDelay; int _tennisPlayerDelay; @@ -85,29 +85,29 @@ public: bool _endSoundPlaying; const ObjAnimation *getAnimation(int animIndex); - + void buildDrawList(DrawList &drawList); void buildDrawList0(DrawList &drawList); void buildDrawList1(DrawList &drawList); void buildDrawList2(DrawList &drawList); - + void drawSprites(); - + void initObjs(); Obj *getFreeObject(); Obj *findTennisBall(int startObjIndex); bool isHit(Obj *obj1, Obj *obj2); - + void initObjects(); void initObjects0(); void initObjects1(); void initObjects2(); - + void initVars(); void initVars0(); void initVars1(); void initVars2(); - + bool updateStatus(int mouseX, int mouseY, uint mouseButtons); bool updateStatus0(int mouseX, int mouseY, uint mouseButtons); bool updateStatus1(int mouseX, int mouseY, uint mouseButtons); diff --git a/engines/bbvs/minigames/minigame.cpp b/engines/bbvs/minigames/minigame.cpp index aae18072d9..58d98a9df8 100644 --- a/engines/bbvs/minigames/minigame.cpp +++ b/engines/bbvs/minigames/minigame.cpp @@ -44,13 +44,13 @@ Minigame::~Minigame() { int Minigame::drawNumber(DrawList &drawList, int number, int x, int y) { int digits = 1, rightX = x; - + for (int mag = 10; number / mag != 0; mag *= 10) ++digits; - + rightX = x + digits * 10; x = rightX; - + while (digits--) { const int n = number % 10; x -= 10; diff --git a/engines/bbvs/minigames/minigame.h b/engines/bbvs/minigames/minigame.h index 675dec360d..1c24110519 100644 --- a/engines/bbvs/minigames/minigame.h +++ b/engines/bbvs/minigames/minigame.h @@ -51,30 +51,30 @@ public: virtual ~Minigame(); virtual bool run(bool fromMainGame) = 0; protected: - BbvsEngine *_vm; + BbvsEngine *_vm; SpriteModule *_spriteModule; - + int _gameState; int _gameTicks; bool _gameResult; bool _gameDone; bool _fromMainGame; int _hiScoreTable[kMinigameCount]; - + int _backgroundSpriteIndex, _titleScreenSpriteIndex; - + const ObjAnimation *_numbersAnim; - + int drawNumber(DrawList &drawList, int number, int x, int y); void playSound(uint index, bool loop = false); void stopSound(uint index); bool isSoundPlaying(uint index); bool isAnySoundPlaying(const uint *indices, uint count); - + void saveHiscore(int minigameNum, int score); int loadHiscore(int minigameNum); - + }; } // End of namespace Bbvs diff --git a/engines/bbvs/saveload.cpp b/engines/bbvs/saveload.cpp index ff53cc457b..e7725713fd 100644 --- a/engines/bbvs/saveload.cpp +++ b/engines/bbvs/saveload.cpp @@ -73,7 +73,7 @@ void BbvsEngine::savegame(const char *filename, const char *description) { byte descriptionLen = strlen(description); out->writeByte(descriptionLen); out->write(description, descriptionLen); - + Graphics::saveThumbnail(*out); // Not used yet, reserved for future usage @@ -86,7 +86,7 @@ void BbvsEngine::savegame(const char *filename, const char *description) { out->writeUint32LE(saveTime); out->writeUint32LE(playTime); // Header end - + out->write(_snapshot, _snapshotStream->pos()); out->finalize(); @@ -103,15 +103,15 @@ void BbvsEngine::loadgame(const char *filename) { SaveHeader header; kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); - + if (errorCode != kRSHENoError) { warning("Error loading savegame '%s'", filename); delete in; return; } - + g_engine->setTotalPlayTime(header.playTime * 1000); - + memset(_sceneObjects, 0, sizeof(_sceneObjects)); for (int i = 0; i < kSceneObjectsCount; ++i) { _sceneObjects[i].walkDestPt.x = -1; @@ -120,7 +120,7 @@ void BbvsEngine::loadgame(const char *filename) { _currSceneNum = 0; _newSceneNum = in->readUint32LE(); - + initScene(false); _prevSceneNum = in->readUint32LE(); @@ -157,16 +157,16 @@ void BbvsEngine::loadgame(const char *filename) { obj->frameIndex = in->readUint32LE(); obj->frameTicks = in->readUint32LE(); obj->walkCount = in->readUint32LE(); - obj->xIncr = in->readUint32LE(); + obj->xIncr = in->readUint32LE(); obj->yIncr = in->readUint32LE(); - obj->turnValue = in->readUint32LE(); - obj->turnCount = in->readUint32LE(); + obj->turnValue = in->readUint32LE(); + obj->turnCount = in->readUint32LE(); obj->turnTicks = in->readUint32LE(); obj->walkDestPt.x = in->readUint16LE(); obj->walkDestPt.y = in->readUint16LE(); obj->anim = obj->animIndex > 0 ? _gameModule->getAnimation(obj->animIndex) : 0; } - + updateWalkableRects(); // Restart scene background sounds @@ -259,10 +259,10 @@ void BbvsEngine::saveSnapshot() { _snapshotStream->writeUint32LE(obj->frameIndex); _snapshotStream->writeUint32LE(obj->frameTicks); _snapshotStream->writeUint32LE(obj->walkCount); - _snapshotStream->writeUint32LE(obj->xIncr); + _snapshotStream->writeUint32LE(obj->xIncr); _snapshotStream->writeUint32LE(obj->yIncr); - _snapshotStream->writeUint32LE(obj->turnValue); - _snapshotStream->writeUint32LE(obj->turnCount); + _snapshotStream->writeUint32LE(obj->turnValue); + _snapshotStream->writeUint32LE(obj->turnCount); _snapshotStream->writeUint32LE(obj->turnTicks); _snapshotStream->writeUint16LE(obj->walkDestPt.x); _snapshotStream->writeUint16LE(obj->walkDestPt.y); diff --git a/engines/bbvs/scene.cpp b/engines/bbvs/scene.cpp index 0d86eb4dbc..a89c88fd82 100644 --- a/engines/bbvs/scene.cpp +++ b/engines/bbvs/scene.cpp @@ -34,7 +34,7 @@ static const int kAfterVideoSceneNum[] = { void BbvsEngine::loadScene(int sceneNum) { debug(0, "BbvsEngine::loadScene() sceneNum: %d", sceneNum); - + Common::String sprFilename = Common::String::format("vnm/vspr%04d.vnm", sceneNum); Common::String gamFilename = Common::String::format("vnm/game%04d.vnm", sceneNum); @@ -42,7 +42,7 @@ void BbvsEngine::loadScene(int sceneNum) { _spriteModule->load(sprFilename.c_str()); _gameModule->load(gamFilename.c_str()); - + Palette palette = _spriteModule->getPalette(); _screen->setPalette(palette); @@ -106,10 +106,10 @@ void BbvsEngine::initScene(bool sounds) { loadScene(_newSceneNum); _currSceneNum = _newSceneNum; _newSceneNum = 0; - + for (int i = 0; i < _gameModule->getSceneObjectDefsCount(); ++i) _sceneObjects[i].sceneObjectDef = _gameModule->getSceneObjectDef(i); - + for (int i = 0; i < _gameModule->getSceneObjectInitsCount(); ++i) { SceneObjectInit *soInit = _gameModule->getSceneObjectInit(i); if (evalCondition(soInit->conditions)) { @@ -149,10 +149,10 @@ void BbvsEngine::initScene(bool sounds) { } } } - + _cameraPos = _gameModule->getCameraInit(_currCameraNum)->cameraPos; _newCameraPos = _cameraPos; - + _walkAreaActions.clear(); for (int i = 0; i < _gameModule->getActionsCount(); ++i) { Action *action = _gameModule->getAction(i); @@ -165,7 +165,7 @@ void BbvsEngine::initScene(bool sounds) { _activeItemIndex = 0; _activeItemType = kITEmpty; - + for (int i = 0; i < _gameModule->getActionsCount(); ++i) { Action *action = _gameModule->getAction(i); if (evalCondition(action->conditions)) { @@ -183,7 +183,7 @@ void BbvsEngine::initScene(bool sounds) { break; } } - + if (sounds) updateBackgroundSounds(); @@ -192,7 +192,7 @@ void BbvsEngine::initScene(bool sounds) { bool BbvsEngine::changeScene() { writeContinueSavegame(); - + if (_newSceneNum >= 27 && _newSceneNum <= 30) { // Run minigames stopSpeech(); @@ -221,7 +221,7 @@ bool BbvsEngine::changeScene() { } return true; - + } } // End of namespace Bbvs diff --git a/engines/bbvs/spritemodule.cpp b/engines/bbvs/spritemodule.cpp index 8eae7f9a6a..f8b0d9afd5 100644 --- a/engines/bbvs/spritemodule.cpp +++ b/engines/bbvs/spritemodule.cpp @@ -41,15 +41,15 @@ SpriteModule::~SpriteModule() { void SpriteModule::load(const char *filename) { unload(); - + Common::File fd; if (!fd.open(filename)) error("SpriteModule::load() Could not open %s", filename); - + fd.readUint32LE(); // Skip magic fd.readUint32LE(); // Skip unused fd.readUint32LE(); // Skip filesize - + _paletteOffs = fd.readUint32LE(); fd.readUint32LE(); // Skip unused flagsTbl1Ofs fd.readUint32LE(); // Skip unused flagsTbl2Ofs @@ -57,18 +57,18 @@ void SpriteModule::load(const char *filename) { _paletteStart = fd.readUint32LE(); _paletteCount = fd.readUint32LE(); _spritesCount = fd.readUint32LE(); - + debug(0, "_paletteOffs: %08X", _paletteOffs); debug(0, "_spriteTblOffs: %08X", _spriteTblOffs); debug(0, "_paletteStart: %d", _paletteStart); debug(0, "_paletteCount: %d", _paletteCount); debug(0, "_spritesCount: %d", _spritesCount); - + _spriteDataSize = fd.size(); _spriteData = new byte[_spriteDataSize]; fd.seek(0); fd.read(_spriteData, _spriteDataSize); - + // Convert palette byte *palette = _spriteData + _paletteOffs; for (int i = 0; i < _paletteCount; ++i) { diff --git a/engines/bbvs/videoplayer.cpp b/engines/bbvs/videoplayer.cpp index fda9372ade..9ea73ad10b 100644 --- a/engines/bbvs/videoplayer.cpp +++ b/engines/bbvs/videoplayer.cpp @@ -42,7 +42,7 @@ void BbvsEngine::playVideo(int videoNum) { warning("Couldn't switch to a RGB color video mode to play a video."); return; } - + Video::VideoDecoder *videoDecoder = new Video::AVIDecoder(); if (!videoDecoder->loadFile(videoFilename)) { delete videoDecoder; @@ -74,7 +74,7 @@ void BbvsEngine::playVideo(int videoNum) { } delete videoDecoder; - + initGraphics(320, 240, false); } diff --git a/engines/bbvs/walk.cpp b/engines/bbvs/walk.cpp index 077110b867..5ef14101a0 100644 --- a/engines/bbvs/walk.cpp +++ b/engines/bbvs/walk.cpp @@ -46,7 +46,7 @@ static const int8 kWalkAnimTbl[32] = { void BbvsEngine::startWalkObject(SceneObject *sceneObject) { if (_buttheadObject != sceneObject && _beavisObject != sceneObject) return; - + initWalkAreas(sceneObject); _sourceWalkAreaPt.x = sceneObject->x >> 16; _sourceWalkAreaPt.y = sceneObject->y >> 16; @@ -60,7 +60,7 @@ void BbvsEngine::startWalkObject(SceneObject *sceneObject) { _destWalkArea = getWalkAreaAtPos(_destWalkAreaPt); if (!_destWalkArea) return; - + if (_sourceWalkArea != _destWalkArea) { _currWalkDistance = kMaxDistance; walkFindPath(_sourceWalkArea, 0); @@ -68,12 +68,12 @@ void BbvsEngine::startWalkObject(SceneObject *sceneObject) { } walkObject(sceneObject, _destWalkAreaPt, sceneObject->sceneObjectDef->walkSpeed); - + } void BbvsEngine::updateWalkObject(SceneObject *sceneObject) { int animIndex; - + if (sceneObject->walkCount > 0 && (sceneObject->xIncr != 0 || sceneObject->yIncr != 0)) { if (ABS(sceneObject->xIncr) <= ABS(sceneObject->yIncr)) sceneObject->turnValue = sceneObject->yIncr >= 0 ? 0 : 4; @@ -89,7 +89,7 @@ void BbvsEngine::updateWalkObject(SceneObject *sceneObject) { Animation *anim = 0; if (animIndex > 0) anim = _gameModule->getAnimation(animIndex); - + if (sceneObject->anim != anim) { if (anim) { sceneObject->anim = anim; @@ -305,12 +305,12 @@ bool BbvsEngine::canButtheadWalkToDest(const Common::Point &destPt) { } void BbvsEngine::canWalkToDest(WalkArea *walkArea, int infoCount) { - + if (_destWalkArea == walkArea) { _walkReachedDestArea = true; return; } - + if (_gameModule->getFieldC() <= 320 || infoCount <= 20) { walkArea->checked = true; for (int linkIndex = 0; linkIndex < walkArea->linksCount; ++linkIndex) { @@ -364,10 +364,10 @@ int BbvsEngine::calcDistance(const Common::Point &pt1, const Common::Point &pt2) void BbvsEngine::walkFoundPath(int count) { debug(5, "BbvsEngine::walkFoundPath(%d)", count); - + Common::Point midPt = _sourceWalkAreaPt; int totalMidPtDistance = 0; - + if (count > 0) { Common::Point lastMidPt; int halfCount = (count + 1) >> 1; @@ -384,13 +384,13 @@ void BbvsEngine::walkFoundPath(int count) { if (distance >= _currWalkDistance) return; - + debug(5, "BbvsEngine::walkFoundPath() distance smaller"); _currWalkDistance = distance; Common::Point destPt = _destWalkAreaPt, newDestPt; - + while (1) { int index = 0; @@ -408,7 +408,7 @@ void BbvsEngine::walkFoundPath(int count) { WalkInfo *walkInfo = _walkInfoPtrs[--count]; destPt.x = walkInfo->x; destPt.y = walkInfo->y; - + if (walkInfo->direction) { newDestPt.x = walkInfo->x; newDestPt.y = walkInfo->y + walkInfo->delta - 1; diff --git a/engines/cge/POTFILES b/engines/cge/POTFILES new file mode 100644 index 0000000000..55430683c3 --- /dev/null +++ b/engines/cge/POTFILES @@ -0,0 +1,2 @@ +engines/cge/detection.cpp + diff --git a/engines/cge/cge.h b/engines/cge/cge.h index a65069ff46..c43358f252 100644 --- a/engines/cge/cge.h +++ b/engines/cge/cge.h @@ -80,12 +80,6 @@ class Talk; #define kSayTheEnd 41 -enum GameType { - kGameTypeNone = 0, - kGameTypeSoltys, - kGameTypeSfinx -}; - // our engine debug channels enum { kCGEDebugBitmap = 1 << 0, diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index 4c2f81c1ae..da5eb2b1f2 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -28,134 +28,75 @@ #include "base/plugins.h" #include "graphics/thumbnail.h" #include "cge/cge.h" +#include "cge/fileio.h" namespace CGE { -struct CgeGameDescription { - ADGameDescription desc; - GameType gameType; -}; - #define GAMEOPTION_COLOR_BLIND_DEFAULT_OFF GUIO_GAMEOPTIONS1 -} // End of namespace CGE - static const PlainGameDescriptor CGEGames[] = { { "soltys", "Soltys" }, - { "sfinx", "Sfinx" }, { 0, 0 } }; -namespace CGE { - -static const CgeGameDescription gameDescriptions[] = { - - { - { - "soltys", "", - { - {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, - {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437572}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO0() - }, - kGameTypeSoltys - }, +static const ADGameDescription gameDescriptions[] = { { + "soltys", "Freeware", { - "soltys", "Soltys Freeware", - { - {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, - {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "0c33e2c304821a2444d297fc5e2d67c6", 50176}, + {"vol.dat", 0, "f9ae2e7f8f7cac91378cdafca43faf1e", 8437676}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Demo (not supported)", { - "soltys", "Soltys Demo (not supported)", - { - {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788}, - {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710}, - AD_LISTEND - }, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "1e077c8ff58109a187f07ac54b0c873a", 18788}, + {"vol.dat", 0, "75d385a6074c58b69f7730481f256051", 1796710}, + AD_LISTEND }, - kGameTypeSoltys + Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Demo (not supported)", { - "soltys", "Soltys Demo (not supported)", - { - {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168}, - {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "f17987487fab1ebddd781d8d02fedecc", 7168}, + {"vol.dat", 0, "c5d9b15863cab61dc125551576dece04", 1075272}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_DEMO , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Freeware v1.0", { - "soltys", "Soltys Freeware v1.0", - { - {"vol.cat", 0, "f1675684c68ab90272f5776f8f2c3974", 50176}, - {"vol.dat", 0, "4ffeff4abc99ac5999b55ccfc56ab1df", 8430868}, - AD_LISTEND - }, - Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "f1675684c68ab90272f5776f8f2c3974", 50176}, + {"vol.dat", 0, "4ffeff4abc99ac5999b55ccfc56ab1df", 8430868}, + AD_LISTEND }, - kGameTypeSoltys + Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Freeware v1.0", { - "soltys", "Soltys Freeware v1.0", - { - {"vol.cat", 0, "20fdce799adb618100ef9ee2362be875", 50176}, - {"vol.dat", 0, "0e43331c846094d77f5dd201827e0a3b", 8439339}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "20fdce799adb618100ef9ee2362be875", 50176}, + {"vol.dat", 0, "0e43331c846094d77f5dd201827e0a3b", 8439339}, + AD_LISTEND }, - kGameTypeSoltys + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, { + "soltys", "Freeware v1.0", { - "soltys", "Soltys Freeware v1.0", - { - {"vol.cat", 0, "fcae86b20eaa5cedec17b24fa5e85eb4", 50176}, - {"vol.dat", 0, "ff10d54acc2c95696c57e05819b6906f", 8450151}, - AD_LISTEND - }, - Common::ES_ESP, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + {"vol.cat", 0, "fcae86b20eaa5cedec17b24fa5e85eb4", 50176}, + {"vol.dat", 0, "ff10d54acc2c95696c57e05819b6906f", 8450151}, + AD_LISTEND }, - kGameTypeSoltys + Common::ES_ESP, Common::kPlatformDOS, ADGF_NO_FLAGS , GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) }, - { - { - // Polish version, provided by Strangerke - "sfinx", "Sfinx Freeware", - { - {"vol.cat", 0, "21197b287d397c53261b6616bf0dd880", 129024}, - {"vol.dat", 0, "de14291869a8eb7c2732ab783c7542ef", 34180844}, - AD_LISTEND - }, - Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) - }, - kGameTypeSfinx - }, - - {AD_TABLE_END_MARKER, kGameTypeNone} -}; -static const ADFileBasedFallback fileBasedFallback[] = { - { &gameDescriptions[0].desc, { "vol.cat", "vol.dat", 0 } }, - { 0, { 0 } } + AD_TABLE_END_MARKER }; -} // End of namespace CGE static const ADExtraGuiOptionsMap optionsList[] = { { @@ -173,14 +114,10 @@ static const ADExtraGuiOptionsMap optionsList[] = { class CGEMetaEngine : public AdvancedMetaEngine { public: - CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(CGE::CgeGameDescription), CGEGames, optionsList) { + CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(ADGameDescription), CGEGames, optionsList) { _singleid = "soltys"; } - virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { - return detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback); - } - virtual const char *getName() const { return "CGE"; } @@ -189,6 +126,7 @@ public: return "Soltys (c) 1994-1996 L.K. Avalon"; } + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual int getMaximumSaveSlot() const; @@ -197,6 +135,44 @@ public: virtual void removeSaveState(const char *target, int slot) const; }; +static const ADFileBasedFallback fileBasedFallback[] = { + { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } }, + { 0, { 0 } } +}; + +static ADGameDescription s_fallbackDesc = { + "Soltys", + "Unknown version", + AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor + Common::UNK_LANG, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) +}; + +const ADGameDescription *CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADFilePropertiesMap filesProps; + + const ADGameDescription *game; + game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback, &filesProps); + + if (!game) + return nullptr; + + SearchMan.clear(); + SearchMan.addDirectory(fslist.begin()->getParent().getPath(), fslist.begin()->getParent()); + ResourceManager *resman; + resman = new ResourceManager(); + bool result = resman->exist("CGE.SAY"); + delete resman; + + if (!result) + return nullptr; + + reportUnknown(fslist.begin()->getParent(), filesProps); + return &s_fallbackDesc; +} + bool CGEMetaEngine::hasFeature(MetaEngineFeature f) const { return (f == kSupportsListSaves) || @@ -306,9 +282,10 @@ bool CGEMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD } return desc != 0; } +} // End of namespace CGE #if PLUGIN_ENABLED_DYNAMIC(CGE) - REGISTER_PLUGIN_DYNAMIC(CGE, PLUGIN_TYPE_ENGINE, CGEMetaEngine); +REGISTER_PLUGIN_DYNAMIC(CGE, PLUGIN_TYPE_ENGINE, CGE::CGEMetaEngine); #else - REGISTER_PLUGIN_STATIC(CGE, PLUGIN_TYPE_ENGINE, CGEMetaEngine); +REGISTER_PLUGIN_STATIC(CGE, PLUGIN_TYPE_ENGINE, CGE::CGEMetaEngine); #endif diff --git a/engines/cge/fileio.cpp b/engines/cge/fileio.cpp index 2b1f74db02..df5c31d367 100644 --- a/engines/cge/fileio.cpp +++ b/engines/cge/fileio.cpp @@ -77,7 +77,7 @@ ResourceManager::ResourceManager() { _buff[i]._page = new BtPage; _buff[i]._pageNo = kBtValNone; _buff[i]._index = -1; - assert(_buff[i]._page != NULL); + assert(_buff[i]._page != nullptr); } } @@ -118,10 +118,16 @@ uint16 ResourceManager::read(byte *buf, uint16 length) { BtPage *ResourceManager::getPage(int level, uint16 pageId) { debugC(1, kCGEDebugFile, "ResourceManager::getPage(%d, %d)", level, pageId); + if (level >= kBtLevel) + return nullptr; + if (_buff[level]._pageNo != pageId) { int32 pos = pageId * kBtSize; _buff[level]._pageNo = pageId; - assert(_catFile->size() > pos); + + if (_catFile->size() <= pos) + return nullptr; + // In the original, there was a check verifying if the // purpose was to write a new file. This should only be // to create a new file, thus it was removed. @@ -146,11 +152,13 @@ BtKeypack *ResourceManager::find(const char *key) { uint16 nxt = kBtValRoot; while (!_catFile->eos()) { BtPage *pg = getPage(lev, nxt); + if (!pg) + return nullptr; + // search if (pg->_header._down != kBtValNone) { int i; for (i = 0; i < pg->_header._count; i++) { - // Does this work, or does it have to compare the entire buffer? if (scumm_strnicmp((const char *)key, (const char*)pg->_inner[i]._key, kBtKeySize) < 0) break; } @@ -167,13 +175,17 @@ BtKeypack *ResourceManager::find(const char *key) { return &pg->_leaf[i]; } } - return NULL; + return nullptr; } bool ResourceManager::exist(const char *name) { debugC(1, kCGEDebugFile, "ResourceManager::exist(%s)", name); - return scumm_stricmp(find(name)->_key, name) == 0; + BtKeypack* result = find(name); + if (!result) + return false; + + return scumm_stricmp(result->_key, name) == 0; } uint16 ResourceManager::catRead(byte *buf, uint16 length) { @@ -228,7 +240,7 @@ uint32 EncryptedStream::read(byte *dataPtr, uint32 dataSize) { } bool EncryptedStream::err() { - return (_error & _readStream->err()); + return (_error || _readStream->err()); } bool EncryptedStream::eos() { diff --git a/engines/cge/vga13h.cpp b/engines/cge/vga13h.cpp index babcb7e667..4d3a103663 100644 --- a/engines/cge/vga13h.cpp +++ b/engines/cge/vga13h.cpp @@ -640,7 +640,7 @@ Vga::Vga(CGEEngine *vm) : _frmCnt(0), _msg(NULL), _name(NULL), _setPal(false), _ if (ConfMan.getBool("enable_color_blind")) _mono = 1; - + _oldColors = (Dac *)malloc(sizeof(Dac) * kPalCount); _newColors = (Dac *)malloc(sizeof(Dac) * kPalCount); diff --git a/engines/cge2/POTFILES b/engines/cge2/POTFILES new file mode 100644 index 0000000000..1e904763ec --- /dev/null +++ b/engines/cge2/POTFILES @@ -0,0 +1 @@ +engines/cge2/detection.cpp diff --git a/engines/cge2/bitmap.cpp b/engines/cge2/bitmap.cpp new file mode 100644 index 0000000000..0f442b8c77 --- /dev/null +++ b/engines/cge2/bitmap.cpp @@ -0,0 +1,458 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/bitmap.h" +#include "cge2/cge2.h" +#include "cge2/vga13h.h" +#include "cge2/talk.h" +#include "common/system.h" +#include "common/debug.h" +#include "common/debug-channels.h" + +namespace CGE2 { + +Bitmap::Bitmap() : _w(0), _h(0), _v(nullptr), _b(nullptr), _map(0), _vm(nullptr) { +} + +void Bitmap::setVM(CGE2Engine *vm) { + _vm = vm; +} + +Bitmap::Bitmap(CGE2Engine *vm, const char *fname) : _w(0), _h(0), _v(nullptr), _b(nullptr), _map(0), _vm(vm) { + Common::String path; + + if (!strcmp(fname, "04tal201")) { + path = "04tal202"; + warning("Workaround for missing VBM: 04tal201"); + } else if (!strcmp(fname, "11oqlist-")) { + path = "11oqlist"; + warning("Workaround for wrong VBM name: 11oqlist-"); + } else + path = fname; + + path = setExtension(path, ".VBM"); + + if (_vm->_resman->exist(path.c_str())) { + EncryptedStream file(_vm, path.c_str()); + if (file.err()) + error("Unable to find VBM [%s]", fname); + if (!loadVBM(&file)) + error("Bad VBM [%s]", fname); + } else { + warning("Missing VBM [%s]", path.c_str()); + } +} + +Bitmap::Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 *map) : _w(w), _h(h), _v(nullptr), _map(0), _b(nullptr), _vm(vm) { + if (map) + code(map); +} + +// following routine creates filled rectangle +// immediately as VGA video chunks, in near memory as fast as possible, +// especially for text line real time display +Bitmap::Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 fill) + : _w((w + 3) & ~3), // only full uint32 allowed! + _h(h), _map(0), _b(nullptr), _vm(vm) { + + uint16 dsiz = _w >> 2; // data size (1 plane line size) + uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap + uint16 psiz = _h * lsiz; // - last gape, but + plane trailer + uint8 *v = new uint8[4 * psiz + _h * sizeof(*_b)];// the same for 4 planes + // + room for wash table + + WRITE_LE_UINT16(v, (kBmpCPY | dsiz)); // data chunk hader + memset(v + 2, fill, dsiz); // data bytes + WRITE_LE_UINT16(v + lsiz - 2, (kBmpSKP | ((kScrWidth / 4) - dsiz))); // gap + + // Replicate lines + byte *destP; + for (destP = v + lsiz; destP < (v + psiz); destP += lsiz) + Common::copy(v, v + lsiz, destP); + + WRITE_LE_UINT16(v + psiz - 2, kBmpEOI); // plane trailer uint16 + + // Replicate planes + for (destP = v + psiz; destP < (v + 4 * psiz); destP += psiz) + Common::copy(v, v + psiz, destP); + + HideDesc *b = (HideDesc *)(v + 4 * psiz); + b->_skip = (kScrWidth - _w) >> 2; + b->_hide = _w >> 2; + + // Replicate across the entire table + for (HideDesc *hdP = b + 1; hdP < (b + _h); hdP++) + *hdP = *b; + + b->_skip = 0; // fix the first entry + _v = v; + _b = b; +} + +Bitmap::Bitmap(CGE2Engine *vm, const Bitmap &bmp) : _w(bmp._w), _h(bmp._h), _v(nullptr), _map(0), _b(nullptr), _vm(vm) { + uint8 *v0 = bmp._v; + if (!v0) + return; + + uint16 vsiz = (uint8 *)(bmp._b) - (uint8 *)(v0); + uint16 siz = vsiz + _h * sizeof(HideDesc); + uint8 *v1 = new uint8[siz]; + memcpy(v1, v0, siz); + _b = (HideDesc *)((_v = v1) + vsiz); +} + +Bitmap::~Bitmap() { + release(); +} + +void Bitmap::release() { + if (_v != nullptr) + delete[] _v; + _v = nullptr; +} + +Bitmap &Bitmap::operator=(const Bitmap &bmp) { + if (this == &bmp) + return *this; + + uint8 *v0 = bmp._v; + _w = bmp._w; + _h = bmp._h; + _map = 0; + _vm = bmp._vm; + delete[] _v; + _v = nullptr; + + if (v0) { + uint16 vsiz = (uint8 *)bmp._b - (uint8 *)v0; + uint16 siz = vsiz + _h * sizeof(HideDesc); + uint8 *v1 = new uint8[siz]; + memcpy(v1, v0, siz); + _b = (HideDesc *)((_v = v1) + vsiz); + } + return *this; +} + +// Blatant rip from hopkins engine where it's ripped from gob engine. Hi DrMcCoy, hi Strangerke! ;> +Common::String Bitmap::setExtension(const Common::String &str, const Common::String &ext) { + if (str.empty()) + return str; + + const char *dot = strrchr(str.c_str(), '.'); + if (dot) + return Common::String(str.c_str(), dot - str.c_str()) + ext; + + return str + ext; +} + +BitmapPtr Bitmap::code(uint8 *map) { + if (!map) + return nullptr; + + uint16 cnt; + + if (_v) { // old X-map exists, so remove it + delete[] _v; + _v = nullptr; + } + + while (true) { // at most 2 times: for (V == NULL) & for allocated block; + uint8 *im = _v + 2; + uint16 *cp = (uint16 *) _v; + + if (_v) { // 2nd pass - fill the hide table + for (uint i = 0; i < _h; i++) { + _b[i]._skip = 0xFFFF; + _b[i]._hide = 0x0000; + } + } + for (int bpl = 0; bpl < 4; bpl++) { // once per each bitplane + uint8 *bm = map; + bool skip = (bm[bpl] == kPixelTransp); + uint16 j; + + cnt = 0; + for (uint i = 0; i < _h; i++) { // once per each line + uint8 pix; + for (j = bpl; j < _w; j += 4) { + pix = bm[j]; + if (_v && (pix != kPixelTransp)) { + if (j < _b[i]._skip) + _b[i]._skip = j; + + if (j >= _b[i]._hide) + _b[i]._hide = j + 1; + } + if (((pix == kPixelTransp) != skip) || (cnt >= 0x3FF0)) { // end of block + cnt |= (skip) ? kBmpSKP : kBmpCPY; + if (_v) + WRITE_LE_UINT16(cp, cnt); // store block description uint16 + + cp = (uint16 *) im; + im += 2; + skip = (pix == kPixelTransp); + cnt = 0; + } + if (!skip) { + if (_v) + *im = pix; + im++; + } + cnt++; + } + + bm += _w; + if (_w < kScrWidth) { + if (skip) + cnt += (kScrWidth - j + 3) / 4; + else { + cnt |= kBmpCPY; + if (_v) + WRITE_LE_UINT16(cp, cnt); + + cp = (uint16 *) im; + im += 2; + skip = true; + cnt = (kScrWidth - j + 3) / 4; + } + } + } + if (cnt && ! skip) { + cnt |= kBmpCPY; + if (_v) + WRITE_LE_UINT16(cp, cnt); + + cp = (uint16 *) im; + im += 2; + } + if (_v) + WRITE_LE_UINT16(cp, kBmpEOI); + cp = (uint16 *) im; + im += 2; + } + if (_v) + break; + + uint16 sizV = (uint16)(im - 2 - _v); + _v = new uint8[sizV + _h * sizeof(*_b)]; + _b = (HideDesc *)(_v + sizV); + } + cnt = 0; + for (uint i = 0; i < _h; i++) { + if (_b[i]._skip == 0xFFFF) { // whole line is skipped + _b[i]._skip = (cnt + kScrWidth) >> 2; + cnt = 0; + } else { + uint16 s = _b[i]._skip & ~3; + uint16 h = (_b[i]._hide + 3) & ~3; + _b[i]._skip = (cnt + s) >> 2; + _b[i]._hide = (h - s) >> 2; + cnt = kScrWidth - h; + } + } + + return this; +} + +bool Bitmap::solidAt(V2D pos) { + pos.x += _w >> 1; + pos.y = _h - pos.y; + + if (!pos.limited(V2D(_vm, _w, _h))) + return false; + + uint8 *m = _v; + uint16 r = static_cast<uint16>(pos.x) % 4; + uint16 n0 = (kScrWidth * pos.y + pos.x) / 4; + uint16 n = 0; + + while (r) { + uint16 w, t; + + w = READ_LE_UINT16(m); + m += 2; + t = w & 0xC000; + w &= 0x3FFF; + + switch (t) { + case kBmpEOI: + r--; + // No break on purpose + case kBmpSKP: + w = 0; + break; + case kBmpREP: + w = 1; + break; + } + m += w; + } + + while (true) { + uint16 w, t; + + w = READ_LE_UINT16(m); + m += 2; + t = w & 0xC000; + w &= 0x3FFF; + + if (n > n0) + return false; + + n += w; + switch (t) { + case kBmpEOI: + return false; + case kBmpSKP: + w = 0; + break; + case kBmpREP: + case kBmpCPY: + if ((n - w <= n0) && (n > n0)) + return true; + break; + } + m += ((t == kBmpREP) ? 1 : w); + } +} + +bool Bitmap::loadVBM(EncryptedStream *f) { + uint16 p = 0, n = 0; + if (!f->err()) + f->read((uint8 *)&p, sizeof(p)); + p = FROM_LE_16(p); + + if (!f->err()) + f->read((uint8 *)&n, sizeof(n)); + n = FROM_LE_16(n); + + if (!f->err()) + f->read((uint8 *)&_w, sizeof(_w)); + _w = FROM_LE_16(_w); + + if (!f->err()) + f->read((uint8 *)&_h, sizeof(_h)); + _h = FROM_LE_16(_h); + + if (!f->err()) { + if (p) { + if (_vm->_bitmapPalette) { + // Read in the palette + byte palData[kPalSize]; + f->read(palData, kPalSize); + + const byte *srcP = palData; + for (int idx = 0; idx < kPalCount; idx++, srcP += 3) { + _vm->_bitmapPalette[idx]._r = *srcP; + _vm->_bitmapPalette[idx]._g = *(srcP + 1); + _vm->_bitmapPalette[idx]._b = *(srcP + 2); + } + } else + f->seek(f->pos() + kPalSize); + } + } + _v = new uint8[n]; + + if (!f->err()) + f->read(_v, n); + + _b = (HideDesc *)(_v + n - _h * sizeof(HideDesc)); + return (!f->err()); +} + +void Bitmap::xLatPos(V2D& p) { + p.x -= (_w >> 1); + p.y = kWorldHeight - p.y - _h; +} + +#define _ kPixelTransp, +#define L 1, +#define G 2, +#define D 3, +#define kDesignSize 240 + +uint8 *Bitmap::makeSpeechBubbleTail(int which, uint8 colorSet[][4]) { + static const uint8 kSLDesign[kDesignSize] = { + G G G G G G G G G _ _ _ _ _ _ + L G G G G G G G G D _ _ _ _ _ + _ L G G G G G G G D _ _ _ _ _ + _ _ L G G G G G G G D _ _ _ _ + _ _ _ L G G G G G G D _ _ _ _ + _ _ _ _ L G G G G G D _ _ _ _ + _ _ _ _ _ L G G G G G D _ _ _ + _ _ _ _ _ _ L G G G G D _ _ _ + _ _ _ _ _ _ _ L G G G D _ _ _ + _ _ _ _ _ _ _ _ L G G G D _ _ + _ _ _ _ _ _ _ _ _ L G G D _ _ + _ _ _ _ _ _ _ _ _ _ L G D _ _ + _ _ _ _ _ _ _ _ _ _ _ L G D _ + _ _ _ _ _ _ _ _ _ _ _ _ L D _ + _ _ _ _ _ _ _ _ _ _ _ _ _ L D + _ _ _ _ _ _ _ _ _ _ _ _ _ _ D + }; + + static const uint8 kSRDesign[kDesignSize] = { + _ _ _ _ _ _ G G G G G G G G G + _ _ _ _ _ L G G G G G G G G D + _ _ _ _ _ L G G G G G G G D _ + _ _ _ _ L G G G G G G G D _ _ + _ _ _ _ L G G G G G G D _ _ _ + _ _ _ _ L G G G G G D _ _ _ _ + _ _ _ L G G G G G D _ _ _ _ _ + _ _ _ L G G G G D _ _ _ _ _ _ + _ _ _ L G G G D _ _ _ _ _ _ _ + _ _ L G G G D _ _ _ _ _ _ _ _ + _ _ L G G D _ _ _ _ _ _ _ _ _ + _ _ L G D _ _ _ _ _ _ _ _ _ _ + _ L G D _ _ _ _ _ _ _ _ _ _ _ + _ L D _ _ _ _ _ _ _ _ _ _ _ _ + L D _ _ _ _ _ _ _ _ _ _ _ _ _ + D _ _ _ _ _ _ _ _ _ _ _ _ _ _ + }; + + uint8 *des = new uint8[kDesignSize]; + switch (which) { + case 0: + memcpy(des, kSLDesign, sizeof(kSLDesign)); + break; + case 1: + memcpy(des, kSRDesign, sizeof(kSRDesign)); + break; + default: + error("Wrong parameter in Bitmap::makeSpeechBubbleTail!"); + break; + } + + for (int i = 0; i < kDesignSize; i++) { + if ((des[i] >= 1) && (des[i] <= 3)) + des[i] = colorSet[kCBSay][des[i]]; + } + + return des; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/bitmap.h b/engines/cge2/bitmap.h new file mode 100644 index 0000000000..613222fc6e --- /dev/null +++ b/engines/cge2/bitmap.h @@ -0,0 +1,94 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_BITMAP_H +#define CGE2_BITMAP_H + +#include "cge2/general.h" +#include "common/file.h" + +namespace CGE2 { + +class CGE2Engine; +class EncryptedStream; +class V2D; + +#define kMaxPath 128 + +enum { + kBmpEOI = 0x0000, + kBmpSKP = 0x4000, + kBmpREP = 0x8000, + kBmpCPY = 0xC000 +}; + +#include "common/pack-start.h" + +struct HideDesc { + uint16 _skip; + uint16 _hide; +}; + +#include "common/pack-end.h" + +class Bitmap { + CGE2Engine *_vm; + + Common::String setExtension(const Common::String &str, const Common::String &ext); + bool loadVBM(EncryptedStream *f); +public: + uint16 _w; + uint16 _h; + uint8 *_v; + int32 _map; + HideDesc *_b; + + Bitmap(); + Bitmap(CGE2Engine *vm, const char *fname); + Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 *map); + Bitmap(CGE2Engine *vm, uint16 w, uint16 h, uint8 fill); + Bitmap(CGE2Engine *vm, const Bitmap &bmp); + ~Bitmap(); + + void setVM(CGE2Engine *vm); + Bitmap *code(uint8 *map); + Bitmap &operator=(const Bitmap &bmp); + void release(); + void hide(V2D pos); + void show(V2D pos); + bool solidAt(V2D pos); + void xLatPos(V2D &p); + + static uint8 *makeSpeechBubbleTail(int des, uint8 colorSet[][4]); +}; + + +typedef Bitmap *BitmapPtr; + +} // End of namespace CGE2 + +#endif // CGE2_BITMAP_H diff --git a/engines/cge2/cge2.cpp b/engines/cge2/cge2.cpp new file mode 100644 index 0000000000..ee62e20ac6 --- /dev/null +++ b/engines/cge2/cge2.cpp @@ -0,0 +1,207 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "engines/util.h" +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "cge2/cge2.h" +#include "cge2/bitmap.h" +#include "cge2/vga13h.h" +#include "cge2/sound.h" +#include "cge2/text.h" +#include "cge2/hero.h" +#include "cge2/general.h" +#include "cge2/spare.h" +#include "cge2/talk.h" +#include "cge2/cge2_main.h" +#include "cge2/map.h" + +namespace CGE2 { + +CGE2Engine::CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription) + : Engine(syst), _gameDescription(gameDescription), _randomSource("cge2") { + + // Debug/console setup + DebugMan.addDebugChannel(kCGE2DebugOpcode, "opcode", "CGE2 opcode debug channel"); + + _resman = nullptr; + _vga = nullptr; + _midiPlayer = nullptr; + _fx = nullptr; + _sound = nullptr; + _text = nullptr; + for (int i = 0; i < 2; i++) + _heroTab[i] = nullptr; + _eye = nullptr; + for (int i = 0; i < kSceneMax; i++) + _eyeTab[i] = nullptr; + _spare = nullptr; + _commandHandler = nullptr; + _commandHandlerTurbo = nullptr; + _font = nullptr; + _infoLine = nullptr; + _mouse = nullptr; + _keyboard = nullptr; + _talk = nullptr; + for (int i = 0; i < kMaxPoint; i++) + _point[i] = nullptr; + _sys = nullptr; + _busyPtr = nullptr; + for (int i = 0; i < 2; i++) + _vol[i] = nullptr; + _eventManager = nullptr; + _map = nullptr; + _console = nullptr; + _quitFlag = false; + _bitmapPalette = nullptr; + _gamePhase = kPhaseIntro; + _now = 1; + _sex = 1; + _mouseTop = kWorldHeight / 3; + _dark = false; + _lastFrame = 0; + _lastTick = 0; + _waitSeq = 0; + _waitRef = 0; + _soundStat._wait = nullptr; + _soundStat._ref[0] = 0; + _soundStat._ref[1] = 0; + _taken = false; + _endGame = false; + _req = 1; + _midiNotify = nullptr; + _spriteNotify = nullptr; + _startGameSlot = 0; + + _sayCap = ConfMan.getBool("subtitles"); + _sayVox = !ConfMan.getBool("speech_mute"); + _muteAll = ConfMan.getBool("mute"); + if (_muteAll) { + _oldMusicVolume = _oldSfxVolume = 0; + _music = _sayVox = false; + } else { + _oldMusicVolume = ConfMan.getInt("music_volume"); + _oldSfxVolume = ConfMan.getInt("sfx_volume"); + _music = _oldMusicVolume != 0; + } +} + +void CGE2Engine::init() { + // Create debugger console + _console = new CGE2Console(this); + _resman = new ResourceManager(); + _vga = new Vga(this); + _fx = new Fx(this, 16); + _sound = new Sound(this); + _midiPlayer = new MusicPlayer(this); + _text = new Text(this, "CGE"); + + for (int i = 0; i < 2; i++) + _heroTab[i] = new HeroTab(this); + + _eye = new V3D(); + for (int i = 0; i < kSceneMax; i++) + _eyeTab[i] = new V3D(); + + _spare = new Spare(this); + _commandHandler = new CommandHandler(this, false); + _commandHandlerTurbo = new CommandHandler(this, true); + _font = new Font(this); + _infoLine = new InfoLine(this, kInfoW); + _mouse = new Mouse(this); + _keyboard = new Keyboard(this); + + for (int i = 0; i < kMaxPoint; i++) + _point[i] = new V3D(); + + _sys = new System(this); + _eventManager = new EventManager(this); + _map = new Map(this); + _startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1; +} + +void CGE2Engine::deinit() { + // Remove all of our debug levels here + DebugMan.clearAllDebugChannels(); + + delete _console; + + delete _spare; + delete _resman; + delete _vga; + delete _fx; + delete _sound; + delete _midiPlayer; + delete _text; + + for (int i = 0; i < 2; i++) + delete _heroTab[i]; + + for (int i = 0; i < kSceneMax; i++) + delete _eyeTab[i]; + + delete _eye; + delete _commandHandler; + delete _commandHandlerTurbo; + delete _font; + delete _infoLine; + delete _mouse; + delete _keyboard; + + if (_talk != nullptr) + delete _talk; + + for (int i = 0; i < kMaxPoint; i++) + delete _point[i]; + + delete _sys; + delete _eventManager; + delete _map; +} + +bool CGE2Engine::hasFeature(EngineFeature f) const { + return (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime) + || (f == kSupportsRTL); +} + +Common::Error CGE2Engine::run() { + syncSoundSettings(); + initGraphics(kScrWidth, kScrHeight, false); + + init(); + cge2_main(); + deinit(); + + ConfMan.setBool("subtitles", _sayCap); + ConfMan.setBool("speech_mute", !_sayVox); + ConfMan.flushToDisk(); + + return Common::kNoError; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/cge2.h b/engines/cge2/cge2.h new file mode 100644 index 0000000000..fbe4cb3abc --- /dev/null +++ b/engines/cge2/cge2.h @@ -0,0 +1,338 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_H +#define CGE2_H + +#include "common/random.h" +#include "common/savefile.h" +#include "common/serializer.h" +#include "engines/engine.h" +#include "engines/advancedDetector.h" +#include "common/system.h" +#include "cge2/fileio.h" +#include "cge2/console.h" +#include "audio/mixer.h" + +namespace CGE2 { + +class Vga; +class Sprite; +class MusicPlayer; +class Fx; +class Sound; +class Text; +struct HeroTab; +class FXP; +class V3D; +class V2D; +struct Dac; +class Spare; +class CommandHandler; +class InfoLine; +class Mouse; +class Keyboard; +class Talk; +class Hero; +class Bitmap; +class System; +class EventManager; +class Font; +class Map; +struct SavegameHeader; + +#define kScrWidth 320 +#define kScrHeight 240 +#define kScrDepth 480 +#define kPanHeight 40 +#define kWorldHeight (kScrHeight - kPanHeight) +#define kMaxFile 128 +#define kPathMax 128 +#define kDimMax 8 +#define kWayMax 10 +#define kPocketMax 4 +#define kSceneMax 100 +#define kMaxPoint 4 +#define kInfoX 160 +#define kInfoY -11 +#define kInfoW 180 +#define kPocketsWidth 59 +#define kLineMax 512 + +#define kIntroExt ".I80" +#define kTabName "CGE.TAB" +#define kPocketFull 170 +#define kGameFrameDelay (750 / 50) +#define kGameTickDelay (750 / 62) + +#define kMusicRef 122 +#define kPowerRef 123 +#define kDvolRef 124 +#define kMvolRef 125 +#define kBusyRef 127 + +#define kOffUseCount 130 +#define kOffUseText 131 + +#define kSysTimeRate 6 // 12 Hz +#define kBlinkRate 4 // 3 Hz + +#define kQuitTitle 200 +#define kQuitText 201 +#define kNoQuitText 202 + +#define kSavegameVersion 1 +#define kSavegameStrSize 12 +#define kSavegameStr "SCUMMVM_CGE2" + +#define kColorNum 6 + +struct SavegameHeader { + uint8 version; + Common::String saveName; + Graphics::Surface *thumbnail; + int saveYear, saveMonth, saveDay; + int saveHour, saveMinutes; +}; + +enum ColorBank { kCBRel, kCBStd, kCBSay, kCBInf, kCBMnu, kCBWar }; + +enum GamePhase { kPhaseInGame, kPhaseIntro, kPhaseOver }; + +// our engine debug channels +enum { + kCGE2DebugOpcode = 1 << 0 +}; + +enum CallbackType { + kNullCB = 0, kQGame, kXScene +}; + +enum Action { kNear, kMTake, kFTake, kActions }; + +typedef void (CGE2Engine::*NotifyFunctionType)(); + +class CGE2Engine : public Engine { +private: + uint32 _lastFrame, _lastTick; + void tick(); + + CGE2Console *_console; + void init(); + void deinit(); + + Common::String generateSaveName(int slot); + void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header); + void saveGame(int slotNumber, const Common::String &desc); + bool loadGame(int slotNumber); + void syncHeader(Common::Serializer &s); + void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream); + void resetGame(); +public: + CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription); + virtual bool hasFeature(EngineFeature f) const; + virtual bool canSaveGameStateCurrently(); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual Common::Error loadGameState(int slot); + virtual Common::Error run(); + + static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + + GUI::Debugger *getDebugger() { + return _console; + } + + bool showTitle(const char *name); + void cge2_main(); + char *mergeExt(char *buf, const char *name, const char *ext); + void inf(const char *text, ColorBank col = kCBInf); + void movie(const char *ext); + void runGame(); + void loadHeroes(); + void loadScript(const char *fname, bool onlyToolbar = false); + Sprite *loadSprite(const char *fname, int ref, int scene, V3D &pos); + void badLab(const char *fn); + void sceneUp(int cav); + void sceneDown(); + void closePocket(); + void switchScene(int scene); + void storeHeroPos(); + void showBak(int ref); + void loadTab(); + int newRandom(int range); + void openPocket(); + void selectPocket(int n); + void busy(bool on); + void feedSnail(Sprite *spr, Action snq, Hero *hero); + int freePockets(int sx); + int findActivePocket(int ref); + void pocFul(); + void killText(); + void mainLoop(); + void handleFrame(); + Sprite *locate(int ref); + bool isHero(Sprite *spr); + void loadUser(); + void loadPos(); + void releasePocket(Sprite *spr); + void switchHero(int sex); + void offUse(); + void setAutoColors(); + bool cross(const V2D &a, const V2D &b, const V2D &c, const V2D &d); + bool contain(const V2D &a, const V2D &b, const V2D &p); + long det(const V2D &a, const V2D &b, const V2D &c); + int sgn(long n); + int mapCross(const V2D &a, const V2D &b); + Sprite *spriteAt(V2D pos); + void keyClick(); + void swapInPocket(Sprite *spr, Sprite *xspr); + void busyStep(); + + void optionTouch(int opt, uint16 mask); + void switchColorMode(); + void switchMusic(); + void quit(); + void setVolume(int idx, int cnt); + void checkVolumeSwitches(); + void switchCap(); + void switchVox(); + void switchSay(); + void initToolbar(); + void initVolumeSwitch(Sprite *volSwitch, int val); + void checkMute(); + + void checkSounds(); + + void setEye(const V3D &e); + void setEye(const V2D& e2, int z = -kScrWidth); + void setEye(const char *s); + + int number(char *s); + char *token(char *s); + char *tail(char *s); + int takeEnum(const char **tab, const char *text); + ID ident(const char *s); + bool testBool(char *s); + + void snKill(Sprite *spr); + void snHide(Sprite *spr, int val); + void snMidi(int val); + void snSeq(Sprite *spr, int val); + void snRSeq(Sprite *spr, int val); + void snSend(Sprite *spr, int val); + void snSwap(Sprite *spr, int val); + void snCover(Sprite *spr, int val); + void snUncover(Sprite *spr, Sprite *spr2); + void snKeep(Sprite *spr, int val); + void snGive(Sprite *spr, int val); + void snGoto(Sprite *spr, int val); + void snPort(Sprite *spr, int port); + void snMouse(bool on); + void snNNext(Sprite *spr, Action act, int val); + void snRNNext(Sprite *spr, int val); + void snRMTNext(Sprite *spr, int val); + void snRFTNext(Sprite *spr, int val); + void snRmNear(Sprite *spr); + void snRmMTake(Sprite *spr); + void snRmFTake(Sprite *spr); + void snSetRef(Sprite *spr, int val); + void snFlash(bool on); + void snCycle(int cnt); + void snWalk(Sprite *spr, int val); + void snReach(Sprite *spr, int val); + void snSound(Sprite *spr, int wav, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); + void snRoom(Sprite *spr, bool on); + void snGhost(Bitmap *bmp); + void snSay(Sprite *spr, int val); + + void hide1(Sprite *spr); + Sprite *expandSprite(Sprite *spr); + void qGame(); + void xScene(); + + const ADGameDescription *_gameDescription; + + Common::RandomSource _randomSource; + + bool _quitFlag; + Dac *_bitmapPalette; + GamePhase _gamePhase; // Original name: startupmode + int _now; + int _sex; + int _mouseTop; + bool _dark; + int _waitSeq; + int _waitRef; + + struct { + int *_wait; + int _ref[2]; + } _soundStat; + + bool _taken; + bool _endGame; + int _req; + NotifyFunctionType _midiNotify; + NotifyFunctionType _spriteNotify; + int _startGameSlot; + + bool _sayCap; + bool _sayVox; + int _oldMusicVolume; + int _oldSfxVolume; + bool _music; + bool _muteAll; + + ResourceManager *_resman; + Vga *_vga; + MusicPlayer *_midiPlayer; + Fx *_fx; + Sound *_sound; + Text *_text; + HeroTab *_heroTab[2]; + V3D *_eye; + V3D *_eyeTab[kSceneMax]; + Spare *_spare; + CommandHandler *_commandHandler; + CommandHandler *_commandHandlerTurbo; + Font *_font; + InfoLine *_infoLine; + Mouse *_mouse; + Keyboard *_keyboard; + Talk *_talk; + V3D *_point[kMaxPoint]; + System *_sys; + Sprite *_busyPtr; + Sprite *_vol[2]; + EventManager *_eventManager; + Map *_map; +}; + +} // End of namespace CGE2 + +#endif // CGE2_H diff --git a/engines/cge2/cge2_main.cpp b/engines/cge2/cge2_main.cpp new file mode 100644 index 0000000000..4e3d229618 --- /dev/null +++ b/engines/cge2/cge2_main.cpp @@ -0,0 +1,959 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "sound.h" +#include "cge2/cge2_main.h" +#include "cge2/cge2.h" +#include "cge2/vga13h.h" +#include "cge2/text.h" +#include "cge2/snail.h" +#include "cge2/hero.h" +#include "cge2/spare.h" +#include "cge2/map.h" + +namespace CGE2 { + +System::System(CGE2Engine *vm) : Sprite(vm), _vm(vm) { + _blinkCounter = 0; + _blinkSprite = nullptr; + tick(); +} + +void System::touch(uint16 mask, V2D pos, Common::KeyCode keyCode) { + if (mask & kEventKeyb) { + if (keyCode == Common::KEYCODE_ESCAPE) { + // The original was calling keyClick() + // The sound is uselessly annoying and noisy, so it has been removed + _vm->killText(); + if (_vm->_gamePhase == kPhaseIntro) { + _vm->_commandHandler->addCommand(kCmdClear, -1, 0, nullptr); + return; + } + } + } else { + if (_vm->_gamePhase != kPhaseInGame) + return; + _vm->_infoLine->setText(nullptr); + + if (mask & kMouseLeftUp) { + if (pos.y >= 0) { // world + if (!_vm->_talk && pos.y < _vm->_mouseTop) + _vm->_heroTab[_vm->_sex]->_ptr->walkTo(pos); + } else { // panel + if (_vm->_commandHandler->idle()) { + int sex = pos.x < kPocketsWidth; + if (sex || pos.x >= kScrWidth - kPocketsWidth) { + _vm->switchHero(sex); + if (_vm->_sex == sex) { + int dx = kPocketsWidth >> 1, + dy = 1 - (kPanHeight >> 1); + Sprite *s; + if (!sex) + pos.x -= kScrWidth - kPocketsWidth; + dx -= pos.x; + dy -= pos.y; + if (dx * dx + dy * dy > 10 * 10) { + int n = 0; + if (1 - pos.y >= (kPanHeight >> 1)) + n += 2; + if (pos.x >= (kPocketsWidth >> 1)) + ++n; + s = _vm->_heroTab[_vm->_sex]->_pocket[n]; + if (_vm->_sys->_blinkSprite) + _vm->_sys->_blinkSprite->_flags._hide = false; + if (_vm->_sys->_blinkSprite == s) + _vm->_sys->_blinkSprite = nullptr; + else + _vm->_sys->_blinkSprite = s; + } + } + } + } + } + } + } +} + +void System::tick() { + _time = kSysTimeRate; + + if (_blinkCounter) + --_blinkCounter; + else { + if (_blinkSprite) + _blinkSprite->_flags._hide ^= 1; + _blinkCounter = kBlinkRate; + } +} + +int CGE2Engine::number(char *str) { + char *s = token(str); + if (s == nullptr) + error("Wrong input for CGE2Engine::number()"); + int r = atoi(s); + char *pp = strchr(s, ':'); + if (pp) + r = (r << 8) + atoi(pp + 1); + return r; +} + +char *CGE2Engine::token(char *s) { + return strtok(s, " =\t,;/()"); +} + +char *CGE2Engine::tail(char *s) { + if (s && (*s == '=')) + s++; + return s; +} + +int CGE2Engine::takeEnum(const char **tab, const char *text) { + if (text) { + for (const char **e = tab; *e; e++) { + if (scumm_stricmp(text, *e) == 0) + return e - tab; + } + } + return -1; +} + +ID CGE2Engine::ident(const char *s) { + return ID(takeEnum(EncryptedStream::kIdTab, s)); +} + +bool CGE2Engine::testBool(char *s) { + return number(s) != 0; +} + +void CGE2Engine::badLab(const char *fn) { + error("Misplaced label in %s!", fn); +} + +Sprite *CGE2Engine::loadSprite(const char *fname, int ref, int scene, V3D &pos) { + int shpcnt = 0; + int seqcnt = 0; + int cnt[kActions]; + + for (int i = 0; i < kActions; i++) + cnt[i] = 0; + + ID section = kIdPhase; + bool frnt = true; + bool east = false; + bool port = false; + bool tran = false; + Hero *h; + ID id; + + char tmpStr[kLineMax + 1]; + mergeExt(tmpStr, fname, kSprExt); + + if (_resman->exist(tmpStr)) { // sprite description file exist + EncryptedStream sprf(this, tmpStr); + if (sprf.err()) + error("Bad SPR [%s]", tmpStr); + + int label = kNoByte; + Common::String line; + + for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()){ + if (line.empty()) + continue; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *p = token(tmpStr); + if (*p == '@') { + if (label != kNoByte) + badLab(fname); + label = atoi(p + 1); + continue; + } + + id = ident(p); + switch (id) { + case kIdName: // will be taken in Expand routine + if (label != kNoByte) + badLab(fname); + break; + case kIdType: + if (label != kNoByte) + badLab(fname); + break; + case kIdNear: + case kIdMTake: + case kIdFTake: + case kIdPhase: + case kIdSeq: + if (label != kNoByte) + badLab(fname); + section = id; + break; + case kIdFront: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + frnt = testBool(p); + break; + case kIdEast: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + east = testBool(p); + break; + case kIdPortable: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + port = testBool(p); + break; + case kIdTransparent: + if (label != kNoByte) + badLab(fname); + p = token(nullptr); + tran = testBool(p); + break; + default: + if (id >= kIdNear) + break; + switch (section) { + case kIdNear: + case kIdMTake: + case kIdFTake: + if (_commandHandler->getComId(p) >= 0) + ++cnt[section]; + else + error("Bad line %d [%s]", sprf.getLineCount(), tmpStr); + break; + case kIdPhase: + if (label != kNoByte) + badLab(fname); + ++shpcnt; + break; + case kIdSeq: + if (label != kNoByte) + badLab(fname); + ++seqcnt; + break; + default: + break; + } + break; + } + label = kNoByte; + } + + if (!shpcnt) + error("No shapes - %s", fname); + } else // No sprite description: mono-shaped sprite with only .BMP file. + ++shpcnt; + + // Make sprite of chosen type: + Sprite *sprite = nullptr; + char c = *fname | 0x20; + if (c >= 'a' && c <= 'z' && fname[1] == '0' && fname[2] == '\0') { + h = new Hero(this); + h->gotoxyz(pos); + sprite = h; + } else { + sprite = new Sprite(this); + sprite->gotoxyz(pos); + } + + if (sprite) { + sprite->_ref = ref; + sprite->_scene = scene; + + sprite->_flags._frnt = frnt; + sprite->_flags._east = east; + sprite->_flags._port = port; + sprite->_flags._tran = tran; + sprite->_flags._kill = true; + + // Extract the filename, without the extension + Common::strlcpy(sprite->_file, fname, sizeof(sprite->_file)); + char *p = strchr(sprite->_file, '.'); + if (p) + *p = '\0'; + + sprite->_shpCnt = shpcnt; + sprite->_seqCnt = seqcnt; + + for (int i = 0; i < kActions; i++) + sprite->_actionCtrl[i]._cnt = cnt[i]; + } + + return sprite; +} + +void CGE2Engine::loadScript(const char *fname, bool onlyToolbar) { + EncryptedStream scrf(this, fname); + + if (scrf.err()) + return; + + bool ok = true; + int lcnt = 0; + + char tmpStr[kLineMax + 1]; + Common::String line; + + for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) { + if (line.empty()) + continue; + + lcnt++; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + ok = false; // not OK if break + + V3D P; + + // sprite ident number + int SpI = number(tmpStr); + + if (onlyToolbar && SpI >= 141) + return; + + // sprite file name + char *SpN; + if ((SpN = token(nullptr)) == nullptr) + break; + + // sprite scene + int SpA = number(nullptr); + + // sprite column + P._x = number(nullptr); + + // sprite row + P._y = number(nullptr); + + // sprite Z pos + P._z = number(nullptr); + + // sprite life + bool BkG = number(nullptr) == 0; + + ok = true; // no break: OK + + Sprite *sprite = loadSprite(SpN, SpI, SpA, P); + if (sprite) { + if (BkG) + sprite->_flags._back = true; + + int n = _spare->count(); + if (_spare->locate(sprite->_ref) == nullptr) + _spare->dispose(sprite); + else + delete sprite; + + if (_spare->count() == n) + error("Duplicate reference! %s", SpN); + } + } + + if (!ok) + error("Bad INI line %d [%s]", scrf.getLineCount(), fname); +} + +void CGE2Engine::movie(const char *ext) { + assert(ext); + + if (_quitFlag) + return; + + char fn[12]; + snprintf(fn, 12, "CGE%s", ext); + + if (_resman->exist(fn)) { + int now = _now; + _now = atoi(ext + 2); + loadScript(fn); + sceneUp(_now); + _keyboard->setClient(_sys); + + while (!_commandHandler->idle() && !_quitFlag) + mainLoop(); + + _keyboard->setClient(nullptr); + _commandHandler->addCommand(kCmdClear, -1, 0, nullptr); + _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr); + _spare->clear(); + _vga->_showQ->clear(); + _now = now; + } +} + +void CGE2Engine::sceneUp(int cav) { + _now = cav; + int bakRef = _now << 8; + if (_music) + _midiPlayer->loadMidi(bakRef); + showBak(bakRef); + *_eye = *(_eyeTab[_now]); + _mouseTop = V2D(this, V3D(0, 1, kScrDepth)).y; + _map->load(_now); + _spare->takeScene(_now); + openPocket(); + + for (int i = 0; i < 2; i++) { + Hero *h = _heroTab[i]->_ptr; + if (h && h->_scene == _now) { + V2D p = *_heroTab[i]->_posTab[_now]; + h->gotoxyz(V3D(p.x, 0, p.y)); + h->clrHide(); + _vga->_showQ->insert(h); + h->park(); + h->setCurrent(); + h->setContact(); + } + } + + _sound->stop(); + _fx->clear(); + + selectPocket(-1); + _infoLine->setText(nullptr); + busy(false); + + if (!_dark) + _vga->sunset(); + + _vga->show(); + _vga->copyPage(1, 0); + _vga->show(); + _vga->sunrise(_vga->_sysPal); + + _dark = false; + + if (_gamePhase == kPhaseInGame) + _mouse->on(); + + feedSnail(_vga->_showQ->locate(bakRef + 255), kNear, _heroTab[_sex]->_ptr); +} + +void CGE2Engine::sceneDown() { + busy(true); + _soundStat._wait = nullptr; // unlock snail + Sprite *spr = _vga->_showQ->locate((_now << 8) | 254); + + if (spr) + feedSnail(spr, kNear, _heroTab[_sex]->_ptr); + + while (!(_commandHandler->idle() && _commandHandlerTurbo->idle())) { + _commandHandlerTurbo->runCommand(); + _commandHandler->runCommand(); + } + + closePocket(); + for (int i = 0; i < 2; i++) + _spare->update(_vga->_showQ->remove(_heroTab[i]->_ptr)); + _spare->dispose(); +} + +void CGE2Engine::switchScene(int scene) { + if (scene == _now) + return; + + _req = scene; + storeHeroPos(); + *(_eyeTab[_now]) = *_eye; + + if (scene < 0) + _commandHandler->addCallback(kCmdExec, -1, 0, kQGame); // quit game + else { + if (_heroTab[_sex]->_ptr->_scene == _now) { + _heroTab[_sex]->_ptr->setScene(scene); + if (_heroTab[!_sex]->_ptr->_scene == _now) + _heroTab[!_sex]->_ptr->setScene(scene); + } + _mouse->off(); + if (_heroTab[_sex]->_ptr) + _heroTab[_sex]->_ptr->park(); + killText(); + _commandHandler->addCallback(kCmdExec, -1, 0, kXScene); // switch scene + } +} + +void CGE2Engine::storeHeroPos() { + for (int i = 0; i < 2; i++) { + Hero *h = _heroTab[i]->_ptr; + if (h->_scene == _now) { + delete _heroTab[i]->_posTab[_now]; + V2D *temp = new V2D(this, h->_pos3D._x.trunc(), h->_pos3D._z.trunc()); + _heroTab[i]->_posTab[_now] = temp; + } + } +} + +void CGE2Engine::showBak(int ref) { + Sprite *spr = _spare->locate(ref); + if (spr != nullptr) { + _bitmapPalette = _vga->_sysPal; + spr->expand(); + _bitmapPalette = nullptr; + spr->show(2); + _vga->copyPage(1, 2); + } +} + +void CGE2Engine::mainLoop() { + if (_gamePhase == kPhaseInGame) + checkSounds(); + + _vga->show(); + _commandHandlerTurbo->runCommand(); + _commandHandler->runCommand(); + + // Handle a delay between game frames + handleFrame(); + + // Handle any pending events + _eventManager->poll(); + + // Check shouldQuit() + _quitFlag = shouldQuit(); +} + +void CGE2Engine::checkSounds() { + checkMute(); + _sound->checkSoundHandles(); + checkVolumeSwitches(); + _midiPlayer->syncVolume(); + syncSoundSettings(); +} + +void CGE2Engine::handleFrame() { + // Game frame delay + uint32 millis = g_system->getMillis(); + while (!_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) { + // Handle any pending events + _eventManager->poll(); + + if (millis >= (_lastTick + kGameTickDelay)) { + // Dispatch the tick to any active objects + tick(); + _lastTick = millis; + } + + // Slight delay + g_system->delayMillis(5); + millis = g_system->getMillis(); + } + _lastFrame = millis; + + if (millis >= (_lastTick + kGameTickDelay)) { + // Dispatch the tick to any active objects + tick(); + _lastTick = millis; + } +} + +Sprite *CGE2Engine::locate(int ref) { + _taken = false; + Sprite *spr = _vga->_showQ->locate(ref); + if (!spr) { + spr = _spare->locate(ref); + if (spr) + _taken = true; + } + return spr; +} + +bool CGE2Engine::isHero(Sprite *spr) { + return spr && spr->_ref / 10 == 14; +} + +void CGE2Engine::tick() { + // system pseudo-sprite + if (_sys && _sys->_time && (--_sys->_time == 0)) + _sys->tick(); + + for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) { + if (spr->_time && (--spr->_time == 0)) + spr->tick(); + + if (_waitRef && (_waitRef == spr->_ref) && spr->seqTest(_waitSeq)) + _waitRef = 0; + } + + _mouse->tick(); +} + +void CGE2Engine::busy(bool on) { + if (on) { + _spriteNotify = _midiNotify = &CGE2::CGE2Engine::busyStep; + busyStep(); + } else { + if (_busyPtr) + _busyPtr->step(0); + _spriteNotify = _midiNotify = nullptr; + } +} + +void CGE2Engine::busyStep() { + if (_busyPtr) { + _busyPtr->step((_busyPtr->_seqPtr) ? -1 : 1); + _busyPtr->show(0); + } +} + +void CGE2Engine::runGame() { + if (_quitFlag) + return; + + loadUser(); + sceneUp(_now); + initToolbar(); + + // main loop + while (!_endGame && !_quitFlag) + mainLoop(); + + // If leaving the game (close window, return to launcher, etc. + // - except finishing the game), explicitly save it's state: + if (!_endGame && canSaveGameStateCurrently()) + qGame(); + + _keyboard->setClient(nullptr); + _commandHandler->addCommand(kCmdClear, -1, 0, nullptr); + _commandHandlerTurbo->addCommand(kCmdClear, -1, 0, nullptr); + _mouse->off(); +} + +void CGE2Engine::loadUser() { + loadPos(); + + if (_startGameSlot != -1) + loadGame(_startGameSlot); + else { + loadScript("CGE.INI"); + loadHeroes(); + } +} + +void CGE2Engine::loadHeroes() { // Original name: loadGame() + // load sprites & pocket + + Sprite *s; + Hero *h = nullptr; + + // initialize Andzia/Anna + s = _spare->take(142); + if (s) { + h = new Hero(this); + *(Sprite*)h = *s; + delete s; + h->expand(); + _spare->update(h); + } + _heroTab[0]->_ptr = h; + s = _spare->locate(152); + _vga->_showQ->insert(s); + _heroTab[0]->_face = s; + + // initialize Wacek/Vincent + s = _spare->take(141); + if (s) { + h = new Hero(this); + *(Sprite*)h = *s; + delete s; + h->expand(); + _spare->update(h); + } + _heroTab[1]->_ptr = h; + s = _spare->locate(151); + _vga->_showQ->insert(s); + _heroTab[1]->_face = s; + + //--- start! + switchHero(_sex); +} + +void CGE2Engine::loadPos() { + if (_resman->exist("CGE.HXY")) { + for (int cav = 0; cav < kSceneMax; cav++) + _heroTab[1]->_posTab[cav] = new V2D(this, 180, 10); + + EncryptedStream file(this, "CGE.HXY"); + + for (int cav = 0; cav < kSceneMax; cav++) { + _heroTab[0]->_posTab[cav] = new V2D(this); + _heroTab[0]->_posTab[cav]->x = file.readSint16LE(); + _heroTab[0]->_posTab[cav]->y = file.readSint16LE(); + } + + for (int cav = 0; cav < 41; cav++) { // (564 - 400) / 4 = 41 + _heroTab[1]->_posTab[cav]->x = file.readSint16LE(); + _heroTab[1]->_posTab[cav]->y = file.readSint16LE(); + } + } else + error("Missing file: CGE.HXY"); +} + +void CGE2Engine::loadTab() { + setEye(_text->getText(240)); + for (int i = 0; i < kSceneMax; i++) + *(_eyeTab[i]) = *_eye; + + if (_resman->exist(kTabName)) { + EncryptedStream f(this, kTabName); + + for (int i = 0; i < kSceneMax; i++) { + uint32 v = f.readUint32LE(); + _eyeTab[i]->_x = FXP(v >> 8, static_cast<int>((int8)(v & 0xff))); + + v = f.readUint32LE(); + _eyeTab[i]->_y = FXP(v >> 8, static_cast<int>((int8)(v & 0xff))); + + v = f.readUint32LE(); + _eyeTab[i]->_z = FXP(v >> 8, static_cast<int>((int8)(v & 0xff))); + } + } +} + +void CGE2Engine::cge2_main() { + loadTab(); + + if (_startGameSlot != -1) { + // Starting up a savegame from the launcher + runGame(); + return; + } + + if (showTitle("WELCOME")) { + movie(kIntroExt); + + if (_text->getText(255) != nullptr) { + runGame(); + _gamePhase = kPhaseOver; + } + + _vga->sunset(); + } else + _vga->sunset(); +} + +char *CGE2Engine::mergeExt(char *buf, const char *name, const char *ext) { + strcpy(buf, name); + char *dot = strrchr(buf, '.'); + if (!dot) + strcat(buf, ext); + + return buf; +} + +void CGE2Engine::setEye(const V3D &e) { + *_eye = e; +} + +void CGE2Engine::setEye(const V2D& e2, int z) { + _eye->_x = e2.x; + _eye->_y = e2.y; + _eye->_z = z; +} + +void CGE2Engine::setEye(const char *s) { + char *tempStr = new char[strlen(s) + 1]; + strcpy(tempStr, s); + _eye->_x = atoi(token(tempStr)); + _eye->_y = atoi(token(nullptr)); + _eye->_z = atoi(token(nullptr)); + delete[] tempStr; +} + +int CGE2Engine::newRandom(int range) { + if (!range) + return 0; + + return _randomSource.getRandomNumber(range - 1); +} + +bool CGE2Engine::showTitle(const char *name) { + if (_quitFlag) + return false; + + _bitmapPalette = _vga->_sysPal; + BitmapPtr LB = new Bitmap[1]; + LB[0] = Bitmap(this, name); + _bitmapPalette = nullptr; + + Sprite D(this, LB, 1); + D._flags._kill = true; + D.gotoxyz(kScrWidth >> 1, -(kPanHeight >> 1)); + + _vga->sunset(); + D.show(2); + _vga->copyPage(1, 2); + _vga->copyPage(0, 1); + _vga->sunrise(_vga->_sysPal); + _vga->update(); + + g_system->delayMillis(2500); + + return true; +} + +void CGE2Engine::killText() { + if (!_talk) + return; + + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk); + _talk = nullptr; +} + +void CGE2Engine::switchHero(int sex) { + if (sex != _sex) { + int scene = _heroTab[sex]->_ptr->_scene; + if (_sys->_blinkSprite) { + _sys->_blinkSprite->_flags._hide = false; + _sys->_blinkSprite = nullptr; + } + + if (scene >= 0) { + _commandHandler->addCommand(kCmdSeq, -1, 2, _heroTab[_sex]->_face); + _sex ^= 1; + switchScene(scene); + } + } + Sprite *face = _heroTab[_sex]->_face; + if (face->_seqPtr == 0) + _commandHandler->addCommand(kCmdSeq, -1, 1, face); +} + +void Sprite::touch(uint16 mask, V2D pos, Common::KeyCode keyCode) { + if ((mask & kEventAttn) != 0) + return; + + if (_vm->_gamePhase == kPhaseInGame) + _vm->_infoLine->setText(name()); + + if (_ref < 0) + return; // cannot access system sprites + + if (_ref / 10 == 12) { + _vm->optionTouch(_ref % 10, mask); + return; + } + + if ((mask & kMouseLeftUp) && _vm->_commandHandler->idle()) { + if (_vm->isHero(this) && !_vm->_sys->_blinkSprite) { + _vm->switchHero((this == _vm->_heroTab[1]->_ptr) ? 1 : 0); + } else if (_flags._kept) { // sprite in pocket + for (int sex = 0; sex < 2; ++sex) { + for (int p = 0; p < kPocketMax; ++p) { + if (_vm->_heroTab[sex]->_pocket[p] == this) { + _vm->switchHero(sex); + if (_vm->_sex == sex) { + if (_vm->_sys->_blinkSprite) + _vm->_sys->_blinkSprite->_flags._hide = false; + + if (_vm->_sys->_blinkSprite == this) + _vm->_sys->_blinkSprite = nullptr; + else + _vm->_sys->_blinkSprite = this; + } + } + } + } + } else { // sprite NOT in pocket + Hero *h = _vm->_heroTab[_vm->_sex]->_ptr; + if (!_vm->_talk) { + // the "+3" is a hack used to work around a script issue in scene 5 + if ((_ref & 0xFF) < 200 && h->distance(this) > (h->_maxDist << 1) + 3) + h->walkTo(this); + else if (_vm->_sys->_blinkSprite) { + if (works(_vm->_sys->_blinkSprite)) { + _vm->feedSnail(_vm->_sys->_blinkSprite, (_vm->_sex) ? kMTake : kFTake, _vm->_heroTab[_vm->_sex]->_ptr); + _vm->_sys->_blinkSprite->_flags._hide = false; + _vm->_sys->_blinkSprite = nullptr; + } else + _vm->offUse(); + + _vm->selectPocket(-1); + // else, no pocket sprite selected + } else if (_flags._port) { // portable + if (_vm->findActivePocket(-1) < 0) + _vm->pocFul(); + else { + _vm->_commandHandler->addCommand(kCmdReach, -2, _ref, nullptr); + _vm->_commandHandler->addCommand(kCmdKeep, -1, -1, this); + _flags._port = false; + } + } else { // non-portable + Action a = h->action(); + if (_actionCtrl[a]._cnt) { + CommandHandler::Command *cmdList = snList(a); + if (cmdList[_actionCtrl[a]._ptr]._commandType == kCmdNext) + _vm->offUse(); + else + _vm->feedSnail(this, a, h); + } else + _vm->offUse(); + } + } + } + } +} + +void CGE2Engine::keyClick() { + _commandHandlerTurbo->addCommand(kCmdSound, -1, 5, nullptr); +} + +void CGE2Engine::offUse() { + int seq = 0; + int offUseCount = atoi(_text->getText(kOffUseCount)); + + // This fixes the issue of empty speech bubbles in the original. + // Now we only let this cycle pass if it randoms a valid value for getText(). + int txt = 0; + do { + txt = kOffUseText + _sex * offUseCount + newRandom(offUseCount); + } while (_text->getText(txt) == nullptr); + + Hero *h = _heroTab[_sex]->_ptr; + h->park(); + _commandHandler->addCommand(kCmdWait, -1, -1, h); + _commandHandler->addCommand(kCmdSeq, -1, seq, h); + if (!_sayVox) + _commandHandler->addCommand(kCmdSound, -1, 6 + _sex, h); + _commandHandler->addCommand(kCmdWait, -1, -1, h); + _commandHandler->addCommand(kCmdSay, -1, txt, h); +} + +Sprite *CGE2Engine::spriteAt(V2D pos) { + Sprite *spr; + + for (spr = _vga->_showQ->last(); spr; spr = spr->_prev) { + if (!spr->_flags._hide && !spr->_flags._tran && (spr->getShp()->solidAt(pos - spr->_pos2D))) + break; + } + + return spr; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/cge2_main.h b/engines/cge2/cge2_main.h new file mode 100644 index 0000000000..4d92c33e50 --- /dev/null +++ b/engines/cge2/cge2_main.h @@ -0,0 +1,52 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_MAIN_H +#define CGE2_MAIN_H + +#include "cge2/events.h" + +namespace CGE2 { + +#define kShowScummVMVersion 15 + +class System : public Sprite { +public: + int _blinkCounter; + Sprite *_blinkSprite; + + System(CGE2Engine *vm); + + virtual void touch(uint16 mask, V2D pos, Common::KeyCode keyCode); + void tick(); +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE2 + +#endif // CGE2_MAIN_H diff --git a/engines/cge2/configure.engine b/engines/cge2/configure.engine new file mode 100644 index 0000000000..6ccbfee088 --- /dev/null +++ b/engines/cge2/configure.engine @@ -0,0 +1,3 @@ +# This file is included from the main "configure" script +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine cge2 "CGE2" no diff --git a/engines/cge2/console.cpp b/engines/cge2/console.cpp new file mode 100644 index 0000000000..c67c7ab788 --- /dev/null +++ b/engines/cge2/console.cpp @@ -0,0 +1,33 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "cge2/console.h" + +namespace CGE2 { + +CGE2Console::CGE2Console(CGE2Engine *vm) : GUI::Debugger() { +} + +CGE2Console::~CGE2Console() { +} + +} // End of namespace CGE diff --git a/engines/cge2/console.h b/engines/cge2/console.h new file mode 100644 index 0000000000..15956bf728 --- /dev/null +++ b/engines/cge2/console.h @@ -0,0 +1,40 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CGE2_CONSOLE_H +#define CGE2_CONSOLE_H + +#include "gui/debugger.h" + +namespace CGE2 { + +class CGE2Engine; + +class CGE2Console : public GUI::Debugger { +public: + CGE2Console(CGE2Engine *vm); + virtual ~CGE2Console(); +}; + +} // End of namespace CGE + +#endif // CGE2_CONSOLE_H diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp new file mode 100644 index 0000000000..6e1b93d0b8 --- /dev/null +++ b/engines/cge2/detection.cpp @@ -0,0 +1,280 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/cge2.h" +#include "cge2/fileio.h" +#include "engines/advancedDetector.h" +#include "common/translation.h" +#include "graphics/surface.h" + +namespace CGE2 { + +#define GAMEOPTION_COLOR_BLIND_DEFAULT_OFF GUIO_GAMEOPTIONS1 + +static const PlainGameDescriptor CGE2Games[] = { + { "sfinx", "Sfinx" }, + { 0, 0 } +}; + +static const ADGameDescription gameDescriptions[] = { + { + "sfinx", "Freeware", + { + { "vol.cat", 0, "21197b287d397c53261b6616bf0dd880", 129024 }, + { "vol.dat", 0, "de14291869a8eb7c2732ab783c7542ef", 34180844 }, + AD_LISTEND + }, + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + }, + + { + "sfinx", "Freeware v1.0", + { + {"vol.cat", 0, "aa402aed24a72c53a4d1211c456b79dd", 129024}, + {"vol.dat", 0, "5966ac26d91d664714349669f9dd09b5", 34180164}, + AD_LISTEND + }, + Common::PL_POL, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + }, + + { + "sfinx", "Freeware v0.3", + { + {"vol.cat", 0, "f158e469dccbebc5a632eb848df89779", 129024}, + {"vol.dat", 0, "d40a6b4ae173d6930be54ba56bee15d5", 34183430}, + AD_LISTEND + }, + Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + }, + + { + "sfinx", "Freeware v1.0", + { + {"vol.cat", 0, "f158e469dccbebc5a632eb848df89779", 129024}, + {"vol.dat", 0, "d40a6b4ae173d6930be54ba56bee15d5", 34183443}, + AD_LISTEND + }, + Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) + }, + + AD_TABLE_END_MARKER +}; + +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_COLOR_BLIND_DEFAULT_OFF, + { + _s("Color Blind Mode"), + _s("Enable Color Blind Mode by default"), + "enable_color_blind", + false + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + +class CGE2MetaEngine : public AdvancedMetaEngine { +public: + CGE2MetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(ADGameDescription), CGE2Games, optionsList) { + _singleid = "sfinx"; + } + + virtual const char *getName() const { + return "CGE2"; + } + + virtual const char *getOriginalCopyright() const { + return "Sfinx (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon"; + } + + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual bool hasFeature(MetaEngineFeature f) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + virtual void removeSaveState(const char *target, int slot) const; +}; + +static const ADFileBasedFallback fileBasedFallback[] = { + { &gameDescriptions[0], { "vol.cat", "vol.dat", 0 } }, + { 0, { 0 } } +}; + +static ADGameDescription s_fallbackDesc = { + "Sfinx", + "Unknown version", + AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor + Common::UNK_LANG, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO1(GAMEOPTION_COLOR_BLIND_DEFAULT_OFF) +}; + +// This fallback detection looks identical to the one used for CGE. In fact, the difference resides +// in the ResourceManager which handles a different archive format. The rest of the detection is identical. +const ADGameDescription *CGE2MetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + ADFilePropertiesMap filesProps; + + const ADGameDescription *game; + game = detectGameFilebased(allFiles, fslist, CGE2::fileBasedFallback, &filesProps); + + if (!game) + return 0; + + SearchMan.clear(); + SearchMan.addDirectory(fslist.begin()->getParent().getPath(), fslist.begin()->getParent()); + ResourceManager *resman; + resman = new ResourceManager(); + bool result = resman->exist("CGE.SAY"); + delete resman; + + if (!result) + return 0; + + reportUnknown(fslist.begin()->getParent(), filesProps); + return &s_fallbackDesc; +} + +bool CGE2MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + if (desc) + *engine = new CGE2::CGE2Engine(syst, desc); + + return desc != 0; +} + +bool CGE2MetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup); +} + +int CGE2MetaEngine::getMaximumSaveSlot() const { + return 99; +} + +SaveStateList CGE2MetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".???"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(filename->c_str() + filename->size() - 3); + + if (slotNum >= 0 && slotNum <= 99) { + + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + CGE2::SavegameHeader header; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + file->read(buffer, kSavegameStrSize + 1); + + if (!strncmp(buffer, kSavegameStr, kSavegameStrSize + 1)) { + // Valid savegame + if (CGE2::CGE2Engine::readSavegameHeader(file, header)) { + saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); + if (header.thumbnail) { + header.thumbnail->free(); + delete header.thumbnail; + } + } + } else { + // Must be an original format savegame + saveList.push_back(SaveStateDescriptor(slotNum, "Unknown")); + } + + delete file; + } + } + } + + return saveList; +} + +SaveStateDescriptor CGE2MetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); + + if (f) { + CGE2::SavegameHeader header; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + f->read(buffer, kSavegameStrSize + 1); + + bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) && + CGE2::CGE2Engine::readSavegameHeader(f, header); + delete f; + + if (!hasHeader) { + // Original savegame perhaps? + SaveStateDescriptor desc(slot, "Unknown"); + return desc; + } else { + // Create the return descriptor + SaveStateDescriptor desc(slot, header.saveName); + desc.setThumbnail(header.thumbnail); + desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); + desc.setSaveTime(header.saveHour, header.saveMinutes); + + // Slot 0 is used for the 'automatic save on exit' save in Soltys, thus + // we prevent it from being deleted or overwritten by accident. + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + + return desc; + } + } + + return SaveStateDescriptor(); +} + +void CGE2MetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} + +} // End of namespace CGE2 + +#if PLUGIN_ENABLED_DYNAMIC(CGE2) + REGISTER_PLUGIN_DYNAMIC(CGE2, PLUGIN_TYPE_ENGINE, CGE2::CGE2MetaEngine); +#else + REGISTER_PLUGIN_STATIC(CGE2, PLUGIN_TYPE_ENGINE, CGE2::CGE2MetaEngine); +#endif diff --git a/engines/cge2/events.cpp b/engines/cge2/events.cpp new file mode 100644 index 0000000000..85743c8011 --- /dev/null +++ b/engines/cge2/events.cpp @@ -0,0 +1,293 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "gui/saveload.h" +#include "gui/about.h" +#include "gui/message.h" +#include "common/config-manager.h" +#include "common/events.h" +#include "engines/advancedDetector.h" +#include "cge2/events.h" +#include "cge2/text.h" +#include "cge2/cge2_main.h" + +namespace CGE2 { + +/*----------------- KEYBOARD interface -----------------*/ + +Keyboard::Keyboard(CGE2Engine *vm) : _client(nullptr), _vm(vm) { +} + +Keyboard::~Keyboard() { +} + +Sprite *Keyboard::setClient(Sprite *spr) { + SWAP(_client, spr); + return spr; +} + +bool Keyboard::getKey(Common::Event &event) { + Common::KeyCode keycode = event.kbd.keycode; + + switch (keycode) { + case Common::KEYCODE_F1: + if (event.type == Common::EVENT_KEYUP) + return false; + // Display ScummVM version and translation strings + for (int i = 0; i < 3; i++) + _vm->_commandHandler->addCommand(kCmdInf, 1, kShowScummVMVersion + i, NULL); + return false; + case Common::KEYCODE_F5: + if (_vm->canSaveGameStateCurrently()) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true); + int16 savegameId = dialog->runModalWithCurrentTarget(); + Common::String savegameDescription = dialog->getResultString(); + delete dialog; + + if (savegameId != -1) + _vm->saveGameState(savegameId, savegameDescription); + } + return false; + case Common::KEYCODE_F7: + if (_vm->canLoadGameStateCurrently()) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false); + int16 savegameId = dialog->runModalWithCurrentTarget(); + delete dialog; + + if (savegameId != -1) + _vm->loadGameState(savegameId); + } + return false; + case Common::KEYCODE_d: + if (event.kbd.flags & Common::KBD_CTRL) { + // Start the debugger + _vm->getDebugger()->attach(); + _vm->getDebugger()->onFrame(); + return false; + } + break; + case Common::KEYCODE_x: + if (event.kbd.flags & Common::KBD_ALT) { + _vm->quit(); + return false; + } + break; + case Common::KEYCODE_F10: + if (_vm->_commandHandler->idle()) + _vm->switchScene(-1); // Exits the game. + return false; + default: + break; + } + + return true; +} + +void Keyboard::newKeyboard(Common::Event &event) { + if (!getKey(event)) + return; + + if ((event.type == Common::EVENT_KEYDOWN) && _client) { + CGE2Event &evt = _vm->_eventManager->getNextEvent(); + evt._x = 0; + evt._y = 0; + evt._keyCode = event.kbd.keycode; // Keycode + evt._mask = kEventKeyb; // Event mask + evt._spritePtr = _client; // Sprite pointer + } +} + +/*----------------- MOUSE interface -----------------*/ + +Mouse::Mouse(CGE2Engine *vm) : Sprite(vm), _busy(nullptr), _hold(nullptr), _hx(0), _point(vm), _vm(vm) { + _hold = nullptr; + _hx = 0; + _hy = 0; + _exist = true; + _buttons = 0; + _busy = nullptr; + _active = false; + _flags._kill = false; + + setSeq(_stdSeq8); + + BitmapPtr MC = new Bitmap[2]; + MC[0] = Bitmap(_vm, "MOUSE"); + MC[1] = Bitmap(_vm, "DUMMY"); + setShapeList(MC, 2); + + step(1); + on(); + off(); +} + +Mouse::~Mouse() { + off(); +} + +void Mouse::on() { + if (_seqPtr && _exist) { + _active = true; + step(0); + if (_busy) + _busy->step(0); + } +} + +void Mouse::off() { + if (_seqPtr == 0) { + if (_exist) + _active = false; + + step(1); + if (_busy) + _busy->step(1); + } +} + +void Mouse::newMouse(Common::Event &event) { + if (!_active) + return; + + CGE2Event &evt = _vm->_eventManager->getNextEvent(); + evt._x = event.mouse.x; + evt._y = event.mouse.y; + evt._keyCode = Common::KEYCODE_INVALID; + evt._spritePtr = _vm->spriteAt(V2D(_vm, evt._x, evt._y)); + + switch (event.type) { + case Common::EVENT_MOUSEMOVE: + evt._mask = kMouseRoll; + break; + case Common::EVENT_LBUTTONDOWN: + evt._mask = kMouseLeftDown; + _buttons |= 1; + break; + case Common::EVENT_LBUTTONUP: + evt._mask = kMouseLeftUp; + _buttons &= ~1; + break; + case Common::EVENT_RBUTTONDOWN: + evt._mask = kMouseRightDown; + _buttons |= 2; + break; + case Common::EVENT_RBUTTONUP: + evt._mask = kMouseRightUp; + _buttons &= ~2; + break; + default: + break; + } +} + +/*----------------- EventManager interface -----------------*/ + +EventManager::EventManager(CGE2Engine *vm) : _vm(vm) { + _eventQueueHead = 0; + _eventQueueTail = 0; + memset(&_eventQueue, 0, kEventMax * sizeof(CGE2Event)); + memset(&_event, 0, sizeof(Common::Event)); +} + +void EventManager::poll() { + while (g_system->getEventManager()->pollEvent(_event)) { + _event.mouse.y = kWorldHeight - _event.mouse.y; + switch (_event.type) { + case Common::EVENT_KEYDOWN: + case Common::EVENT_KEYUP: + // Handle keyboard events + _vm->_keyboard->newKeyboard(_event); + handleEvents(); + break; + case Common::EVENT_MOUSEMOVE: + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_RBUTTONUP: + // Handle mouse events + _vm->_mouse->newMouse(_event); + handleEvents(); + break; + default: + break; + } + } +} + +void EventManager::handleEvents() { + while (_eventQueueTail != _eventQueueHead) { + CGE2Event e = _eventQueue[_eventQueueTail]; + _vm->_mouse->_point = V2D(_vm, e._x, e._y); + if (e._mask) { + if (e._mask & kMouseMask) { + e._spritePtr = _vm->spriteAt(_vm->_mouse->_point); + e._x += (_vm->_mouse->_siz.x >> 1); + e._y -= _vm->_mouse->_siz.y; + if (_vm->_mouse->_hold && (e._spritePtr != _vm->_mouse->_hold)) { + _vm->_mouse->_hold->touch(e._mask | kEventAttn, + V2D(_vm, e._x - _vm->_mouse->_hold->_pos2D.x, e._y - _vm->_mouse->_hold->_pos2D.y), e._keyCode); + } + // update mouse cursor position + if (e._mask & kMouseRoll) + _vm->_mouse->gotoxyz(V2D(_vm, e._x, e._y)); + } + + // activate current touched SPRITE + if (e._spritePtr) { + if (e._mask & kEventKeyb) + e._spritePtr->touch(e._mask, _vm->_mouse->_point, e._keyCode); + else + e._spritePtr->touch(e._mask, _vm->_mouse->_point - e._spritePtr->_pos2D, e._keyCode); + } else if (_vm->_sys) + _vm->_sys->touch(e._mask, _vm->_mouse->_point, e._keyCode); + + // discard Text if button released + if (e._mask & (kMouseLeftUp | kMouseRightUp)) + _vm->killText(); + } + _eventQueueTail = (_eventQueueTail + 1) % kEventMax; + } +} + +void EventManager::clearEvent(Sprite *spr) { + if (spr) { + for (uint16 e = _eventQueueTail; e != _eventQueueHead; e = (e + 1) % kEventMax) { + if (_eventQueue[e]._spritePtr == spr) + _eventQueue[e]._mask = 0; + } + } else + _eventQueueTail = _eventQueueHead; +} + +CGE2Event &EventManager::getNextEvent() { + CGE2Event &evt = _eventQueue[_eventQueueHead]; + _eventQueueHead = (_eventQueueHead + 1) % kEventMax; + + return evt; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/events.h b/engines/cge2/events.h new file mode 100644 index 0000000000..d1aaca2ded --- /dev/null +++ b/engines/cge2/events.h @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_EVENTS_H +#define CGE2_EVENTS_H + +#include "common/events.h" +#include "cge2/talk.h" +#include "cge2/vga13h.h" + +namespace CGE2 { + +/*----------------- KEYBOARD interface -----------------*/ + +#define kEventMax 256 + +enum EventMask { + kMouseRoll = 1 << 0, + kMouseLeftDown = 1 << 1, + kMouseLeftUp = 1 << 2, + kMouseRightDown = 1 << 3, + kMouseRightUp = 1 << 4, + kEventAttn = 1 << 5, + kMouseMask = (kMouseRoll | kMouseLeftDown | kMouseLeftUp | kMouseRightDown | kMouseRightUp), + kEventKeyb = 1 << 7 +}; + +class Keyboard { +private: + bool getKey(Common::Event &event); + CGE2Engine *_vm; +public: + Sprite *_client; + + void newKeyboard(Common::Event &event); + Sprite *setClient(Sprite *spr); + + Keyboard(CGE2Engine *vm); + ~Keyboard(); +}; + +/*----------------- MOUSE interface -----------------*/ + +struct CGE2Event { + uint16 _mask; + uint16 _x; + uint16 _y; + Common::KeyCode _keyCode; + Sprite *_spritePtr; +}; + +class Mouse : public Sprite { +public: + V2D _point; + Sprite *_hold; + bool _active; + int _hx; + int _hy; + bool _exist; + int _buttons; + Sprite *_busy; + Mouse(CGE2Engine *vm); + ~Mouse(); + void on(); + void off(); + void newMouse(Common::Event &event); +private: + CGE2Engine *_vm; +}; + +/*----------------- EventManager interface -----------------*/ + +class EventManager { +private: + CGE2Engine *_vm; + Common::Event _event; + CGE2Event _eventQueue[kEventMax]; + uint16 _eventQueueHead; + uint16 _eventQueueTail; + + void handleEvents(); +public: + EventManager(CGE2Engine *vm); + void poll(); + void clearEvent(Sprite *spr); + + CGE2Event &getNextEvent(); +}; + +} // End of namespace CGE + +#endif // #define CGE2_EVENTS_H diff --git a/engines/cge2/fileio.cpp b/engines/cge2/fileio.cpp new file mode 100644 index 0000000000..acb5bb870f --- /dev/null +++ b/engines/cge2/fileio.cpp @@ -0,0 +1,282 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "common/system.h" +#include "common/str.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/memstream.h" +#include "cge2/cge2.h" +#include "cge2/fileio.h" + +namespace CGE2 { + +/*----------------------------------------------------------------------- + * BtPage + *-----------------------------------------------------------------------*/ +void BtPage::readBTree(Common::ReadStream &s) { + _header._count = s.readUint16LE(); + _header._down = s.readUint16LE(); + + if (_header._down == kBtValNone) { + // Leaf list + for (int i = 0; i < kBtLeafCount; ++i) { + s.read(_leaf[i]._key, kBtKeySize); + _leaf[i]._pos = s.readUint32LE(); + _leaf[i]._size = s.readUint32LE(); + } + } else { + // Root index + for (int i = 0; i < kBtInnerCount; ++i) { + s.read(_inner[i]._key, kBtKeySize); + _inner[i]._down = s.readUint16LE(); + } + } +} + +/*----------------------------------------------------------------------- + * ResourceManager + *-----------------------------------------------------------------------*/ +ResourceManager::ResourceManager() { + _datFile = new Common::File(); + _datFile->open(kDatName); + + _catFile = new Common::File(); + _catFile->open(kCatName); + + if (!_datFile->isOpen() || !_catFile->isOpen()) + error("Unable to open data files"); + + for (int i = 0; i < kBtLevel; i++) { + _buff[i]._page = new BtPage; + _buff[i]._pageNo = kBtValNone; + _buff[i]._index = -1; + assert(_buff[i]._page != nullptr); + } +} + +ResourceManager::~ResourceManager() { + _datFile->close(); + delete _datFile; + + _catFile->close(); + delete _catFile; + + for (int i = 0; i < kBtLevel; i++) + delete _buff[i]._page; +} + +void ResourceManager::xCrypt(byte *buf, uint16 length) { + byte *b = buf; + + for (uint16 i = 0; i < length; i++) + *b++ ^= kCryptSeed; +} + +bool ResourceManager::seek(int32 offs, int whence) { + return _datFile->seek(offs, whence); +} + +uint16 ResourceManager::read(byte *buf, uint16 length) { + if (!_datFile->isOpen()) + return 0; + + uint16 bytesRead = _datFile->read(buf, length); + if (!bytesRead) + error("Read %s - %d bytes", _datFile->getName(), length); + xCrypt(buf, length); + return bytesRead; +} + +BtPage *ResourceManager::getPage(int level, uint16 pageId) { + if (_buff[level]._pageNo != pageId) { + int32 pos = pageId * kBtSize; + _buff[level]._pageNo = pageId; + + if (_catFile->size() <= pos) + return nullptr; + + // In the original, there was a check verifying if the + // purpose was to write a new file. This should only be + // to create a new file, thus it was removed. + _catFile->seek(pageId * kBtSize, SEEK_SET); + + // Read in the page + byte buffer[kBtSize]; + int bytesRead = catRead(buffer, kBtSize); + + // Unpack it into the page structure + Common::MemoryReadStream stream(buffer, bytesRead, DisposeAfterUse::NO); + _buff[level]._page->readBTree(stream); + _buff[level]._index = -1; + } + return _buff[level]._page; +} + +BtKeypack *ResourceManager::find(const char *key) { + int lev = 0; + uint16 nxt = kBtValRoot; + while (!_catFile->eos()) { + BtPage *pg = getPage(lev, nxt); + if (!pg) + return nullptr; + + // search + if (pg->_header._down != kBtValNone) { + int i; + for (i = 0; i < pg->_header._count; i++) { + // Does this work, or does it have to compare the entire buffer? + if (scumm_strnicmp((const char *)key, (const char*)pg->_inner[i]._key, kBtKeySize) < 0) + break; + } + nxt = (i) ? pg->_inner[i - 1]._down : pg->_header._down; + _buff[lev]._index = i - 1; + lev++; + } else { + int i; + for (i = 0; i < pg->_header._count - 1; i++) { + if (scumm_stricmp((const char *)key, (const char *)pg->_leaf[i]._key) <= 0) + break; + } + + // Hack to work around a mix between 24piram_ and 24pirami + if (!strcmp(key, "24piram_.SPR") && (scumm_stricmp((const char *)key, (const char *)pg->_leaf[i]._key) < 0)) + ++i; + // + + _buff[lev]._index = i; + return &pg->_leaf[i]; + } + } + return nullptr; +} + +bool ResourceManager::exist(const char *name) { + BtKeypack *result = find(name); + if (!result) + return false; + + return scumm_stricmp(result->_key, name) == 0; +} + +uint16 ResourceManager::catRead(byte *buf, uint16 length) { + if (!_catFile->isOpen()) + return 0; + + uint16 bytesRead = _catFile->read(buf, length); + if (!bytesRead) + error("Read %s - %d bytes", _catFile->getName(), length); + xCrypt(buf, length); + return bytesRead; +} + +/*----------------------------------------------------------------------- + * EncryptedStream + *-----------------------------------------------------------------------*/ +EncryptedStream::EncryptedStream(CGE2Engine *vm, const char *name) : _vm(vm), _lineCount(0) { + _error = false; + BtKeypack *kp = _vm->_resman->find(name); + if (scumm_stricmp(kp->_key, name) != 0) + _error = true; + + _vm->_resman->seek(kp->_pos); + byte *dataBuffer; + int bufSize; + + if ((strlen(name) > 4) && (scumm_stricmp(name + strlen(name) - 4, ".SPR") == 0)) { + // SPR files have some inconsistencies. Some have extra 0x1A at the end, some others + // do not have a carriage return at the end of the last line + // Therefore, we remove this ending 0x1A and add extra new lines. + // This fixes bug #3537527 + dataBuffer = (byte *)malloc(kp->_size + 2); + _vm->_resman->read(dataBuffer, kp->_size); + if (dataBuffer[kp->_size - 1] == 0x1A) + dataBuffer[kp->_size - 1] = '\n'; + dataBuffer[kp->_size] = '\n'; + dataBuffer[kp->_size + 1] = '\n'; + bufSize = kp->_size + 2; + } else { + dataBuffer = (byte *)malloc(kp->_size); + _vm->_resman->read(dataBuffer, kp->_size); + bufSize = kp->_size; + } + + _readStream = new Common::MemoryReadStream(dataBuffer, bufSize, DisposeAfterUse::YES); +} + +uint32 EncryptedStream::read(byte *dataPtr, uint32 dataSize) { + return _readStream->read(dataPtr, dataSize); +} + +int16 EncryptedStream::readSint16LE() { + return _readStream->readSint16LE(); +} + +uint32 EncryptedStream::readUint32LE() { + return _readStream->readUint32LE(); +} + +bool EncryptedStream::err() { + return (_error || _readStream->err()); +} + +bool EncryptedStream::eos() { + return _readStream->eos(); +} + +bool EncryptedStream::seek(int32 offset) { + return _readStream->seek(offset); +} + +Common::String EncryptedStream::readLine() { + _lineCount++; + Common::String line = _readStream->readLine(); + if (!line.empty() && (line[0] == ';' || line[0] == '.' || line[0] == '*')) + line.clear(); // Returns an empty string, if the line is invalid. + return line; +} + +int32 EncryptedStream::size() { + return _readStream->size(); +} + +int32 EncryptedStream::pos() { + return _readStream->pos(); +} + +EncryptedStream::~EncryptedStream() { + delete _readStream; +} + +const char *EncryptedStream::kIdTab[] = { + "[near]", "[mtake]", "[ftake]", "[phase]", "[seq]", + "Name", "Type", "Front", "East", + "Portable", "Transparent", + nullptr +}; + +} // End of namespace CGE2 diff --git a/engines/cge2/fileio.h b/engines/cge2/fileio.h new file mode 100644 index 0000000000..e236c73b49 --- /dev/null +++ b/engines/cge2/fileio.h @@ -0,0 +1,133 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_FILEIO_H +#define CGE2_FILEIO_H + +#include "common/file.h" + +namespace CGE2 { + +class CGE2Engine; + +#define kBtSize 2048 +#define kBtKeySize 13 +#define kBtLevel 2 +#define kBtInnerCount ((kBtSize - 4 /*sizeof(Header) */) / (kBtKeySize + 2 /*sizeof(Inner) */)) +#define kBtLeafCount ((kBtSize - 4 /*sizeof(Header) */) / (kBtKeySize + 4 + 4 /*sizeof(BtKeypack) */)) +#define kBtValNone 0xFFFF +#define kBtValRoot 0 +#define kCatName "VOL.CAT" +#define kDatName "VOL.DAT" +#define kCryptSeed 0xA5 + +enum ID { + kIdNear, kIdMTake, kIdFTake, kIdPhase, kIdSeq, + kIdName, kIdType, kIdFront, kIdEast, + kIdPortable, kIdTransparent, + kIdNone = -1 +}; + +struct BtKeypack { + char _key[kBtKeySize]; + uint32 _pos; + uint32 _size; +}; + +struct Inner { + uint8 _key[kBtKeySize]; + uint16 _down; +}; + +struct Header { + uint16 _count; + uint16 _down; +}; + +struct BtPage { + Header _header; + union { + // dummy filler to make proper size of union + uint8 _data[kBtSize - 4]; /* 4 is the size of struct Header */ + // inner version of data: key + word-sized page link + Inner _inner[kBtInnerCount]; + // leaf version of data: key + all user data + BtKeypack _leaf[kBtLeafCount]; + }; + + void readBTree(Common::ReadStream &s); +}; + +class ResourceManager { +private: + struct { + BtPage *_page; + uint16 _pageNo; + int _index; + } _buff[kBtLevel]; + + BtPage *getPage(int level, uint16 pageId); + uint16 catRead(byte *buf, uint16 length); + Common::File *_catFile; + Common::File *_datFile; + void xCrypt(byte *buf, uint16 length); +public: + ResourceManager(); + ~ResourceManager(); + uint16 read(byte *buf, uint16 length); + bool seek(int32 offs, int whence = SEEK_SET); + + BtKeypack *find(const char *key); + bool exist(const char *name); +}; + +class EncryptedStream { +private: + CGE2Engine *_vm; + Common::SeekableReadStream *_readStream; + int _lineCount; + bool _error; +public: + EncryptedStream(CGE2Engine *vm, const char *name); + ~EncryptedStream(); + bool err(); + bool eos(); + bool seek(int32 offset); + int32 pos(); + int32 size(); + uint32 read(byte *dataPtr, uint32 dataSize); + int16 readSint16LE(); + uint32 readUint32LE(); + Common::String readLine(); + int getLineCount() { return _lineCount; } + + static const char *kIdTab[]; +}; + +} // End of namespace CGE2 + +#endif // CGE2_FILEIO_H diff --git a/engines/cge2/general.h b/engines/cge2/general.h new file mode 100644 index 0000000000..7213c2e24d --- /dev/null +++ b/engines/cge2/general.h @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_GENERAL_H +#define CGE2_GENERAL_H + +#include "common/file.h" + +namespace CGE2 { + +class CGE2Engine; + +struct Dac { + uint8 _r; + uint8 _g; + uint8 _b; +}; + +} // End of namespace CGE2 + +#endif // CGE2_GENERAL_H diff --git a/engines/cge2/hero.cpp b/engines/cge2/hero.cpp new file mode 100644 index 0000000000..42ae67cc2b --- /dev/null +++ b/engines/cge2/hero.cpp @@ -0,0 +1,622 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/hero.h" +#include "cge2/text.h" +#include "cge2/map.h" + +namespace CGE2 { + +Hero::Hero(CGE2Engine *vm) : Sprite(vm), _contact(nullptr), _dir(kNoDir), + _curDim(0), _tracePtr(-1), _ignoreMap(false), _maxDist(0) { + + for (int i = 0; i < kDimMax; i++) + _dim[i] = nullptr; + + _reachStart = _reachCycle = _sayStart = _funStart = 0; + _funDel0 = _funDel = 0; +} + +Hero::~Hero() { + contract(); +} + +Sprite *Hero::expand() { + if (_ext) + return this; + + char fname[kMaxPath]; + _vm->mergeExt(fname, _file, kSprExt); + + if (_ext != nullptr) + delete _ext; + + _ext = new SprExt(_vm); + + if (!*_file) + return this; + + for (int i = 0; i < kDimMax; i++) { + if (_dim[i] != nullptr) { + delete[] _dim[i]; + _dim[i] = nullptr; + } + } + for (int i = 0; i < kDimMax; i++) { + _dim[i] = new Bitmap[_shpCnt]; + for (int j = 0; j < _shpCnt; j++) + _dim[i][j].setVM(_vm); + } + + int cnt[kActions]; + + for (int i = 0; i < kActions; i++) + cnt[i] = 0; + + for (int i = 0; i < kActions; i++) { + byte n = _actionCtrl[i]._cnt; + if (n) + _ext->_actions[i] = new CommandHandler::Command[n]; + else + _ext->_actions[i] = nullptr; + } + + Seq *curSeq = nullptr; + if (_seqCnt) + curSeq = new Seq[_seqCnt]; + + if (_vm->_resman->exist(fname)) { // sprite description file exist + EncryptedStream sprf(_vm, fname); + if (sprf.err()) + error("Bad SPR [%s]", fname); + + ID section = kIdPhase; + ID id; + Common::String line; + char tmpStr[kLineMax + 1]; + int shpcnt = 0; + int seqcnt = 0; + int maxnow = 0; + int maxnxt = 0; + + for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) { + if (line.empty()) + continue; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *p = _vm->token(tmpStr); + + id = _vm->ident(p); + switch (id) { + case kIdNear: + case kIdMTake: + case kIdFTake: + case kIdPhase: + case kIdSeq: + section = id; + break; + case kIdName: + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + for (p = tmpStr; *p != '='; p++) // We search for the = + ; + setName(_vm->tail(p)); + break; + default: + if (id >= kIdNear) + break; + Seq *s; + switch (section) { + case kIdNear: + case kIdMTake: + case kIdFTake: + id = (ID)_vm->_commandHandler->getComId(p); + if (_actionCtrl[section]._cnt) { + CommandHandler::Command *c = &_ext->_actions[section][cnt[section]++]; + c->_commandType = CommandType(id); + c->_ref = _vm->number(nullptr); + c->_val = _vm->number(nullptr); + c->_spritePtr = nullptr; + } + break; + case kIdSeq: + s = &curSeq[seqcnt++]; + s->_now = atoi(p); + if (s->_now > maxnow) + maxnow = s->_now; + s->_next = _vm->number(nullptr); + switch (s->_next) { + case 0xFF: + s->_next = seqcnt; + break; + case 0xFE: + s->_next = seqcnt - 1; + break; + } + if (s->_next > maxnxt) + maxnxt = s->_next; + s->_dx = _vm->number(nullptr); + s->_dy = _vm->number(nullptr); + s->_dz = _vm->number(nullptr); + s->_dly = _vm->number(nullptr); + break; + case kIdPhase: + for (int i = 0; i < kDimMax; i++) { + char *q = p; + q[1] = '0' + i; + Bitmap b(_vm, q); + _dim[i][shpcnt] = b; + if (!shpcnt) + _hig[i] = b._h; + } + ++shpcnt; + break; + default: + break; + } + } + } + + if (curSeq) { + if (maxnow >= shpcnt) + error("Bad PHASE in SEQ %s", fname); + if (maxnxt >= seqcnt) + error("Bad JUMP in SEQ %s", fname); + setSeq(curSeq); + } else + setSeq(_stdSeq8); + + setShapeList(_dim[0], shpcnt); + } + + char *tempStr = _vm->_text->getText(_ref + 100); + char *text = new char[strlen(tempStr) + 1]; + strcpy(text, tempStr); + _reachStart = atoi(_vm->token(text)); + _reachCycle = atoi(_vm->token(nullptr)); + _sayStart = atoi(_vm->token(nullptr)); + _funStart = atoi(_vm->token(nullptr)); + _funDel = _funDel0 = (72 / _ext->_seq[0]._dly) * atoi(_vm->token(nullptr)); + delete[] text; + + int i = stepSize() / 2; + _maxDist = (int)sqrt(double(i * i * 2)); + setCurrent(); + + return this; +} + +Sprite *Hero::contract() { + for (int i = 0; i < kDimMax; i++) { + if (_dim[i] != nullptr) { + delete[] _dim[i]; + if (_ext->_shpList == _dim[i]) + _ext->_shpList = nullptr; + _dim[i] = nullptr; + } + } + Sprite::contract(); + return this; +} + +void Hero::setCurrent() { + FXP m = _vm->_eye->_z / (_pos3D._z - _vm->_eye->_z); + FXP tmp = m * _siz.y; + int h = -(tmp.trunc()); + + int i = 0; + for (; i < kDimMax - 1; i++) { + if (h >= (_hig[i] + _hig[i + 1]) / 2) + break; + } + + _ext->_shpList = _dim[_curDim = i]; +} + +void Hero::hStep() { + if (!_ignoreMap && _ext) { + Seq *seq = _ext->_seq; + int ptr = seq[_seqPtr]._next; + seq += ptr; + if (seq->_dx | seq->_dz) { + V2D p0(_vm, _pos3D._x.round(), _pos3D._z.round()); + V2D p1(_vm, p0.x + seq->_dx, p0.y + seq->_dz); + if (mapCross(p0, p1)) { + park(); + return; + } + } + } + step(); +} + +Sprite *Hero::setContact() { + Sprite *spr; + int md = _maxDist << 1; + for (spr = _vm->_vga->_showQ->first(); spr; spr = spr->_next) { + if (spr->_actionCtrl[kNear]._cnt && ((spr->_ref & 255) != 255) && (distance(spr) <= md)) { + if (spr == _contact) + return nullptr; + else + break; + } + } + return (_contact = spr); +} + +void Hero::tick() { + int z = _pos3D._z.trunc(); + //-- maybe not exactly wid/2, but wid/3 ? + int d = ((_siz.x / 2) * _vm->_eye->_z.trunc()) / (_vm->_eye->_z.trunc() - z); + + if (_dir != kNoDir) { // just walking... + if (_flags._hold || _tracePtr < 0) + park(); + else { + Sprite *spr = setContact(); + if (spr) + _vm->feedSnail(spr, kNear, this); + } + } + //--------------------------------------------------------------- + if (_tracePtr >= 0) { + if (distance(_trace[_tracePtr]) <= _maxDist) + --_tracePtr; + + if (_tracePtr < 0) + park(); + else { + int stp = stepSize() / 2; + int dx = _trace[_tracePtr]._x.round() - _pos3D._x.round(); + int dz = _trace[_tracePtr]._z.round() - _pos3D._z.round(); + Dir dir = (dx > stp) ? kEE : ((-dx > stp) ? kWW : ((dz > stp) ? kNN : kSS)); + turn(dir); + } + } + + //--------------------------------------------------------------- + hStep(); + setCurrent(); + switch (_dir) { + case kSS: + if (_pos3D._z < stepSize() / 2) + park(); + break; + case kWW: + if (_pos2D.x <= d) + park(); + break; + case kNN: + if (_pos3D._z > kScrDepth) + park(); + break; + case kEE: + if (_pos2D.x >= kScrWidth - 1 - d) + park(); + break; + default: + break; + } + if (_flags._trim) + gotoxyz_(_pos2D); + + if (_pos3D._z.trunc() != z) + _flags._zmov = true; + + if (--_funDel == 0) + fun(); +} + +int Hero::distance(V3D pos) { + V3D di = _pos3D - pos; + int x = di._x.round(); + int z = di._z.round(); + int retval = (int)sqrt((double)x * x + z * z); + return retval; +} + +int Hero::distance(Sprite *spr) { + V3D pos = spr->_pos3D; + int mdx = (spr->_siz.x >> 1) + (_siz.x >> 1); + int dx = (_pos3D._x - spr->_pos3D._x).round(); + if (dx < 0) { + mdx = -mdx; + if (dx > mdx) + pos._x = _pos3D._x; + else + pos._x += mdx; + } else if (dx < mdx) + pos._x = _pos3D._x; + else + pos._x += mdx; + + return distance(pos); +} + +void Hero::turn(Dir d) { + Dir dir = (_dir == kNoDir) ? kSS : _dir; + if (d != _dir) { + step((d == dir) ? 57 : (8 + 4 * dir + d)); + _dir = d; + } + resetFun(); +} + +void Hero::park() { + if (_dir != kNoDir) { + step(8 + 5 * _dir); + _dir = kNoDir; + _trace[0] = _pos3D; + _tracePtr = -1; + setCurrent(); + _flags._zmov = true; + } + _ignoreMap = false; + if (_time == 0) + ++_time; +} + +bool Hero::lower(Sprite * spr) { + return (spr->_pos3D._y + (spr->_siz.y >> 2) < 10); +} + +void Hero::reach(int mode) { + Sprite *spr = nullptr; + if (mode >= 4) { + spr = _vm->_vga->_showQ->locate(mode); + if (spr) { + mode = !spr->_flags._east; // 0-1 + if (lower(spr)) // 2-3 + mode += 2; + } + } + // note: insert SNAIL commands in reverse order + _vm->_commandHandler->insertCommand(kCmdPause, -1, 24, nullptr); + _vm->_commandHandler->insertCommand(kCmdSeq, -1, _reachStart + _reachCycle * mode, this); + if (spr) { + _vm->_commandHandler->insertCommand(kCmdWait, -1, -1, this); + _vm->_commandHandler->insertCommand(kCmdWalk, -1, spr->_ref, this); + } + // sequence is not finished, + // now it is just at sprite appear (disappear) point + resetFun(); +} + +void Hero::fun() { + if (_vm->_commandHandler->idle()) { + park(); + _vm->_commandHandler->addCommand(kCmdWait, -1, -1, this); + _vm->_commandHandler->addCommand(kCmdSeq, -1, _funStart, this); + } + _funDel = _funDel0 >> 2; +} + +int Hero::len(V2D v) { + return (int)sqrt(double(v.x * v.x + v.y * v.y)); +} + +bool Hero::findWay(){ + V2D p0(_vm, _pos3D._x.round(), _pos3D._z.round()); + V2D p1(_vm, _trace[_tracePtr]._x.round(), _trace[_tracePtr]._z.round()); + V2D ph(_vm, p1.x, p0.y); + V2D pv(_vm, p0.x, p1.y); + bool pvOk = (!mapCross(p0, pv) && !mapCross(pv, p1)); + bool phOk = (!mapCross(p0, ph) && !mapCross(ph, p1)); + int md = (_maxDist >> 1); + if (pvOk && (len(ph - p0) <= md || len(p1 - ph) <= md)) + return true; + + if (phOk && (len(pv - p0) <= md || len(p1 - pv) <= md)) + return true; + + if (pvOk) { + _trace[++_tracePtr] = V3D(pv.x, 0, pv.y); + return true; + } + + if (phOk) { + _trace[++_tracePtr] = V3D(ph.x, 0, ph.y); + return true; + } + + return false; +} + +int Hero::snap(int p, int q, int grid) { + int d = abs(q - p) % grid; + if (d > (grid >> 1)) + d -= grid; + return (q >= p) ? (q - d) : (q + d); +} + +void Hero::walkTo(V3D pos) { + if (distance(pos) <= _maxDist) + return; + + int stp = stepSize(); + pos._x = snap(_pos3D._x.round(), pos._x.round(), stp); + pos._y = 0; + pos._z = snap(_pos3D._z.round(), pos._z.round(), stp); + + V2D p0(_vm, _pos3D._x.round(), _pos3D._z.round()); + V2D p1(_vm, pos._x.round(), pos._z.round()); + resetFun(); + int cnt = mapCross(p0, p1); + if ((cnt & 1) == 0) { // even == way exists + _trace[_tracePtr = 0] = pos; + if (!findWay()) { + int i; + ++_tracePtr; + for (i = stp; i < kMaxTry; i += stp) { + _trace[_tracePtr] = pos + V3D(i, 0, 0); + if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay()) + break; + + _trace[_tracePtr] = pos + V3D(-i, 0, 0); + if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay()) + break; + + _trace[_tracePtr] = pos + V3D(0, 0, i); + if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay()) + break; + + _trace[_tracePtr] = pos + V3D(0, 0, -i); + if (!mapCross(_trace[_tracePtr - 1], _trace[_tracePtr]) && findWay()) + break; + } + if (i >= kMaxTry) + _trace[_tracePtr] = V3D(_pos3D._x, 0, pos._z); // not found + } + } +} + +void Hero::walkTo(Sprite *spr) { + int mdx = _siz.x >> 1; + int stp = (stepSize() + 1) / 2; + if (!spr->_flags._east) + mdx = -mdx; + walkTo(spr->_pos3D + V3D(mdx, 0, (!spr->_flags._frnt || spr->_pos3D._z < 8) ? stp : -stp)); +} + +V3D Hero::screenToGround(V2D pos) { + FXP z = _vm->_eye->_z + (_vm->_eye->_y * _vm->_eye->_z) / (FXP(pos.y) - _vm->_eye->_y); + FXP x = _vm->_eye->_x - ((FXP(pos.x) - _vm->_eye->_x) * (z - _vm->_eye->_z)) / _vm->_eye->_z; + return V3D(x.round(), 0, z.round()); +} + +int Hero::cross(const V2D &a, const V2D &b) { + int x = _pos3D._x.trunc(); + int z = _pos3D._z.trunc(); + int r = ((_siz.x / 3) * _vm->_eye->_z.trunc()) / (_vm->_eye->_z.trunc() - z); + return _vm->cross(a, b, V2D(_vm, x - r, z), V2D(_vm, x + r, z)) << 1; +} + +bool CGE2Engine::cross(const V2D &a, const V2D &b, const V2D &c, const V2D &d) { + if (contain(a, b, c) || contain(a, b, d) || contain(c, d, a) || contain(c, d, b)) + return true; + + return sgn(det(a, b, c)) != sgn(det(a, b, d)) && sgn(det(c, d, a)) != sgn(det(c, d, b)); +} + +bool CGE2Engine::contain(const V2D &a, const V2D &b, const V2D &p) { + if (det(a, b, p)) + return false; + + return ((long)(a.x - p.x) * (p.x - b.x) >= 0 && (long)(a.y - p.y) * (p.y - b.y) >= 0); +} + +long CGE2Engine::det(const V2D &a, const V2D &b, const V2D &c) { + return ((long)a.x * b.y + (long)b.x * c.y + (long)c.x * a.y) - ((long)c.x * b.y + (long)b.x * a.y + (long)a.x * c.y); +} + +int CGE2Engine::sgn(long n) { + return (n == 0) ? 0 : ((n > 0) ? 1 : -1); +} + +int Hero::mapCross(const V2D &a, const V2D &b) { + Hero *o = other(); + int n = (o->_scene == _scene) ? o->cross(a, b) : 0; + if (!_ignoreMap) + n += _vm->mapCross(a, b); + + return n; +} + +int Hero::mapCross(const V3D &a, const V3D &b) { + return mapCross(V2D(_vm, a._x.round(), a._z.round()), V2D(_vm, b._x.round(), b._z.round())); +} + +int CGE2Engine::mapCross(const V2D &a, const V2D &b) { + int cnt = 0; + V2D *n0 = nullptr; + V2D *p = nullptr; + for (int i = 0; i < _map->size(); i++) { + V2D *n = _map->getCoord(i); + if (p) { + if (cross(a, b, *n0, *n)) + ++cnt; + + if (*n == *p) + p = nullptr; + } else { + p = n; + } + n0 = n; + } + return cnt; +} + +void Hero::setScene(int c) { + Sprite::setScene(c); + resetFun(); +} + +void Hero::operator++() { + if (_curDim > 0) + _ext->_shpList = _dim[--_curDim]; +} + +void Hero::operator--() { + if (_curDim < kDimMax - 1) + _ext->_shpList = _dim[++_curDim]; +} + +bool Sprite::works(Sprite *spr) { + if (!spr || !spr->_ext) + return false; + + bool ok = false; + + Action a = _vm->_heroTab[_vm->_sex]->_ptr->action(); + CommandHandler::Command *ct = spr->_ext->_actions[a]; + if (ct) { + int i = spr->_actionCtrl[a]._ptr; + int n = spr->_actionCtrl[a]._cnt; + while (i < n && !ok) { + CommandHandler::Command *c = &ct[i++]; + if (c->_commandType != kCmdUse) + break; + ok = (c->_ref == _ref); + if (c->_val > 255) { + if (ok) { + int p = spr->labVal(a, c->_val >> 8); + if (p >= 0) + spr->_actionCtrl[a]._ptr = p; + else + ok = false; + } + } else { + if (c->_val && c->_val != _vm->_now) + ok = false; + break; + } + } + } + + return ok; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/hero.h b/engines/cge2/hero.h new file mode 100644 index 0000000000..3b5329e12c --- /dev/null +++ b/engines/cge2/hero.h @@ -0,0 +1,114 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_HERO_H +#define CGE2_HERO_H + +#include "cge2/cge2.h" +#include "cge2/vga13h.h" +#include "cge2/snail.h" + +namespace CGE2 { + +#define kMaxTry 400 + +class Hero; + +struct HeroTab { + Hero *_ptr; + Sprite *_face; + Sprite *_pocket[kPocketMax + 1]; + int _downPocketId[kPocketMax + 1]; + int _pocPtr; + V2D *_posTab[kSceneMax]; + HeroTab(CGE2Engine *vm) { + _ptr = nullptr; + _face = nullptr; + for (int i = 0; i < kPocketMax + 1; i++) { + _pocket[i] = nullptr; + _downPocketId[i] = -1; + } + _pocPtr = 0; + for (int i = 0; i < kSceneMax; i++) + _posTab[i] = nullptr; + } + ~HeroTab() { + for (int i = 0; i < kSceneMax; i++) + delete _posTab[i]; + } +}; + +class Hero : public Sprite { + int _hig[kDimMax]; + Sprite *_contact; +public: + BitmapPtr _dim[kDimMax]; + V3D _trace[kWayMax]; + enum Dir { kNoDir = -1, kSS, kWW, kNN, kEE } _dir; + int _curDim; + int _tracePtr; + int _reachStart, _reachCycle, _sayStart, _funStart; + int _funDel0, _funDel; + int _maxDist; + bool _ignoreMap; + Hero(CGE2Engine *vm); + ~Hero(); + void tick(); + Sprite *expand(); + Sprite *contract(); + Sprite *setContact(); + int stepSize() { return _ext->_seq[7]._dx; } + int distance(V3D pos); + int distance(Sprite * spr); + void turn(Dir d); + void park(); + int len(V2D v); + bool findWay(); + static int snap(int p, int q, int grid); + void walkTo(V3D pos); + void walkTo(V2D pos) { walkTo(screenToGround(pos)); } + V3D screenToGround(V2D pos); + void walkTo(Sprite *spr); + void say() { step(_sayStart); } + void fun(); + void resetFun() { _funDel = _funDel0; } + void hStep(); + bool lower(Sprite * spr); + int cross(const V2D &a, const V2D &b); + int mapCross(const V2D &a, const V2D &b); + int mapCross(const V3D &a, const V3D &b); + Hero *other() { return _vm->_heroTab[!(_ref & 1)]->_ptr;} + Action action() { return (Action)(_ref % 10); } + void reach(int mode); + void setCurrent(); + void setScene(int c); + void operator++(); + void operator--(); +}; + +} // End of namespace CGE2 + +#endif // CGE2_HERO_H diff --git a/engines/cge2/inventory.cpp b/engines/cge2/inventory.cpp new file mode 100644 index 0000000000..e62aa01e99 --- /dev/null +++ b/engines/cge2/inventory.cpp @@ -0,0 +1,104 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/cge2.h" +#include "cge2/hero.h" + +namespace CGE2 { + +int CGE2Engine::findActivePocket(int ref) { + for (int i = 0; i < kPocketMax; i++) { + Sprite *spr = _heroTab[_sex]->_pocket[i]; + if (ref >= 0) { + if (spr && (spr->_ref == ref)) + return i; + } else if (!spr) + return i; + } + return -1; +} + +void CGE2Engine::selectPocket(int n) { + Sprite **p = _heroTab[_sex]->_pocket; + int &pp = _heroTab[_sex]->_pocPtr; + if ((n < 0) || (pp == n)) { + n = findActivePocket(-1); + if (n >= 0) + pp = n; + } else if (p[n]) + pp = n; +} + +void CGE2Engine::pocFul() { + Hero *h = _heroTab[_sex]->_ptr; + h->park(); + _commandHandler->addCommand(kCmdWait, -1, -1, h); + _commandHandler->addCommand(kCmdSound, -1, 2, h); + _commandHandler->addCommand(kCmdSay, -1, kPocketFull + _sex, h); +} + +void CGE2Engine::releasePocket(Sprite *spr) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kPocketMax; j++) { + Sprite *&poc = _heroTab[i]->_pocket[j]; + if (poc == spr) { + spr->_flags._kept = false; + poc = nullptr; + return; + } + } + } +} + +int CGE2Engine::freePockets(int sx) { + int n = 0; + for (int i = 0; i < kPocketMax; i++){ + if (_heroTab[sx]->_pocket[i] == nullptr) + ++n; + } + return n; +} + +void CGE2Engine::openPocket() { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kPocketMax + 1; j++) { + int ref = (int)_heroTab[i]->_downPocketId[j]; + _heroTab[i]->_pocket[j] = (ref == -1) ? nullptr : _vga->_showQ->locate(ref); + } + } +} + +void CGE2Engine::closePocket() { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kPocketMax + 1; j++) { + Sprite *spr = _heroTab[i]->_pocket[j]; + _heroTab[i]->_downPocketId[j] = (spr) ? spr->_ref : -1; + } + } +} + +} // End of namespace CGE2 diff --git a/engines/cge2/map.cpp b/engines/cge2/map.cpp new file mode 100644 index 0000000000..275e15f55a --- /dev/null +++ b/engines/cge2/map.cpp @@ -0,0 +1,92 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/map.h" + +namespace CGE2 { + +Map::Map(CGE2Engine *vm) :_vm(vm) {} + +Map::~Map() { + _container.clear(); +} + +void Map::clear() { + _container.clear(); +} + +void Map::load(int scene) { + clear(); + + const char *fname = "%.2d.MAP"; + Common::String fileName = Common::String::format(fname, scene); + if (!_vm->_resman->exist(fileName.c_str())) + return; + + EncryptedStream file(_vm, fileName.c_str()); + + Common::String line; + for (line = file.readLine(); !file.eos(); line = file.readLine()) { + if (line.empty()) + continue; + + char tmpStr[kLineMax + 1]; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *currPos = tmpStr; + int x = nextNum(currPos); + while (true) { + int y = nextNum(nullptr); + _container.push_back(V2D(_vm, convertCoord(x), convertCoord(y))); + x = nextNum(nullptr); + if (x == -1) // We stop if there are no more data left to process in the current line. + break; + } + } +} + +int Map::nextNum(char *currPos) { + currPos = strtok(currPos, " (),"); + if (currPos == nullptr) + return -1; + int num = atoi(currPos); + return num; +} + +int Map::convertCoord(int coord) { + return (coord + (kMapGrid >> 1)) & kMapMask; +} + +int Map::size() { + return _container.size(); +} + +V2D *Map::getCoord(int idx) { + return &_container[idx]; +} + +} // End of namespace CGE2 diff --git a/engines/cge2/map.h b/engines/cge2/map.h new file mode 100644 index 0000000000..206479b929 --- /dev/null +++ b/engines/cge2/map.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_MAP_H +#define CGE2_MAP_H + +#include "cge2/vga13h.h" + +namespace CGE2 { + +#define kMapGrid 4 +#define kMapMask (~(kMapGrid - 1)) + +class Map { + CGE2Engine *_vm; + Common::Array<V2D> _container; + + int convertCoord(int coord); + int nextNum(char *currPos); +public: + Map(CGE2Engine *vm); + ~Map(); + void clear(); + void load(int scene); + int size(); + V2D *getCoord(int idx); +}; + +} // End of namespace CGE2 + +#endif // CGE2_MAP_H diff --git a/engines/cge2/module.mk b/engines/cge2/module.mk new file mode 100644 index 0000000000..4b321d88a8 --- /dev/null +++ b/engines/cge2/module.mk @@ -0,0 +1,30 @@ +MODULE := engines/cge2 + +MODULE_OBJS = \ + cge2.o \ + detection.o \ + fileio.o \ + vga13h.o \ + bitmap.o \ + sound.o \ + cge2_main.o \ + text.o \ + hero.o \ + snail.o \ + spare.o \ + talk.o \ + events.o \ + map.o \ + vmenu.o \ + saveload.o \ + toolbar.o \ + inventory.o \ + console.o + +# This module can be built as a plugin +ifeq ($(ENABLE_CGE2), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/cge2/saveload.cpp b/engines/cge2/saveload.cpp new file mode 100644 index 0000000000..7735c077a6 --- /dev/null +++ b/engines/cge2/saveload.cpp @@ -0,0 +1,279 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "common/config-manager.h" +#include "common/savefile.h" +#include "common/system.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" +#include "cge2/events.h" +#include "cge2/snail.h" +#include "cge2/hero.h" +#include "cge2/text.h" +#include "cge2/sound.h" +#include "cge2/cge2_main.h" + +namespace CGE2 { + +#define kSavegameCheckSum (1997 + _now + _sex + kWorldHeight) +#define kBadSVG 99 + +bool CGE2Engine::canSaveGameStateCurrently() { + return (_gamePhase == kPhaseInGame) && _mouse->_active && + _commandHandler->idle() && (_soundStat._wait == nullptr); +} + +Common::Error CGE2Engine::saveGameState(int slot, const Common::String &desc) { + storeHeroPos(); + saveGame(slot, desc); + sceneUp(_now); + return Common::kNoError; +} + +void CGE2Engine::saveGame(int slotNumber, const Common::String &desc) { + // Set up the serializer + Common::String slotName = generateSaveName(slotNumber); + Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName); + + // Write out the ScummVM savegame header + SavegameHeader header; + header.saveName = desc; + header.version = kSavegameVersion; + writeSavegameHeader(saveFile, header); + + // Write out the data of the savegame + sceneDown(); + syncGame(nullptr, saveFile); + + // Finish writing out game data + saveFile->finalize(); + delete saveFile; +} + +bool CGE2Engine::canLoadGameStateCurrently() { + return (_gamePhase == kPhaseInGame) && _mouse->_active; +} + +Common::Error CGE2Engine::loadGameState(int slot) { + _commandHandler->clear(); + _commandHandlerTurbo->clear(); + sceneDown(); + if (!loadGame(slot)) + return Common::kReadingFailed; + sceneUp(_now); + initToolbar(); + return Common::kNoError; +} + +bool CGE2Engine::loadGame(int slotNumber) { + Common::MemoryReadStream *readStream; + + // Open up the savegame file + Common::String slotName = generateSaveName(slotNumber); + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName); + + // Read the data into a data buffer + int size = saveFile->size(); + byte *dataBuffer = (byte *)malloc(size); + saveFile->read(dataBuffer, size); + readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES); + delete saveFile; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + readStream->read(buffer, kSavegameStrSize + 1); + + if (strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) != 0) { + delete readStream; + return false; + } else { + SavegameHeader saveHeader; + + if (!readSavegameHeader(readStream, saveHeader)) { + delete readStream; + return false; + } + + // Delete the thumbnail + saveHeader.thumbnail->free(); + delete saveHeader.thumbnail; + } + + resetGame(); + + // Get in the savegame + syncGame(readStream, nullptr); + delete readStream; + + loadHeroes(); + + return true; +} + +void CGE2Engine::resetGame() { + _busyPtr = nullptr; + busy(false); + _spare->clear(); + _vga->_showQ->clear(); + loadScript("CGE.INI", true); + delete _infoLine; + _infoLine = new InfoLine(this, kInfoW); +} + +void CGE2Engine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) { + // Write out a savegame header + out->write(kSavegameStr, kSavegameStrSize + 1); + + out->writeByte(kSavegameVersion); + + // Write savegame name + out->write(header.saveName.c_str(), header.saveName.size() + 1); + + // Get the active palette + uint8 thumbPalette[256 * 3]; + g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256); + + // Stop the heroes from moving and redraw them before taking the picture. + for (int i = 0; i < 2; i++) + _heroTab[i]->_ptr->park(); + _vga->show(); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + Graphics::Surface *s = _vga->_page[0]; + ::createThumbnail(thumb, (const byte *)s->getPixels(), kScrWidth, kScrHeight, thumbPalette); + Graphics::saveThumbnail(*out, *thumb); + thumb->free(); + delete thumb; + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); +} + +bool CGE2Engine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { + header.thumbnail = nullptr; + + // Get the savegame version + header.version = in->readByte(); + if (header.version > kSavegameVersion) + return false; + + // Read in the string + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) + return false; + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +void CGE2Engine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) { + Common::Serializer s(readStream, writeStream); + + // Synchronise header data + syncHeader(s); + + // Synchronise _spare + _spare->sync(s); + + if (s.isSaving()) { + // Save the references of the items in the heroes pockets: + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kPocketMax; j++) { + int ref = _heroTab[i]->_downPocketId[j]; + s.syncAsSint16LE(ref); + } + } + } else { + // Load items to the pockets + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kPocketMax; j++) { + int ref = 0; + s.syncAsSint16LE(ref); + _heroTab[i]->_downPocketId[j] = ref; + } + } + } + + // Heroes' _posTabs + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kSceneMax; j++) { + s.syncAsSint16LE(_heroTab[i]->_posTab[j]->x); + s.syncAsSint16LE(_heroTab[i]->_posTab[j]->y); + } + } +} + +void CGE2Engine::syncHeader(Common::Serializer &s) { + s.syncAsUint16LE(_now); + s.syncAsUint16LE(_sex); + s.syncAsUint16LE(_vga->_rot._len); + s.syncAsUint16LE(_waitSeq); + s.syncAsUint16LE(_waitRef); + + if (s.isSaving()) { + // Write checksum + int checksum = kSavegameCheckSum; + s.syncAsUint16LE(checksum); + } else { + // Read checksum and validate it + uint16 checksum = 0; + s.syncAsUint16LE(checksum); + if (checksum != kSavegameCheckSum) + error("%s", _text->getText(kBadSVG)); + } +} + +/** +* Support method that generates a savegame name +* @param slot Slot number +*/ +Common::String CGE2Engine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/snail.cpp b/engines/cge2/snail.cpp new file mode 100644 index 0000000000..7580ef4425 --- /dev/null +++ b/engines/cge2/snail.cpp @@ -0,0 +1,865 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/snail.h" +#include "cge2/fileio.h" +#include "cge2/hero.h" +#include "cge2/text.h" +#include "cge2/sound.h" +#include "cge2/events.h" + +namespace CGE2 { + +const char *CommandHandler::_commandText[] = { + "NOP", "USE", "PAUSE", "INF", "CAVE", "SETX", "SETY", "SETZ", "ADD", + "FLASH", "CYCLE", "CLEAR", "MOUSE", "MAP", "MIDI", ".DUMMY.", "WAIT", + "HIDE", "ROOM", "SAY", "SOUND", "KILL", "RSEQ", "SEQ", "SEND", "SWAP", + "KEEP", "GIVE", "GETPOS", "GOTO", "PORT", "NEXT", "NNEXT", "MTNEXT", + "FTNEXT", "RNNEXT", "RMTNEXT", "RFTNEXT", "RMNEAR", "RMMTAKE", "RMFTAKE", + "SETREF", "WALKTO", "REACH", "COVER", "UNCOVER", "EXEC", "GHOST", + nullptr }; + +CommandHandler::CommandHandler(CGE2Engine *vm, bool turbo) + : _turbo(turbo), _textDelay(false), _timerExpiry(0), _talkEnable(true), + _head(0), _tail(0), _commandList((Command *)malloc(sizeof(Command)* 256)), + _vm(vm) { +} + +CommandHandler::~CommandHandler() { + free(_commandList); +} + +void CommandHandler::runCommand() { + if (!_turbo && _vm->_soundStat._wait) { + if (*(_vm->_soundStat._wait)) + return; + + ++_vm->_soundStat._ref[0]; + if (_vm->_fx->exist(_vm->_soundStat._ref[1], _vm->_soundStat._ref[0])) { + int16 oldRepeat = _vm->_sound->getRepeat(); + _vm->_sound->setRepeat(1); + _vm->_sound->play(Audio::Mixer::kSpeechSoundType, _vm->_fx->load(_vm->_soundStat._ref[1], _vm->_soundStat._ref[0]), _vm->_sound->_smpinf._span); + _vm->_sound->setRepeat(oldRepeat); + return; + } + _vm->_soundStat._wait = nullptr; + } + + uint8 tmpHead = _head; + while (_tail != tmpHead) { + Command tailCmd = _commandList[_tail]; + + if (!_turbo) { // only for the slower one + if (_vm->_waitRef) + break; + + if (_timerExpiry) { + // Delay in progress + if (_timerExpiry > g_system->getMillis()) + // Delay not yet ended + break; + + // Delay is finished + _timerExpiry = 0; + } else if (_textDelay) { + if (_vm->_talk) { + _vm->snKill((Sprite *)_vm->_talk); + _vm->_talk = nullptr; + } + _textDelay = false; + } + + if (_vm->_talk && tailCmd._commandType != kCmdPause) + break; + } + ++_tail; + _vm->_taken = false; + Sprite *spr = nullptr; + if (tailCmd._commandType > kCmdSpr) + spr = (tailCmd._ref < 0) ? ((Sprite *)tailCmd._spritePtr) : _vm->locate(tailCmd._ref); + + Common::String sprStr; + if (spr && *spr->_file && (tailCmd._commandType != kCmdGhost)) + // In case of kCmdGhost _spritePtr stores a pointer to a Bitmap, not to a Sprite... + sprStr = Common::String(spr->_file); + else + sprStr = "None"; + + if (sprStr.empty()) + sprStr = "None"; + debugC(1, kCGE2DebugOpcode, "Command: %s; Ref: %d; Val: %d; Sprite: %s;", getComStr(tailCmd._commandType), tailCmd._ref, tailCmd._val, sprStr.c_str()); + + switch (tailCmd._commandType) { + case kCmdUse: + break; + case kCmdPause: + _timerExpiry = g_system->getMillis() + tailCmd._val * kCommandFrameDelay; + if (_vm->_talk) + _textDelay = true; + break; + case kCmdWait: + if (spr && spr->active() && (spr->_scene == _vm->_now || spr->_scene == 0)) { + _vm->_waitSeq = tailCmd._val; + _vm->_waitRef = spr->_ref; + } + break; + case kCmdHide: + _vm->snHide(spr, tailCmd._val); + break; + case kCmdSay: + _vm->snSay(spr, tailCmd._val); + break; + case kCmdInf: + if (_talkEnable) + _vm->inf(((tailCmd._val) >= 0) ? _vm->_text->getText(tailCmd._val) : (const char *)tailCmd._spritePtr); + break; + case kCmdCave: + _vm->switchScene(tailCmd._val); + break; + case kCmdMidi: + _vm->snMidi(tailCmd._val); + break; + case kCmdKill: + _vm->snKill(spr); + break; + case kCmdSeq: + _vm->snSeq(spr, tailCmd._val); + break; + case kCmdRSeq: + _vm->snRSeq(spr, tailCmd._val); + break; + case kCmdSend: + _vm->snSend(spr, tailCmd._val); + break; + case kCmdSwap: + _vm->snSwap(spr, tailCmd._val); + break; + case kCmdCover: + _vm->snCover(spr, tailCmd._val); + break; + case kCmdUncover: + _vm->snUncover(spr, (tailCmd._val >= 0) ? _vm->locate(tailCmd._val) : ((Sprite *)tailCmd._spritePtr)); + break; + case kCmdKeep: + _vm->snKeep(spr, tailCmd._val); + break; + case kCmdGive: + _vm->snGive(spr, tailCmd._val); + break; + case kCmdSetX: + _vm->_point[tailCmd._val]->_x = tailCmd._ref; + break; + case kCmdSetY: + _vm->_point[tailCmd._val]->_y = tailCmd._ref; + break; + case kCmdSetZ: + _vm->_point[tailCmd._val]->_z = tailCmd._ref; + break; + case kCmdAdd: + *(_vm->_point[tailCmd._ref]) = *(_vm->_point[tailCmd._ref]) + *(_vm->_point[tailCmd._val]); + break; + case kCmdGetPos: + if (spr) + *(_vm->_point[tailCmd._val]) = spr->_pos3D; + break; + case kCmdGoto: + _vm->snGoto(spr, tailCmd._val); + break; + case kCmdPort: + _vm->snPort(spr, tailCmd._val); + break; + case kCmdNext: + break; + case kCmdMouse: + _vm->snMouse(tailCmd._val != 0); + break; + case kCmdNNext: + _vm->snNNext(spr, kNear, tailCmd._val); + break; + case kCmdMTNext: + _vm->snNNext(spr, kMTake, tailCmd._val); + break; + case kCmdFTNext: + _vm->snNNext(spr, kFTake, tailCmd._val); + break; + case kCmdRNNext: + _vm->snRNNext(spr, tailCmd._val); + break; + case kCmdRMTNext: + _vm->snRMTNext(spr, tailCmd._val); + break; + case kCmdRFTNext: + _vm->snRFTNext(spr, tailCmd._val); + break; + case kCmdRMNear: + _vm->snRmNear(spr); + break; + case kCmdRMMTake: + _vm->snRmMTake(spr); + break; + case kCmdRMFTake: + _vm->snRmFTake(spr); + break; + case kCmdSetRef: + _vm->snSetRef(spr, tailCmd._val); + break; + case kCmdFlash: + _vm->snFlash(tailCmd._val != 0); + break; + case kCmdCycle: + _vm->snCycle(tailCmd._val); + break; + case kCmdWalk: + _vm->snWalk(spr, tailCmd._val); + break; + case kCmdReach: + _vm->snReach(spr, tailCmd._val); + break; + case kCmdSound: + _vm->snSound(spr, tailCmd._val); + _vm->_sound->setRepeat(1); + break; + case kCmdMap: + _vm->_heroTab[tailCmd._ref & 1]->_ptr->_ignoreMap = tailCmd._val == 0; + break; + case kCmdRoom: + _vm->snRoom(spr, tailCmd._val); + break; + case kCmdExec: + switch (tailCmd._cbType) { + case kQGame: + _vm->qGame(); + break; + case kXScene: + _vm->xScene(); + break; + default: + error("Unknown Callback Type in SNEXEC"); + break; + } + break; + case kCmdGhost: + _vm->snGhost((Bitmap *)tailCmd._spritePtr); + break; + case kCmdNop: // Do nothing. + break; + default: + warning("Unhandled command"); + break; + } + + if (_vm->_taken && spr) + _vm->_spare->dispose(spr); + + if (!_turbo) + break; + } +} + +void CGE2Engine::snKill(Sprite *spr) { + if (spr) { + if (spr->_flags._kept) + releasePocket(spr); + Sprite *nx = spr->_next; + hide1(spr); + _vga->_showQ->remove(spr); + _eventManager->clearEvent(spr); + if (spr->_flags._kill) { + _spare->take(spr->_ref); + delete spr; + } else { + spr->setScene(-1); + _spare->dispose(spr); + } + if (nx && nx->_flags._slav) + snKill(nx); + } +} + +void CGE2Engine::snHide(Sprite *spr, int val) { + if (spr) { + spr->_flags._hide = (val >= 0) ? (val != 0) : (!spr->_flags._hide); + if (spr->_flags._shad) + spr->_prev->_flags._hide = spr->_flags._hide; + } +} + +void CGE2Engine::snMidi(int val) { + if (val < 0) + _midiPlayer->killMidi(); + else if (_music) + _midiPlayer->loadMidi(val); +} + +void CGE2Engine::snSeq(Sprite *spr, int val) { + if (spr) { + if (isHero(spr) && (val == 0)) + ((Hero*)spr)->park(); + else + spr->step(val); + } +} + +void CGE2Engine::snRSeq(Sprite *spr, int val) { + if (spr) + snSeq(spr, spr->_seqPtr + val); +} + +void CGE2Engine::snSend(Sprite *spr, int val) { + if (!spr) + return; + + // Sending", spr->_file + // from scene", spr->_scene + // to scene", val + bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr); + bool val1 = (val == 0 || val == _now); + spr->_scene = val; + releasePocket(spr); + if (val1 != was1) { + if (was1) { + // deactivating + hide1(spr); + spr->_flags._slav = false; + if ((spr == _heroTab[_sex]->_ptr) && (_heroTab[!_sex]->_ptr->_scene == _now)) + switchHero(!_sex); + _spare->dispose(spr); + } else { + // activating + if (byte(spr->_ref) == 0) + _bitmapPalette = _vga->_sysPal; + _vga->_showQ->insert(spr); + if (isHero(spr)) { + V2D p = *_heroTab[spr->_ref & 1]->_posTab[val]; + spr->gotoxyz(V3D(p.x, 0, p.y)); + ((Hero*)spr)->setCurrent(); + } + _taken = false; + _bitmapPalette = nullptr; + } + } +} + +void CGE2Engine::snSwap(Sprite *spr, int val) { + bool tak = _taken; + Sprite *xspr = locate(val); + if (spr && xspr) { + bool was1 = (_vga->_showQ->locate(spr->_ref) != nullptr); + bool xwas1 = (_vga->_showQ->locate(val) != nullptr); + + int tmp = spr->_scene; + spr->setScene(xspr->_scene); + xspr->setScene(tmp); + + SWAP(spr->_pos2D, xspr->_pos2D); + SWAP(spr->_pos3D, xspr->_pos3D); + if (spr->_flags._kept) + swapInPocket(spr, xspr); + if (xwas1 != was1) { + if (was1) { + hide1(spr); + _spare->dispose(spr); + } else + expandSprite(spr); + if (xwas1) { + hide1(xspr); + _spare->dispose(xspr); + } else { + expandSprite(xspr); + _taken = false; + } + } + } + if (_taken) + _spare->dispose(xspr); + _taken = tak; +} + +void CGE2Engine::snCover(Sprite *spr, int val) { + bool tak = _taken; + Sprite *xspr = locate(val); + if (spr && xspr) { + spr->_flags._hide = true; + xspr->setScene(spr->_scene); + xspr->gotoxyz(spr->_pos3D); + expandSprite(xspr); + if ((xspr->_flags._shad = spr->_flags._shad) == true) { + _vga->_showQ->insert(_vga->_showQ->remove(spr->_prev), xspr); + spr->_flags._shad = false; + } + feedSnail(xspr, kNear, _heroTab[_sex]->_ptr); + _taken = false; + } + if (_taken) + _spare->dispose(xspr); + _taken = tak; +} + +void CGE2Engine::snUncover(Sprite *spr, Sprite *spr2) { + if (spr && spr2) { + spr->_flags._hide = false; + spr->setScene(spr2->_scene); + if ((spr->_flags._shad = spr2->_flags._shad) == true) { + _vga->_showQ->insert(_vga->_showQ->remove(spr2->_prev), spr); + spr2->_flags._shad = false; + } + spr->gotoxyz(spr2->_pos3D); + snSend(spr2, -1); + if (spr->_time == 0) + ++spr->_time; + } +} + +void CGE2Engine::snKeep(Sprite *spr, int stp) { + int sex = _sex; + if (stp > 127) { + _sex = stp & 1; // for another hero + stp = -1; + } + HeroTab *ht = _heroTab[_sex]; + selectPocket(-1); + int pp = ht->_pocPtr; + + if (spr && !spr->_flags._kept && ht->_pocket[pp] == nullptr) { + V3D pos(14, -10, -1); + int16 oldRepeat = _sound->getRepeat(); + _sound->setRepeat(1); + snSound(ht->_ptr, 3); + _sound->setRepeat(oldRepeat); + if (_taken) { + _vga->_showQ->insert(spr); + _taken = false; + } + ht->_pocket[pp] = spr; + spr->setScene(0); + spr->_flags._kept = true; + if (!_sex) + pos._x += kScrWidth - 58; + if (pp & 1) + pos._x += 29; + if (pp >> 1) + pos._y -= 20; + pos._y -= (spr->_siz.y / 2); + spr->gotoxyz(pos); + if (stp >= 0) + spr->step(stp); + } + _sex = sex; + selectPocket(-1); +} + +void CGE2Engine::snGive(Sprite *spr, int val) { + if (spr) { + int p = findActivePocket(spr->_ref); + if (p >= 0) { + releasePocket(spr); + spr->setScene(_now); + if (val >= 0) + spr->step(val); + } + } + selectPocket(-1); +} + +void CGE2Engine::snGoto(Sprite *spr, int val) { + if (spr) { + V3D eye = *_eye; + if (spr->_scene > 0) + setEye(*_eyeTab[spr->_scene]); + spr->gotoxyz(*_point[val]); + setEye(eye); + } +} + +void CGE2Engine::snPort(Sprite *spr, int port) { + if (spr) + spr->_flags._port = (port < 0) ? !spr->_flags._port : (port != 0); +} + +void CGE2Engine::snMouse(bool on) { + if (on) + _mouse->on(); + else + _mouse->off(); +} + +void CGE2Engine::snNNext(Sprite *spr, Action act, int val) { + if (spr) { + if (val > 255) + val = spr->labVal(act, val >> 8); + spr->_actionCtrl[act]._ptr = val; + } +} + +void CGE2Engine::snRNNext(Sprite *spr, int val) { + if (spr) + spr->_actionCtrl[kNear]._ptr += val; +} + +void CGE2Engine::snRMTNext(Sprite *spr, int val) { + if (spr) + spr->_actionCtrl[kMTake]._ptr += val; +} + +void CGE2Engine::snRFTNext(Sprite * spr, int val) { + if (spr) + spr->_actionCtrl[kFTake]._ptr += val; +} + +void CGE2Engine::snRmNear(Sprite *spr) { + if (spr) + spr->_actionCtrl[kNear]._cnt = 0; +} + +void CGE2Engine::snRmMTake(Sprite *spr) { + if (spr) + spr->_actionCtrl[kMTake]._cnt = 0; +} + +void CGE2Engine::snRmFTake(Sprite *spr) { + if (spr) + spr->_actionCtrl[kFTake]._cnt = 0; +} + +void CGE2Engine::snSetRef(Sprite *spr, int val) { + if (spr) + spr->_ref = val; +} + +void CGE2Engine::snFlash(bool on) { + if (on) { + Dac *pal = (Dac *)malloc(sizeof(Dac) * kPalCount); + if (pal) { + memcpy(pal, _vga->_sysPal, kPalSize); + for (int i = 0; i < kPalCount; i++) { + register int c; + c = pal[i]._r << 1; + pal[i]._r = (c < 64) ? c : 63; + c = pal[i]._g << 1; + pal[i]._g = (c < 64) ? c : 63; + c = pal[i]._b << 1; + pal[i]._b = (c < 64) ? c : 63; + } + _vga->setColors(pal, 64); + } + + free(pal); + } else + _vga->setColors(_vga->_sysPal, 64); + _dark = false; +} + +void CGE2Engine::snCycle(int cnt) { + _vga->_rot._len = cnt; +} + +void CGE2Engine::snWalk(Sprite *spr, int val) { + if (isHero(spr)) { + if (val < kMaxPoint) + ((Hero *)spr)->walkTo(*_point[val]); + else { + Sprite *s = _vga->_showQ->locate(val); + if (s) + ((Hero *)spr)->walkTo(s); + } + ((Hero *)spr)->_time = 1; + } +} + +void CGE2Engine::snReach(Sprite *spr, int val) { + if (isHero(spr)) + ((Hero *)spr)->reach(val); +} + +void CGE2Engine::snSound(Sprite *spr, int wav, Audio::Mixer::SoundType soundType) { + if (wav == -1) + _sound->stop(); + else { + if (_sound->_smpinf._counter && wav < 20) + return; + if (_soundStat._wait && ((wav & 255) > 80)) + return; + + _soundStat._ref[1] = wav; + _soundStat._ref[0] = !_fx->exist(_soundStat._ref[1]); + _sound->play(soundType, _fx->load(_soundStat._ref[1], _soundStat._ref[0]), + (spr) ? (spr->_pos2D.x / (kScrWidth / 16)) : 8); + } +} + +void CGE2Engine::snRoom(Sprite *spr, bool on) { + if (!isHero(spr)) + return; + + int sex = spr->_ref & 1; + Sprite **p = _heroTab[sex]->_pocket; + if (on) { + if (freePockets(sex) == 0 && p[kPocketMax] == nullptr) { + SWAP(p[kPocketMax], p[kPocketMax - 1]); + snHide(p[kPocketMax], 1); + } + } else if (p[kPocketMax]) { + for (int i = 0; i < kPocketMax; i++) { + if (p[i] == nullptr) { + snHide(p[kPocketMax], 0); + SWAP(p[kPocketMax], p[i]); + break; + } + } + } +} + +void CGE2Engine::snGhost(Bitmap *bmp) { + V2D p(this, bmp->_map & 0xFFFF, bmp->_map >> 16); + bmp->hide(p); + bmp->release(); + delete[] bmp->_b; + bmp->_b = nullptr; + delete bmp; + bmp = nullptr; +} + +void CGE2Engine::snSay(Sprite *spr, int val) { + if (spr && spr->active() && _commandHandler->_talkEnable) { + //-- mouth animation + if (isHero(spr) && spr->seqTest(-1)) + ((Hero *)spr)->say(); + if (_sayCap) + _text->say(_text->getText(val), spr); + if (_sayVox) { + int i = val; + if (i < 256) + i -= 100; + int16 oldRepeat = _sound->getRepeat(); + _sound->setRepeat(1); + snSound(spr, i, Audio::Mixer::kSpeechSoundType); + _sound->setRepeat(oldRepeat); + _soundStat._wait = &_sound->_smpinf._counter; + } + } +} + +void CGE2Engine::hide1(Sprite *spr) { + _commandHandlerTurbo->addCommand(kCmdGhost, -1, 0, spr->ghost()); +} + +void CGE2Engine::swapInPocket(Sprite *spr, Sprite *xspr) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < kPocketMax; j++) { + Sprite *&poc = _heroTab[i]->_pocket[j]; + if (poc == spr) { + spr->_flags._kept = false; + poc = xspr; + xspr->_flags._kept = true; + xspr->_flags._port = false; + return; + } + } + } +} + +Sprite *CGE2Engine::expandSprite(Sprite *spr) { + if (spr) + _vga->_showQ->insert(spr); + return spr; +} + +void CGE2Engine::qGame() { + // Write out the user's progress + saveGame(0, Common::String("Automatic Savegame")); + + busy(false); + _vga->sunset(); + _endGame = true; +} + +void CGE2Engine::xScene() { + sceneDown(); + sceneUp(_req); +} + +void CommandHandler::addCommand(CommandType com, int ref, int val, void *ptr) { + if (ref == -2) + ref = 142 - _vm->_sex; + Command *headCmd = &_commandList[_head++]; + headCmd->_commandType = com; + headCmd->_ref = ref; + headCmd->_val = val; + headCmd->_spritePtr = ptr; + headCmd->_cbType = kNullCB; + if (headCmd->_commandType == kCmdClear) { + clear(); + } +} + +void CommandHandler::addCallback(CommandType com, int ref, int val, CallbackType cbType) { + Command *headCmd = &_commandList[_head++]; + headCmd->_commandType = com; + headCmd->_ref = ref; + headCmd->_val = val; + headCmd->_spritePtr = nullptr; + headCmd->_cbType = cbType; + if (headCmd->_commandType == kCmdClear) { + _tail = _head; + _vm->killText(); + _timerExpiry = 0; + } +} + +void CommandHandler::insertCommand(CommandType com, int ref, int val, void *ptr) { + if (ref == -2) + ref = 142 - _vm->_sex; + --_tail; + Command *tailCmd = &_commandList[_tail]; + tailCmd->_commandType = com; + tailCmd->_ref = ref; + tailCmd->_val = val; + tailCmd->_spritePtr = ptr; + tailCmd->_cbType = kNullCB; + if (com == kCmdClear) { + _tail = _head; + _vm->killText(); + _timerExpiry = 0; + } +} + +bool CommandHandler::idle() { + return (!_vm->_waitRef && _head == _tail); +} + +void CommandHandler::clear() { + _tail = _head; + _vm->killText(); + _timerExpiry = 0; +} + +int CommandHandler::getComId(const char *com) { + int i = _vm->takeEnum(_commandText, com); + return (i < 0) ? i : i + kCmdCom0 + 1; +} + +const char *CommandHandler::getComStr(CommandType cmdType) { + return _commandText[cmdType - kCmdNop]; +} + +void CGE2Engine::feedSnail(Sprite *spr, Action snq, Hero *hero) { + if (!spr || !spr->active()) + return; + + int cnt = spr->_actionCtrl[snq]._cnt; + if (cnt) { + byte ptr = spr->_actionCtrl[snq]._ptr; + CommandHandler::Command *comtab = spr->snList(snq); + CommandHandler::Command *c = &comtab[ptr]; + CommandHandler::Command *q = &comtab[cnt]; + + if (hero != nullptr) { + int pocFre = freePockets(hero->_ref & 1); + int pocReq = 0; + CommandHandler::Command *p = c; + for (; p < q && p->_commandType != kCmdNext; p++) { // scan commands + // drop from pocket? + if ((p->_commandType == kCmdSend && p->_val != _now) + || p->_commandType == kCmdGive) { + int ref = p->_ref; + if (ref < 0) + ref = spr->_ref; + if (findActivePocket(ref) >= 0) + --pocReq; + } + // make/dispose additional room? + if (p->_commandType == kCmdRoom) { + if (p->_val == 0) + ++pocReq; + else + --pocReq; + } + // put into pocket? + if (p->_commandType == kCmdKeep) + ++pocReq; + // overloaded? + if (pocReq > pocFre) { + pocFul(); + return; + } + } + } + + while (c < q) { + if ((c->_val == -1) && (c->_commandType == kCmdWalk || c->_commandType == kCmdReach)) + c->_val = spr->_ref; + + if (c->_commandType == kCmdNext) { + Sprite *s; + + switch (c->_ref) { + case -2: + s = hero; + break; + case -1: + s = spr; + break; + default: + s = _vga->_showQ->locate(c->_ref); + break; + } + + if (s && s->_actionCtrl[snq]._cnt) { + int v; + switch (c->_val) { + case -1: + v = int(c - comtab + 1); + break; + case -2: + v = int(c - comtab); + break; + case -3: + v = -1; + break; + default: + v = c->_val; + if ((v > 255) && s) + v = s->labVal(snq, v >> 8); + break; + } + if (v >= 0) + s->_actionCtrl[snq]._ptr = v; + } + + if (s == spr) + break; + } + + _commandHandler->addCommand(c->_commandType, c->_ref, c->_val, spr); + + ++c; + } + } + +} + +} // End of namespace CGE2. diff --git a/engines/cge2/snail.h b/engines/cge2/snail.h new file mode 100644 index 0000000000..7e618daac8 --- /dev/null +++ b/engines/cge2/snail.h @@ -0,0 +1,129 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SNAIL_H +#define CGE2_SNAIL_H + +#include "cge2/cge2.h" + +namespace CGE2 { + +#define kCommandFrameRate 80 +#define kCommandFrameDelay (1000 / kCommandFrameRate) +#define kNoByte -1 // Recheck this! We have no proof for it's original value. + + +enum CommandType { + kCmdCom0 = 128, + kCmdNop, // NOP :: do nothing + kCmdUse, // USE <spr> <cav>|<lab> :: hint for using + kCmdPause, // PAUSE -1 <dly> :: delay <dly>/72 seconds + kCmdInf, // INF -1 <ref> :: show text referrenced by <ref> + kCmdCave, // CAVE -1 <cav> :: go to board <cav> + kCmdSetX, // SETX <x> <idx> :: set sprite shift in x axis + kCmdSetY, // SETX <y> <idx> :: set sprite shift in y axis + kCmdSetZ, // SETX <z> <idx> :: set sprite shift in z axis + kCmdAdd, // ADD <idx1> <idx2> :: sum vectors + kCmdFlash, // FLASH -1 0|1 :: lighten whole image (on/off) + kCmdCycle, // CYCLE <cnt> :: rotate <cnt> colors from 1 + kCmdClear, // CLEAR -1 0 :: clear kCmdAIL queue + kCmdMouse, // MOUSE -1 0|1 :: enable mouse (on/off) + kCmdMap, // MAP 0|1 0 :: temporarily turn off map for hero + kCmdMidi, // MIDI -1 <midi> :: play MIDI referenced by <midi> (-1 = off) + + kCmdSpr, + + kCmdWait, // WAIT <spr> <seq>|-1 :: wait for SEQ <seq> (-1 = freeze) + kCmdHide, // HIDE <spr> 0|1 :: visibility of sprite + kCmdRoom, // ROOM <hero> 0|1 :: additional room in pocket (no/yes) + kCmdSay, // SAY <spr> <ref> :: say text referenced by <ref> + kCmdSound, // SOUND <spr> <ref> :: play sound effect referenced by <ref> + kCmdKill, // KILL <spr> 0 :: remove sprite + kCmdRSeq, // RSEQ <spr> <nr> :: relative jump SEQ <nr> lines + kCmdSeq, // SEQ <spr> <seq> :: jump to certain SEQ + kCmdSend, // SEND <spr> <cav> :: move sprite to board <cav> + kCmdSwap, // SWAP <spr1> spr2> :: sprite exchange + kCmdKeep, // KEEP <spr> <seq> :: take sprite into pocket and jump to <seq> + kCmdGive, // GIVE <spr> <seq> :: remove sprite from pocket and jump to <seq> + kCmdGetPos, // GETPOS <spr> <idx> :: take sprite's position + kCmdGoto, // GOTO <spr> <idx> :: move sprite to position + kCmdPort, // PORT <spr> 0|1 :: clear/set "takeability" of sprite + kCmdNext, // NEXT <spr> <nr> :: jump to <nr> - NEAR or TAKE + kCmdNNext, // NNEXT <spr> <nr> :: jump to <nr> - NEAR + kCmdMTNext, // MTNEXT <spr> <nr> :: jump to <nr> - TAKE + kCmdFTNext, // FTNEXT <spr> <nr> :: jump to <nr> - TAKE + kCmdRNNext, // RNNEXT <spr> <nr> :: relative jump to <nr> - NEAR + kCmdRMTNext, // RMTNEXT <spr> <nr> :: relative jump to <nr> - TAKE + kCmdRFTNext, // RFTNEXT <spr> <nr> :: relative jump to <nr> - TAKE + kCmdRMNear, // RMNEAR <spr> 0 :: remove NEAR list + kCmdRMMTake, // RMMTAKE <spr> 0 :: remove TAKE list + kCmdRMFTake, // RMFTAKE <spr> 0 :: remove TAKE list + kCmdSetRef, // SETREF <spr> <ref> :: change reference of sprite <spr> to <ref> + kCmdWalk, // WALKTO <hero> <ref>|<point> :: go close to the sprite or point + kCmdReach, // REACH <hero> <ref>|<m> :: reach the sprite or point with <m> method + kCmdCover, // COVER <sp1> <sp2> :: cover sprite <sp1> with sprite <sp2> + kCmdUncover, // UNCOVER <sp1> <sp2> :: restore the state before COVER + + kCmdExec, + kCmdGhost +}; + +class CommandHandler { +public: + struct Command { + CommandType _commandType; + byte _lab; + int _ref; + int _val; + void *_spritePtr; + CallbackType _cbType; + } *_commandList; + static const char *_commandText[]; + bool _talkEnable; + + CommandHandler(CGE2Engine *vm, bool turbo); + ~CommandHandler(); + void runCommand(); + void addCommand(CommandType com, int ref, int val, void *ptr); + void addCallback(CommandType com, int ref, int val, CallbackType cbType); + void insertCommand(CommandType com, int ref, int val, void *ptr); + bool idle(); + void clear(); + int getComId(const char *com); + const char *getComStr(CommandType cmdType); +private: + CGE2Engine *_vm; + bool _turbo; + uint8 _head; + uint8 _tail; + bool _textDelay; + uint32 _timerExpiry; // "pause" in the original. +}; + +} // End of namespace CGE2 + +#endif diff --git a/engines/cge2/sound.cpp b/engines/cge2/sound.cpp new file mode 100644 index 0000000000..57ec5983e8 --- /dev/null +++ b/engines/cge2/sound.cpp @@ -0,0 +1,273 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/sound.h" +#include "common/config-manager.h" +#include "common/memstream.h" +#include "audio/decoders/raw.h" +#include "audio/audiostream.h" +#include "cge2/cge2.h" + +namespace CGE2 { + +DataCk::DataCk(byte *buf, int bufSize) { + _buf = buf; + _ckSize = bufSize; +} + +DataCk::~DataCk() { + free(_buf); +} + +Sound::Sound(CGE2Engine *vm) : _vm(vm) { + _audioStream = nullptr; + _soundRepeatCount = 1; + open(); +} + +Sound::~Sound() { + close(); +} + +void Sound::close() { + _vm->_midiPlayer->killMidi(); +} + +void Sound::open() { + setRepeat(1); + if (_vm->_commandHandlerTurbo != nullptr) + _vm->switchSay(); + play(Audio::Mixer::kSFXSoundType, _vm->_fx->load(99, 99)); +} + +void Sound::setRepeat(int16 count) { + _soundRepeatCount = count; +} + +int16 Sound::getRepeat() { + return _soundRepeatCount; +} + +void Sound::play(Audio::Mixer::SoundType soundType, DataCk *wav, int pan) { + if (wav) { + stop(); + _smpinf._saddr = &*(wav->addr()); + _smpinf._slen = (uint16)wav->size(); + _smpinf._span = pan; + _smpinf._counter = getRepeat(); + sndDigiStart(&_smpinf, soundType); + } +} + +void Sound::sndDigiStart(SmpInfo *PSmpInfo, Audio::Mixer::SoundType soundType) { + // Create an audio stream wrapper for sound + Common::MemoryReadStream *stream = new Common::MemoryReadStream(PSmpInfo->_saddr, + PSmpInfo->_slen, DisposeAfterUse::NO); + _audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES); + + // Decide which handle to use + Audio::SoundHandle *handle = nullptr; + switch (soundType) { + case Audio::Mixer::kSFXSoundType: + handle = &_sfxHandle; + break; + case Audio::Mixer::kSpeechSoundType: + handle = &_speechHandle; + break; + default: + error("Wrong sound type passed to sndDigiStart()"); + } + + // Start the new sound + _vm->_mixer->playStream(soundType, handle, + Audio::makeLoopingAudioStream(_audioStream, (uint)PSmpInfo->_counter)); + + // CGE pan: + // 8 = Center + // Less = Left + // More = Right + _vm->_mixer->setChannelBalance(*handle, (int8)CLIP(((PSmpInfo->_span - 8) * 16), -127, 127)); +} + +void Sound::stop() { + sndDigiStop(_sfxHandle); + sndDigiStop(_speechHandle); + _audioStream = nullptr; +} + +void Sound::checkSoundHandles() { + if (!_vm->_mixer->isSoundHandleActive(_speechHandle) && !_vm->_mixer->isSoundHandleActive(_sfxHandle)) + _smpinf._counter = 0; +} + +void Sound::sndDigiStop(Audio::SoundHandle &handle) { + if (_vm->_mixer->isSoundHandleActive(handle)) + _vm->_mixer->stopHandle(handle); +} + +Fx::Fx(CGE2Engine *vm, int size) : _current(nullptr), _vm(vm) { +} + +Fx::~Fx() { + clear(); +} + +void Fx::clear() { + if (_current) + delete _current; + _current = nullptr; +} + +Common::String Fx::name(int ref, int sub) { + const char *fxname = "%.2dfx%.2d.WAV"; + const char *subName = "%.2dfx%.2d?.WAV"; + const char *p = (sub) ? subName : fxname; + Common::String filename = Common::String::format(p, ref >> 8, ref & 0xFF); + if (sub) + filename.setChar('@' + sub, 6); + return filename; +} + +bool Fx::exist(int ref, int sub) { + return _vm->_resman->exist(name(ref, sub).c_str()); +} + +DataCk *Fx::load(int ref, int sub) { + Common::String filename = name(ref, sub); + EncryptedStream file(_vm, filename.c_str()); + clear(); + return (_current = loadWave(&file)); +} + +DataCk *Fx::loadWave(EncryptedStream *file) { + byte *data = (byte *)malloc(file->size()); + + if (!data) + return 0; + + file->read(data, file->size()); + + return new DataCk(data, file->size()); +} + +MusicPlayer::MusicPlayer(CGE2Engine *vm) : _vm(vm) { + _data = nullptr; + _isGM = false; + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + // TODO: Load cmf.ins with the instrument table. It seems that an + // interface for such an operation is supported for AdLib. Maybe for + // this card, setting instruments is necessary. + + _driver->setTimerCallback(this, &timerCallback); + } + _dataSize = -1; +} + +MusicPlayer::~MusicPlayer() { + killMidi(); +} + +void MusicPlayer::killMidi() { + Audio::MidiPlayer::stop(); + + free(_data); + _data = nullptr; +} + +void MusicPlayer::loadMidi(int ref) { + if (_vm->_midiNotify != nullptr) + (_vm->*_vm->_midiNotify)(); + + // Work out the filename and check the given MIDI file exists + Common::String filename = Common::String::format("%.2dSG%.2d.MID", ref >> 8, ref & 0xFF); + if (!_vm->_resman->exist(filename.c_str())) + return; + + // Stop any currently playing MIDI file + killMidi(); + + // Read in the data for the file + EncryptedStream mid(_vm, filename.c_str()); + _dataSize = mid.size(); + _data = (byte *)malloc(_dataSize); + mid.read(_data, _dataSize); + + // Start playing the music + sndMidiStart(); +} + +void MusicPlayer::sndMidiStart() { + _isGM = true; + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_data, _dataSize)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + // Al the tracks are supposed to loop + _isLooping = true; + _isPlaying = true; + } +} + +void MusicPlayer::send(uint32 b) { + if (((b & 0xF0) == 0xC0) && !_isGM && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + + Audio::MidiPlayer::send(b); +} + +void MusicPlayer::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/sound.h b/engines/cge2/sound.h new file mode 100644 index 0000000000..6673b67b7a --- /dev/null +++ b/engines/cge2/sound.h @@ -0,0 +1,131 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SOUND_H +#define CGE2_SOUND_H + +#include "cge2/fileio.h" +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" +#include "audio/fmopl.h" +#include "audio/mididrv.h" +#include "audio/midiparser.h" +#include "audio/midiplayer.h" +#include "audio/mixer.h" +#include "common/memstream.h" + +namespace CGE2 { + +class CGE2Engine; + +// sample info +struct SmpInfo { + const uint8 *_saddr; // address + uint16 _slen; // length + uint16 _span; // left/right pan (0-15) + int _counter; // number of time the sample should be played +}; + +class DataCk { + byte *_buf; + int _ckSize; +public: + DataCk(byte *buf, int bufSize); + ~DataCk(); + inline const byte *addr() { + return _buf; + } + inline int size() { + return _ckSize; + } +}; + +class Sound { +public: + SmpInfo _smpinf; + + Sound(CGE2Engine *vm); + ~Sound(); + void open(); + void close(); + void play(Audio::Mixer::SoundType soundType, DataCk *wav, int pan = 8); + int16 getRepeat(); + void setRepeat(int16 count); + void stop(); + void checkSoundHandles(); +private: + int _soundRepeatCount; + CGE2Engine *_vm; + Audio::SoundHandle _sfxHandle; + Audio::SoundHandle _speechHandle; + Audio::RewindableAudioStream *_audioStream; + + void sndDigiStart(SmpInfo *PSmpInfo, Audio::Mixer::SoundType soundType); + void sndDigiStop(Audio::SoundHandle &handle); +}; + +class Fx { + CGE2Engine *_vm; + + DataCk *loadWave(EncryptedStream *file); + Common::String name(int ref, int sub); +public: + DataCk *_current; + + Fx(CGE2Engine *vm, int size); + ~Fx(); + void clear(); + bool exist(int ref, int sub = 0); + DataCk *load(int ref, int sub = 0); +}; + +class MusicPlayer: public Audio::MidiPlayer { +private: + CGE2Engine *_vm; + byte *_data; + int _dataSize; + bool _isGM; + + // Start MIDI File + void sndMidiStart(); + + // Stop MIDI File + void sndMidiStop(); +public: + MusicPlayer(CGE2Engine *vm); + ~MusicPlayer(); + + void loadMidi(int ref); + void killMidi(); + + virtual void send(uint32 b); + virtual void sendToChannel(byte channel, uint32 b); +}; + +} // End of namespace CGE2 + +#endif // CGE2_SOUND_H diff --git a/engines/cge2/spare.cpp b/engines/cge2/spare.cpp new file mode 100644 index 0000000000..76bdbfa7ef --- /dev/null +++ b/engines/cge2/spare.cpp @@ -0,0 +1,128 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/spare.h" + +namespace CGE2 { + +void Spare::sync(Common::Serializer &s) { + int size = 0; + if (s.isSaving()) { + for (uint i = 0; i < _container.size(); i++) + if (_container[i]->_ref >= 141) + size++; + s.syncAsSint16LE(size); + + for (uint i = 0; i < _container.size(); i++) { + if (_container[i]->_ref >= 141) + _container[i]->sync(s); + } + } else { + s.syncAsSint16LE(size); + + for (int i = 0; i < size; i++) { + Sprite *sprite = new Sprite(_vm); + sprite->sync(s); + update(sprite); + } + } +} + +void Spare::clear() { + for (uint i = 0; i < _container.size(); i++) + delete _container[i]; + + _container.clear(); +} + +Sprite *Spare::locate(int ref) { + for (uint i = 0; i < _container.size(); ++i) { + if (_container[i]->_ref == ref) + return _container[i]; + } + return nullptr; +} + +Sprite *Spare::take(int ref) { + Sprite *spr = nullptr; + if ((spr = locate(ref)) != nullptr) { + for (uint i = 0; i < _container.size(); ++i) { + if (spr == _container[i]) { + _container.remove_at(i); + break; + } + } + } + return spr; +} + +void Spare::takeScene(int cav) { + int bakRef = cav << 8; + Common::Array<Sprite*> tempCont = _container; + for (uint i = 0; i < tempCont.size(); ++i) { + Sprite *spr = tempCont[i]; + int c = spr->_scene; + if ((c == _vm->_now || c == 0) && spr->_ref != bakRef) { + spr = locate(spr->_ref); + _vm->_vga->_showQ->insert(spr); + } + } +} + +void Spare::store(Sprite *spr) { + _container.push_back(spr); +} + +void Spare::update(Sprite *spr) { + Sprite *sp = locate(spr->_ref); + if (sp == nullptr) + store(spr); + else { + sp->contract(); + *sp = *spr; + } +} + +void Spare::dispose(Sprite *spr) { + if (spr) { + _vm->_vga->_showQ->remove(spr); + update(spr->contract()); + } +} + +void Spare::dispose(int ref) { + dispose(_vm->_vga->_showQ->locate(ref)); +} + +void Spare::dispose() { + for (uint i = 0; i < _container.size(); ++i) { + if (_container[i]->_ref > 255) + dispose(_container[i]); + } +} + +} // End of namespace CGE2 diff --git a/engines/cge2/spare.h b/engines/cge2/spare.h new file mode 100644 index 0000000000..24a97712ff --- /dev/null +++ b/engines/cge2/spare.h @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SPARE_H +#define CGE2_SPARE_H + +#include "cge2/vga13h.h" + +namespace CGE2 { + +class Spare { + CGE2Engine *_vm; + Common::Array<Sprite*> _container; +public: + Spare(CGE2Engine *vm) : _vm(vm) {} + ~Spare() { clear(); } + void store(Sprite *spr); + Sprite *locate(int ref); + Sprite *take(int ref); + void takeScene(int cav); + void update(Sprite *spr); + void dispose(Sprite *spr); + void dispose(int ref); + void dispose(); + void sync(Common::Serializer &s); + uint16 count() { return _container.size(); } + void clear(); +}; + +} // End of namespace CGE2 + +#endif // CGE2_SPARE_H diff --git a/engines/cge2/talk.cpp b/engines/cge2/talk.cpp new file mode 100644 index 0000000000..8e6be6cac2 --- /dev/null +++ b/engines/cge2/talk.cpp @@ -0,0 +1,312 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/general.h" +#include "cge2/talk.h" + +namespace CGE2 { + +void CGE2Engine::setAutoColors() { + Dac def[4] = { + { 0, 0, 0 }, + { 220 >> 2, 220 >> 2, 220 >> 2 }, + { 190 >> 2, 190 >> 2, 190 >> 2 }, + { 160 >> 2, 160 >> 2, 160 >> 2 }, + }; + Dac pal[kPalCount]; + _vga->getColors(pal); + for (int i = 0; i < 4; i++) + _font->_colorSet[kCBRel][i] = _vga->closest(pal, def[i]); +} + +Font::Font(CGE2Engine *vm) : _vm(vm) { + _map = new uint8[kMapSize]; + _pos = new uint16[kPosSize]; + _widthArr = new uint8[kWidSize]; + + load(); +} + +Font::~Font() { + delete[] _map; + delete[] _pos; + delete[] _widthArr; +} + +void Font::load() { + char path[10]; + strcpy(path, "CGE.CFT"); + if (!_vm->_resman->exist(path)) + error("Missing Font file! %s", path); + + EncryptedStream fontFile(_vm, path); + assert(!fontFile.err()); + + fontFile.read(_widthArr, kWidSize); + assert(!fontFile.err()); + + uint16 p = 0; + for (uint16 i = 0; i < kPosSize; i++) { + _pos[i] = p; + p += _widthArr[i]; + } + fontFile.read(_map, p); + + strcpy(path, "CGE.TXC"); + if (!_vm->_resman->exist(path)) + error("Missing Color file! %s", path); + + // Reading in _colorSet: + EncryptedStream colorFile(_vm, path); + assert(!colorFile.err()); + + char tmpStr[kLineMax + 1]; + int n = 0; + + for (Common::String line = colorFile.readLine(); !colorFile.eos(); line = colorFile.readLine()){ + if (line.empty()) + continue; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + _colorSet[n][0] = _vm->number(tmpStr); + + for (int i = 1; i < 4; i++) + _colorSet[n][i] = _vm->number(nullptr); + + n++; + } +} + +uint16 Font::width(const char *text) { + uint16 w = 0; + if (!text) + return 0; + while (*text) + w += _widthArr[(unsigned char)*(text++)]; + return w; +} + +Talk::Talk(CGE2Engine *vm, const char *text, TextBoxStyle mode, ColorBank color, bool wideSpace) + : Sprite(vm), _mode(mode), _created(false), _wideSpace(wideSpace), _vm(vm) { + _color = _vm->_font->_colorSet[color]; + + if (color == kCBRel) + _vm->setAutoColors(); + update(text); +} + +Talk::Talk(CGE2Engine *vm, ColorBank color) + : Sprite(vm), _mode(kTBPure), _created(false), _wideSpace(false), _vm(vm) { + _color = _vm->_font->_colorSet[color]; + + if (color == kCBRel) + _vm->setAutoColors(); +} + +uint8 *Talk::box(V2D siz) { + uint16 n, r = (_mode == kTBRound) ? kTextRoundCorner : 0; + const byte lt = _color[1], bg = _color[2], dk = _color[3]; + + if (siz.x < 8) + siz.x = 8; + if (siz.y < 8) + siz.y = 8; + uint8 *b = new uint8[n = siz.area()]; + memset(b, bg, n); + + if (_mode) { + uint8 *p = b; + uint8 *q = b + n - siz.x; + memset(p, lt, siz.x); + memset(q, dk, siz.x); + while (p < q) { + p += siz.x; + *(p - 1) = dk; + *p = lt; + } + p = b; + for (int i = 0; i < r; i++) { + int j = 0; + for (; j < r - i; j++) { + p[j] = kPixelTransp; + p[siz.x - j - 1] = kPixelTransp; + q[j] = kPixelTransp; + q[siz.x - j - 1] = kPixelTransp; + } + p[j] = lt; + p[siz.x - j - 1] = dk; + q[j] = lt; + q[siz.x - j - 1] = dk; + p += siz.x; + q -= siz.x; + } + } + return b; +} + +void Talk::update(const char *text) { + const uint16 vmarg = (_mode) ? kTextVMargin : 0; + const uint16 hmarg = (_mode) ? kTextHMargin : 0; + uint16 mw; + uint16 mh; + uint16 ln = vmarg; + uint8 *m; + uint8 *map; + uint8 fg = _color[0]; + + if (_created) { + mw = _ext->_shpList->_w; + mh = _ext->_shpList->_h; + delete _ext->_shpList; + } else { + uint16 k = 2 * hmarg; + mh = 2 * vmarg + kFontHigh; + mw = 0; + for (const char *p = text; *p; p++) { + if ((*p == '|') || (*p == '\n')) { + mh += kFontHigh + kTextLineSpace; + if (k > mw) + mw = k; + k = 2 * hmarg; + } else if ((*p == 0x20) && (_vm->_font->_widthArr[(unsigned char)*p] > 4) && (!_wideSpace)) + k += _vm->_font->_widthArr[(unsigned char)*p] - 2; + else + k += _vm->_font->_widthArr[(unsigned char)*p]; + } + if (k > mw) + mw = k; + + _created = true; + } + + V2D sz(_vm, mw, mh); + map = box(sz); + + m = map + ln * mw + hmarg; + + while (*text) { + if ((*text == '|') || (*text == '\n')) + m = map + (ln += kFontHigh + kTextLineSpace) * mw + hmarg; + else { + int cw = _vm->_font->_widthArr[(unsigned char)*text]; + uint8 *f = _vm->_font->_map + _vm->_font->_pos[(unsigned char)*text]; + + // Handle properly space size, after it was enlarged to display properly + // 'F1' text. + int8 fontStart = 0; + if ((*text == 0x20) && (cw > 4) && (!_wideSpace)) + fontStart = 2; + + for (int i = fontStart; i < cw; i++) { + uint8 *pp = m; + uint16 n; + uint16 b = *(f++); + for (n = 0; n < kFontHigh; n++) { + if (b & 1) + *pp = fg; + b >>= 1; + pp += mw; + } + m++; + } + } + text++; + } + BitmapPtr b = new Bitmap[1]; + b[0] = Bitmap(_vm, sz.x, sz.y, map); + delete[] map; + setShapeList(b, 1); +} + +InfoLine::InfoLine(CGE2Engine *vm, uint16 w, ColorBank color) +: Talk(vm), _oldText(nullptr), _newText(nullptr), _realTime(false), _vm(vm) { + _wideSpace = false; + BitmapPtr b = new Bitmap[1]; + if (color == kCBRel) + _vm->setAutoColors(); + _color = _vm->_font->_colorSet[color]; + V2D siz = V2D(_vm, w, kFontHigh); + b[0] = Bitmap(_vm, siz.x, siz.y, _color[2]); + setShapeList(b, 1); +} + +void InfoLine::update(const char *text) { + if (!_realTime && (text == _oldText)) + return; + + _oldText = text; + + uint16 w = _ext->_shpList->_w; + uint16 h = _ext->_shpList->_h; + uint8 *v = _ext->_shpList->_v; + uint16 dsiz = w >> 2; // data size (1 plane line size) + uint16 lsiz = 2 + dsiz + 2; // uint16 for line header, uint16 for gap + uint16 psiz = h * lsiz; // - last gape, but + plane trailer + uint16 size = 4 * psiz; // whole map size + uint8 fg = _color[0]; + uint8 bg = _color[2]; + + // clear whole rectangle + memset(v + 2, bg, dsiz); // data bytes + for (byte *pDest = v + lsiz; pDest < (v + psiz); pDest += lsiz) { + Common::copy(v, v + lsiz, pDest); + } + *(uint16 *)(v + psiz - 2) = TO_LE_16(kBmpEOI); // plane trailer uint16 + for (byte *pDest = v + psiz; pDest < (v + 4 * psiz); pDest += psiz) { + Common::copy(v, v + psiz, pDest); + } + + // paint text line + if (_newText) { + uint8 *p = v + 2, *q = p + size; + + while (*text) { + uint16 cw = _vm->_font->_widthArr[(unsigned char)*text]; + uint8 *fp = _vm->_font->_map + _vm->_font->_pos[(unsigned char)*text]; + + // Handle properly space size, after it was enlarged to display properly + // 'F1' text. + int8 fontStart = 0; + if ((*text == 0x20) && (cw > 4) && (!_wideSpace)) + fontStart = 2; + + for (int i = fontStart; i < cw; i++) { + uint16 b = fp[i]; + for (uint16 n = 0; n < kFontHigh; n++) { + if (b & 1) + *p = fg; + b >>= 1; + p += lsiz; + } + if (p >= q) + p = p - size + 1; + } + text++; + } + } +} + +} // End of namespace CGE2 diff --git a/engines/cge2/talk.h b/engines/cge2/talk.h new file mode 100644 index 0000000000..d7484655cc --- /dev/null +++ b/engines/cge2/talk.h @@ -0,0 +1,94 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_TALK_H +#define CGE2_TALK_H + +#include "cge2/general.h" +#include "cge2/vga13h.h" + +namespace CGE2 { + +#define kTextHMargin (6&~1) // EVEN horizontal margins! +#define kTextVMargin 5 // vertical margins +#define kTextLineSpace 2 // line spacing +#define kTextRoundCorner 3 // rounded corners +#define kWidSize 256 +#define kPosSize 256 +#define kMapSize (256*8) +#define kFontHigh 8 +#define kCaptionSide 24 +#define kInfName 101 +#define kSayName 102 + +class Font { + void load(); + CGE2Engine *_vm; +public: + uint8 *_widthArr; + uint16 *_pos; + uint8 *_map; + uint8 _colorSet[kColorNum][4]; + Font(CGE2Engine *vm); + ~Font(); + uint16 width(const char *text); +}; + +enum TextBoxStyle { kTBPure, kTBRect, kTBRound }; + +class Talk : public Sprite { +protected: + TextBoxStyle _mode; + bool _created; + uint8 *box(V2D siz); + bool _wideSpace; +public: + uint8 *_color; + + Talk(CGE2Engine *vm, const char *text, TextBoxStyle mode = kTBPure, ColorBank color = kCBStd, bool wideSpace = false); + Talk(CGE2Engine *vm, ColorBank color = kCBStd); + + void update(const char *text); +private: + CGE2Engine *_vm; +}; + +class InfoLine : public Talk { + const char *_oldText, *_newText; +public: + bool _realTime; + InfoLine(CGE2Engine *vm, uint16 wid, ColorBank color = kCBStd); + void update(const char *text); + void update() { update(_newText); } + void setText(const char *txt) { _newText = txt; } +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE2 + +#endif // CGE2_TALK_H diff --git a/engines/cge2/text.cpp b/engines/cge2/text.cpp new file mode 100644 index 0000000000..d51c04843d --- /dev/null +++ b/engines/cge2/text.cpp @@ -0,0 +1,197 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/text.h" +#include "common/str.h" + +namespace CGE2 { + +Text::Text(CGE2Engine *vm, const char *fname) : _vm(vm) { + _vm->mergeExt(_fileName, fname, kSayExt); + if (!_vm->_resman->exist(_fileName)) + error("No talk (%s)", _fileName); + _txtCount = count(); + if (_txtCount == -1) + error("Unable to read dialog file %s", _fileName); + + _txtCount += 2; + _cache = new Handler[_txtCount]; + for (_size = 0; _size < _txtCount; _size++) { + _cache[_size]._ref = 0; + _cache[_size]._text = nullptr; + } + load(); + + _cache[_txtCount - 1]._ref = -1; + _cache[_txtCount - 1]._text = new char[3]; + strcpy(_cache[_txtCount - 1]._text, ""); +} + +Text::~Text() { + clear(); + delete[] _cache; +} + +int16 Text::count() { + EncryptedStream tf(_vm, _fileName); + if (tf.err()) + return -1; + + Common::String line; + char tmpStr[kLineMax + 1]; + + int counter = 0; + + for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) { + char *s; + assert(line.size() <= 513); + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + if ((s = strtok(tmpStr, " =,;/\t\n")) == nullptr) + continue; + if (!Common::isDigit(*s)) + continue; + + counter++; + } + return counter; +} + +void Text::clear() { + for (int i = 0; i < _txtCount; i++) { + if (_cache[i]._ref) { + _cache[i]._ref = 0; + delete[] _cache[i]._text; + _cache[i]._text = nullptr; + } + } +} + +void Text::load() { + EncryptedStream tf(_vm, _fileName); + assert(!tf.err()); + + Common::String line; + char tmpStr[kLineMax + 1]; + int idx; + + for (idx = 0, line = tf.readLine(); !tf.eos(); line = tf.readLine()) { + int n = line.size(); + char *s; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + if ((s = strtok(tmpStr, " =,;/\t\n")) == nullptr) + continue; + if (!Common::isDigit(*s)) + continue; + + int r = _vm->number(s); + + s += strlen(s); + if (s < tmpStr + n) + ++s; + + _cache[idx]._ref = r; + _cache[idx]._text = new char[strlen(s) + 1]; + strcpy(_cache[idx]._text, s); + idx++; + } +} + +char *Text::getText(int ref) { + int i; + for (i = 0; (i < _size) && (_cache[i]._ref != ref); i++) + ; + + if (i < _size) + return _cache[i]._text; + + warning("getText: Unable to find ref %d:%d", ref / 256, ref % 256); + return nullptr; +} + +void Text::say(const char *text, Sprite *spr) { + _vm->killText(); + + _vm->_talk = new Talk(_vm, text, kTBRound, kCBSay); + + Speaker *speaker = new Speaker(_vm); + + bool east = spr->_flags._east; + V2D d(_vm, 20, spr->_siz.y - 2); + if (!east) + d.x = -d.x; + if (_vm->isHero(spr)) + d = d.scale(spr->_pos3D._z.trunc()); + V2D pos = spr->_pos2D + d; + uint16 sw = (speaker->_siz.x >> 1); + if (!east) + sw = -sw; + + if (east) { + if (pos.x + sw + kTextRoundCorner + kCaptionSide >= kScrWidth) + east = false; + } else if (pos.x <= kCaptionSide + kTextRoundCorner - sw) + east = true; + + if (east != (d.x > 0)) { + d.x = -d.x; + sw = -sw; + } + pos.x = spr->_pos2D.x + d.x + sw; + + _vm->_talk->_flags._kill = true; + _vm->_talk->setName(getText(kSayName)); + _vm->_talk->gotoxyz(pos.x, pos.y + speaker->_siz.y - 1, 0); + + speaker->gotoxyz(pos.x, _vm->_talk->_pos3D._y.trunc() - speaker->_siz.y + 1, 0); + speaker->_flags._slav = true; + speaker->_flags._kill = true; + speaker->setName(getText(kSayName)); + speaker->step(east); + + _vm->_vga->_showQ->append(_vm->_talk); + _vm->_vga->_showQ->append(speaker); +} + +void CGE2Engine::inf(const char *text, ColorBank col) { + killText(); + _talk = new Talk(this, text, kTBRect, col, true); + _talk->_flags._kill = true; + _talk->setName(_text->getText(kInfName)); + _talk->center(); + _vga->_showQ->append(_talk); +} + +void Text::sayTime(Sprite *spr) { + TimeDate curTime; + _vm->_system->getTimeAndDate(curTime); + + char t[6]; + snprintf(t, 6, "%d:%02d", curTime.tm_hour, curTime.tm_min); + say(t, spr); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/text.h b/engines/cge2/text.h new file mode 100644 index 0000000000..88ed501158 --- /dev/null +++ b/engines/cge2/text.h @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_TEXT_H +#define CGE2_TEXT_H + +#include "cge2/talk.h" +#include "cge2/cge2.h" + +namespace CGE2 { + +#define kSayExt ".SAY" +#define kSysTextMax 1000 +#define kTextNoMouse 95 +#define kInfName 101 +#define kSayName 102 +#define kInfRef 301 +#define kSayRef 302 + + +class Text { + struct Handler { + int _ref; + char *_text; + } *_cache; + int _size; + int16 _txtCount; + char _fileName[kPathMax]; + void load(); + int16 count(); +public: + Text(CGE2Engine *vm, const char *fname); + ~Text(); + void clear(); + char *getText(int ref); + void say(const char *text, Sprite *spr); + void sayTime(Sprite *spr); +private: + CGE2Engine *_vm; +}; + +} // End of namespace CGE2 + +#endif // CGE2_TEXT_H diff --git a/engines/cge2/toolbar.cpp b/engines/cge2/toolbar.cpp new file mode 100644 index 0000000000..0cd220c984 --- /dev/null +++ b/engines/cge2/toolbar.cpp @@ -0,0 +1,224 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "sound.h" +#include "common/config-manager.h" +#include "cge2/cge2.h" +#include "cge2/events.h" +#include "cge2/vmenu.h" +#include "cge2/text.h" +#include "cge2/cge2_main.h" + +namespace CGE2 { + +#define kSoundNumToStateRate 25.7 +// == 257 / 10; where 10 equals to the volume switches' number of states [0..9] +// and ScummVM has a scale of 257 different values for setting sounds. + +#define kSoundStateToNumRate 28.45 +// == 256 / 9 + 0.1; where 256 is the positive range of numbers we can set the volume to +// and the 10 states of a switch cut this range up to 9 equally big parts. +// We don't take into account 0 regarding the 256 different values (it would be the 257th), +// since 0 * x == 0. +// 0.1 is only for correct rounding at the 10th state. + +void CGE2Engine::optionTouch(int opt, uint16 mask) { + bool notMuted = !ConfMan.getBool("mute"); + switch (opt) { + case 1: + if (mask & kMouseLeftUp) + switchColorMode(); + break; + case 2: + if ((mask & kMouseLeftUp) && notMuted) + switchMusic(); + break; + case 3: + if (mask & kMouseLeftUp) + quit(); + break; + case 4: + if ((mask & (kMouseLeftUp | kMouseRightUp)) && notMuted) + setVolume(opt - 4, (mask & kMouseLeftUp) ? 1 : -1); + break; + case 5: + if ((mask & (kMouseLeftUp | kMouseRightUp)) && notMuted) + setVolume(opt - 4, (mask & kMouseLeftUp) ? 1 : -1); + break; + case 8: + if ((mask & kMouseLeftUp) && notMuted) + switchCap(); + break; + case 9: + if ((mask & kMouseLeftUp) && notMuted) + switchVox(); + break; + default: + break; + } +} + +void CGE2Engine::switchColorMode() { + _commandHandlerTurbo->addCommand(kCmdSeq, 121, _vga->_mono = !_vga->_mono, nullptr); + ConfMan.setBool("enable_color_blind", _vga->_mono); + ConfMan.flushToDisk(); + keyClick(); + _vga->setColors(_vga->_sysPal, 64); +} + +void CGE2Engine::switchMusic() { + _music = !_music; + _mixer->muteSoundType(Audio::Mixer::kMusicSoundType, !_music); + _commandHandlerTurbo->addCommand(kCmdSeq, kMusicRef, _music, nullptr); + keyClick(); + _commandHandlerTurbo->addCommand(kCmdMidi, -1, _music ? (_now << 8) : -1, nullptr); +} + +void CGE2Engine::quit() { + if (_commandHandler->idle()) { + if (VMenu::_addr) { + _commandHandlerTurbo->addCommand(kCmdKill, -1, 0, VMenu::_addr); + ReturnToGameChoice rqsChoice(this); + rqsChoice.proc(); + } else { + Common::Array<Choice *> quitMenu; // Deleted in VMenu's destructor. + quitMenu.push_back(new ExitGameChoice(this)); + quitMenu.push_back(new ReturnToGameChoice(this)); + (new VMenu(this, quitMenu, V2D(this, -1, -1), kCBMnu))->setName(_text->getText(kQuitTitle)); + _commandHandlerTurbo->addCommand(kCmdSeq, kPowerRef, 0, nullptr); + keyClick(); + } + } +} + +void CGE2Engine::setVolume(int idx, int cnt) { + if (cnt && _vol[idx]) { + int p = _vol[idx]->_seqPtr + cnt; + if ((p >= 0) && (p < _vol[idx]->_seqCnt)) { + _vol[idx]->step(p); + int newVolume = (int)(p * kSoundStateToNumRate); + switch (idx) { + case 0: + _oldSfxVolume = ConfMan.getInt("sfx_volume"); + ConfMan.setInt("sfx_volume", newVolume); + break; + case 1: + _oldMusicVolume = ConfMan.getInt("music_volume"); + ConfMan.setInt("music_volume", newVolume); + break; + default: + break; + } + } + } +} + +void CGE2Engine::checkVolumeSwitches() { + int musicVolume = ConfMan.getInt("music_volume"); + if (musicVolume != _oldMusicVolume) + _vol[1]->step((int)(musicVolume / kSoundNumToStateRate)); + + int sfxVolume = ConfMan.getInt("sfx_volume"); + if (sfxVolume != _oldSfxVolume) + _vol[0]->step((int)(sfxVolume / kSoundNumToStateRate)); +} + +void CGE2Engine::switchCap() { + _sayCap = !_sayCap; + if (!_sayCap) + _sayVox = true; + keyClick(); + switchSay(); +} + +void CGE2Engine::switchVox() { + _sayVox = !_sayVox; + _mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, _sayVox); + if (!_sayVox) + _sayCap = true; + keyClick(); + switchSay(); +} + +void CGE2Engine::switchSay() { + _commandHandlerTurbo->addCommand(kCmdSeq, 129, _sayVox, nullptr); + _commandHandlerTurbo->addCommand(kCmdSeq, 128, _sayCap, nullptr); +} + +void CGE2Engine::initToolbar() { + selectPocket(-1); + + _commandHandlerTurbo->addCommand(kCmdSeq, kMusicRef, _music, nullptr); + if (!_music) + _midiPlayer->killMidi(); + + switchSay(); + + _infoLine->gotoxyz(V3D(kInfoX, kInfoY, 0)); + _infoLine->setText(nullptr); + _vga->_showQ->insert(_infoLine); + + _gamePhase = kPhaseInGame; + _mouse->center(); + _mouse->off(); + _mouse->on(); + + _keyboard->setClient(_sys); + _commandHandler->addCommand(kCmdSeq, kPowerRef, 1, nullptr); + + _busyPtr = _vga->_showQ->locate(kBusyRef); + + _vol[0] = _vga->_showQ->locate(kDvolRef); + _vol[1] = _vga->_showQ->locate(kMvolRef); + + if (_vol[0]) { + int val = ConfMan.getInt("sfx_volume"); + initVolumeSwitch(_vol[0], val); + } + + if (_vol[1]) { + int val = ConfMan.getInt("music_volume"); + initVolumeSwitch(_vol[1], val); + } +} + +void CGE2Engine::initVolumeSwitch(Sprite *volSwitch, int val) { + int state = 0; + state = (int)(val / kSoundNumToStateRate); + volSwitch->step(state); +} + +void CGE2Engine::checkMute() { + bool mute = ConfMan.getBool("mute"); + if (mute != _muteAll) { + switchMusic(); + switchVox(); + _muteAll = mute; + } +} + +} // End of namespace CGE2 diff --git a/engines/cge2/vga13h.cpp b/engines/cge2/vga13h.cpp new file mode 100644 index 0000000000..f4064f3565 --- /dev/null +++ b/engines/cge2/vga13h.cpp @@ -0,0 +1,1219 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "common/array.h" +#include "common/config-manager.h" +#include "common/rect.h" +#include "graphics/palette.h" +#include "cge2/general.h" +#include "cge2/vga13h.h" +#include "cge2/bitmap.h" +#include "cge2/text.h" +#include "cge2/cge2_main.h" +#include "cge2/cge2.h" +#include "cge2/vga13h.h" + +namespace CGE2 { + +void V3D::sync(Common::Serializer &s) { + _x.sync(s); + _y.sync(s); + _z.sync(s); +} + +FXP FXP::operator*(const FXP& x) const { + FXP y; + int32 t1 = (v >> 8) * x.v; + int32 t2 = ((v & 0xFF) * x.v) >> 8; + + y.v = t1 + t2; + return y; +} + +FXP FXP::operator/(const FXP& x) const { + FXP y; + if (x.v != 0) { + int32 v1 = this->v; + int32 v2 = x.v; + bool negFlag = false; + + if (v1 < 0) { + v1 = -v1; + negFlag = true; + } + if (v2 < 0) { + v2 = -v2; + negFlag ^= true; + } + + int32 v3 = v1 / v2; + v1 -= v3 * v2; + v3 <<= 8; + + if (v1 < 0xFFFFFF) + v1 <<= 8; + else + v2 >>= 8; + + v3 += v1 / v2; + + if (negFlag) + v3 = -v3; + + y.v = v3; + } + + return y; +} + +void FXP::sync(Common::Serializer &s) { + s.syncAsSint32LE(v); +} + +Seq *getConstantSeq(bool seqFlag) { + const Seq seq1[] = { { 0, 0, 0, 0, 0, 0 } }; + const Seq seq2[] = { { 0, 1, 0, 0, 0, 0 }, { 1, 0, 0, 0, 0, 0 } }; + + Seq *seq; + if (seqFlag) { + seq = (Seq *)malloc(1 * sizeof(Seq)); + *seq = seq1[0]; + } else { + seq = (Seq *)malloc(2 * sizeof(Seq)); + seq[0] = seq2[0]; + seq[1] = seq2[1]; + } + + return seq; +} + +byte Sprite::_constY = 0; +byte Sprite::_follow = 0; + +Seq Sprite::_stdSeq8[] = +{ { 0, 0, 0, 0, 0, 0 }, + { 1, 1, 0, 0, 0, 0 }, + { 2, 2, 0, 0, 0, 0 }, + { 3, 3, 0, 0, 0, 0 }, + { 4, 4, 0, 0, 0, 0 }, + { 5, 5, 0, 0, 0, 0 }, + { 6, 6, 0, 0, 0, 0 }, + { 7, 7, 0, 0, 0, 0 }, +}; + +SprExt::SprExt(CGE2Engine *vm) + : _p0(vm, 0, 0), _p1(vm, 0, 0), + _b0(nullptr), _b1(nullptr), _shpList(nullptr), + _location(0), _seq(nullptr), _name(nullptr) { + for (int i = 0; i < kActions; i++) + _actions[i] = nullptr; +} + +Sprite::Sprite(CGE2Engine *vm) + : _siz(_vm, 0, 0), _seqPtr(kNoSeq), _seqCnt(0), _shpCnt(0), + _next(nullptr), _prev(nullptr), _time(0), + _ext(nullptr), _ref(-1), _scene(0), _vm(vm), + _pos2D(_vm, kScrWidth >> 1, 0), _pos3D(kScrWidth >> 1, 0, 0) { + memset(_actionCtrl, 0, sizeof(_actionCtrl)); + memset(_file, 0, sizeof(_file)); + memset(&_flags, 0, sizeof(_flags)); + _flags._frnt = 1; +} + +Sprite::Sprite(CGE2Engine *vm, BitmapPtr shpP, int cnt) + : _siz(_vm, 0, 0), _seqPtr(kNoSeq), _seqCnt(0), _shpCnt(0), + _next(nullptr), _prev(nullptr), _time(0), + _ext(nullptr), _ref(-1), _scene(0), _vm(vm), + _pos2D(_vm, kScrWidth >> 1, 0), _pos3D(kScrWidth >> 1, 0, 0) { + memset(_actionCtrl, 0, sizeof(_actionCtrl)); + memset(_file, 0, sizeof(_file)); + memset(&_flags, 0, sizeof(_flags)); + _flags._frnt = 1; + + setShapeList(shpP, cnt); +} + +Sprite::~Sprite() { + contract(); +} + +BitmapPtr Sprite::getShp() { + SprExt *e = _ext; + if (!e || !e->_seq) + return nullptr; + + int i = e->_seq[_seqPtr]._now; + if (i >= _shpCnt) + error("Invalid PHASE in SPRITE::Shp() %s - %d", _file, i); + return e->_shpList + i; +} + +void Sprite::setShapeList(BitmapPtr shp, int cnt) { + _shpCnt = cnt; + _siz.x = 0; + _siz.y = 0; + + if (shp) { + for (int i = 0; i < cnt; i++) { + BitmapPtr p = shp + i; + if (p->_w > _siz.x) + _siz.x = p->_w; + if (p->_h > _siz.y) + _siz.y = p->_h; + } + expand(); + _ext->_shpList = shp; + if (!_ext->_seq) { + setSeq(_stdSeq8); + _seqCnt = (cnt < ARRAYSIZE(_stdSeq8)) ? cnt : ARRAYSIZE(_stdSeq8); + } + } +} + +Seq *Sprite::setSeq(Seq *seq) { + expand(); + + Seq *s = _ext->_seq; + _ext->_seq = seq; + if (_seqPtr == kNoSeq) + step(0); + else if (_time == 0) + step(_seqPtr); + return s; +} + +bool Sprite::seqTest(int n) { + if (n >= 0) + return (_seqPtr == n); + if (_ext) + return (_ext->_seq[_seqPtr]._next == _seqPtr); + return true; +} + +void Sprite::setName(char *newName) { + if (!_ext) + return; + + if (_ext->_name) { + delete[] _ext->_name; + _ext->_name = nullptr; + } + if (newName) { + _ext->_name = new char[strlen(newName) + 1]; + strcpy(_ext->_name, newName); + } +} + +int Sprite::labVal(Action snq, int lab) { + int lv = -1; + if (active()) { + int count = _actionCtrl[snq]._cnt; + CommandHandler::Command *com = snList(snq); + + int i = 0; + for (; i < count; i++) { + if (com[i]._lab == lab) + break; + } + + if (i < count) + return i; + } else { + char tmpStr[kLineMax + 1]; + _vm->mergeExt(tmpStr, _file, kSprExt); + + if (_vm->_resman->exist(tmpStr)) { // sprite description file exist + EncryptedStream sprf(_vm, tmpStr); + if (sprf.err()) + error("Bad SPR [%s]", tmpStr); + + int cnt = 0; + ID section = kIdPhase; + ID id; + Common::String line; + + while (lv == -1 && !sprf.eos()) { + line = sprf.readLine(); + if (line.empty()) + continue; + + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *p; + p = _vm->token(tmpStr); + + if (*p == '@') { + if ((int)section == (int)snq && atoi(p + 1) == lab) + lv = cnt; + } else { + id = _vm->ident(p); + switch (id) { + case kIdMTake: + case kIdFTake: + case kIdNear: + case kIdPhase: + case kIdSeq: + section = id; + break; + default: + if (id < 0 && (int)section == (int)snq) + ++cnt; + break; + } + } + } + } + } + return lv; +} + +CommandHandler::Command *Sprite::snList(Action type) { + SprExt *e = _ext; + return (e) ? e->_actions[type] : nullptr; +} + +Sprite *Sprite::expand() { + if (_ext) + return this; + + if (_vm->_spriteNotify != nullptr) + (_vm->*_vm->_spriteNotify)(); + + char fname[kPathMax]; + _vm->mergeExt(fname, _file, kSprExt); + + if (_ext != nullptr) + delete _ext; + _ext = new SprExt(_vm); + + if (!*_file) + return this; + + BitmapPtr shplist = new Bitmap[_shpCnt]; + + int cnt[kActions], + shpcnt = 0, + seqcnt = 0, + maxnow = 0, + maxnxt = 0; + + for (int i = 0; i < kActions; i++) + cnt[i] = 0; + + for (int i = 0; i < kActions; i++){ + byte n = _actionCtrl[i]._cnt; + if (n) + _ext->_actions[i] = new CommandHandler::Command[n]; + else + _ext->_actions[i] = nullptr; + } + + Seq *curSeq = nullptr; + if (_seqCnt) + curSeq = new Seq[_seqCnt]; + + if (_vm->_resman->exist(fname)) { // sprite description file exist + EncryptedStream sprf(_vm, fname); + if (sprf.err()) + error("Bad SPR [%s]", fname); + + int label = kNoByte; + ID section = kIdPhase; + ID id; + Common::String line; + char tmpStr[kLineMax + 1]; + + for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) { + if (line.empty()) + continue; + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + + char *p = _vm->token(tmpStr); + if (*p == '@') { + label = atoi(p + 1); + continue; + } + + id = _vm->ident(p); + switch (id) { + case kIdType: + break; + case kIdNear: + case kIdMTake: + case kIdFTake: + case kIdPhase: + case kIdSeq: + section = id; + break; + case kIdName: + Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr)); + for (p = tmpStr; *p != '='; p++) // We search for the = + ; + setName(_vm->tail(p)); + break; + default: + if (id >= kIdNear) + break; + Seq *s; + switch (section) { + case kIdNear: + case kIdMTake: + case kIdFTake: + id = (ID)_vm->_commandHandler->getComId(p); + if (_actionCtrl[section]._cnt) { + CommandHandler::Command *c = &_ext->_actions[section][cnt[section]++]; + c->_commandType = CommandType(id); + c->_lab = label; + c->_ref = _vm->number(nullptr); + c->_val = _vm->number(nullptr); + c->_spritePtr = nullptr; + } + break; + case kIdSeq: + s = &curSeq[seqcnt++]; + s->_now = atoi(p); + if (s->_now > maxnow) + maxnow = s->_now; + s->_next = _vm->number(nullptr); + switch (s->_next) { + case 0xFF: + s->_next = seqcnt; + break; + case 0xFE: + s->_next = seqcnt - 1; + break; + } + if (s->_next > maxnxt) + maxnxt = s->_next; + s->_dx = _vm->number(nullptr); + s->_dy = _vm->number(nullptr); + s->_dz = _vm->number(nullptr); + s->_dly = _vm->number(nullptr); + break; + case kIdPhase: + shplist[shpcnt] = Bitmap(_vm, p); + shpcnt++; + break; + default: + break; + } + break; + } + label = kNoByte; + } + + if (!shpcnt) + error("No shapes - %s", fname); + } else // no sprite description: try to read immediately from .BMP + shplist[shpcnt++] = Bitmap(_vm, _file); + + if (curSeq) { + if (maxnow >= shpcnt) + error("Bad PHASE in SEQ %s", fname); + if (maxnxt && (maxnxt >= seqcnt)) + error("Bad JUMP in SEQ %s", fname); + setSeq(curSeq); + } else { + setSeq(_stdSeq8); + _seqCnt = (shpcnt < ARRAYSIZE(_stdSeq8)) ? shpcnt : ARRAYSIZE(_stdSeq8); + } + + setShapeList(shplist, shpcnt); + + if (_file[2] == '~') { // FLY-type sprite + Seq *nextSeq = _ext->_seq; + int x = (nextSeq + 1)->_dx, y = (nextSeq + 1)->_dy, z = (nextSeq + 1)->_dz; + // random position + nextSeq->_dx = _vm->newRandom(x + x) - x; + nextSeq->_dy = _vm->newRandom(y + y) - y; + nextSeq->_dz = _vm->newRandom(z + z) - z; + gotoxyz(_pos3D + V3D(nextSeq->_dx, nextSeq->_dy, nextSeq->_dz)); + } + + return this; +} + +Sprite *Sprite::contract() { + SprExt *e = _ext; + if (!e) + return this; + + if (_file[2] == '~') { // FLY-type sprite + Seq *curSeq = _ext->_seq; + // return to middle + gotoxyz(_pos3D - V3D(curSeq->_dx, curSeq->_dy, curSeq->_dz)); + curSeq->_dx = curSeq->_dy = curSeq->_dz = 0; + } + + if (_vm->_spriteNotify != nullptr) + (_vm->*_vm->_spriteNotify)(); + + if (e->_name) { + delete[] e->_name; + e->_name = nullptr; + } + + if (e->_shpList) { + for (int i = 0; i < _shpCnt; i++) + e->_shpList[i].release(); + delete[] e->_shpList; + e->_shpList = nullptr; + } + + if (e->_seq) { + if (e->_seq == _stdSeq8) + _seqCnt = 0; + else { + delete[] e->_seq; + e->_seq = nullptr; + } + } + + for (int i = 0; i < kActions; i++) { + if (e->_actions[i]) { + delete[] e->_actions[i]; + e->_actions[i] = nullptr; + } + } + + delete _ext; + _ext = nullptr; + + return this; +} + +void Sprite::backShow() { + expand(); + show(2); + show(1); + _vm->_spare->dispose(this); +} + +void Sprite::step(int nr) { + if (nr >= 0) + _seqPtr = nr; + + if (_ext && _ext->_seq) { + V3D p = _pos3D; + Seq *seq = nullptr; + + if (nr < 0) + _seqPtr = _ext->_seq[_seqPtr]._next; + + if (_file[2] == '~') { // FLY-type sprite + seq = _ext->_seq; + // return to middle + p._x -= seq->_dx; + p._y -= seq->_dy; + p._z -= seq->_dz; + // generate motion + if (_vm->newRandom(10) < 5) { + if ((seq + 1)->_dx) + seq->_dx += _vm->newRandom(3) - 1; + if ((seq + 1)->_dy) + seq->_dy += _vm->newRandom(3) - 1; + if ((seq + 1)->_dz) + seq->_dz += _vm->newRandom(3) - 1; + } + if (seq->_dx < -(seq + 1)->_dx) + seq->_dx += 2; + if (seq->_dx >= (seq + 1)->_dx) + seq->_dx -= 2; + if (seq->_dy < -(seq + 1)->_dy) + seq->_dy += 2; + if (seq->_dy >= (seq + 1)->_dy) + seq->_dy -= 2; + if (seq->_dz < -(seq + 1)->_dz) + seq->_dz += 2; + if (seq->_dz >= (seq + 1)->_dz) + seq->_dz -= 2; + p._x += seq->_dx; + p._y += seq->_dy; + p._z += seq->_dz; + gotoxyz(p); + } else { + seq = _ext->_seq + _seqPtr; + if (seq) { + if (seq->_dz == 127 && seq->_dx != 0) { + _vm->_commandHandlerTurbo->addCommand(kCmdSound, -1, 256 * seq->_dy + seq->_dx, this); + } else { + p._x += seq->_dx; + p._y += seq->_dy; + p._z += seq->_dz; + gotoxyz(p); + } + } + } + if (seq && (seq->_dly >= 0)) + _time = seq->_dly; + } else if (_vm->_waitRef && _vm->_waitRef == _ref) + _vm->_waitRef = 0; +} + +void Sprite::tick() { + step(); +} + +void Sprite::setScene(int c) { + _scene = c; +} + +void Sprite::gotoxyz(int x, int y, int z) { + gotoxyz(V3D(x, y, z)); +} + +void Sprite::gotoxyz() { + gotoxyz(_pos3D); +} + +void Sprite::gotoxyz(V2D pos) { + V2D o = _pos2D; + int ctr = _siz.x >> 1; + int rem = _siz.x - ctr; + byte trim = 0; + + if (_ref / 10 == 14) { // HERO + int z = _pos3D._z.trunc(); + ctr = (ctr * _vm->_eye->_z.trunc()) / (_vm->_eye->_z.trunc() - z); + rem = (rem * _vm->_eye->_z.trunc()) / (_vm->_eye->_z.trunc() - z); + ctr = (ctr * 3) / 4; + rem = (rem * 3) / 4; + } + + if (pos.x - ctr < 0) { + pos.x = ctr; + ++trim; + } + if (pos.x + rem > kScrWidth) { + pos.x = kScrWidth - rem; + ++trim; + } + _pos2D.x = pos.x; + + if (pos.y < -kPanHeight) { + pos.y = -kPanHeight; + ++trim; + } + if (pos.y + _siz.y > kWorldHeight) { + pos.y = kWorldHeight - _siz.y; + ++trim; + } + _pos2D.y = pos.y; + + _flags._trim = (trim != 0); + + if (!_follow) { + FXP m = _vm->_eye->_z / (_pos3D._z - _vm->_eye->_z); + _pos3D._x = (_vm->_eye->_x + (_vm->_eye->_x - _pos2D.x) / m); + _pos3D._x.round(); + + if (!_constY) { + _pos3D._y = _vm->_eye->_y + (_vm->_eye->_y - _pos2D.y) / m; + _pos3D._y.round(); + } + } + + if (_next && _next->_flags._slav) + _next->gotoxyz(_next->_pos2D - o + _pos2D); + + if (_flags._shad) + _prev->gotoxyz(_prev->_pos2D - o + _pos2D); +} + +void Sprite::gotoxyz_(V2D pos) { + _constY++; + gotoxyz(pos); + --_constY; +} + +void Sprite::gotoxyz(V3D pos) { + _follow++; + if (pos._z != _pos3D._z) + _flags._zmov = true; + gotoxyz(V2D(_vm, _pos3D = pos)); + --_follow; +} + +void Sprite::center() { + gotoxyz(kScrWidth >> 1, (kWorldHeight - _siz.y) >> 1, 0); +} + +void Sprite::show() { + SprExt *e = _ext; + if (e) { + e->_p0 = e->_p1; + e->_b0 = e->_b1; + e->_p1 = _pos2D; + e->_b1 = getShp(); + + if (!_flags._hide) + e->_b1->show(e->_p1); + } +} + +void Sprite::show(uint16 pg) { + assert(pg < 4); + Graphics::Surface *a = _vm->_vga->_page[1]; + _vm->_vga->_page[1] = _vm->_vga->_page[pg]; + getShp()->show(_pos2D); + _vm->_vga->_page[1] = a; +} + +void Sprite::hide() { + SprExt *e = _ext; + if (e->_b0) + e->_b0->hide(e->_p0); +} + +BitmapPtr Sprite::ghost() { + SprExt *e = _ext; + if (!e->_b1) + return nullptr; + + BitmapPtr bmp = new Bitmap(_vm, 0, 0, (uint8 *)nullptr); + bmp->_w = e->_b1->_w; + bmp->_h = e->_b1->_h; + bmp->_b = new HideDesc[bmp->_h]; + memcpy(bmp->_b, e->_b1->_b, sizeof(HideDesc)* bmp->_h); + uint8 *v = new uint8[1]; + *v = (e->_p1.y << 16) + e->_p1.x; + bmp->_v = v; + bmp->_map = (e->_p1.y << 16) + e->_p1.x; + + return bmp; +} + +void Sprite::sync(Common::Serializer &s) { + s.syncAsUint16LE(_ref); + s.syncAsByte(_scene); + + // bitfield in-memory storage is unpredictable, so to avoid + // any issues, pack/unpack everything manually + uint16 flags = 0; + if (s.isLoading()) { + s.syncAsUint16LE(flags); + _flags._hide = flags & 0x0001; + _flags._drag = flags & 0x0002; + _flags._hold = flags & 0x0004; + _flags._trim = flags & 0x0008; + _flags._slav = flags & 0x0010; + _flags._kill = flags & 0x0020; + _flags._xlat = flags & 0x0040; + _flags._port = flags & 0x0080; + _flags._kept = flags & 0x0100; + _flags._frnt = flags & 0x0200; + _flags._east = flags & 0x0400; + _flags._near = flags & 0x0800; + _flags._shad = flags & 0x1000; + _flags._back = flags & 0x2000; + _flags._zmov = flags & 0x4000; + _flags._tran = flags & 0x8000; + } else { + flags = (flags << 1) | (_flags._tran ? 1 : 0); + flags = (flags << 1) | (_flags._zmov ? 1 : 0); + flags = (flags << 1) | (_flags._back ? 1 : 0); + flags = (flags << 1) | (_flags._shad ? 1 : 0); + flags = (flags << 1) | (_flags._near ? 1 : 0); + flags = (flags << 1) | (_flags._east ? 1 : 0); + flags = (flags << 1) | (_flags._frnt ? 1 : 0); + flags = (flags << 1) | (_flags._kept ? 1 : 0); + flags = (flags << 1) | (_flags._port ? 1 : 0); + flags = (flags << 1) | (_flags._xlat ? 1 : 0); + flags = (flags << 1) | (_flags._kill ? 1 : 0); + flags = (flags << 1) | (_flags._slav ? 1 : 0); + flags = (flags << 1) | (_flags._trim ? 1 : 0); + flags = (flags << 1) | (_flags._hold ? 1 : 0); + flags = (flags << 1) | (_flags._drag ? 1 : 0); + flags = (flags << 1) | (_flags._hide ? 1 : 0); + s.syncAsUint16LE(flags); + } + + s.syncAsSint16LE(_pos2D.x); + s.syncAsSint16LE(_pos2D.y); + + _pos3D.sync(s); + + s.syncAsSint16LE(_siz.x); + s.syncAsSint16LE(_siz.y); + + s.syncAsUint16LE(_time); + for (int i = 0; i < kActions; i++){ + s.syncAsByte(_actionCtrl[i]._ptr); + s.syncAsByte(_actionCtrl[i]._cnt); + } + s.syncAsSint16LE(_seqPtr); + s.syncAsSint16LE(_seqCnt); + s.syncAsUint16LE(_shpCnt); + s.syncBytes((byte *)&_file[0], 9); + _file[8] = '\0'; +} + +Queue::Queue(bool show) : _head(nullptr), _tail(nullptr) { +} + +void Queue::append(Sprite *spr) { + if (spr->_flags._back) + spr->backShow(); + else { + spr->expand(); + if (_tail) { + spr->_prev = _tail; + _tail->_next = spr; + } else + _head = spr; + + _tail = spr; + } +} + +void Queue::insert(Sprite *spr, Sprite *nxt) { + if (spr->_flags._back) + spr->backShow(); + else { + spr->expand(); + if (nxt == _head) { + spr->_next = _head; + _head = spr; + if (!_tail) + _tail = spr; + } else { + spr->_next = nxt; + spr->_prev = nxt->_prev; + if (spr->_prev) + spr->_prev->_next = spr; + } + if (spr->_next) + spr->_next->_prev = spr; + } +} + +void Queue::insert(Sprite *spr) { + if (locate(spr)) + return; // We only queue it if it's not already queued. + + Sprite *s; + for (s = _head; s; s = s->_next) { + if (s->_pos3D._z < spr->_pos3D._z) + break; + } + + if (s) + insert(spr, s); + else + append(spr); +} + +Sprite *Queue::remove(Sprite *spr) { + if (spr == _head) + _head = spr->_next; + + if (spr == _tail) + _tail = spr->_prev; + + if (spr->_next) + spr->_next->_prev = spr->_prev; + + if (spr->_prev) + spr->_prev->_next = spr->_next; + + spr->_prev = nullptr; + spr->_next = nullptr; + return spr; +} + +Sprite *Queue::locate(int ref) { + for (Sprite *spr = _head; spr; spr = spr->_next) { + if (spr->_ref == ref) + return spr; + } + return nullptr; +} + +bool Queue::locate(Sprite *spr) { + Sprite *s; + for (s = _head; s; s = s->_next) { + if (s == spr) + return true; + } + + return false; +} + +Vga::Vga(CGE2Engine *vm) : _frmCnt(0), _msg(nullptr), _name(nullptr), _setPal(false), _vm(vm) { + _rot._org = 1; + _rot._len = 0; + _rot._cnt = 0; + _rot._dly = 1; + + _oldColors = nullptr; + _newColors = nullptr; + _showQ = new Queue(true); + _sysPal = new Dac[kPalCount]; + + for (int idx = 0; idx < 4; idx++) { + _page[idx] = new Graphics::Surface(); + _page[idx]->create(kScrWidth, kScrHeight, Graphics::PixelFormat::createFormatCLUT8()); + } + + _mono = ConfMan.getBool("enable_color_blind"); + + _oldColors = (Dac *)malloc(sizeof(Dac) * kPalCount); + _newColors = (Dac *)malloc(sizeof(Dac) * kPalCount); + getColors(_oldColors); + sunset(); + setColors(); + clear(0); +} + +Vga::~Vga() { + Common::String buffer = ""; + + free(_oldColors); + free(_newColors); + if (_msg) + buffer = Common::String(_msg); + + if (_name) + buffer = buffer + " [" + _name + "]"; + + debugN("%s", buffer.c_str()); + + delete _showQ; + delete[] _sysPal; + + for (int idx = 0; idx < 4; idx++) { + _page[idx]->free(); + delete _page[idx]; + } +} + +void Vga::waitVR() { + // Since some of the game parts rely on using vertical sync as a delay mechanism, + // we're introducing a short delay to simulate it + g_system->delayMillis(5); +} + +void Vga::getColors(Dac *tab) { + byte palData[kPalSize]; + g_system->getPaletteManager()->grabPalette(palData, 0, kPalCount); + palToDac(palData, tab); +} + +uint8 Vga::closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB) { +#define f(col, lum) ((((uint16)(col)) << 8) / lum) + uint16 i, dif = 0xFFFF, found = 0; + uint16 L = colR + colG + colB; + if (!L) + L++; + uint16 R = f(colR, L), G = f(colG, L), B = f(colB, L); + for (i = 0; i < 256; i++) { + uint16 l = pal[i]._r + pal[i]._g + pal[i]._b; + if (!l) + l++; + int r = f(pal[i]._r, l), g = f(pal[i]._g, l), b = f(pal[i]._b, l); + uint16 D = ((r > R) ? (r - R) : (R - r)) + + ((g > G) ? (g - G) : (G - g)) + + ((b > B) ? (b - B) : (B - b)) + + ((l > L) ? (l - L) : (L - l)) * 10 ; + + if (D < dif) { + found = i; + dif = D; + if (D == 0) + break; // exact! + } + } + return found; +#undef f +} + +uint8 Vga::closest(Dac *pal, Dac x) { + int exp = (sizeof(long) * 8 - 1); + long D = (1 << exp) - 1; // Maximum value of long. + long R = x._r; + long G = x._g; + long B = x._b; + int idx = 255; + for (int n = 0; n < 256; n++) { + long dR = R - pal[n]._r; + long dG = G - pal[n]._g; + long dB = B - pal[n]._b, + d = dR * dR + dG * dG + dB * dB; + if (d < D) { + idx = n; + D = d; + if (!d) + break; + } + } + return idx; +} + +uint8 *Vga::glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB) { + uint8 *x = (uint8 *)malloc(256); + if (x) { + for (uint16 i = 0; i < 256; i++) { + x[i] = closest(pal, ((uint16)(pal[i]._r) * colR) / 255, + ((uint16)(pal[i]._g) * colG) / 255, + ((uint16)(pal[i]._b) * colB) / 255); + } + } + return x; +} + +void Vga::palToDac(const byte *palData, Dac *tab) { + const byte *colP = palData; + for (int idx = 0; idx < kPalCount; idx++, colP += 3) { + tab[idx]._r = *colP >> 2; + tab[idx]._g = *(colP + 1) >> 2; + tab[idx]._b = *(colP + 2) >> 2; + } +} + +void Vga::dacToPal(const Dac *tab, byte *palData) { + for (int idx = 0; idx < kPalCount; idx++, palData += 3) { + *palData = tab[idx]._r << 2; + *(palData + 1) = tab[idx]._g << 2; + *(palData + 2) = tab[idx]._b << 2; + } +} + +void Vga::setColors(Dac *tab, int lum) { + Dac *palP = tab, *destP = _newColors; + for (int idx = 0; idx < kPalCount; idx++, palP++, destP++) { + destP->_r = (palP->_r * lum) >> 6; + destP->_g = (palP->_g * lum) >> 6; + destP->_b = (palP->_b * lum) >> 6; + } + + if (_mono) { + destP = _newColors; + for (int idx = 0; idx < kPalCount; idx++, destP++) { + // Form a grayscale color from 30% R, 59% G, 11% B + uint8 intensity = (((int)destP->_r * 77) + ((int)destP->_g * 151) + ((int)destP->_b * 28)) >> 8; + destP->_r = intensity; + destP->_g = intensity; + destP->_b = intensity; + } + } + + _setPal = true; +} + +void Vga::setColors() { + memset(_newColors, 0, kPalSize); + updateColors(); +} + +void Vga::sunrise(Dac *tab) { + for (int i = 0; i <= 64; i += kFadeStep) { + setColors(tab, i); + waitVR(); + updateColors(); + g_system->updateScreen(); + } +} + +void Vga::sunset() { + Dac tab[256]; + getColors(tab); + for (int i = 64; i >= 0; i -= kFadeStep) { + setColors(tab, i); + waitVR(); + updateColors(); + g_system->updateScreen(); + } +} + +void Vga::show() { + _vm->_infoLine->update(); + + for (Sprite *spr = _showQ->first(); spr; spr = spr->_next) { + spr->show(); + } + + _vm->_mouse->show(); + update(); + rotate(); + + for (Sprite *spr = _showQ->first(); spr; spr = spr->_next) { + spr->hide(); + if (spr->_flags._zmov) { + Sprite *s = nullptr; + Sprite *p = spr->_prev; + Sprite *n = spr->_next; + + if (spr->_flags._shad) { + s = p; + p = s->_prev; + } + + if ((p && spr->_pos3D._z > p->_pos3D._z) || + (n && spr->_pos3D._z < n->_pos3D._z)) { + _showQ->insert(_showQ->remove(spr)); + } + spr->_flags._zmov = false; + } + } + _vm->_mouse->hide(); +} + +void Vga::updateColors() { + byte palData[kPalSize]; + dacToPal(_newColors, palData); + g_system->getPaletteManager()->setPalette(palData, 0, 256); +} + +void Vga::update() { + SWAP(Vga::_page[0], Vga::_page[1]); + + if (_setPal) { + updateColors(); + _setPal = false; + } + + g_system->copyRectToScreen(Vga::_page[0]->getPixels(), kScrWidth, 0, 0, kScrWidth, kScrHeight); + g_system->updateScreen(); +} + +void Vga::rotate() { + if (_rot._len) { + Dac c; + getColors(_newColors); + c = _newColors[_rot._org]; + memmove(_newColors + _rot._org, _newColors + _rot._org + 1, (_rot._len - 1) * sizeof(Dac)); + _newColors[_rot._org + _rot._len - 1] = c; + _setPal = true; + } +} + +void Vga::clear(uint8 color) { + for (int paneNum = 0; paneNum < 4; paneNum++) + _page[paneNum]->fillRect(Common::Rect(0, 0, kScrWidth, kScrHeight), color); +} + +void Vga::copyPage(uint16 d, uint16 s) { + _page[d]->copyFrom(*_page[s]); +} + +void Bitmap::show(V2D pos) { + xLatPos(pos); + + const byte *srcP = (const byte *)_v; + byte *screenStartP = (byte *)_vm->_vga->_page[1]->getPixels(); + byte *screenEndP = (byte *)_vm->_vga->_page[1]->getBasePtr(0, kScrHeight); + + // Loop through processing data for each plane. The game originally ran in plane mapped mode, where a + // given plane holds each fourth pixel sequentially. So to handle an entire picture, each plane's data + // must be decompressed and inserted into the surface + for (int planeCtr = 0; planeCtr < 4; planeCtr++) { + byte *destP = (byte *)_vm->_vga->_page[1]->getBasePtr(pos.x + planeCtr, pos.y); + + for (;;) { + uint16 v = READ_LE_UINT16(srcP); + srcP += 2; + int cmd = v >> 14; + int count = v & 0x3FFF; + + if (cmd == 0) { + // End of image + break; + } + + // Handle a set of pixels + while (count-- > 0) { + // Transfer operation + switch (cmd) { + case 1: + // SKIP + break; + case 2: + // REPEAT + if (destP >= screenStartP && destP < screenEndP) + *destP = *srcP; + break; + case 3: + // COPY + if (destP >= screenStartP && destP < screenEndP) + *destP = *srcP; + srcP++; + break; + } + + // Move to next dest position + destP += 4; + } + + if (cmd == 2) + srcP++; + } + } +} + +void Bitmap::hide(V2D pos) { + xLatPos(pos); + + // Perform clipping to screen + int w = MIN<int>(_w, kScrWidth - pos.x); + int h = MIN<int>(_h, kScrHeight - pos.y); + if (pos.x < 0) { + w -= -pos.x; + pos.x = 0; + if (w < 0) + return; + } + if (pos.y < 0) { + h -= -pos.y; + pos.y = 0; + if (h < 0) + return; + } + + // Perform copying of screen section + for (int yp = pos.y; yp < pos.y + h; yp++) { + if (yp >= 0 && yp < kScrHeight) { + const byte *srcP = (const byte *)_vm->_vga->_page[2]->getBasePtr(pos.x, yp); + byte *destP = (byte *)_vm->_vga->_page[1]->getBasePtr(pos.x, yp); + + Common::copy(srcP, srcP + w, destP); + } + } +} + +Speaker::Speaker(CGE2Engine *vm): Sprite(vm), _vm(vm) { + // Set the sprite list + BitmapPtr SP = new Bitmap[2]; + uint8 *map = Bitmap::makeSpeechBubbleTail(0, _vm->_font->_colorSet); + SP[0] = Bitmap(_vm, 15, 16, map); + delete[] map; + map = Bitmap::makeSpeechBubbleTail(1, _vm->_font->_colorSet); + SP[1] = Bitmap(_vm, 15, 16, map); + delete[] map; + setShapeList(SP, 2); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/vga13h.h b/engines/cge2/vga13h.h new file mode 100644 index 0000000000..553f183de4 --- /dev/null +++ b/engines/cge2/vga13h.h @@ -0,0 +1,308 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_VGA13H_H +#define CGE2_VGA13H_H + +#include "common/serializer.h" +#include "common/events.h" +#include "graphics/surface.h" +#include "cge2/general.h" +#include "cge2/bitmap.h" +#include "cge2/snail.h" +#include "cge2/spare.h" +#include "cge2/cge2.h" + +namespace CGE2 { + +#define kFadeStep 2 +#define kVgaColDark 207 +#define kVgaColDarkGray 225 /*219*/ +#define kVgaColGray 231 +#define kVgaColLightGray 237 +#define kPixelTransp 0xFE +#define kNoSeq (-1) +#define kNoPtr ((uint8)-1) +#define kSprExt ".SPR" +#define kPalCount 256 +#define kPalSize (kPalCount * 3) + +class FXP { + int32 v; +public: + FXP(void) : v(0) {} + FXP (int i0, int f0 = 0) : v((i0 * 256) + ((i0 < 0) ? -f0 : f0)) {} + FXP &operator=(const int &x) { v = x << 8; return *this; } + FXP operator+(const FXP &x) const { FXP y; y.v = v + x.v; return y; } + FXP operator-(const FXP &x) const { FXP y; y.v = v - x.v; return y; } + FXP operator*(const FXP &x) const; + FXP operator/(const FXP &x) const; + + friend int &operator+=(int &a, const FXP &b) { return a += b.trunc(); } + friend int &operator-=(int &a, const FXP &b) { return a -= b.trunc(); } + friend FXP &operator+=(FXP &a, const int &b) { a.v += b << 8; return a; } + friend FXP &operator-=(FXP &a, const int &b) { a.v -= b << 8; return a; } + friend bool operator==(const FXP &a, const FXP &b) { return a.v == b.v; } + friend bool operator!=(const FXP &a, const FXP &b) { return a.v != b.v; } + friend bool operator<(const FXP &a, const FXP &b) { return a.v < b.v; } + friend bool operator>(const FXP &a, const FXP &b) { return a.v > b.v; } + int trunc(void) const { return v >> 8; } + int round(void) const { return (v + 0x80) >> 8; } + bool empty() const { return v == 0; } + void sync(Common::Serializer &s); +}; + +class V3D { +public: + FXP _x, _y, _z; + V3D() { } + V3D(FXP x, FXP y, FXP z = 0) : _x(x), _y(y), _z(z) { } + V3D(const V3D &p) : _x(p._x), _y(p._y), _z(p._z) { } + V3D operator+(const V3D &p) const { return V3D(_x + p._x, _y + p._y, _z + p._z); } + V3D operator-(const V3D &p) const { return V3D(_x - p._x, _y - p._y, _z - p._z); } + V3D operator*(long n) const { return V3D(_x * n, _y * n, _z * n); } + V3D operator/ (long n) const { return V3D(_x / n, _y / n, _z / n); } + bool operator==(const V3D &p) const { return _x == p._x && _y == p._y && _z == p._z; } + bool operator!=(const V3D &p) const { return _x != p._x || _y != p._y || _z != p._z; } + V3D& operator+=(const V3D &x) { return *this = *this + x; } + V3D& operator-=(const V3D &x) { return *this = *this - x; } + void sync(Common::Serializer &s); +}; + +class V2D : public Common::Point { + CGE2Engine *_vm; +public: + V2D &operator=(const V3D &p3) { + FXP m = _vm->_eye->_z / (p3._z - _vm->_eye->_z); + FXP posx = _vm->_eye->_x + (_vm->_eye->_x - p3._x) * m; + x = posx.round(); + FXP posy = _vm->_eye->_y + (_vm->_eye->_y - p3._y) * m; + y = posy.round(); + return *this; + } + V2D(CGE2Engine *vm) : _vm(vm) { } + V2D(CGE2Engine *vm, const V3D &p3) : _vm(vm) { *this = p3; } + V2D(CGE2Engine *vm, int posx, int posy) : _vm(vm), Common::Point(posx, posy) { } + bool operator<(const V2D &p) const { return (x < p.x) && (y < p.y); } + bool operator<=(const V2D &p) const { return (x <= p.x) && (y <= p.y); } + bool operator>(const V2D &p) const { return (x > p.x) && (y > p.y); } + bool operator>=(const V2D &p) const { return (x >= p.x) && (y >= p.y); } + V2D operator+(const V2D &p) const { return V2D(_vm, x + p.x, y + p.y); } + V2D operator-(const V2D &p) const { return V2D(_vm, x - p.x, y - p.y); } + bool operator==(const V3D &p) const { V3D tmp(x, y); return tmp._x == p._x && tmp._y == p._y && tmp._z == p._z; } + bool operator!=(const V3D &p) const { V3D tmp(x, y); return tmp._x != p._x || tmp._y != p._y || tmp._z == p._z; } + bool operator==(const V2D &p) const { return x == p.x && y == p.y; } + uint16 area() { return x * y; } + bool limited(const V2D &p) { + return ((x < p.x) && (y < p.y)); + } + V2D scale (int z) { + FXP m = _vm->_eye->_z / (_vm->_eye->_z - z); + FXP posx = m * x; + FXP posy = m * y; + return V2D(_vm, posx.trunc(), posy.trunc()); + } +}; + +struct Seq { + uint8 _now; + uint8 _next; + int8 _dx; + int8 _dy; + int8 _dz; + int _dly; +}; + +class SprExt { +public: + V2D _p0; + V2D _p1; + BitmapPtr _b0; + BitmapPtr _b1; + BitmapPtr _shpList; + int _location; + Seq *_seq; + char *_name; + CommandHandler::Command *_actions[kActions]; + SprExt(CGE2Engine *vm); +}; + +class Sprite { +protected: + SprExt *_ext; + CGE2Engine *_vm; +public: + int _ref; + signed char _scene; + struct Flags { + bool _hide; // general visibility switch + bool _drag; // sprite is moveable + bool _hold; // sprite is held with mouse + bool _trim; // Trim flag + bool _slav; // slave object + bool _kill; // dispose memory after remove + bool _xlat; // 2nd way display: xlat table + bool _port; // portable + bool _kept; // kept in pocket + bool _frnt; // stay in front of sprite + bool _east; // talk to east (in opposite to west) + bool _near; // Near action lock + bool _shad; // shadow + bool _back; // 'send to background' request + bool _zmov; // sprite needs Z-update in queue + bool _tran; // transparent (untouchable) + } _flags; + V2D _pos2D; + V3D _pos3D; + V2D _siz; + uint16 _time; + struct { byte _ptr, _cnt; } _actionCtrl[kActions]; + int _seqPtr; + int _seqCnt; + int _shpCnt; + char _file[kMaxFile]; + // Following trailer is not saved with the game: + Sprite *_prev; + Sprite *_next; + static byte _constY; + static byte _follow; + static Seq _stdSeq8[]; + + bool works(Sprite *spr); + bool seqTest(int n); + inline bool active() { + return _ext != nullptr; + } + Sprite(CGE2Engine *vm); + Sprite(CGE2Engine *vm, BitmapPtr shp, int cnt); + virtual ~Sprite(); + BitmapPtr getShp(); + void setShapeList(BitmapPtr shp, int cnt); + void moveShapesHi(); + void moveShapesLo(); + int labVal(Action snq, int lab); + virtual Sprite *expand(); + virtual Sprite *contract(); + void backShow(); + void setName(char *newName); + inline char *name() { + return (_ext) ? _ext->_name : nullptr; + } + void gotoxyz(int x, int y, int z = 0); + void gotoxyz(); + void gotoxyz(V2D pos); + void gotoxyz_(V2D pos); + void gotoxyz(V3D pos); + void center(); + void show(uint16 pg); + void hide(uint16 pg); + void show(); + void hide(); + BitmapPtr ghost(); + void step(int nr = -1); + Seq *setSeq(Seq *seq); + CommandHandler::Command *snList(Action type); + virtual void touch(uint16 mask, V2D pos, Common::KeyCode keyCode); + virtual void tick(); + virtual void setScene(int c); + void clrHide() { if (_ext) _ext->_b0 = nullptr; } + + void sync(Common::Serializer &s); + + static void (*notify) (); +}; + +class Queue { + Sprite *_head; + Sprite *_tail; +public: + Queue(bool show); + + void append(Sprite *spr); + void insert(Sprite *spr, Sprite *nxt); + void insert(Sprite *spr); + Sprite *remove(Sprite *spr); + Sprite *first() { + return _head; + } + Sprite *last() { + return _tail; + } + Sprite *locate(int ref); + bool locate(Sprite *spr); + void clear() { _head = _tail = nullptr; } +}; + +class Vga { + CGE2Engine *_vm; + bool _setPal; + Dac *_oldColors; + Dac *_newColors; + const char *_msg; + const char *_name; + + void updateColors(); + void setColors(); + void waitVR(); + uint8 closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB); + +public: + uint32 _frmCnt; + Queue *_showQ; + bool _mono; + Graphics::Surface *_page[4]; + Dac *_sysPal; + struct { uint8 _org, _len, _cnt, _dly; } _rot; + + Vga(CGE2Engine *vm); + ~Vga(); + + uint8 *glass(Dac *pal, const uint8 colR, const uint8 colG, const uint8 colB); + void getColors(Dac *tab); + void setColors(Dac *tab, int lum); + void clear(uint8 color); + void copyPage(uint16 d, uint16 s); + void sunrise(Dac *tab); + void sunset(); + void show(); + void update(); + void rotate(); + uint8 closest(Dac *pal, Dac x); + + void palToDac(const byte *palData, Dac *tab); + void dacToPal(const Dac *tab, byte *palData); +}; + +class Speaker: public Sprite { + CGE2Engine *_vm; +public: + Speaker(CGE2Engine *vm); +}; + +} // End of namespace CGE2 + +#endif // CGE2_VGA13H_H diff --git a/engines/cge2/vmenu.cpp b/engines/cge2/vmenu.cpp new file mode 100644 index 0000000000..6afe5e9a61 --- /dev/null +++ b/engines/cge2/vmenu.cpp @@ -0,0 +1,162 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/text.h" +#include "cge2/vmenu.h" +#include "cge2/events.h" + +namespace CGE2 { + +Choice::Choice(CGE2Engine *vm) : _vm(vm), _text(nullptr) {} + +ExitGameChoice::ExitGameChoice(CGE2Engine *vm) : Choice(vm) { + _text = _vm->_text->getText(kQuitText); +} + +void ExitGameChoice::proc() { + _vm->switchScene(-1); +} + +ReturnToGameChoice::ReturnToGameChoice(CGE2Engine *vm) : Choice(vm) { + _text = _vm->_text->getText(kNoQuitText); +} + +void ReturnToGameChoice::proc() { + _vm->_commandHandlerTurbo->addCommand(kCmdSeq, kPowerRef, 1, nullptr); + _vm->keyClick(); +} + +MenuBar::MenuBar(CGE2Engine *vm, uint16 w, byte *c) : Talk(vm) { + _color = c; + int h = kFontHigh + 2 * kMenuBarVerticalMargin, i = (w += 2 * kMenuBarHorizontalMargin) * h; + uint8 *p = new uint8[i]; + uint8 *p1; + uint8 *p2; + uint8 lt = _color[kLt]; + uint8 rb = _color[kRb]; + BitmapPtr b; + + memset(p + w, kPixelTransp, i - 2 * w); + memset(p, lt, w); + memset(p + i - w, rb, w); + p1 = p; + p2 = p + i - 1; + for (i = 0; i < h; i++) { + *p1 = lt; + *p2 = rb; + p1 += w; + p2 -= w; + } + b = new Bitmap[1]; + b[0] = Bitmap(vm, w, h, p); + delete[] p; + setShapeList(b, 1); + _flags._slav = true; + _flags._tran = true; + _flags._kill = true; +} + +VMenu *VMenu::_addr = nullptr; + +VMenu::VMenu(CGE2Engine *vm, Common::Array<Choice *> list, V2D pos, ColorBank col) + : Talk(vm, vmGather(list), kTBRect, col), _menu(list), _bar(nullptr), _items(list.size()), _vm(vm) { + delete[] _vmgt; // Lefotver of vmGather. + + _addr = this; + _recent = -1; + _flags._kill = true; + + if (pos.x < 0 || pos.y < 0) + center(); + else + gotoxyz(V2D(_vm, pos.x - _siz.x / 2, pos.y - (kTextVMargin + kFontHigh / 2))); + + _vm->_vga->_showQ->append(this); + _bar = new MenuBar(_vm, _siz.x - 2 * kTextHMargin, _color); + _bar->gotoxyz(V2D(_vm, _pos2D.x, _pos2D.y + kTextVMargin - kMenuBarVerticalMargin)); + _vm->_vga->_showQ->append(_bar); +} + +char *VMenu::vmGather(Common::Array<Choice *> list) { + int len = 0; + int h = 0; + + for (uint i = 0; i < list.size(); i++) { + len += strlen(list[i]->_text); + ++h; + } + _vmgt = new char[len + h]; + *_vmgt = '\0'; + for (uint i = 0; i < list.size(); i++) { + if (*_vmgt) + strcat(_vmgt, "|"); + strcat(_vmgt, list[i]->_text); + ++h; + } + + return _vmgt; +} + + +VMenu::~VMenu() { + _addr = nullptr; + + for (uint i = 0; i < _menu.size(); i++) { + delete _menu[i]; + } +} + +void VMenu::touch(uint16 mask, V2D pos, Common::KeyCode keyCode) { + if (_items) { + Sprite::touch(mask, pos, keyCode); + + int n = 0; + bool ok = false; + int h = kFontHigh + kTextLineSpace; + pos.y -= kTextVMargin - 1; + if (pos.y >= 0) { + if (pos.x < 0) + pos.x = -pos.x; + n = pos.y / h; + if (n < _items) + ok = (pos.x <= (_siz.x >> 1) - kTextHMargin); + else + n = _items - 1; + } + + _bar->gotoxyz(V2D(_vm, _pos2D.x, _pos2D.y + kTextVMargin + n * h - kMenuBarVerticalMargin)); + n = _items - 1 - n; + + if (ok && (mask & kMouseLeftUp)) { + _items = 0; + _vm->_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, this); + _menu[_recent = n]->proc(); + } + } +} + +} // End of namespace CGE2 diff --git a/engines/cge2/vmenu.h b/engines/cge2/vmenu.h new file mode 100644 index 0000000000..f34812dcf4 --- /dev/null +++ b/engines/cge2/vmenu.h @@ -0,0 +1,89 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_VMENU_H +#define CGE2_VMENU_H + +#define kMenuBarVerticalMargin 1 +#define kMenuBarHorizontalMargin 3 +#define kLt 3 +#define kRb 1 + +#include "cge2/cge2.h" +#include "cge2/talk.h" + +namespace CGE2 { + +class Choice { +protected: + CGE2Engine *_vm; +public: + char *_text; + + virtual void proc() = 0; + + Choice(CGE2Engine *vm); + virtual ~Choice() {}; +}; + +class ExitGameChoice : public Choice { +public: + ExitGameChoice(CGE2Engine *vm); + void proc(); +}; + +class ReturnToGameChoice : public Choice { +public: + ReturnToGameChoice(CGE2Engine *vm); + void proc(); +}; + +class MenuBar : public Talk { +public: + MenuBar(CGE2Engine *vm, uint16 w, byte *c); +}; + +class VMenu : public Talk { + CGE2Engine *_vm; + + uint16 _items; + Common::Array<Choice *> _menu; +public: + char *_vmgt; + static VMenu *_addr; + int _recent; + MenuBar *_bar; + + VMenu(CGE2Engine *vm, Common::Array<Choice *> list, V2D pos, ColorBank col); + ~VMenu(); + void touch(uint16 mask, V2D pos, Common::KeyCode keyCode); + char *vmGather(Common::Array<Choice *> list); +}; + +} // End of namespace CGE2 + +#endif // CGE2_VMENU_H diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 471a29030b..f070338978 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -135,7 +135,7 @@ Common::Error ComposerEngine::run() { else loadLibrary(_pendingPageChanges[i]._pageId); - lastDrawTime = _system->getMillis(); + lastDrawTime = 0; } _pendingPageChanges.clear(); @@ -168,9 +168,10 @@ Common::Error ComposerEngine::run() { else lastDrawTime += frameTime; + tickOldScripts(); + redraw(); - tickOldScripts(); processAnimFrame(); } else if (_needsUpdate) { redraw(); diff --git a/engines/composer/scripting.cpp b/engines/composer/scripting.cpp index 94ca2c1bc8..cd78202ecd 100644 --- a/engines/composer/scripting.cpp +++ b/engines/composer/scripting.cpp @@ -746,6 +746,7 @@ void ComposerEngine::stopOldScript(uint16 id) { for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) { if ((*i)->_id == id) { + removeSprite(0, id); delete *i; i = _oldScripts.reverse_erase(i); } diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 797b6d94b0..c72d77c281 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -41,20 +41,6 @@ namespace Drascula { -struct GameSettings { - const char *gameid; - const char *description; - byte id; - uint32 features; - const char *detectname; -}; - -static const GameSettings drasculaSettings[] = { - {"drascula", "Drascula game", 0, 0, 0}, - - {NULL, NULL, 0, 0, NULL} -}; - DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { _charMap = 0; _itemLocations = 0; diff --git a/engines/dreamweb/POTFILES b/engines/dreamweb/POTFILES index d05d239bb7..64fb20db67 100644 --- a/engines/dreamweb/POTFILES +++ b/engines/dreamweb/POTFILES @@ -1 +1,3 @@ engines/dreamweb/detection.cpp +engines/dreamweb/saveload.cpp + diff --git a/engines/fullpipe/fullpipe.cpp b/engines/fullpipe/fullpipe.cpp index bb0838395d..ebaff32550 100644 --- a/engines/fullpipe/fullpipe.cpp +++ b/engines/fullpipe/fullpipe.cpp @@ -163,7 +163,7 @@ FullpipeEngine::FullpipeEngine(OSystem *syst, const ADGameDescription *gameDesc) for (int i = 0; i < 11; i++) _currSoundList1[i] = 0; - + for (int i = 0; i < 200; i++) _mapTable[i] = 0; @@ -285,7 +285,7 @@ Common::Error FullpipeEngine::run() { freeGameLoader(); _currentScene = 0; _updateTicks = 0; - + loadGam("fullpipe.gam"); _needRestart = false; } diff --git a/engines/fullpipe/gfx.cpp b/engines/fullpipe/gfx.cpp index 61fbf7192f..42846850ca 100644 --- a/engines/fullpipe/gfx.cpp +++ b/engines/fullpipe/gfx.cpp @@ -153,7 +153,7 @@ bool PictureObject::load(MfcArchive &file, bool bigPicture) { if (count > 0) { GameObject *o = new GameObject(); - + o->load(file); _pictureObject2List->push_back(o); } @@ -286,9 +286,9 @@ bool GameObject::load(MfcArchive &file) { _okeyCode = 0; _flags = 0; _field_20 = 0; - + _id = file.readUint16LE(); - + _objectName = file.readPascalString(); _ox = file.readUint32LE(); _oy = file.readUint32LE(); @@ -498,7 +498,7 @@ bool Picture::load(MfcArchive &file) { _x = file.readUint32LE(); _y = file.readUint32LE(); _field_44 = file.readUint16LE(); - + assert(g_fp->_gameProjectVersion >= 2); _width = file.readUint32LE(); diff --git a/engines/fullpipe/input.cpp b/engines/fullpipe/input.cpp index 7c97461a24..b681f4fbe7 100644 --- a/engines/fullpipe/input.cpp +++ b/engines/fullpipe/input.cpp @@ -70,7 +70,7 @@ void setInputDisabled(bool state) { void InputController::addCursor(CursorInfo *cursor) { CursorInfo *newc = new CursorInfo(cursor); Common::Point p; - + cursor->picture->getDimensions(&p); newc->width = p.x; diff --git a/engines/fullpipe/motion.cpp b/engines/fullpipe/motion.cpp index 49cf88434e..9573e0517b 100644 --- a/engines/fullpipe/motion.cpp +++ b/engines/fullpipe/motion.cpp @@ -1040,11 +1040,11 @@ MessageQueue *MovGraph::doWalkTo(StaticANIObject *subj, int xpos, int ypos, int if (!mq || !mq->getExCommandByIndex(0)) return 0; - + ExCommand *ex = mq->getExCommandByIndex(0); - if ((ex->_messageKind != 1 && ex->_messageKind != 20) || - ex->_messageNum != subj->_movement->_id || + if ((ex->_messageKind != 1 && ex->_messageKind != 20) || + ex->_messageNum != subj->_movement->_id || (ex->_field_14 >= 1 && ex->_field_14 <= subj->_movement->_currDynamicPhaseIndex)) subj->playIdle(); } @@ -1416,8 +1416,8 @@ Common::Array<MovArr *> *MovGraph::genMovArr(int x, int y, int *arrSize, int fla movarr = new MovArr; movarr->_link = lnk; - movarr->_dist = ((double)(lnk->_movGraphNode1->_y - lnk->_movGraphNode2->_y) * (double)(lnk->_movGraphNode1->_y - point.y) + - (double)(lnk->_movGraphNode2->_x - lnk->_movGraphNode1->_x) * (double)(point.x - lnk->_movGraphNode1->_x)) / + movarr->_dist = ((double)(lnk->_movGraphNode1->_y - lnk->_movGraphNode2->_y) * (double)(lnk->_movGraphNode1->_y - point.y) + + (double)(lnk->_movGraphNode2->_x - lnk->_movGraphNode1->_x) * (double)(point.x - lnk->_movGraphNode1->_x)) / lnk->_distance / lnk->_distance; movarr->_point = point; @@ -1445,8 +1445,8 @@ Common::Array<MovArr *> *MovGraph::genMovArr(int x, int y, int *arrSize, int fla } else { movarr = new MovArr; movarr->_link = lnk; - movarr->_dist = ((double)(lnk->_movGraphNode1->_y - lnk->_movGraphNode2->_y) * (double)(lnk->_movGraphNode1->_y - y) + - (double)(lnk->_movGraphNode2->_x - lnk->_movGraphNode1->_x) * (double)(x - lnk->_movGraphNode1->_x)) / + movarr->_dist = ((double)(lnk->_movGraphNode1->_y - lnk->_movGraphNode2->_y) * (double)(lnk->_movGraphNode1->_y - y) + + (double)(lnk->_movGraphNode2->_x - lnk->_movGraphNode1->_x) * (double)(x - lnk->_movGraphNode1->_x)) / lnk->_distance / lnk->_distance; movarr->_point.x = x; movarr->_point.y = y; diff --git a/engines/fullpipe/scene.cpp b/engines/fullpipe/scene.cpp index 8463b3ab40..00dd70c1b2 100644 --- a/engines/fullpipe/scene.cpp +++ b/engines/fullpipe/scene.cpp @@ -162,7 +162,7 @@ bool Scene::load(MfcArchive &file) { Background::load(file); _sceneId = file.readUint16LE(); - + _sceneName = file.readPascalString(); debug(0, "scene: <%s> %d", transCyrillic((byte *)_sceneName), _sceneId); diff --git a/engines/fullpipe/scenes/scene02.cpp b/engines/fullpipe/scenes/scene02.cpp index 109a20a07a..fd542d580d 100644 --- a/engines/fullpipe/scenes/scene02.cpp +++ b/engines/fullpipe/scenes/scene02.cpp @@ -134,5 +134,5 @@ int sceneHandler02(ExCommand *ex) { return res; } - + } // End of namespace Fullpipe diff --git a/engines/fullpipe/scenes/scene14.cpp b/engines/fullpipe/scenes/scene14.cpp index 21dbe8101f..446f477133 100644 --- a/engines/fullpipe/scenes/scene14.cpp +++ b/engines/fullpipe/scenes/scene14.cpp @@ -57,7 +57,7 @@ void scene14_initScene(Scene *sc) { ball->_flags &= 0xFFFB; g_vars->scene14_balls.push_back(ball); - + for (uint i = 0; i < 3; i++) { ball = new StaticANIObject(ball); // create a copy diff --git a/engines/fullpipe/scenes/scene15.cpp b/engines/fullpipe/scenes/scene15.cpp index 452f2edeca..efc69a5fa6 100644 --- a/engines/fullpipe/scenes/scene15.cpp +++ b/engines/fullpipe/scenes/scene15.cpp @@ -71,7 +71,7 @@ void scene15_initScene(Scene *sc) { grandma->hide(); g_fp->setObjectState(sO_LeftPipe_15, g_fp->getObjectEnumState(sO_LeftPipe_15, sO_IsOpened)); } - + g_vars->scene15_plusminus = sc->getStaticANIObject1ById(ANI_PLUSMINUS, -1); if (g_fp->getObjectState(sO_Guard_2) == g_fp->getObjectEnumState(sO_Guard_2, sO_Off)) diff --git a/engines/fullpipe/scenes/scene16.cpp b/engines/fullpipe/scenes/scene16.cpp index ed3c51a6c2..e9d3a37efd 100644 --- a/engines/fullpipe/scenes/scene16.cpp +++ b/engines/fullpipe/scenes/scene16.cpp @@ -57,9 +57,9 @@ void scene16_initScene(Scene *sc) { boy[1] = new StaticANIObject(boy[0]); sc->addStaticANIObject(boy[1], 1); - + int idx = 0; - + for (int i = 0; i < 3; i++) { g_vars->scene16_figures.push_back(boy[idx]); @@ -68,7 +68,7 @@ void scene16_initScene(Scene *sc) { if (idx >= 2) idx = 0; } - + g_vars->scene16_figures.push_back(sc->getStaticANIObject1ById(ANI_GIRL, -1)); for (int i = 0; i < 4; i++) { @@ -259,7 +259,7 @@ void sceneHandler16_drink() { mq = new MessageQueue(g_fp->_currentScene->getMessageQueueById(QU_SC16_BOYKICK), 0, 1); mq->replaceKeyCode(-1, g_vars->scene16_walkingBoy->_okeyCode); - + ex = new ExCommand(ANI_MAN, 34, 384, 0, 0, 0, 1, 0, 0, 0); ex->_excFlags |= 3u; ex->_field_14 = 384; diff --git a/engines/fullpipe/scenes/scene23.cpp b/engines/fullpipe/scenes/scene23.cpp index ccfbac9223..f66ea12b4b 100644 --- a/engines/fullpipe/scenes/scene23.cpp +++ b/engines/fullpipe/scenes/scene23.cpp @@ -146,7 +146,7 @@ void scene23_initScene(Scene *sc) { sc->getStaticANIObject1ById(ANI_INV_LEVERHANDLE, -1)->hide(); } - + g_fp->_currentScene = oldsc; } diff --git a/engines/fullpipe/scenes/scene27.cpp b/engines/fullpipe/scenes/scene27.cpp index 1431ceffba..8ec05caaff 100644 --- a/engines/fullpipe/scenes/scene27.cpp +++ b/engines/fullpipe/scenes/scene27.cpp @@ -340,7 +340,7 @@ void sceneHandler27_wipeDo() { bool sceneHandler27_batFallLogic(uint batn) { Bat *bat = g_vars->scene27_bats[batn]; - int y = (bat->currY - 458.0) * 0.4848484848484849 + 734.0; + int y = (int)((bat->currY - 458.0) * 0.4848484848484849 + 734.0); if (y >= bat->currX) return false; diff --git a/engines/fullpipe/scenes/scene28.cpp b/engines/fullpipe/scenes/scene28.cpp index c21ce05502..de5a96e70d 100644 --- a/engines/fullpipe/scenes/scene28.cpp +++ b/engines/fullpipe/scenes/scene28.cpp @@ -46,7 +46,7 @@ void scene28_initScene(Scene *sc) { g_vars->scene28_lift6inside = false; g_fp->_floaters->init(g_fp->getGameLoaderGameVar()->getSubVarByName("SC_28")); - + g_fp->initArcadeKeys("SC_28"); } diff --git a/engines/fullpipe/scenes/scene29.cpp b/engines/fullpipe/scenes/scene29.cpp index 8f82e99ad1..222a541554 100644 --- a/engines/fullpipe/scenes/scene29.cpp +++ b/engines/fullpipe/scenes/scene29.cpp @@ -840,7 +840,7 @@ void sceneHandler29_shootersEscape() { void sceneHandler29_manRideBack() { g_vars->scene29_manX -= 2; - + g_fp->_aniMan->setOXY(g_vars->scene29_manX, g_vars->scene29_manY); } diff --git a/engines/fullpipe/scenes/scene37.cpp b/engines/fullpipe/scenes/scene37.cpp index 09da01f138..324d3ac92d 100644 --- a/engines/fullpipe/scenes/scene37.cpp +++ b/engines/fullpipe/scenes/scene37.cpp @@ -91,7 +91,7 @@ void scene37_initScene(Scene *sc) { g_vars->scene37_rings.push_back(ring); g_fp->setObjectState(sO_LeftPipe_37, g_fp->getObjectEnumState(sO_LeftPipe_37, sO_IsClosed)); - + Scene *oldsc = g_fp->_currentScene; g_fp->_currentScene = sc; diff --git a/engines/fullpipe/sound.cpp b/engines/fullpipe/sound.cpp index a4ca06f489..230d6c39a9 100644 --- a/engines/fullpipe/sound.cpp +++ b/engines/fullpipe/sound.cpp @@ -62,7 +62,7 @@ bool SoundList::load(MfcArchive &file, char *fname) { } return true; - + } bool SoundList::loadFile(const char *fname, char *libname) { diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp index 717de84925..880c2eb0df 100644 --- a/engines/fullpipe/statics.cpp +++ b/engines/fullpipe/statics.cpp @@ -246,7 +246,7 @@ bool StaticANIObject::load(MfcArchive &file) { void StaticANIObject::setOXY(int x, int y) { _ox = x; _oy = y; - + if (_movement) _movement->setOXY(x, y); } @@ -713,7 +713,7 @@ void StaticANIObject::setSpeed(int speed) { void StaticANIObject::setAlpha(int alpha) { for (uint i = 0; i < _movements.size(); i++) _movements[i]->setAlpha(alpha); - + for (uint i = 0; i < _staticsList.size(); i++) _staticsList[i]->setAlpha(alpha); } @@ -1813,7 +1813,7 @@ void Movement::initStatics(StaticANIObject *ani) { _staticsObj2 = ani->addReverseStatics(_currMovement->_staticsObj2); _staticsObj1 = ani->addReverseStatics(_currMovement->_staticsObj1); - + _mx = _currMovement->_mx; _my = _currMovement->_my; @@ -2279,7 +2279,7 @@ bool StaticPhase::load(MfcArchive &file) { _initialCountdown = file.readUint16LE(); _field_6A = file.readUint16LE(); - + if (g_fp->_gameProjectVersion >= 12) { _exCommand = (ExCommand *)file.readClass(); diff --git a/engines/gob/POTFILES b/engines/gob/POTFILES index 9a512469dd..ca7aa14177 100644 --- a/engines/gob/POTFILES +++ b/engines/gob/POTFILES @@ -1,3 +1,5 @@ engines/gob/inter_playtoons.cpp engines/gob/inter_v2.cpp engines/gob/inter_v5.cpp +engines/gob/inter_geisha.cpp + diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp index 40134bbf17..676564a9fb 100644 --- a/engines/gob/inter_v1.cpp +++ b/engines/gob/inter_v1.cpp @@ -1124,8 +1124,6 @@ void Inter_v1::o1_palLoad(OpFuncParams ¶ms) { _vm->_draw->_vgaPalette[i].green = _vm->_game->_script->readByte(); _vm->_draw->_vgaPalette[i].blue = _vm->_game->_script->readByte(); } - - memcpy(_vm->_draw->_vgaPalette, _vm->_draw->_vgaPalette, 16 * 3); break; case 53: diff --git a/engines/groovie/graphics.cpp b/engines/groovie/graphics.cpp index b85277fac7..e0c198f377 100644 --- a/engines/groovie/graphics.cpp +++ b/engines/groovie/graphics.cpp @@ -63,7 +63,7 @@ void GraphicsMan::update() { // Clear the buffer when ending the fade out if (_fading == 2) - _foreground.fillRect(Common::Rect(640, 320), 0); + _foreground.fillRect(Common::Rect(640, _foreground.h), 0); } } @@ -74,6 +74,22 @@ void GraphicsMan::update() { } } +void GraphicsMan::switchToFullScreen(bool fullScreen) { + _foreground.free(); + _background.free(); + + if (fullScreen) { + _foreground.create(640, 480, _vm->_pixelFormat); + _background.create(640, 480, _vm->_pixelFormat); + } else { + _vm->_system->fillScreen(0); + _foreground.create(640, 320, _vm->_pixelFormat); + _background.create(640, 320, _vm->_pixelFormat); + } + + _changed = true; +} + void GraphicsMan::change() { _changed = true; } @@ -84,7 +100,7 @@ void GraphicsMan::mergeFgAndBg() { countf = (byte *)_foreground.getPixels(); countb = (byte *)_background.getPixels(); - for (i = 640 * 320; i; i--) { + for (i = 640 * _foreground.h; i; i--) { if (255 == *(countf)) { *(countf) = *(countb); } @@ -94,7 +110,10 @@ void GraphicsMan::mergeFgAndBg() { } void GraphicsMan::updateScreen(Graphics::Surface *source) { - _vm->_system->copyRectToScreen(source->getPixels(), 640, 0, 80, 640, 320); + if (!isFullScreen()) + _vm->_system->copyRectToScreen(source->getPixels(), source->pitch, 0, 80, 640, 320); + else + _vm->_system->copyRectToScreen(source->getPixels(), source->pitch, 0, 0, 640, 480); change(); } diff --git a/engines/groovie/graphics.h b/engines/groovie/graphics.h index 72ab01deb6..69934f9d68 100644 --- a/engines/groovie/graphics.h +++ b/engines/groovie/graphics.h @@ -38,6 +38,8 @@ public: void update(); void change(); void mergeFgAndBg(); + void switchToFullScreen(bool fullScreen); + bool isFullScreen() { return (_foreground.h == 480); } void updateScreen(Graphics::Surface *source); Graphics::Surface _foreground; // The main surface that most things are drawn to Graphics::Surface _background; // Used occasionally, mostly (only?) in puzzles diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp index 379fcabc07..f14cacd6b8 100644 --- a/engines/groovie/roq.cpp +++ b/engines/groovie/roq.cpp @@ -28,6 +28,7 @@ #include "groovie/groovie.h" #include "common/debug.h" +#include "common/debug-channels.h" #include "common/rect.h" #include "common/substream.h" #include "common/textconsole.h" @@ -46,6 +47,7 @@ namespace Groovie { ROQPlayer::ROQPlayer(GroovieEngine *vm) : VideoPlayer(vm), _codingTypeCount(0), + _fg(&_vm->_graphicsMan->_foreground), _bg(&_vm->_graphicsMan->_background), _firstFrame(true) { @@ -63,6 +65,22 @@ ROQPlayer::~ROQPlayer() { } uint16 ROQPlayer::loadInternal() { + if (DebugMan.isDebugChannelEnabled(kDebugVideo)) { + int8 i; + debugN(1, "Groovie::ROQ: New ROQ: bitflags are "); + for (i = 15; i >= 0; i--) { + debugN(1, "%d", _flags & (1 << i)? 1 : 0); + if (i % 4 == 0) { + debugN(1, " "); + } + } + debug(1, " <- 0 "); + } + + // Flags: + // - 2 For overlay videos, show the whole video + _flagTwo = ((_flags & (1 << 2)) != 0); + // Begin reading the file debugC(1, kDebugVideo, "Groovie::ROQ: Loading video"); @@ -106,13 +124,23 @@ uint16 ROQPlayer::loadInternal() { } void ROQPlayer::buildShowBuf() { + if (_alpha) + _fg->copyFrom(*_bg); + for (int line = 0; line < _bg->h; line++) { - uint32 *out = (uint32 *)_bg->getBasePtr(0, line); + uint32 *out = _alpha ? (uint32 *)_fg->getBasePtr(0, line) : (uint32 *)_bg->getBasePtr(0, line); uint32 *in = (uint32 *)_currBuf->getBasePtr(0, line / _scaleY); for (int x = 0; x < _bg->w; x++) { - // Copy a pixel - *out++ = *in; + // Copy a pixel, checking the alpha channel first + if (_alpha && !(*in & 0xFF)) + out++; + else if (_fg->h == 480 && *in == _vm->_pixelFormat.RGBToColor(255, 255, 255)) + // Handle transparency in Gamepad videos + // TODO: For now, we detect these videos by checking for full screen + out++; + else + *out++ = *in; // Skip to the next pixel if (!(x % _scaleX)) @@ -145,19 +173,27 @@ bool ROQPlayer::playFrameInternal() { } // Wait until the current frame can be shown - waitFrame(); + // Don't wait if we're just showing one frame + if (!playFirstFrame()) + waitFrame(); if (_dirty) { // Update the screen - _syst->copyRectToScreen(_bg->getPixels(), _bg->pitch, 0, (_syst->getHeight() - _bg->h) / 2, _bg->w, _bg->h); + void *src = (_alpha) ? _fg->getPixels() : _bg->getPixels(); + _syst->copyRectToScreen(src, _bg->pitch, 0, (_syst->getHeight() - _bg->h) / 2, _bg->w, _bg->h); _syst->updateScreen(); + // For overlay videos, set the background buffer when the video ends + if (_alpha && (!_flagTwo || (_flagTwo && _file->eos()))) + _bg->copyFrom(*_fg); + // Clear the dirty flag _dirty = false; } - // Return whether the video has ended - return _file->eos(); + // Report the end of the video if we reached the end of the file or if we + // just wanted to play one frame. + return _file->eos() || playFirstFrame(); } bool ROQPlayer::readBlockHeader(ROQBlockHeader &blockHeader) { @@ -277,9 +313,17 @@ bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) { _prevBuf->create(width, height, _vm->_pixelFormat); } + // Switch from/to fullscreen, if needed + if (_bg->h != 480 && height == 480) + _vm->_graphicsMan->switchToFullScreen(true); + else if (_bg->h == 480 && height != 480) + _vm->_graphicsMan->switchToFullScreen(false); + // Clear the buffers with black - _currBuf->fillRect(Common::Rect(width, height), _vm->_pixelFormat.RGBToColor(0, 0, 0)); - _prevBuf->fillRect(Common::Rect(width, height), _vm->_pixelFormat.RGBToColor(0, 0, 0)); + if (!_alpha) { + _currBuf->fillRect(Common::Rect(width, height), _vm->_pixelFormat.RGBToColor(0, 0, 0)); + _prevBuf->fillRect(Common::Rect(width, height), _vm->_pixelFormat.RGBToColor(0, 0, 0)); + } return true; } @@ -448,7 +492,7 @@ bool ROQPlayer::processBlockSoundMono(ROQBlockHeader &blockHeader) { } // Initialize the audio stream if needed - if (!_audioStream) { + if (!_audioStream && !playFirstFrame()) { _audioStream = Audio::makeQueuingAudioStream(22050, false); Audio::SoundHandle sound_handle; g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream); @@ -477,7 +521,10 @@ bool ROQPlayer::processBlockSoundMono(ROQBlockHeader &blockHeader) { #ifdef SCUMM_LITTLE_ENDIAN flags |= Audio::FLAG_LITTLE_ENDIAN; #endif - _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2, DisposeAfterUse::YES, flags); + if (!playFirstFrame()) + _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2, DisposeAfterUse::YES, flags); + else + free(buffer); return true; } @@ -491,7 +538,7 @@ bool ROQPlayer::processBlockSoundStereo(ROQBlockHeader &blockHeader) { } // Initialize the audio stream if needed - if (!_audioStream) { + if (!_audioStream && !playFirstFrame()) { _audioStream = Audio::makeQueuingAudioStream(22050, true); Audio::SoundHandle sound_handle; g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream); @@ -533,7 +580,10 @@ bool ROQPlayer::processBlockSoundStereo(ROQBlockHeader &blockHeader) { #ifdef SCUMM_LITTLE_ENDIAN flags |= Audio::FLAG_LITTLE_ENDIAN; #endif - _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2, DisposeAfterUse::YES, flags); + if (!playFirstFrame()) + _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2, DisposeAfterUse::YES, flags); + else + free(buffer); return true; } diff --git a/engines/groovie/roq.h b/engines/groovie/roq.h index 7e7d38580e..ce1a3a2d58 100644 --- a/engines/groovie/roq.h +++ b/engines/groovie/roq.h @@ -57,6 +57,7 @@ private: bool processBlockSoundMono(ROQBlockHeader &blockHeader); bool processBlockSoundStereo(ROQBlockHeader &blockHeader); bool processBlockAudioContainer(ROQBlockHeader &blockHeader); + bool playFirstFrame() { return _alpha && !_flagTwo; } void paint2(byte i, int destx, int desty); void paint4(byte i, int destx, int desty); @@ -74,8 +75,11 @@ private: uint32 _codebook2[256 * 4]; byte _codebook4[256 * 4]; + // Flags + bool _flagTwo; + // Buffers - Graphics::Surface *_bg; + Graphics::Surface *_fg, *_bg; Graphics::Surface *_currBuf, *_prevBuf; void buildShowBuf(); byte _scaleX, _scaleY; diff --git a/engines/groovie/script.cpp b/engines/groovie/script.cpp index 7625151082..eef97b6ff9 100644 --- a/engines/groovie/script.cpp +++ b/engines/groovie/script.cpp @@ -179,12 +179,20 @@ void Script::directGameLoad(int slot) { // TODO: Return to the main script, likely reusing most of o_returnscript() - // HACK: We set variable 0x19 to the slot to load, and set the current - // instruction to the one that actually loads the saved game specified - // in that variable. This will change in other versions of the game and - // in other games. - setVariable(0x19, slot); - _currentInstruction = 0x287; + // HACK: We set the slot to load in the appropriate variable, and set the + // current instruction to the one that actually loads the saved game + // specified in that variable. This differs depending on the game and its + // version. + if (_version == kGroovieT7G) { + // 7th Guest + setVariable(0x19, slot); + _currentInstruction = 0x287; + } else { + // 11th Hour + setVariable(0xF, slot); + // FIXME: This bypasses a lot of the game's initialization procedure + _currentInstruction = 0xE78E; + } // TODO: We'll probably need to start by running the beginning of the // script to let it do the soundcard initialization and then do the @@ -350,9 +358,10 @@ bool Script::hotspot(Common::Rect rect, uint16 address, uint8 cursor) { // Show hotspots when debugging if (DebugMan.isDebugChannelEnabled(kDebugHotspots)) { - rect.translate(0, -80); + if (!_vm->_graphicsMan->isFullScreen()) + rect.translate(0, -80); _vm->_graphicsMan->_foreground.frameRect(rect, 250); - _vm->_system->copyRectToScreen(_vm->_graphicsMan->_foreground.getPixels(), _vm->_graphicsMan->_foreground.pitch, 0, 80, 640, 320); + _vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_foreground); _vm->_system->updateScreen(); } @@ -962,7 +971,7 @@ void Script::o_strcmpnejmp_var() { // 0x21 void Script::o_copybgtofg() { // 0x22 debugC(1, kDebugScript, "COPY_BG_TO_FG"); - memcpy(_vm->_graphicsMan->_foreground.getPixels(), _vm->_graphicsMan->_background.getPixels(), 640 * 320); + memcpy(_vm->_graphicsMan->_foreground.getPixels(), _vm->_graphicsMan->_background.getPixels(), 640 * _vm->_graphicsMan->_foreground.h); } void Script::o_strcmpeqjmp() { // 0x23 @@ -1198,6 +1207,7 @@ void Script::o_copyrecttobg() { // 0x37 uint16 top = readScript16bits(); uint16 right = readScript16bits(); uint16 bottom = readScript16bits(); + uint16 baseTop = (!_vm->_graphicsMan->isFullScreen()) ? 80 : 0; // Sanity checks to prevent bad pointer access crashes if (left > right) { @@ -1216,9 +1226,9 @@ void Script::o_copyrecttobg() { // 0x37 bottom = top; top = j; } - if (top < 80) { - warning("COPYRECT top < 80... clamping"); - top = 80; + if (top < baseTop) { + warning("COPYRECT top < baseTop... clamping"); + top = baseTop; } if (top >= 480) { warning("COPYRECT top >= 480... clamping"); @@ -1243,13 +1253,13 @@ void Script::o_copyrecttobg() { // 0x37 debugC(1, kDebugScript, "COPYRECT((%d,%d)->(%d,%d))", left, top, right, bottom); - fg = (byte *)_vm->_graphicsMan->_foreground.getBasePtr(left, top - 80); - bg = (byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - 80); + fg = (byte *)_vm->_graphicsMan->_foreground.getBasePtr(left, top - baseTop); + bg = (byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - baseTop); for (i = 0; i < height; i++) { memcpy(bg + offset, fg + offset, width); offset += 640; } - _vm->_system->copyRectToScreen(_vm->_graphicsMan->_background.getBasePtr(left, top - 80), 640, left, top, width, height); + _vm->_system->copyRectToScreen(_vm->_graphicsMan->_background.getBasePtr(left, top - baseTop), 640, left, top, width, height); _vm->_graphicsMan->change(); } @@ -1595,8 +1605,7 @@ void Script::o_hotspot_outrect() { bool contained = rect.contains(mousepos); if (!contained) { - error("hotspot-outrect unimplemented"); - // TODO: what to do with address? + _currentInstruction = address; } } @@ -1670,15 +1679,29 @@ void Script::o2_vdxtransition() { void Script::o2_copyscreentobg() { uint16 val = readScript16bits(); + // TODO: Parameter + if (val) + warning("o2_copyscreentobg: Param is %d", val); + + Graphics::Surface *screen = _vm->_system->lockScreen(); + _vm->_graphicsMan->_background.copyFrom(screen->getSubArea(Common::Rect(0, 80, 640, 320))); + _vm->_system->unlockScreen(); + debugC(1, kDebugScript, "CopyScreenToBG3: 0x%04X", val); - error("Unimplemented Opcode 0x4F"); } void Script::o2_copybgtoscreen() { uint16 val = readScript16bits(); + // TODO: Parameter + if (val) + warning("o2_copybgtoscreen: Param is %d", val); + + Graphics::Surface *screen = _vm->_system->lockScreen(); + _vm->_graphicsMan->_background.copyRectToSurface(*screen, 0, 80, Common::Rect(0, 0, 640, 320 - 80)); + _vm->_system->unlockScreen(); + debugC(1, kDebugScript, "CopyBG3ToScreen: 0x%04X", val); - error("Unimplemented Opcode 0x50"); } void Script::o2_setvideoskip() { @@ -1686,6 +1709,12 @@ void Script::o2_setvideoskip() { debugC(1, kDebugScript, "SetVideoSkip (0x%04X)", _videoSkipAddress); } +void Script::o2_stub42() { + uint8 arg = readScript8bits(); + // TODO: Switch with 5 cases (0 - 5). Anything above 5 is a NOP + debugC(1, kDebugScript, "STUB42 (0x%02X)", arg); +} + void Script::o2_stub52() { uint8 arg = readScript8bits(); debugC(1, kDebugScript, "STUB52 (0x%02X)", arg); @@ -1859,7 +1888,7 @@ Script::OpcodeFunc Script::_opcodesV2[NUM_OPCODES] = { &Script::o_loadscript, &Script::o_setvideoorigin, // 0x40 &Script::o_sub, - &Script::o_cellmove, + &Script::o2_stub42, &Script::o_returnscript, &Script::o_sethotspotright, // 0x44 &Script::o_sethotspotleft, diff --git a/engines/groovie/script.h b/engines/groovie/script.h index 35e52593de..a9f6143509 100644 --- a/engines/groovie/script.h +++ b/engines/groovie/script.h @@ -238,6 +238,7 @@ private: void o2_setvideoskip(); void o2_copyscreentobg(); void o2_copybgtoscreen(); + void o2_stub42(); void o2_stub52(); void o2_setscriptend(); }; diff --git a/engines/hopkins/POTFILES b/engines/hopkins/POTFILES new file mode 100644 index 0000000000..1ea7d5111b --- /dev/null +++ b/engines/hopkins/POTFILES @@ -0,0 +1,2 @@ +engines/hopkins/detection.cpp + diff --git a/engines/hopkins/computer.cpp b/engines/hopkins/computer.cpp index 84d5c631c7..18b16cb4c8 100644 --- a/engines/hopkins/computer.cpp +++ b/engines/hopkins/computer.cpp @@ -883,7 +883,7 @@ void ComputerManager::getScoreName() { _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); byte *ptr = _vm->_fileIO->loadFile("ALPHA.SPR"); _vm->_graphicsMan->fadeInBreakout(); - + // Figure out the line to put the new high score on int scoreLine = 0; while (scoreLine < 5 && _breakoutScore < atol(_score[scoreLine]._score.c_str())) diff --git a/engines/hopkins/files.cpp b/engines/hopkins/files.cpp index 6620f2878c..3100ed6cdc 100644 --- a/engines/hopkins/files.cpp +++ b/engines/hopkins/files.cpp @@ -74,7 +74,7 @@ int FileManager::readStream(Common::ReadStream &stream, void *buf, size_t nbytes * It's now using the config manager and a per-engine GUI option. */ void FileManager::initCensorship() { - _vm->_globals->_censorshipFl = ConfMan.getBool("enable_gore"); + _vm->_globals->_censorshipFl = !ConfMan.getBool("enable_gore"); } /** diff --git a/engines/hopkins/sound.cpp b/engines/hopkins/sound.cpp index 773c714899..6660233740 100644 --- a/engines/hopkins/sound.cpp +++ b/engines/hopkins/sound.cpp @@ -261,9 +261,9 @@ void SoundManager::loadAnimSound() { } } -void SoundManager::playAnimSound(int soundNumber) { +void SoundManager::playAnimSound(int animFrame) { if (!_vm->_globals->_censorshipFl && _specialSoundNum == 2) { - switch (soundNumber) { + switch (animFrame) { case 20: playSample(5); break; @@ -273,44 +273,59 @@ void SoundManager::playAnimSound(int soundNumber) { playSample(1); break; case 75: - playSample(2); + // This removes the sound of the gun played while the guard is being shot, as this part of the scene has been + // removed in the Polish version of the game + if (_vm->getLanguage() != Common::PL_POL) + playSample(2); + break; + case 95: + // This fixes an original bug in the Polish version of the game, which was literally butchered for some reason + if (_vm->getLanguage() == Common::PL_POL) + playSample(3); break; case 109: - playSample(3); + if (_vm->getLanguage() != Common::PL_POL) + playSample(3); + break; + case 108: + // This fixes an original bug in the Polish version of the game, which was literally butchered for some reason + if (_vm->getLanguage() == Common::PL_POL) + playSample(4); break; case 122: - playSample(4); + if (_vm->getLanguage() != Common::PL_POL) + playSample(4); break; } - } else if (_specialSoundNum == 1 && soundNumber == 17) + } else if (_specialSoundNum == 1 && animFrame == 17) playSoundFile("SOUND42.WAV"); - else if (_specialSoundNum == 5 && soundNumber == 19) + else if (_specialSoundNum == 5 && animFrame == 19) playWav(1); - else if (_specialSoundNum == 14 && soundNumber == 625) + else if (_specialSoundNum == 14 && animFrame == 625) playWav(1); - else if (_specialSoundNum == 16 && soundNumber == 25) + else if (_specialSoundNum == 16 && animFrame == 25) playWav(1); else if (_specialSoundNum == 17) { - if (soundNumber == 6) + if (animFrame == 6) playSample(1); - else if (soundNumber == 14) + else if (animFrame == 14) playSample(2); - else if (soundNumber == 67) + else if (animFrame == 67) playSample(3); - } else if (_specialSoundNum == 198 && soundNumber == 15) + } else if (_specialSoundNum == 198 && animFrame == 15) playWav(1); - else if (_specialSoundNum == 199 && soundNumber == 72) + else if (_specialSoundNum == 199 && animFrame == 72) playWav(1); - else if (_specialSoundNum == 208 && soundNumber == 40) + else if (_specialSoundNum == 208 && animFrame == 40) playWav(1); - else if (_specialSoundNum == 210 && soundNumber == 2) + else if (_specialSoundNum == 210 && animFrame == 2) playWav(1); - else if (_specialSoundNum == 211 && soundNumber == 22) + else if (_specialSoundNum == 211 && animFrame == 22) playWav(1); else if (_specialSoundNum == 229) { - if (soundNumber == 15) + if (animFrame == 15) playWav(1); - else if (soundNumber == 91) + else if (animFrame == 91) playWav(2); } } diff --git a/engines/hopkins/sound.h b/engines/hopkins/sound.h index 97cdcdc1dd..1fb4f9ae71 100644 --- a/engines/hopkins/sound.h +++ b/engines/hopkins/sound.h @@ -116,7 +116,7 @@ public: ~SoundManager(); void loadAnimSound(); - void playAnimSound(int soundNumber); + void playAnimSound(int animFrame); void loadSample(int wavIndex, const Common::String &file); void playSample(int wavIndex, int voiceMode = 9); diff --git a/engines/hopkins/talk.cpp b/engines/hopkins/talk.cpp index df7b26c82c..00c4ab0332 100644 --- a/engines/hopkins/talk.cpp +++ b/engines/hopkins/talk.cpp @@ -68,7 +68,7 @@ void TalkManager::startAnimatedCharacterDialogue(const Common::String &filename) getStringFromBuffer(40, spriteFilename, (const char *)_characterBuffer); getStringFromBuffer(0, _questionsFilename, (const char *)_characterBuffer); getStringFromBuffer(20, _answersFilename, (const char *)_characterBuffer); - + switch (_vm->_globals->_language) { case LANG_FR: _answersFilename = _questionsFilename = "RUE.TXT"; diff --git a/engines/kyra/POTFILES b/engines/kyra/POTFILES index 16888e2c5a..e5b380e52e 100644 --- a/engines/kyra/POTFILES +++ b/engines/kyra/POTFILES @@ -1,3 +1,4 @@ engines/kyra/detection.cpp engines/kyra/lol.cpp engines/kyra/sound_midi.cpp +engines/kyra/saveload_eob.cpp diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h index 1ada9a87ba..2ee0262ef2 100644 --- a/engines/kyra/detection_tables.h +++ b/engines/kyra/detection_tables.h @@ -1568,6 +1568,22 @@ const KYRAGameDescription adGameDescs[] = { EOB_FLAGS }, + { // Italian fan translation + { + "eob", + 0, + { + { "EOBDATA3.PAK", 0, "3ed915ab5b94d60dbfe1b55379889c51", -1 }, + { 0, 0, 0, 0 } + }, + Common::IT_ITA, + Common::kPlatformDOS, + ADGF_TESTING, + GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS) + }, + EOB_FLAGS + }, + { { "eob2", diff --git a/engines/kyra/items_lol.cpp b/engines/kyra/items_lol.cpp index f2a9637dfe..e1f864ddd2 100644 --- a/engines/kyra/items_lol.cpp +++ b/engines/kyra/items_lol.cpp @@ -265,7 +265,7 @@ bool LoLEngine::addItemToInventory(Item itemIndex) { gui_drawInventory(); } - assert(pos > 0 && pos < 48); + assert(pos >= 0 && pos < 48); _inventory[pos] = itemIndex; gui_drawInventory(); diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp index f950f9d5aa..89ee41e859 100644 --- a/engines/kyra/sound_adlib.cpp +++ b/engines/kyra/sound_adlib.cpp @@ -61,7 +61,7 @@ public: ~AdLibDriver(); void initDriver(); - void setSoundData(uint8 *data); + void setSoundData(uint8 *data, uint32 size); void queueTrack(int track, int volume); bool isChannelPlaying(int channel) const; void stopAllChannels(); @@ -130,13 +130,13 @@ private: struct Channel { bool lock; // New to ScummVM uint8 opExtraLevel2; - uint8 *dataptr; + const uint8 *dataptr; uint8 duration; uint8 repeatCounter; int8 baseOctave; uint8 priority; uint8 dataptrStackPos; - uint8 *dataptrStack[4]; + const uint8 *dataptrStack[4]; int8 baseNote; uint8 unk29; uint8 unk31; @@ -194,7 +194,7 @@ private: void setupDuration(uint8 duration, Channel &channel); void setupNote(uint8 rawNote, Channel &channel, bool flag = false); - void setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel); + void setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel); void noteOn(Channel &channel); void adjustVolume(Channel &channel); @@ -216,14 +216,24 @@ private: // * One for instruments, starting at offset 500. uint8 *getProgram(int progId) { - uint16 offset = READ_LE_UINT16(_soundData + 2 * progId); - //TODO: Check in LoL CD AdLib driver - if (offset == 0xFFFF) - return 0; - return _soundData + READ_LE_UINT16(_soundData + 2 * progId); + const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId); + + // In case an invalid offset is specified we return nullptr to + // indicate an error. 0xFFFF seems to indicate "this is not a valid + // program/instrument". However, 0 is also invalid because it points + // inside the offset table itself. We also ignore any offsets outside + // of the actual data size. + // The original does not contain any safety checks and will simply + // read outside of the valid sound data in case an invalid offset is + // encountered. + if (offset == 0 || offset >= _soundDataSize) { + return nullptr; + } else { + return _soundData + offset; + } } - uint8 *getInstrument(int instrumentId) { + const uint8 *getInstrument(int instrumentId) { return getProgram(_numPrograms + instrumentId); } @@ -231,7 +241,7 @@ private: void executePrograms(); struct ParserOpcode { - typedef int (AdLibDriver::*POpcode)(uint8 *&dataptr, Channel &channel, uint8 value); + typedef int (AdLibDriver::*POpcode)(const uint8 *&dataptr, Channel &channel, uint8 value); POpcode function; const char *name; }; @@ -240,61 +250,61 @@ private: const ParserOpcode *_parserOpcodeTable; int _parserOpcodeTableSize; - int update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value); - int update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value); - int update_jump(uint8 *&dataptr, Channel &channel, uint8 value); - int update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value); - int update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value); - int update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playRest(uint8 *&dataptr, Channel &channel, uint8 value); - int update_writeAdLib(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value); - int update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playNote(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_pitchBend(uint8 *&dataptr, Channel &channel, uint8 value); - int update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_nop(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value); + int update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_jump(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_nop(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value); + int update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value); + int updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value); private: // These variables have not yet been named, but some of them are partly // known nevertheless: @@ -358,6 +368,7 @@ private: FM_OPL *_adlib; uint8 *_soundData; + uint32 _soundDataSize; struct QueueEntry { QueueEntry() : data(0), id(0), volume(0) {} @@ -421,6 +432,7 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) { memset(_channels, 0, sizeof(_channels)); _soundData = 0; + _soundDataSize = 0; _vibratoAndAMDepthBits = _curRegOffset = 0; @@ -522,7 +534,7 @@ void AdLibDriver::initDriver() { resetAdLibState(); } -void AdLibDriver::setSoundData(uint8 *data) { +void AdLibDriver::setSoundData(uint8 *data, uint32 size) { Common::StackLock lock(_mutex); // Drop all tracks that are still queued. These would point to the old @@ -536,6 +548,7 @@ void AdLibDriver::setSoundData(uint8 *data) { } _soundData = data; + _soundDataSize = size; } void AdLibDriver::queueTrack(int track, int volume) { @@ -791,7 +804,7 @@ void AdLibDriver::executePrograms() { // This fixes a subtle music bug where the // wrong music would play when getting the // quill in Kyra 1. - uint8 *dataptr = channel.dataptr; + const uint8 *dataptr = channel.dataptr; while (dataptr) { uint8 opcode = *dataptr++; uint8 param = *dataptr++; @@ -1030,7 +1043,7 @@ void AdLibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) { writeOPL(0xB0 + _curChannel, channel.regBx); } -void AdLibDriver::setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel) { +void AdLibDriver::setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel) { debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels)); if (_curChannel >= 9) @@ -1340,12 +1353,12 @@ uint8 AdLibDriver::calculateOpLevel2(Channel &channel) { // parser opcodes -int AdLibDriver::update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.repeatCounter = value; return 0; } -int AdLibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value) { ++dataptr; if (--channel.repeatCounter) { int16 add = READ_LE_UINT16(dataptr - 2); @@ -1354,14 +1367,23 @@ int AdLibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 val return 0; } -int AdLibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value) { if (value == 0xFF) return 0; - uint8 *ptr = getProgram(value); - //TODO: Check in LoL CD AdLib driver - if (!ptr) + const uint8 *ptr = getProgram(value); + + // In case we encounter an invalid program we simply ignore it and do + // nothing instead. The original did not care about invalid programs and + // simply tried to play them anyway... But to avoid crashes due we ingore + // them. + // This, for example, happens in the Lands of Lore intro when Scotia gets + // the ring in the intro. + if (!ptr) { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupProgram: Invalid program %d specified", value); return 0; + } + uint8 chan = *ptr++; uint8 priority = *ptr++; @@ -1390,12 +1412,12 @@ int AdLibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 va return 0; } -int AdLibDriver::update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.spacing1 = value; return 0; } -int AdLibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_jump(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; int16 add = READ_LE_UINT16(dataptr); dataptr += 2; if (_version == 1) @@ -1407,7 +1429,7 @@ int AdLibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) { return 0; } -int AdLibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; int16 add = READ_LE_UINT16(dataptr); dataptr += 2; channel.dataptrStack[channel.dataptrStackPos++] = dataptr; @@ -1418,17 +1440,17 @@ int AdLibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint return 0; } -int AdLibDriver::update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value) { dataptr = channel.dataptrStack[--channel.dataptrStackPos]; return 0; } -int AdLibDriver::update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.baseOctave = value; return 0; } -int AdLibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.priority = 0; if (_curChannel != 9) noteOff(channel); @@ -1436,30 +1458,30 @@ int AdLibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 val return 2; } -int AdLibDriver::update_playRest(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value) { setupDuration(value, channel); noteOff(channel); return (value != 0); } -int AdLibDriver::update_writeAdLib(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value) { writeOPL(value, *dataptr++); return 0; } -int AdLibDriver::update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value) { setupNote(value, channel); value = *dataptr++; setupDuration(value, channel); return (value != 0); } -int AdLibDriver::update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.baseNote = value; return 0; } -int AdLibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.unk18 = value; channel.unk19 = value; channel.unk20 = channel.unk21 = *dataptr++; @@ -1483,7 +1505,7 @@ int AdLibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, return 0; } -int AdLibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value) { Channel &channel2 = _channels[value]; channel2.duration = 0; channel2.priority = 0; @@ -1491,8 +1513,16 @@ int AdLibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint return 0; } -int AdLibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 *ptr = getProgram(value); +int AdLibDriver::update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value) { + const uint8 *ptr = getProgram(value); + + // Safety check in case an invalid program is specified. This would make + // getProgram return a nullptr and thus cause invalid memory reads. + if (!ptr) { + debugC(3, kDebugLevelSound, "AdLibDriver::update_waitForEndOfProgram: Invalid program %d specified", value); + return 0; + } + uint8 chan = *ptr; if (!_channels[chan].dataptr) @@ -1502,12 +1532,25 @@ int AdLibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, u return 2; } -int AdLibDriver::update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value) { - setupInstrument(_curRegOffset, getInstrument(value), channel); +int AdLibDriver::update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value) { + const uint8 *instrument = getInstrument(value); + + // We add a safety check to avoid setting up invalid instruments. This is + // not done in the original. However, to avoid crashes due to invalid + // memory reads we simply ignore the request. + // This happens, for example, in Hand of Fate when using the swampsnake + // potion on Zanthia to scare off the rat in the cave in the first chapter + // of the game. + if (!instrument) { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupInstrument: Invalid instrument %d specified", value); + return 0; + } + + setupInstrument(_curRegOffset, instrument, channel); return 0; } -int AdLibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.unk29 = value; channel.unk30 = READ_BE_UINT16(dataptr); dataptr += 2; @@ -1516,19 +1559,19 @@ int AdLibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, u return 0; } -int AdLibDriver::update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; channel.primaryEffect = 0; channel.unk30 = 0; return 0; } -int AdLibDriver::update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.baseFreq = value; return 0; } -int AdLibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.unk32 = value; channel.unk33 = *dataptr++; uint8 temp = *dataptr++; @@ -1539,12 +1582,12 @@ int AdLibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, u return 0; } -int AdLibDriver::update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.priority = value; return 0; } -int AdLibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value) { value >>= 1; _unkValue1 = _unkValue2 = value; _callbackTimer = 0xFF; @@ -1552,7 +1595,7 @@ int AdLibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value) { if (_unkValue5) { if (_unkValue4 & value) { _unkValue5 = 0; @@ -1568,50 +1611,50 @@ int AdLibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value return 2; } -int AdLibDriver::update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.opExtraLevel1 = value; adjustVolume(channel); return 0; } -int AdLibDriver::update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value) { setupDuration(value, channel); return (value != 0); } -int AdLibDriver::update_playNote(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value) { setupDuration(value, channel); noteOn(channel); return (value != 0); } -int AdLibDriver::update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.fractionalSpacing = value & 7; return 0; } -int AdLibDriver::update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value) { _tempo = value; return 0; } -int AdLibDriver::update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; channel.secondaryEffect = 0; return 0; } -int AdLibDriver::update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.tempo = value; return 0; } -int AdLibDriver::update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.opExtraLevel3 = value; return 0; } -int AdLibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value) { int channelBackUp = _curChannel; _curChannel = value; @@ -1623,7 +1666,7 @@ int AdLibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 return 0; } -int AdLibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value) { int channelBackUp = _curChannel; _curChannel = value; @@ -1638,7 +1681,7 @@ int AdLibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uin // Apart from initializing to zero, these two functions are the only ones that // modify _vibratoAndAMDepthBits. -int AdLibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value) { if (value & 1) _vibratoAndAMDepthBits |= 0x80; else @@ -1648,7 +1691,7 @@ int AdLibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 valu return 0; } -int AdLibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value) { if (value & 1) _vibratoAndAMDepthBits |= 0x40; else @@ -1658,13 +1701,13 @@ int AdLibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 return 0; } -int AdLibDriver::update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.opExtraLevel1 += value; adjustVolume(channel); return 0; } -int AdLibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value) { int channelBackUp = _curChannel; _curChannel = value; @@ -1693,7 +1736,7 @@ int AdLibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value) { if (_curChannel >= 9) return 0; @@ -1714,35 +1757,35 @@ int AdLibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; channel.primaryEffect = 0; return 0; } -int AdLibDriver::update_pitchBend(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.pitchBend = value; setupNote(channel.rawNote, channel, true); return 0; } -int AdLibDriver::update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; channel.tempo = _tempo; return 0; } -int AdLibDriver::update_nop(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_nop(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; return 0; } -int AdLibDriver::update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.durationRandomness = value; return 0; } -int AdLibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value) { int tempo = channel.tempo + (int8)value; if (tempo <= 0) @@ -1754,7 +1797,7 @@ int AdLibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, ui return 0; } -int AdLibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value) { uint8 entry = *dataptr++; _tablePtr1 = _unkTable2[entry++]; _tablePtr2 = _unkTable2[entry]; @@ -1765,27 +1808,43 @@ int AdLibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) { int channelBackUp = _curChannel; int regOffsetBackUp = _curRegOffset; _curChannel = 6; _curRegOffset = _regOffset[6]; - setupInstrument(_curRegOffset, getInstrument(value), channel); + const uint8 *instrument; + instrument = getInstrument(value); + if (instrument) { + setupInstrument(_curRegOffset, instrument, channel); + } else { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 6 specified", value); + } _unkValue6 = channel.opLevel2; _curChannel = 7; _curRegOffset = _regOffset[7]; - setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel); + instrument = getInstrument(*dataptr++); + if (instrument) { + setupInstrument(_curRegOffset, instrument, channel); + } else { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 7 specified", value); + } _unkValue7 = channel.opLevel1; _unkValue8 = channel.opLevel2; _curChannel = 8; _curRegOffset = _regOffset[8]; - setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel); + instrument = getInstrument(*dataptr++); + if (instrument) { + setupInstrument(_curRegOffset, instrument, channel); + } else { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 8 specified", value); + } _unkValue9 = channel.opLevel1; _unkValue10 = channel.opLevel2; @@ -1810,7 +1869,7 @@ int AdLibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, ui return 0; } -int AdLibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) { // Any instrument that we want to play, and which was already playing, // is temporarily keyed off. Instruments that were off already, or // which we don't want to play, retain their old on/off status. This is @@ -1830,7 +1889,7 @@ int AdLibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uin return 0; } -int AdLibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value) { --dataptr; _rhythmSectionBits = 0; @@ -1841,7 +1900,7 @@ int AdLibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, u return 0; } -int AdLibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value) { uint8 value2 = *dataptr++; if (value & 1) { @@ -1882,7 +1941,7 @@ int AdLibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value) { uint8 value2 = *dataptr++; if (value & 1) { @@ -1923,7 +1982,7 @@ int AdLibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value) { uint8 value2 = *dataptr++; if (value & 1) { @@ -1964,17 +2023,17 @@ int AdLibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value return 0; } -int AdLibDriver::update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value) { _soundTrigger = value; return 0; } -int AdLibDriver::update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.tempoReset = value; return 0; } -int AdLibDriver::updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value) { +int AdLibDriver::updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value) { channel.unk39 = value; channel.unk40 = *dataptr++; return 0; @@ -2487,13 +2546,13 @@ void SoundAdLibPC::internalLoadFile(Common::String file) { _soundDataPtr = new uint8[soundDataSize]; assert(_soundDataPtr); - memcpy(_soundDataPtr, p, soundDataSize*sizeof(uint8)); + memcpy(_soundDataPtr, p, soundDataSize); delete[] fileData; fileData = p = 0; fileSize = 0; - _driver->setSoundData(_soundDataPtr); + _driver->setSoundData(_soundDataPtr, soundDataSize); _soundFileLoaded = file; } diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 9b3b92b9c7..731f2f4ce5 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -39,7 +39,7 @@ namespace Kyra { -#define RESFILE_VERSION 85 +#define RESFILE_VERSION 86 namespace { bool checkKyraDat(Common::SeekableReadStream *file) { diff --git a/engines/kyra/staticres_eob.cpp b/engines/kyra/staticres_eob.cpp index 54cc3066ec..aa7adc7461 100644 --- a/engines/kyra/staticres_eob.cpp +++ b/engines/kyra/staticres_eob.cpp @@ -464,20 +464,34 @@ void EoBCoreEngine::initStaticResource() { // EOB I doesn't have load and save menus, because there is only one single // save slot. Instead of emulating this we provide a menu similiar to EOB II. - static const char *const saveLoadStrings[3][4] = { - { "Cancel", "Empty Slot", "Save Game", "Load Game" }, - { "Abbr.", "Leerer Slot", "Speichern", " Laden" }, - { 0, 0, 0, 0 } + static const char *const saveLoadStrings[4][4] = { + { "Cancel", "Empty Slot", "Save Game", "Load Game" }, + { "Abbr.", "Leerer Slot", "Speichern", " Laden" }, + { " < < ", "Posizione Vuota", "Salva", "Carica" }, + { 0, 0, 0, 0 } }; - static const char *const errorSlotEmptyString[3] = { + static const char *const errorSlotEmptyString[4] = { "There is no game\rsaved in that slot!", "Hier ist noch kein\rSpiel gespeichert!", + "Non c'\x0E alcun gioco\rsalvato in quella\rposizione!", 0 }; - _saveLoadStrings = saveLoadStrings[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)]; - _errorSlotEmptyString = errorSlotEmptyString[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)]; + if (_flags.lang == Common::EN_ANY) { + _saveLoadStrings = saveLoadStrings[0]; + _errorSlotEmptyString = errorSlotEmptyString[0]; + } else if (_flags.lang == Common::DE_DEU) { + _saveLoadStrings = saveLoadStrings[1]; + _errorSlotEmptyString = errorSlotEmptyString[1]; + } else if (_flags.lang == Common::IT_ITA) { + _saveLoadStrings = saveLoadStrings[2]; + _errorSlotEmptyString = errorSlotEmptyString[2]; + } else { + _saveLoadStrings = saveLoadStrings[3]; + _errorSlotEmptyString = errorSlotEmptyString[3]; + } + _menuOkString = "OK"; } diff --git a/engines/kyra/text_rpg.cpp b/engines/kyra/text_rpg.cpp index 03acde8497..5cd99738ef 100644 --- a/engines/kyra/text_rpg.cpp +++ b/engines/kyra/text_rpg.cpp @@ -30,7 +30,7 @@ namespace Kyra { enum { - kEoBTextBufferSize = 2048 + kEoBTextBufferSize = 2560 }; TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(engine), _screen(scr), @@ -216,7 +216,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) { break; default: - if (_vm->game() == GI_LOL || (unsigned char)c > 30) { + if (_vm->game() == GI_EOB1 || _vm->game() == GI_LOL || (unsigned char)c > 30) { _lineWidth += (sjisTextMode ? 4 : (_screen->_currentFont == Screen::FID_SJIS_FNT ? 9 : _screen->getCharWidth((uint8)c))); _currentLine[_numCharsLeft++] = c; _currentLine[_numCharsLeft] = 0; diff --git a/engines/kyra/vqa.cpp b/engines/kyra/vqa.cpp index fb51c05e51..cfd5f6ffc6 100644 --- a/engines/kyra/vqa.cpp +++ b/engines/kyra/vqa.cpp @@ -532,7 +532,7 @@ void VQADecoder::VQAVideoTrack::handleVQFR(Common::SeekableReadStream *stream) { uint32 tag = readTag(stream); uint32 i; size = stream->readUint32BE(); - + switch (tag) { case MKTAG('C','B','F','0'): // Full codebook stream->read(_codeBook, size); diff --git a/engines/mads/animation.cpp b/engines/mads/animation.cpp index 512a3979f9..2b999fa305 100644 --- a/engines/mads/animation.cpp +++ b/engines/mads/animation.cpp @@ -32,10 +32,8 @@ void AAHeader::load(Common::SeekableReadStream *f) { _miscEntriesCount = f->readUint16LE(); _frameEntriesCount = f->readUint16LE(); _messagesCount = f->readUint16LE(); - f->skip(1); - _flags = f->readByte(); - - f->skip(2); + _loadFlags = f->readUint16LE(); + _charSpacing = f->readSint16LE(); _bgType = (AnimBgType)f->readUint16LE(); _roomNumber = f->readUint16LE(); f->skip(2); @@ -49,7 +47,7 @@ void AAHeader::load(Common::SeekableReadStream *f) { char buffer[FILENAME_SIZE]; f->read(buffer, FILENAME_SIZE); buffer[FILENAME_SIZE - 1] = '\0'; - _interfaceFile = Common::String(buffer); + _backgroundFile = Common::String(buffer); for (int i = 0; i < 50; ++i) { f->read(buffer, FILENAME_SIZE); @@ -134,7 +132,8 @@ void AnimMiscEntry::load(Common::SeekableReadStream *f) { _numTicks = f->readUint16LE(); _posAdjust.x = f->readSint16LE(); _posAdjust.y = f->readSint16LE(); - _field8 = f->readUint16LE(); + _scroll.x = f->readSByte(); + _scroll.y = f->readSByte(); } /*------------------------------------------------------------------------*/ @@ -160,6 +159,7 @@ Animation *Animation::init(MADSEngine *vm, Scene *scene) { } Animation::Animation(MADSEngine *vm, Scene *scene) : _vm(vm), _scene(scene) { + _flags = 0; _font = nullptr; _resetFlag = false; _messageCtr = 0; @@ -175,6 +175,8 @@ Animation::Animation(MADSEngine *vm, Scene *scene) : _vm(vm), _scene(scene) { _actionDetails._indirectObjectId = -1; _currentFrame = 0; _oldFrameEntry = 0; + _rgbResult = -1; + _palIndex1 = _palIndex2 = -1; } Animation::~Animation() { @@ -189,7 +191,7 @@ Animation::~Animation() { } } -void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface, +void Animation::load(MSurface &backSurface, DepthSurface &depthSurface, const Common::String &resName, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo) { Common::String resourceName = resName; @@ -205,9 +207,10 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface if (_header._bgType == ANIMBG_INTERFACE) flags |= PALFLAG_RESERVED; + _flags = flags; if (flags & ANIMFLAG_LOAD_BACKGROUND) { - loadInterface(interfaceSurface, depthSurface, _header, flags, palCycles, sceneInfo); + loadBackground(backSurface, depthSurface, _header, flags, palCycles, sceneInfo); } if (flags & ANIMFLAG_LOAD_BACKGROUND_ONLY) { // No data @@ -243,7 +246,7 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface for (int i = 0; i < _header._frameEntriesCount; i++) { AnimFrameEntry rec; - rec.load(frameStream, flags & ANIMFLAG_LOAD_BACKGROUND); + rec.load(frameStream, _header._bgType == ANIMBG_INTERFACE); _frameEntries.push_back(rec); } @@ -256,7 +259,7 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface // Chunk 4: Misc Data Common::SeekableReadStream *miscStream = madsPack.getItemStream(streamIndex++); - if (flags & ANIMFLAG_LOAD_BACKGROUND) { + if (_header._bgType == ANIMBG_INTERFACE) { for (int i = 0; i < _header._miscEntriesCount; ++i) { AnimUIEntry rec; rec.load(miscStream); @@ -275,7 +278,7 @@ void Animation::load(UserInterface &interfaceSurface, DepthSurface &depthSurface // If the animation specifies a font, then load it for access delete _font; - if (_header._flags & ANIMFLAG_CUSTOM_FONT) { + if (_header._loadFlags & ANIMFLAG_CUSTOM_FONT) { Common::String fontName = "*" + _header._fontResource; _font = _vm->_font->getFont(fontName.c_str()); } else { @@ -337,9 +340,6 @@ void Animation::startAnimation(int endTrigger) { _unkIndex = -1; //SpriteAsset *asset = _scene->_sprites[_spriteListIndexes[_header._spritesIndex]]; - // TODO: Weird stuff with _unkList. Seems like it's treated as pointers - // here, but in processText, it's used as POINTs? - loadFrame(1); } @@ -385,12 +385,12 @@ bool Animation::drawFrame(SpriteAsset &spriteSet, const Common::Point &pt, int f return 0; } -void Animation::loadInterface(UserInterface &interfaceSurface, DepthSurface &depthSurface, +void Animation::loadBackground(MSurface &backSurface, DepthSurface &depthSurface, AAHeader &header, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo) { _scene->_depthStyle = 0; if (header._bgType <= ANIMBG_FULL_SIZE) { _vm->_palette->_paletteUsage.setEmpty(); - sceneInfo->load(header._roomNumber, flags, header._interfaceFile, 0, depthSurface, interfaceSurface); + sceneInfo->load(header._roomNumber, 0, header._backgroundFile, flags, depthSurface, backSurface); _scene->_depthStyle = sceneInfo->_depthStyle == 2 ? 1 : 0; if (palCycles) { palCycles->clear(); @@ -399,8 +399,8 @@ void Animation::loadInterface(UserInterface &interfaceSurface, DepthSurface &dep } } else if (header._bgType == ANIMBG_INTERFACE) { // Load a scene interface - Common::String resourceName = "*" + header._interfaceFile; - interfaceSurface.load(resourceName); + Common::String resourceName = "*" + header._backgroundFile; + backSurface.load(resourceName); if (palCycles) palCycles->clear(); @@ -415,6 +415,7 @@ bool Animation::hasScroll() const { void Animation::update() { Scene &scene = _vm->_game->_scene; + Palette &palette = *_vm->_palette; if (_header._manualFlag) { int spriteListIndex = _spriteListIndexes[_header._spritesIndex]; @@ -534,28 +535,43 @@ void Animation::update() { // Start displaying the message AnimMessage &me = _messages[idx]; - // The color index to use is dependant on how many messages are currently on-screen - uint8 colIndex; - switch (_messageCtr) { - case 1: - colIndex = 252; - break; - case 2: - colIndex = 16; - break; - default: - colIndex = 250; - break; - } + if (_flags & ANIMFLAG_ANIMVIEW) { + _rgbResult = palette._paletteUsage.checkRGB(me._rgb1, -1, true, &_palIndex1); + _rgbResult = palette._paletteUsage.checkRGB(me._rgb2, _rgbResult, true, &_palIndex2); + + // Update the palette with the two needed colors + int palStart = MIN(_palIndex1, _palIndex2); + int palCount = ABS(_palIndex2 - _palIndex1) + 1; + palette.setPalette(&palette._mainPalette[palStart * 3], palStart, palCount); + } else { + // The color index to use is dependant on how many messages are currently on-screen + switch (_messageCtr) { + case 1: + _palIndex1 = 252; + break; + case 2: + _palIndex1 = 16; + break; + default: + _palIndex1 = 250; + break; + } + _palIndex2 = _palIndex1 + 1; - _vm->_palette->setEntry(colIndex, me._rgb1[0], me._rgb1[1], me._rgb1[2]); - _vm->_palette->setEntry(colIndex + 1, me._rgb2[0], me._rgb2[1], me._rgb2[2]); + _vm->_palette->setEntry(_palIndex1, me._rgb1[0], me._rgb1[1], me._rgb1[2]); + _vm->_palette->setEntry(_palIndex2, me._rgb2[0], me._rgb2[1], me._rgb2[2]); + } // Add a kernel message to display the given text - me._kernelMsgIndex = scene._kernelMessages.add(me._pos, colIndex * 0x101 + 0x100, + me._kernelMsgIndex = scene._kernelMessages.add(me._pos, + _palIndex1 | (_palIndex2 << 8), 0, 0, INDEFINITE_TIMEOUT, me._msg); assert(me._kernelMsgIndex >= 0); ++_messageCtr; + + // If there's an accompanying sound, also play it + if (me._soundId > 0) + _vm->_audio->playSound(me._soundId - 1); } } diff --git a/engines/mads/animation.h b/engines/mads/animation.h index 15086d3e41..8b85a5370d 100644 --- a/engines/mads/animation.h +++ b/engines/mads/animation.h @@ -34,10 +34,11 @@ namespace MADS { enum AnimFlag { - ANIMFLAG_DITHER = 0x0001, // Dither to 16 colors - ANIMFLAG_CUSTOM_FONT = 0x0020, // Load ccustom font + ANIMFLAG_DITHER = 0x1000, // Dither to 16 colors + ANIMFLAG_CUSTOM_FONT = 0x2000, // Load ccustom font ANIMFLAG_LOAD_BACKGROUND = 0x0100, // Load background - ANIMFLAG_LOAD_BACKGROUND_ONLY = 0x0200 // Load background only + ANIMFLAG_LOAD_BACKGROUND_ONLY = 0x0200, // Load background only + ANIMFLAG_ANIMVIEW = 0x4000 // Cutscene animation }; enum AnimBgType { @@ -82,7 +83,7 @@ public: int _msgIndex; int _numTicks; Common::Point _posAdjust; - int _field8; + Common::Point _scroll; /** * Loads data for the record @@ -116,14 +117,15 @@ public: int _miscEntriesCount; int _frameEntriesCount; int _messagesCount; - byte _flags; + int _loadFlags; + int _charSpacing; AnimBgType _bgType; int _roomNumber; bool _manualFlag; int _spritesIndex; Common::Point _scrollPosition; uint32 _scrollTicks; - Common::String _interfaceFile; + Common::String _backgroundFile; Common::StringArray _spriteSetNames; Common::String _lbmFilename; Common::String _spritesFilename; @@ -154,6 +156,9 @@ private: uint32 _nextScrollTimer; int _messageCtr; int _trigger; + int _flags; + int _rgbResult; + int _palIndex1, _palIndex2; TriggerMode _triggerMode; ActionDetails _actionDetails; @@ -166,9 +171,9 @@ private: bool drawFrame(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber); /** - * Load the user interface display for an animation + * Load the user interface display or background for an animation */ - void loadInterface(UserInterface &interfaceSurface, DepthSurface &depthSurface, + void loadBackground(MSurface &backSurface, DepthSurface &depthSurface, AAHeader &header, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo); /** @@ -196,7 +201,7 @@ public: /** * Loads animation data */ - void load(UserInterface &interfaceSurface, DepthSurface &depthSurface, const Common::String &resName, + void load(MSurface &backSurface, DepthSurface &depthSurface, const Common::String &resName, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo); /** @@ -223,6 +228,8 @@ public: int roomNumber() const { return _header._roomNumber; } void resetSpriteSetsCount() { _header._spriteSetsCount = 0; } // CHECKME: See if it doesn't leak the memory when the destructor is called + + SpriteAsset *getSpriteSet(int idx) { return _spriteSets[idx]; } }; } // End of namespace MADS diff --git a/engines/mads/audio.cpp b/engines/mads/audio.cpp index 1c61e13957..def2cd6c62 100644 --- a/engines/mads/audio.cpp +++ b/engines/mads/audio.cpp @@ -37,6 +37,7 @@ AudioPlayer::AudioPlayer(Audio::Mixer *mixer, uint32 gameID) : _mixer(mixer), _g AudioPlayer::~AudioPlayer() { _dsrEntries.clear(); + _filename = ""; } bool AudioPlayer::isPlaying() const { @@ -65,25 +66,27 @@ void AudioPlayer::setDefaultSoundGroup() { } void AudioPlayer::setSoundGroup(const Common::String &filename) { - _dsrEntries.clear(); - - _filename = filename; - _dsrFile.open(filename); - - // Read header - uint16 entryCount = _dsrFile.readUint16LE(); - - for (uint16 i = 0; i < entryCount; i++) { - DSREntry newEntry; - newEntry.frequency = _dsrFile.readUint16LE(); - newEntry.channels = _dsrFile.readUint32LE(); - newEntry.compSize = _dsrFile.readUint32LE(); - newEntry.uncompSize = _dsrFile.readUint32LE(); - newEntry.offset = _dsrFile.readUint32LE(); - _dsrEntries.push_back(newEntry); + if (_filename != filename) { + _dsrEntries.clear(); + + _filename = filename; + _dsrFile.open(filename); + + // Read header + uint16 entryCount = _dsrFile.readUint16LE(); + + for (uint16 i = 0; i < entryCount; i++) { + DSREntry newEntry; + newEntry.frequency = _dsrFile.readUint16LE(); + newEntry.channels = _dsrFile.readUint32LE(); + newEntry.compSize = _dsrFile.readUint32LE(); + newEntry.uncompSize = _dsrFile.readUint32LE(); + newEntry.offset = _dsrFile.readUint32LE(); + _dsrEntries.push_back(newEntry); + } + + _dsrFile.close(); } - - _dsrFile.close(); } void AudioPlayer::playSound(int soundIndex, bool loop) { @@ -126,4 +129,8 @@ void AudioPlayer::playSound(int soundIndex, bool loop) { */ } +void AudioPlayer::stop() { + _mixer->stopHandle(_handle); +} + } // End of namespace M4 diff --git a/engines/mads/audio.h b/engines/mads/audio.h index 21f4bed59a..13c540bf85 100644 --- a/engines/mads/audio.h +++ b/engines/mads/audio.h @@ -46,6 +46,7 @@ public: void setSoundGroup(const Common::String &filename); void setDefaultSoundGroup(); void playSound(int soundIndex, bool loop = false); + void stop(); void setVolume(int volume); bool isPlaying() const; diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp index 6bc6cf572d..99251f9fbb 100644 --- a/engines/mads/debugger.cpp +++ b/engines/mads/debugger.cpp @@ -24,6 +24,7 @@ #include "mads/compression.h" #include "mads/mads.h" #include "mads/debugger.h" +#include "mads/nebular/menu_nebular.h" namespace MADS { @@ -46,6 +47,8 @@ Debugger::Debugger(MADSEngine *vm) : GUI::Debugger(), _vm(vm) { registerCmd("show_item", WRAP_METHOD(Debugger, Cmd_ShowItem)); registerCmd("dump_items", WRAP_METHOD(Debugger, Cmd_DumpItems)); registerCmd("item", WRAP_METHOD(Debugger, Cmd_Item)); + registerCmd("play_anim", WRAP_METHOD(Debugger, Cmd_PlayAnim)); + registerCmd("play_text", WRAP_METHOD(Debugger, Cmd_PlayText)); } static int strToInt(const char *s) { @@ -348,4 +351,44 @@ bool Debugger::Cmd_Item(int argc, const char **argv) { } } +bool Debugger::Cmd_PlayAnim(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Usage: %s <anim name>\n", argv[0]); + return true; + } else { + Common::String resName = argv[1]; + if (resName.hasPrefix("@")) + resName.deleteChar(0); + + Common::File f; + if (f.exists(resName) || f.exists(resName + ".res")) { + AnimationView::execute(_vm, resName); + return false; + } else { + debugPrintf("Could not find resource file\n"); + return true; + } + } +} + +bool Debugger::Cmd_PlayText(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Usage: %s <text name>\n", argv[0]); + return true; + } else { + Common::String resName = argv[1]; + if (resName.hasPrefix("@")) + resName.deleteChar(0); + + Common::File f; + if (f.exists(resName) || f.exists(resName + ".txr")) { + TextView::execute(_vm, resName); + return false; + } else { + debugPrintf("Could not find resource file\n"); + return true; + } + } +} + } // End of namespace MADS diff --git a/engines/mads/debugger.h b/engines/mads/debugger.h index 351eb13615..c8fee5f5b2 100644 --- a/engines/mads/debugger.h +++ b/engines/mads/debugger.h @@ -49,6 +49,8 @@ protected: bool Cmd_ShowItem(int argc, const char **argv); bool Cmd_DumpItems(int argc, const char **argv); bool Cmd_Item(int argc, const char **argv); + bool Cmd_PlayAnim(int argc, const char **argv); + bool Cmd_PlayText(int argc, const char **argv); public: bool _showMousePos; public: diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp index 7e6909d113..5e38f34fc6 100644 --- a/engines/mads/dialogs.cpp +++ b/engines/mads/dialogs.cpp @@ -395,4 +395,78 @@ Dialogs::Dialogs(MADSEngine *vm) _pendingDialog = DIALOG_NONE; } +/*------------------------------------------------------------------------*/ + +FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) { + switch (_vm->getGameID()) { + case GType_RexNebular: + _screenId = 990; + break; + case GType_Phantom: + _screenId = 920; + break; + case GType_Dragonsphere: + _screenId = 922; + break; + default: + error("FullScreenDialog:Unknown game"); + } + _palFlag = true; +} + +FullScreenDialog::~FullScreenDialog() { + _vm->_screen.resetClipBounds(); + _vm->_game->_scene.restrictScene(); +} + +void FullScreenDialog::display() { + Game &game = *_vm->_game; + Scene &scene = game._scene; + + int nextSceneId = scene._nextSceneId; + int currentSceneId = scene._currentSceneId; + int priorSceneId = scene._priorSceneId; + + if (_screenId > 0) { + SceneInfo *sceneInfo = SceneInfo::init(_vm); + sceneInfo->load(_screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface); + } + + scene._priorSceneId = priorSceneId; + scene._currentSceneId = currentSceneId; + scene._nextSceneId = nextSceneId; + + _vm->_events->initVars(); + game._kernelMode = KERNEL_ROOM_INIT; + + byte pal[768]; + if (_vm->_screenFade) { + Common::fill(&pal[0], &pal[PALETTE_SIZE], 0); + _vm->_palette->setFullPalette(pal); + } else { + _vm->_palette->getFullPalette(pal); + _vm->_palette->fadeOut(pal, nullptr, 0, PALETTE_COUNT, 0, 1, 1, 16); + } + + // Set Fx state and palette entries + game._fx = _vm->_screenFade == SCREEN_FADE_SMOOTH ? kTransitionFadeIn : kCenterVertTransition; + game._trigger = 0; + + // Clear the screen and draw the upper and lower horizontal lines + _vm->_screen.empty(); + _vm->_palette->setLowRange(); + _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2); + _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2); + _vm->_screen.resetClipBounds(); + _vm->_screen.copyRectToScreen(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); + + // Restrict the screen to the area between the two lines + _vm->_screen.setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH, + DIALOG_TOP + MADS_SCENE_HEIGHT)); + _vm->_game->_scene.restrictScene(); + + if (_screenId > 0) + scene._spriteSlots.fullRefresh(); +} + } // End of namespace MADS diff --git a/engines/mads/dialogs.h b/engines/mads/dialogs.h index c586a6f1fe..317c7bd792 100644 --- a/engines/mads/dialogs.h +++ b/engines/mads/dialogs.h @@ -30,6 +30,8 @@ namespace MADS { +#define DIALOG_TOP 22 + class Dialog { private: void setDialogPalette(); @@ -226,6 +228,36 @@ public: virtual bool show(int messageId, int objectId = -1) = 0; }; +class FullScreenDialog: public EventTarget { +protected: + /** + * Engine reference + */ + MADSEngine *_vm; + + /** + * Screen/scene to show background from + */ + int _screenId; + + /** + * Flag for palette initialization + */ + bool _palFlag; + + /** + * Handles displaying the screen background and dialog + */ + virtual void display(); +public: + /** + * Constructor + */ + FullScreenDialog(MADSEngine *vm); + + virtual ~FullScreenDialog(); +}; + } // End of namespace MADS #endif /* MADS_DIALOGS_H */ diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp index b544eff2db..94653f9a39 100644 --- a/engines/mads/game.cpp +++ b/engines/mads/game.cpp @@ -433,8 +433,6 @@ void Game::handleKeypress(const Common::Event &event) { default: break; } - - warning("TODO: handleKeypress - %d", (int)event.kbd.keycode); } void Game::synchronize(Common::Serializer &s, bool phase1) { @@ -558,7 +556,7 @@ void Game::writeSavegameHeader(Common::OutSaveFile *out, MADSSavegameHeader &hea if (!_saveThumb) createThumbnail(); Graphics::saveThumbnail(*out, *_saveThumb); - + _saveThumb->free(); delete _saveThumb; _saveThumb = nullptr; diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp index 59eec40bcc..52a0b40561 100644 --- a/engines/mads/mads.cpp +++ b/engines/mads/mads.cpp @@ -68,6 +68,8 @@ MADSEngine::~MADSEngine() { delete _resources; delete _sound; delete _audio; + + _mixer->stopAll(); } void MADSEngine::initialize() { @@ -103,9 +105,6 @@ Common::Error MADSEngine::run() { // Run the game _game->run(); - // Dummy loop to keep application active - _events->delay(9999); - return Common::kNoError; } diff --git a/engines/mads/menu_views.cpp b/engines/mads/menu_views.cpp new file mode 100644 index 0000000000..ee4268a650 --- /dev/null +++ b/engines/mads/menu_views.cpp @@ -0,0 +1,768 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" +#include "mads/game.h" +#include "mads/mads.h" +#include "mads/menu_views.h" +#include "mads/resources.h" +#include "mads/scene.h" +#include "mads/screen.h" + +namespace MADS { + +MenuView::MenuView(MADSEngine *vm) : FullScreenDialog(vm) { + _breakFlag = false; + _redrawFlag = true; + _palFlag = false; +} + +void MenuView::show() { + Scene &scene = _vm->_game->_scene; + EventsManager &events = *_vm->_events; + _vm->_screenFade = SCREEN_FADE_FAST; + + scene._spriteSlots.reset(true); + display(); + + events.setEventTarget(this); + events.hideCursor(); + + while (!_breakFlag && !_vm->shouldQuit()) { + if (_redrawFlag) { + scene._kernelMessages.update(); + + _vm->_game->_scene.drawElements(_vm->_game->_fx, _vm->_game->_fx); + _redrawFlag = false; + } + + _vm->_events->waitForNextFrame(); + _vm->_game->_fx = kTransitionNone; + doFrame(); + } + + events.setEventTarget(nullptr); + _vm->_sound->stop(); +} + +void MenuView::display() { + _vm->_palette->resetGamePalette(4, 8); + + FullScreenDialog::display(); +} + +bool MenuView::onEvent(Common::Event &event) { + if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONDOWN) { + _breakFlag = true; + _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; + return true; + } + + return false; +} + +Common::String MenuView::getResourceName() { + Common::String s(_filename); + s.toLowercase(); + while (s.contains('.')) + s.deleteLastChar(); + + return s; +} + +/*------------------------------------------------------------------------*/ + +char TextView::_resourceName[100]; +#define TEXTVIEW_LINE_SPACING 2 +#define TEXT_ANIMATION_DELAY 100 +#define TV_NUM_FADE_STEPS 40 +#define TV_FADE_DELAY_MILLI 50 + +void TextView::execute(MADSEngine *vm, const Common::String &resName) { + assert(resName.size() < 100); + Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName)); + vm->_dialogs->_pendingDialog = DIALOG_TEXTVIEW; +} + +TextView::TextView(MADSEngine *vm) : MenuView(vm) { + _animating = false; + _panSpeed = 0; + _spareScreen = nullptr; + _scrollCount = 0; + _lineY = -1; + _scrollTimeout = 0; + _panCountdown = 0; + _translationX = 0; + _screenId = -1; + + _font = _vm->_font->getFont(FONT_CONVERSATION); + _vm->_palette->resetGamePalette(4, 0); + + load(); +} + +TextView::~TextView() { + // Turn off palette cycling as well as any playing sound + Scene &scene = _vm->_game->_scene; + scene._cyclingActive = false; + _vm->_sound->stop(); +} + +void TextView::load() { + Common::String scriptName(_resourceName); + scriptName += ".txr"; + + _filename = scriptName; + if (!_script.open(scriptName)) + error("Could not open resource %s", _resourceName); + + processLines(); +} + +void TextView::processLines() { + if (_script.eos()) + error("Attempted to read past end of response file"); + + while (!_script.eos()) { + // Read in the next line + _script.readLine(_currentLine, 79); + char *p = _currentLine + strlen(_currentLine) - 1; + if (*p == '\n') + *p = '\0'; + + // Commented out line, so go loop for another + if (_currentLine[0] == '#') + continue; + + // Process the line + char *cStart = strchr(_currentLine, '['); + if (cStart) { + while (cStart) { + // Loop for possible multiple commands on one line + char *cEnd = strchr(_currentLine, ']'); + if (!cEnd) + error("Unterminated command '%s' in response file", _currentLine); + + *cEnd = '\0'; + processCommand(); + + // Copy rest of line (if any) to start of buffer + Common::strlcpy(_currentLine, cEnd + 1, sizeof(_currentLine)); + + cStart = strchr(_currentLine, '['); + } + + if (_currentLine[0]) { + processText(); + break; + } + + } else { + processText(); + break; + } + } +} + +void TextView::processCommand() { + Scene &scene = _vm->_game->_scene; + Common::String scriptLine(_currentLine + 1); + scriptLine.toUppercase(); + const char *paramP; + const char *commandStr = scriptLine.c_str(); + + if (!strncmp(commandStr, "BACKGROUND", 10)) { + // Set the background + paramP = commandStr + 10; + resetPalette(); + int screenId = getParameter(¶mP); + + SceneInfo *sceneInfo = SceneInfo::init(_vm); + sceneInfo->load(screenId, 0, "", 0, scene._depthSurface, scene._backgroundSurface); + scene._spriteSlots.fullRefresh(); + _redrawFlag = true; + + } else if (!strncmp(commandStr, "GO", 2)) { + _animating = true; + + } else if (!strncmp(commandStr, "PAN", 3)) { + // Set panning values + paramP = commandStr + 3; + int panX = getParameter(¶mP); + int panY = getParameter(¶mP); + int panSpeed = getParameter(¶mP); + + if ((panX != 0) || (panY != 0)) { + _pan = Common::Point(panX, panY); + _panSpeed = panSpeed; + } + + } else if (!strncmp(commandStr, "DRIVER", 6)) { + // Set the driver to use + paramP = commandStr + 7; + + if (!strncmp(paramP, "#SOUND.00", 9)) { + int driverNum = paramP[9] - '0'; + _vm->_sound->init(driverNum); + } + } else if (!strncmp(commandStr, "SOUND", 5)) { + // Set sound number + paramP = commandStr + 5; + int soundId = getParameter(¶mP); + _vm->_sound->command(soundId); + + } else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || + (commandStr[5] == '1'))) { + // Set the text colors + int index = commandStr[5] - '0'; + paramP = commandStr + 6; + + byte r = getParameter(¶mP); + byte g = getParameter(¶mP); + byte b = getParameter(¶mP); + + _vm->_palette->setEntry(5 + index, r, g, b); + + } else if (!strncmp(commandStr, "SPARE", 5)) { + // Sets a secondary background number that can be later switched in with a PAGE command + paramP = commandStr + 6; + int spareIndex = commandStr[5] - '0'; + assert(spareIndex < 4); + int screenId = getParameter(¶mP); + + // Load the spare background + SceneInfo *sceneInfo = SceneInfo::init(_vm); + sceneInfo->_width = MADS_SCREEN_WIDTH; + sceneInfo->_height = MADS_SCENE_HEIGHT; + _spareScreens[spareIndex].setSize(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT); + sceneInfo->loadMadsV1Background(screenId, "", SCENEFLAG_TRANSLATE, + _spareScreens[spareIndex]); + delete sceneInfo; + + } else if (!strncmp(commandStr, "PAGE", 4)) { + // Signals to change to a previous specified secondary background + paramP = commandStr + 4; + int spareIndex = getParameter(¶mP); + + // Only allow background switches if one isn't currently in progress + if (!_spareScreen && _spareScreens[spareIndex].getPixels() != nullptr) { + _spareScreen = &_spareScreens[spareIndex]; + _translationX = 0; + } + + } else { + error("Unknown response command: '%s'", commandStr); + } +} + +int TextView::getParameter(const char **paramP) { + if ((**paramP != '=') && (**paramP != ',')) + return 0; + + int result = 0; + ++*paramP; + while ((**paramP >= '0') && (**paramP <= '9')) { + result = result * 10 + (**paramP - '0'); + ++*paramP; + } + + return result; +} + +void TextView::processText() { + int xStart; + + if (!strcmp(_currentLine, "***")) { + // Special signifier for end of script + _scrollCount = _font->getHeight() * 13; + _lineY = -1; + return; + } + + _lineY = 0; + + // Lines are always centered, except if line contains a '@', in which case the + // '@' marks the position that must be horizontally centered + char *centerP = strchr(_currentLine, '@'); + if (centerP) { + *centerP = '\0'; + xStart = (MADS_SCREEN_WIDTH / 2) - _font->getWidth(_currentLine); + + // Delete the @ character and shift back the remainder of the string + char *p = centerP + 1; + if (*p == ' ') ++p; + strcpy(centerP, p); + + } else { + int lineWidth = _font->getWidth(_currentLine); + xStart = (MADS_SCREEN_WIDTH - lineWidth) / 2; + } + + // Add the new line to the list of pending lines + TextLine tl; + tl._pos = Common::Point(xStart, MADS_SCENE_HEIGHT); + tl._line = _currentLine; + tl._textDisplayIndex = -1; + _textLines.push_back(tl); +} + +void TextView::display() { + FullScreenDialog::display(); +} + +void TextView::resetPalette() { + _vm->_palette->resetGamePalette(8, 8); + _vm->_palette->setEntry(5, 0, 63, 63); + _vm->_palette->setEntry(6, 0, 45, 45); +} + +void TextView::doFrame() { + Scene &scene = _vm->_game->_scene; + if (!_animating) + return; + + // Only update state if wait period has expired + uint32 currTime = g_system->getMillis(); + + // If a screen transition is in progress and it's time for another column, handle it + if (_spareScreen) { + byte *srcP = _spareScreen->getBasePtr(_translationX, 0); + byte *bgP = scene._backgroundSurface.getBasePtr(_translationX, 0); + byte *screenP = (byte *)_vm->_screen.getBasePtr(_translationX, 0); + + for (int y = 0; y < MADS_SCENE_HEIGHT; ++y, srcP += MADS_SCREEN_WIDTH, + bgP += MADS_SCREEN_WIDTH, screenP += MADS_SCREEN_WIDTH) { + *bgP = *srcP; + *screenP = *srcP; + } + + // Flag the column of the screen is modified + _vm->_screen.copyRectToScreen(Common::Rect(_translationX, 0, + _translationX + 1, MADS_SCENE_HEIGHT)); + + // Keep moving the column to copy to the right + if (++_translationX == MADS_SCREEN_WIDTH) { + // Surface transition is complete + _spareScreen = nullptr; + } + } + + // Make sure it's time for an update + if (currTime < _scrollTimeout) + return; + _scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY; + _redrawFlag = true; + + // If any panning values are set, pan the background surface + if ((_pan.x != 0) || (_pan.y != 0)) { + if (_panCountdown > 0) { + --_panCountdown; + return; + } + + // Handle horizontal panning + if (_pan.x != 0) { + byte *lineTemp = new byte[_pan.x]; + for (int y = 0; y < MADS_SCENE_HEIGHT; ++y) { + byte *pixelsP = (byte *)scene._backgroundSurface.getBasePtr(0, y); + + // Copy the first X pixels into temp buffer, move the rest of the line + // to the start of the line, and then move temp buffer pixels to end of line + Common::copy(pixelsP, pixelsP + _pan.x, lineTemp); + Common::copy(pixelsP + _pan.x, pixelsP + MADS_SCREEN_WIDTH, pixelsP); + Common::copy(lineTemp, lineTemp + _pan.x, pixelsP + MADS_SCREEN_WIDTH - _pan.x); + } + + delete[] lineTemp; + } + + // Handle vertical panning + if (_pan.y != 0) { + // Store the bottom Y lines into a temp buffer, move the rest of the lines down, + // and then copy the stored lines back to the top of the screen + byte *linesTemp = new byte[_pan.y * MADS_SCREEN_WIDTH]; + byte *pixelsP = (byte *)scene._backgroundSurface.getBasePtr(0, MADS_SCENE_HEIGHT - _pan.y); + Common::copy(pixelsP, pixelsP + MADS_SCREEN_WIDTH * _pan.y, linesTemp); + + for (int y = MADS_SCENE_HEIGHT - 1; y >= _pan.y; --y) { + byte *destP = (byte *)scene._backgroundSurface.getBasePtr(0, y); + byte *srcP = (byte *)scene._backgroundSurface.getBasePtr(0, y - _pan.y); + Common::copy(srcP, srcP + MADS_SCREEN_WIDTH, destP); + } + + Common::copy(linesTemp, linesTemp + _pan.y * MADS_SCREEN_WIDTH, + (byte *)scene._backgroundSurface.getPixels()); + delete[] linesTemp; + } + + // Flag for a full screen refresh + scene._spriteSlots.fullRefresh(); + } + + // Scroll all active text lines up + for (int i = _textLines.size() - 1; i >= 0; --i) { + TextLine &tl = _textLines[i]; + if (tl._textDisplayIndex != -1) + // Expire the text line that's already on-screen + scene._textDisplay.expire(tl._textDisplayIndex); + + tl._pos.y--; + if (tl._pos.y < 0) { + _textLines.remove_at(i); + } else { + tl._textDisplayIndex = scene._textDisplay.add(tl._pos.x, tl._pos.y, + 0x605, -1, tl._line, _font); + } + } + + if (_scrollCount > 0) { + // Handling final scrolling of text off of screen + if (--_scrollCount == 0) { + scriptDone(); + return; + } + } else { + // Handling a text row + if (++_lineY == (_font->getHeight() + TEXTVIEW_LINE_SPACING)) + processLines(); + } +} + +void TextView::scriptDone() { + _breakFlag = true; + _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; +} + +/*------------------------------------------------------------------------*/ + +char AnimationView::_resourceName[100]; + +void AnimationView::execute(MADSEngine *vm, const Common::String &resName) { + assert(resName.size() < 100); + Common::strlcpy(_resourceName, resName.c_str(), sizeof(_resourceName)); + vm->_dialogs->_pendingDialog = DIALOG_ANIMVIEW; +} + +AnimationView::AnimationView(MADSEngine *vm) : MenuView(vm) { + _redrawFlag = false; + + _soundDriverLoaded = false; + _previousUpdate = 0; + _screenId = -1; + _resetPalette = false; + _resyncMode = NEVER; + _v1 = 0; + _v2 = -1; + _resourceIndex = -1; + _currentAnimation = nullptr; + _sfx = 0; + _soundFlag = _bgLoadFlag = true; + _showWhiteBars = true; + _manualFrameNumber = 0; + _manualSpriteSet = nullptr; + _manualStartFrame = _manualEndFrame = 0; + _manualFrame2 = 0; + _animFrameNumber = 0; + _nextCyclingActive = false; + _sceneInfo = SceneInfo::init(_vm); + + load(); +} + +AnimationView::~AnimationView() { + // Turn off palette cycling as well as any playing sound + Scene &scene = _vm->_game->_scene; + scene._cyclingActive = false; + _vm->_sound->stop(); + _vm->_audio->stop(); + + // Delete data + delete _currentAnimation; + delete _sceneInfo; +} + +void AnimationView::load() { + Common::String resName(_resourceName); + if (!resName.hasSuffix(".")) + resName += ".res"; + + _filename = resName; + if (!_script.open(resName)) + error("Could not open resource %s", resName.c_str()); + + processLines(); +} + +void AnimationView::display() { + Scene &scene = _vm->_game->_scene; + _vm->_palette->initPalette(); + Common::fill(&_vm->_palette->_cyclingPalette[0], &_vm->_palette->_cyclingPalette[PALETTE_SIZE], 0); + + _vm->_palette->resetGamePalette(1, 8); + scene._spriteSlots.reset(); + scene._paletteCycles.clear(); + + MenuView::display(); +} + +bool AnimationView::onEvent(Common::Event &event) { + // Wait for the Escape key or a mouse press + if (((event.type == Common::EVENT_KEYDOWN) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) || + (event.type == Common::EVENT_LBUTTONUP)) { + scriptDone(); + return true; + } + + return false; +} + +void AnimationView::doFrame() { + Scene &scene = _vm->_game->_scene; + + if (_resourceIndex == -1 || _currentAnimation->freeFlag()) { + if (++_resourceIndex == (int)_resources.size()) { + scriptDone(); + } else { + scene._frameStartTime = 0; + loadNextResource(); + } + } else if (_currentAnimation->getCurrentFrame() == 1) { + scene._cyclingActive = _nextCyclingActive; + } + + if (_currentAnimation) { + ++scene._frameStartTime; + _currentAnimation->update(); + _redrawFlag = true; + } +} + +void AnimationView::loadNextResource() { + Scene &scene = _vm->_game->_scene; + Palette &palette = *_vm->_palette; + ResourceEntry &resEntry = _resources[_resourceIndex]; + Common::Array<PaletteCycle> paletteCycles; + + if (resEntry._bgFlag) + palette.resetGamePalette(1, 8); + + palette._mainPalette[253 * 3] = palette._mainPalette[253 * 3 + 1] + = palette._mainPalette[253 * 3 + 2] = 0xb4; + palette.setPalette(&palette._mainPalette[253 * 3], 253, 1); + + // Free any previous messages + scene._kernelMessages.reset(); + + // Handle the bars at the top/bottom + if (resEntry._showWhiteBars) { + // For animations the screen has been clipped to the middle 156 rows. + // So although it's slightly messy, bypass our screen class entirely, + // and draw the horizontal lines directly on the physiacl screen surface + Graphics::Surface *s = g_system->lockScreen(); + s->hLine(0, 20, MADS_SCREEN_WIDTH, 253); + s->hLine(0, 179, MADS_SCREEN_WIDTH, 253); + g_system->unlockScreen(); + } + + // Load the new animation + delete _currentAnimation; + _currentAnimation = Animation::init(_vm, &scene); + int flags = ANIMFLAG_ANIMVIEW | (resEntry._bgFlag ? ANIMFLAG_LOAD_BACKGROUND : 0); + _currentAnimation->load(scene._backgroundSurface, scene._depthSurface, + resEntry._resourceName, flags, &paletteCycles, _sceneInfo); + + // Signal for a screen refresh + scene._spriteSlots.fullRefresh(); + + // If a sound driver has been specified, then load the correct one + if (!_currentAnimation->_header._soundName.empty()) { + const char *chP = strchr(_currentAnimation->_header._soundName.c_str(), '.'); + assert(chP); + + // Handle both Rex naming (xxx.009) and naming in later games (e.g. xxx.ph9) + int driverNum = atoi(chP + 3); + // HACK for Dragon + if (_currentAnimation->_header._soundName == "#SOUND.DRG") + driverNum = 9; + _vm->_sound->init(driverNum); + } + + // Handle any manual setup + if (_currentAnimation->_header._manualFlag) { + _manualFrameNumber = _currentAnimation->_header._spritesIndex; + _manualSpriteSet = _currentAnimation->getSpriteSet(_manualFrameNumber); + } + + // Set the sound data for the animation + _vm->_sound->setEnabled(resEntry._soundFlag); + + Common::String dsrName = _currentAnimation->_header._dsrName; + if (!dsrName.empty()) + _vm->_audio->setSoundGroup(dsrName); + + // Start the new animation + _currentAnimation->startAnimation(0); + + // Handle the palette and cycling palette + scene._cyclingActive = false; + Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE], + &palette._cyclingPalette[0]); + + _vm->_game->_fx = (ScreenTransition)resEntry._fx; + _nextCyclingActive = paletteCycles.size() > 0; + if (!_vm->_game->_fx) { + palette.setFullPalette(palette._mainPalette); + } + + scene.initPaletteAnimation(paletteCycles, _nextCyclingActive && !_vm->_game->_fx); +} + +void AnimationView::scriptDone() { + _breakFlag = true; + _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; +} + +void AnimationView::processLines() { + if (_script.eos()) { + // end of script, end animation + scriptDone(); + return; + } + + while (!_script.eos()) { + // Get in next line + _currentLine.clear(); + char c; + while (!_script.eos() && (c = _script.readByte()) != '\n') { + if (c != '\r' && c != '\0') + _currentLine += c; + } + + // Process the line + while (!_currentLine.empty()) { + if (_currentLine.hasPrefix("-")) { + _currentLine.deleteChar(0); + + processCommand(); + } else { + // Get resource name + Common::String resName; + while (!_currentLine.empty() && (c = _currentLine[0]) != ' ') { + _currentLine.deleteChar(0); + resName += c; + } + + // Add resource into list along with any set state information + _resources.push_back(ResourceEntry(resName, _sfx, _soundFlag, + _bgLoadFlag, _showWhiteBars)); + + // Fx resets between resource entries + _sfx = 0; + } + + // Skip any spaces + while (_currentLine.hasPrefix(" ")) + _currentLine.deleteChar(0); + } + } +} + +void AnimationView::processCommand() { + // Get the command character + char commandChar = toupper(_currentLine[0]); + _currentLine.deleteChar(0); + + // Handle the command + switch (commandChar) { + case 'B': + _soundFlag = !_soundFlag; + break; + case 'H': + // -h[:ex] Disable EMS / XMS high memory support + if (_currentLine.hasPrefix(":")) + _currentLine.deleteChar(0); + while (_currentLine.hasPrefix("e") || _currentLine.hasPrefix("x")) + _currentLine.deleteChar(0); + break; + case 'O': + // -o:xxx Specify opening special effect + assert(_currentLine[0] == ':'); + _currentLine.deleteChar(0); + _sfx = getParameter(); + break; + case 'P': + // Switch to CONCAT mode, which is ignored anyway + break; + case 'R': { + // Resynch timer (always, beginning, never) + assert(_currentLine[0] == ':'); + _currentLine.deleteChar(0); + + char v = toupper(_currentLine[0]); + _currentLine.deleteChar(0); + if (v == 'N') + _resyncMode = NEVER; + else if (v == 'A') + _resyncMode = ALWAYS; + else if (v == 'B') + _resyncMode = BEGINNING; + else + error("Unknown parameter"); + break; + } + case 'W': + // Switch white bars being visible + _showWhiteBars = !_showWhiteBars; + break; + case 'X': + // Exit after animation finishes. Ignore + break; + case 'D': + // Unimplemented and ignored in the original. Ignore as well + break; + case 'Y': + // Reset palette on startup + _resetPalette = true; + break; + default: + error("Unknown command char: '%c'", commandChar); + } +} + +int AnimationView::getParameter() { + int result = 0; + + while (!_currentLine.empty()) { + char c = _currentLine[0]; + + if (c >= '0' && c <= '9') { + _currentLine.deleteChar(0); + result = result * 10 + (c - '0'); + } else { + break; + } + } + + return result; +} + +} // End of namespace MADS diff --git a/engines/mads/menu_views.h b/engines/mads/menu_views.h new file mode 100644 index 0000000000..cc5a13006f --- /dev/null +++ b/engines/mads/menu_views.h @@ -0,0 +1,225 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MADS_MENU_VIEWS_H +#define MADS_MENU_VIEWS_H + +#include "common/scummsys.h" +#include "mads/dialogs.h" +#include "mads/game.h" +#include "mads/msurface.h" + +namespace MADS { + +class MADSEngine; + +class MenuView: public FullScreenDialog { +protected: + bool _breakFlag; + bool _redrawFlag; + Common::String _filename; + + virtual void doFrame() = 0; + + virtual void display(); + + /** + * Event handler + */ + virtual bool onEvent(Common::Event &event); +public: + MenuView(MADSEngine *vm); + + virtual ~MenuView() {} + + virtual void show(); + + Common::String getResourceName(); +}; + +struct TextLine { + Common::Point _pos; + Common::String _line; + int _textDisplayIndex; +}; + +/** + * Scrolling text view + */ +class TextView : public MenuView { +private: + static char _resourceName[100]; + + bool _animating; + Common::Array<TextLine> _textLines; + Common::Point _pan; + int _panSpeed; + MSurface _spareScreens[4]; + int _scrollCount; + int _lineY; + uint32 _scrollTimeout; + int _panCountdown; + int _translationX; + Common::File _script; + char _currentLine[80]; + MSurface *_spareScreen; + Font *_font; +private: + /** + * Load the text resource + */ + void load(); + + /** + * Process the lines + */ + void processLines(); + + /** + * Process a command from the script file + */ + void processCommand(); + + /** + * Process text from the script file + */ + void processText(); + + /** + * Get a parameter from line + */ + int getParameter(const char **paramP); + + /** + * Reset the game palette + */ + void resetPalette(); +protected: + virtual void display(); + + virtual void doFrame(); + + /** + * Called when the script is finished + */ + virtual void scriptDone(); +public: + /** + * Queue the given text resource for display + */ + static void execute(MADSEngine *vm, const Common::String &resName); + + TextView(MADSEngine *vm); + + virtual ~TextView(); +}; + +enum ResyncMode { NEVER, ALWAYS, BEGINNING }; + +struct ResourceEntry { + Common::String _resourceName; + int _fx; + bool _soundFlag; + bool _bgFlag; + bool _showWhiteBars; + + ResourceEntry() {} + ResourceEntry(const Common::String &resName, int fx, bool soundFlag, + bool bgFlag, bool showWhiteBars) { + _resourceName = resName; + _fx = fx; + _soundFlag = soundFlag; + _bgFlag = bgFlag; + _showWhiteBars = showWhiteBars; + } +}; + +struct ResIndexEntry { + int _id; + int _v; + Common::String _resourceName; + + ResIndexEntry() {} +}; + +/** +* Animation cutscene view +*/ +class AnimationView : public MenuView { +private: + static char _resourceName[100]; + + Common::File _script; + uint32 _previousUpdate; + Common::String _currentLine; + bool _soundDriverLoaded; + bool _resetPalette; + ResyncMode _resyncMode; + int _sfx; + bool _soundFlag; + bool _bgLoadFlag; + bool _showWhiteBars; + Common::Array<ResourceEntry> _resources; + Common::Array<ResIndexEntry> _resIndex; + int _v1; + int _v2; + int _resourceIndex; + SceneInfo *_sceneInfo; + Animation *_currentAnimation; + int _manualFrameNumber; + SpriteAsset *_manualSpriteSet; + int _manualStartFrame, _manualEndFrame; + int _manualFrame2; + int _animFrameNumber; + bool _nextCyclingActive; +private: + void load(); + + void processLines(); + + void processCommand(); + + int getParameter(); + + void loadNextResource(); +protected: + virtual void display(); + + virtual void doFrame(); + + virtual bool onEvent(Common::Event &event); + + virtual void scriptDone(); +public: + /** + * Queue the given text resource for display + */ + static void execute(MADSEngine *vm, const Common::String &resName); + + AnimationView(MADSEngine *vm); + + virtual ~AnimationView(); +}; + +} // End of namespace MADS + +#endif /* MADS_MENU_VIEWS_H */ diff --git a/engines/mads/messages.cpp b/engines/mads/messages.cpp index d41696044b..e83b69d210 100644 --- a/engines/mads/messages.cpp +++ b/engines/mads/messages.cpp @@ -546,8 +546,7 @@ void TextDisplayList::draw(MSurface *s) { for (uint idx = 0; idx < size(); ++idx) { TextDisplay &td = (*this)[idx]; if (td._active && (td._expire >= 0)) { - Common::Point destPos(td._bounds.left + _vm->_screen._offset.x, - td._bounds.top + _vm->_screen._offset.y); + Common::Point destPos(td._bounds.left, td._bounds.top); td._font->setColors(0xFF, td._color1, td._color2, 0); td._font->writeString(s, td._msg, destPos, td._spacing, td._bounds.width()); } diff --git a/engines/mads/module.mk b/engines/mads/module.mk index 96353e9ae5..fc04a2f8ba 100644 --- a/engines/mads/module.mk +++ b/engines/mads/module.mk @@ -35,6 +35,7 @@ MODULE_OBJS := \ hotspots.o \ inventory.o \ mads.o \ + menu_views.o \ messages.o \ msurface.o \ palette.o \ diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp index 349f4a5f23..39824bac4b 100644 --- a/engines/mads/msurface.cpp +++ b/engines/mads/msurface.cpp @@ -87,7 +87,6 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo // rectangle is always 0, 0 assert(clipRect.top == 0 && clipRect.left == 0); - // TODO: Put err* and scaled* into SpriteInfo int errX = info.hotX * info.scaleX % 100; int errY = info.hotY * info.scaleY % 100; int scaledWidth = scaleValue(info.width, info.scaleX, errX); @@ -160,7 +159,6 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo if (status == kStatusDraw && clipY == 0) { // Draw previously scaled line - // TODO Implement different drawing types (depth, shadow etc.) byte *tempDst = dst; for (int lineX = 0; lineX < scaledWidth; lineX++) { byte pixel = scaledLineBuf[lineX]; @@ -186,8 +184,6 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo } dst += pitch; heightAmt--; - // TODO depth etc. - //depthAddress += Destination -> Width; errY += 100; if (errY >= 0) @@ -266,11 +262,11 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, int highestDim = MAX(frameWidth, frameHeight); bool lineDist[MADS_SCREEN_WIDTH]; - int distIndex = 0; int distXCount = 0, distYCount = 0; if (scale != -1) { int distCtr = 0; + int distIndex = 0; do { distCtr += scale; if (distCtr < 100) { @@ -356,9 +352,10 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, if (widthAmount > 0) spriteWidth -= widthAmount; - int spriteRight = spriteLeft + spriteWidth; if (spriteWidth <= 0) return; + + int spriteRight = spriteLeft + spriteWidth; if (flipped) { destX += distXCount - 1; spriteLeft = -(distXCount - spriteRight); diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h index cd6bde1271..ebfb1f437a 100644 --- a/engines/mads/msurface.h +++ b/engines/mads/msurface.h @@ -51,10 +51,9 @@ struct SpriteInfo { * MADS graphics surface */ class MSurface : public Graphics::Surface { -private: - bool _freeFlag; protected: static MADSEngine *_vm; + bool _freeFlag; public: /** * Sets the engine refrence used all surfaces @@ -65,6 +64,11 @@ public: * Helper method for calculating new dimensions when scaling a sprite */ static int scaleValue(int value, int scale, int err); + + /** + * Base method for descendents to load their contents + */ + virtual void load(const Common::String &resName) {} public: /** * Basic constructor diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp index f3eddc3fbb..f5355517bd 100644 --- a/engines/mads/nebular/dialogs_nebular.cpp +++ b/engines/mads/nebular/dialogs_nebular.cpp @@ -313,6 +313,18 @@ void DialogsNebular::showDialog() { delete dlg; break; } + case DIALOG_TEXTVIEW: { + TextView *dlg = new RexTextView(_vm); + dlg->show(); + delete dlg; + break; + } + case DIALOG_ANIMVIEW: { + AnimationView *dlg = new RexAnimationView(_vm); + dlg->show(); + delete dlg; + break; + } default: break; } @@ -334,7 +346,6 @@ void DialogsNebular::showScummVMSaveDialog() { } scene->_spriteSlots.reset(); - _vm->_screen._offset.y = 0; scene->loadScene(scene->_currentSceneId, game._aaName, true); scene->_userInterface.noInventoryAnim(); game._scene.drawElements(kTransitionFadeIn, false); @@ -542,60 +553,6 @@ void PictureDialog::restore() { /*------------------------------------------------------------------------*/ -FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) { - _screenId = 990; - _palFlag = true; -} - -FullScreenDialog::~FullScreenDialog() { - _vm->_screen._offset.y = 0; -} - -void FullScreenDialog::display() { - Game &game = *_vm->_game; - Scene &scene = game._scene; - - int nextSceneId = scene._nextSceneId; - int currentSceneId = scene._currentSceneId; - int priorSceneId = scene._priorSceneId; - - scene.loadScene(_screenId, game._aaName, _palFlag); - - scene._priorSceneId = priorSceneId; - scene._currentSceneId = currentSceneId; - scene._nextSceneId = nextSceneId; - - _vm->_screen._offset.y = 22; - _vm->_events->initVars(); - game._kernelMode = KERNEL_ROOM_INIT; - - byte pal[768]; - if (_vm->_screenFade) { - Common::fill(&pal[0], &pal[PALETTE_SIZE], 0); - _vm->_palette->setFullPalette(pal); - } else { - _vm->_palette->getFullPalette(pal); - _vm->_palette->fadeOut(pal, nullptr, 0, PALETTE_COUNT, 0, 1, 1, 16); - } - - _vm->_screen.empty(); - _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2); - _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2); - game._scene._spriteSlots.fullRefresh(); - - game._fx = _vm->_screenFade == SCREEN_FADE_SMOOTH ? kTransitionFadeIn : kCenterVertTransition; - game._trigger = 0; - - _vm->_palette->setEntry(10, 0, 63, 0); - _vm->_palette->setEntry(11, 0, 45, 0); - _vm->_palette->setEntry(12, 63, 63, 0); - _vm->_palette->setEntry(13, 45, 45, 0); - _vm->_palette->setEntry(14, 63, 63, 63); - _vm->_palette->setEntry(15, 45, 45, 45); -} - -/*------------------------------------------------------------------------*/ - GameDialog::DialogLine::DialogLine() { _active = true; _state = DLGSTATE_UNSELECTED; @@ -637,6 +594,9 @@ GameDialog::GameDialog(MADSEngine *vm) : FullScreenDialog(vm) { _vm->_events->waitCursor(); scene.clearVocab(); scene._dynamicHotspots.clear(); + // Clear scene sprites and objects + scene._spriteSlots.reset(); + _vm->_game->_screenObjects.clear(); _vm->_dialogs->_defaultPosition = Common::Point(-1, -1); _menuSpritesIndex = 0; } @@ -644,6 +604,14 @@ GameDialog::GameDialog(MADSEngine *vm) : FullScreenDialog(vm) { void GameDialog::display() { FullScreenDialog::display(); + Palette &palette = *_vm->_palette; + palette.setEntry(10, 0, 63, 0); + palette.setEntry(11, 0, 45, 0); + palette.setEntry(12, 63, 63, 0); + palette.setEntry(13, 45, 45, 0); + palette.setEntry(14, 63, 63, 63); + palette.setEntry(15, 45, 45, 45); + Scene &scene = _vm->_game->_scene; SpriteAsset *menuSprites = new SpriteAsset(_vm, "*MENU", 0); _menuSpritesIndex = scene._sprites.add(menuSprites); @@ -655,7 +623,7 @@ void GameDialog::display() { } GameDialog::~GameDialog() { - _vm->_screen._offset.y = 0; + _vm->_screen.resetClipBounds(); } void GameDialog::clearLines() { @@ -856,10 +824,11 @@ void GameDialog::handleEvents() { _vm->_events->pollEvents(); // Scan for objects in the dialog - int objIndex = screenObjects.scan(events.currentPos() - _vm->_screen._offset, LAYER_GUI); + Common::Point mousePos = events.currentPos() - Common::Point(0, DIALOG_TOP); + int objIndex = screenObjects.scan(mousePos, LAYER_GUI); if (_movedFlag) { - int yp = events.currentPos().y - _vm->_screen._offset.y; + int yp = mousePos.y; if (yp < screenObjects[1]._bounds.top) { if (!events._mouseReleased) _lines[1]._state = DLGSTATE_SELECTED; @@ -939,7 +908,7 @@ void GameDialog::refreshText() { } if (!skipFlag) { - _lines[i]._textDisplayIndex = scene._textDisplay.add(_lines[i]._pos.x, _lines[i]._pos.y, + _lines[i]._textDisplayIndex = scene._textDisplay.add(_lines[i]._pos.x, _lines[i]._pos.y, fontColor, _lines[i]._widthAdjust, _lines[i]._msg, _lines[i]._font); } } @@ -950,6 +919,7 @@ void GameDialog::refreshText() { DifficultyDialog::DifficultyDialog(MADSEngine *vm) : GameDialog(vm) { setLines(); + _vm->_palette->resetGamePalette(18, 10); } void DifficultyDialog::setLines() { @@ -1094,6 +1064,14 @@ void OptionsDialog::display() { void OptionsDialog::show() { Nebular::GameNebular &game = *(Nebular::GameNebular *)_vm->_game; + + // Previous options, restored when cancel is selected + bool prevEasyMouse = _vm->_easyMouse; + bool prevInvObjectsAnimated = _vm->_invObjectsAnimated; + bool prevTextWindowStill = _vm->_textWindowStill; + ScreenFade prevScreenFade = _vm->_screenFade; + StoryMode prevStoryMode = game._storyMode; + do { _selectedLine = 0; GameDialog::show(); @@ -1138,10 +1116,15 @@ void OptionsDialog::show() { switch (_selectedLine) { case 8: // Done - // TODO: Copy from temporary config + // New options will be applied break; case 9: // Cancel - // TODO: Ignore all changes to temporary config + // Revert all options from the saved ones + _vm->_easyMouse = prevEasyMouse; + _vm->_invObjectsAnimated = prevInvObjectsAnimated; + _vm->_textWindowStill = prevTextWindowStill; + _vm->_screenFade = prevScreenFade; + game._storyMode = prevStoryMode; break; default: break; diff --git a/engines/mads/nebular/dialogs_nebular.h b/engines/mads/nebular/dialogs_nebular.h index 1468db38c8..5dbe4da6f0 100644 --- a/engines/mads/nebular/dialogs_nebular.h +++ b/engines/mads/nebular/dialogs_nebular.h @@ -107,36 +107,6 @@ enum DialogTextAlign { ALIGN_NONE = 0, ALIGN_CENTER = -1, ALIGN_AT_CENTER = -2, enum DialogState { DLGSTATE_UNSELECTED = 0, DLGSTATE_SELECTED = 1, DLGSTATE_FOCUSED = 2 }; -class FullScreenDialog: public EventTarget { -protected: - /** - * Engine reference - */ - MADSEngine *_vm; - - /** - * Screen/scene to show background from - */ - int _screenId; - - /** - * Flag for palette initialization - */ - bool _palFlag; - - /** - * Handles displaying the screen background and dialog - */ - virtual void display(); -public: - /** - * Constructor - */ - FullScreenDialog(MADSEngine *vm); - - virtual ~FullScreenDialog(); -}; - class GameDialog: public FullScreenDialog { struct DialogLine { bool _active; @@ -146,7 +116,7 @@ class GameDialog: public FullScreenDialog { Common::String _msg; Font *_font; int _widthAdjust; - + DialogLine(); DialogLine(const Common::String &s); }; @@ -160,7 +130,7 @@ protected: int _menuSpritesIndex; int _lineIndex; int _textLineCount; - + /** * Display the dialog */ diff --git a/engines/mads/nebular/game_nebular.cpp b/engines/mads/nebular/game_nebular.cpp index 902f42507a..fd669bc5cf 100644 --- a/engines/mads/nebular/game_nebular.cpp +++ b/engines/mads/nebular/game_nebular.cpp @@ -27,6 +27,7 @@ #include "mads/game.h" #include "mads/screen.h" #include "mads/msurface.h" +#include "mads/menu_views.h" #include "mads/nebular/game_nebular.h" #include "mads/nebular/dialogs_nebular.h" #include "mads/nebular/globals_nebular.h" @@ -309,6 +310,31 @@ void GameNebular::setSectionHandler() { } void GameNebular::checkShowDialog() { + // Handling to start endgame sequences if the win/lose type has been set + switch (_winStatus) { + case 1: + // No shields failure ending + AnimationView::execute(_vm, "rexend1"); + break; + case 2: + // Shields, but no targetting failure ending + AnimationView::execute(_vm, "rexend2"); + break; + case 3: + // Completed game successfully, so activate quotes item on the main menu + ConfMan.setBool("ShowQuotes", true); + ConfMan.flushToDisk(); + + AnimationView::execute(_vm, "rexend3"); + break; + case 4: + // Decompression ending + TextView::execute(_vm, "ending4"); + break; + } + _winStatus = 0; + + // Loop for showing dialogs, if any need to be shown if (_vm->_dialogs->_pendingDialog && _player._stepEnabled && !_globals[kCopyProtectFailed]) { _player.releasePlayerSprites(); @@ -599,7 +625,7 @@ void GameNebular::doObjectAction() { _objects.addToInventory(OBJ_DURAFAIL_CELLS); if (_difficulty == DIFFICULTY_HARD) { dialogs.showItem(OBJ_DURAFAIL_CELLS, 416); - } + } _globals[kHandsetCellStatus] = 0; break; case 3: diff --git a/engines/mads/nebular/game_nebular.h b/engines/mads/nebular/game_nebular.h index da607d47ee..efa21a2e73 100644 --- a/engines/mads/nebular/game_nebular.h +++ b/engines/mads/nebular/game_nebular.h @@ -133,18 +133,16 @@ public: virtual void synchronize(Common::Serializer &s, bool phase1); }; - +// Section handlers aren't needed in ScummVM implementation class Section1Handler : public SectionHandler { public: Section1Handler(MADSEngine *vm) : SectionHandler(vm) {} - // TODO: Properly implement handler methods virtual void preLoadSection() {} virtual void sectionPtr2() {} virtual void postLoadSection() {} }; -// TODO: Properly implement handler classes typedef Section1Handler Section2Handler; typedef Section1Handler Section3Handler; typedef Section1Handler Section4Handler; diff --git a/engines/mads/nebular/menu_nebular.cpp b/engines/mads/nebular/menu_nebular.cpp index 98eb669616..28de4e5650 100644 --- a/engines/mads/nebular/menu_nebular.cpp +++ b/engines/mads/nebular/menu_nebular.cpp @@ -21,9 +21,12 @@ */ #include "common/scummsys.h" +#include "common/config-manager.h" #include "mads/game.h" #include "mads/mads.h" +#include "mads/menu_views.h" #include "mads/resources.h" +#include "mads/scene.h" #include "mads/screen.h" #include "mads/nebular/menu_nebular.h" @@ -35,44 +38,6 @@ namespace Nebular { #define MADS_MENU_Y ((MADS_SCREEN_HEIGHT - MADS_SCENE_HEIGHT) / 2) #define MADS_MENU_ANIM_DELAY 70 -MenuView::MenuView(MADSEngine *vm) : FullScreenDialog(vm) { - _breakFlag = false; - _redrawFlag = true; - _palFlag = false; -} - -void MenuView::show() { - Scene &scene = _vm->_game->_scene; - EventsManager &events = *_vm->_events; - display(); - - events.setEventTarget(this); - events.hideCursor(); - - while (!_breakFlag && !_vm->shouldQuit()) { - if (_redrawFlag) { - scene.drawElements(_vm->_game->_fx, _vm->_game->_fx); - - _vm->_screen.copyRectToScreen(Common::Rect(0, 0, 320, 200)); - _redrawFlag = false; - } - - _vm->_events->waitForNextFrame(); - _vm->_game->_fx = kTransitionNone; - doFrame(); - } - - events.setEventTarget(nullptr); -} - -void MenuView::display() { - _vm->_palette->resetGamePalette(4, 8); - - FullScreenDialog::display(); -} - -/*------------------------------------------------------------------------*/ - MainMenu::MainMenu(MADSEngine *vm): MenuView(vm) { Common::fill(&_menuItems[0], &_menuItems[7], (SpriteAsset *)nullptr); Common::fill(&_menuItemIndexes[0], &_menuItemIndexes[7], -1); @@ -83,12 +48,23 @@ MainMenu::MainMenu(MADSEngine *vm): MenuView(vm) { _highlightedIndex = -1; _selectedIndex = -1; _buttonDown = false; - + for (int i = 0; i < 7; ++i) _menuItems[i] = nullptr; } MainMenu::~MainMenu() { + Scene &scene = _vm->_game->_scene; + for (int i = 0; i < 7; ++i) { + if (_menuItemIndexes[i] != -1) + scene._sprites.remove(_menuItemIndexes[i]); + } + + scene._spriteSlots.reset(); +} + +bool MainMenu::shouldShowQuotes() { + return ConfMan.hasKey("ShowQuotes") && ConfMan.getBool("ShowQuotes"); } void MainMenu::display() { @@ -107,10 +83,10 @@ void MainMenu::display() { // Register the menu item area in the screen objects MSprite *frame0 = _menuItems[i]->getFrame(0); Common::Point pt(frame0->_offset.x - (frame0->w / 2), - frame0->_offset.y - frame0->h + _vm->_screen._offset.y); + frame0->_offset.y - frame0->h); screenObjects.add( - Common::Rect(pt.x, pt.y, pt.x + frame0->w, pt.y + frame0->h), - LAYER_GUI, CAT_COMMAND, i); + Common::Rect(pt.x, pt.y + DIALOG_TOP, pt.x + frame0->w, + pt.y + frame0->h + DIALOG_TOP), LAYER_GUI, CAT_COMMAND, i); } // Set the cursor for when it's shown @@ -130,6 +106,9 @@ void MainMenu::doFrame() { handleAction((MADSGameAction)_selectedIndex); } else { for (_menuItemIndex = 0; _menuItemIndex < 6; ++_menuItemIndex) { + if (_menuItemIndex == 4 && !shouldShowQuotes()) + continue; + if (_menuItemIndex != _selectedIndex) { addSpriteSlot(); } @@ -147,8 +126,11 @@ void MainMenu::doFrame() { // If the user has chosen to skip the animation, show the full menu immediately if (_skipFlag && _menuItemIndex >= 0) { - // Quickly loop through all the menu items to display each's final frame + // Quickly loop through all the menu items to display each's final frame for (; _menuItemIndex < 6; ++_menuItemIndex) { + if (_menuItemIndex == 4 && !shouldShowQuotes()) + continue; + // Draw the final frame of the menuitem _frameIndex = 0; addSpriteSlot(); @@ -158,9 +140,12 @@ void MainMenu::doFrame() { } else { if ((_menuItemIndex == -1) || (_frameIndex == 0)) { if (++_menuItemIndex == 6) { + // Reached end of display animation _vm->_events->showCursor(); return; + } else if (_menuItemIndex == 4 && !shouldShowQuotes()) { + ++_menuItemIndex; } _frameIndex = _menuItems[_menuItemIndex]->getCount() - 1; @@ -176,7 +161,7 @@ void MainMenu::doFrame() { void MainMenu::addSpriteSlot() { Scene &scene = _vm->_game->_scene; SpriteSlots &spriteSlots = scene._spriteSlots; - + int seqIndex = (_menuItemIndex < 6) ? _menuItemIndex : _frameIndex; spriteSlots.deleteTimer(seqIndex); @@ -270,7 +255,7 @@ bool MainMenu::onEvent(Common::Event &event) { } return true; - case Common::EVENT_MOUSEMOVE: + case Common::EVENT_MOUSEMOVE: if (_buttonDown) { int menuIndex = getHighlightedItem(event.mouse); if (menuIndex != _highlightedIndex) { @@ -302,7 +287,7 @@ bool MainMenu::onEvent(Common::Event &event) { default: break; } - + return false; } @@ -332,13 +317,13 @@ void MainMenu::handleAction(MADSGameAction action) { break; case RESUME_GAME: - // The original resumed the most recently saved game. Instead, + // The original resumed the most recently saved game. Instead, // just show the load game scren _vm->_dialogs->_pendingDialog = DIALOG_RESTORE; return; case SHOW_INTRO: - AnimationView::execute(_vm, "@rexopen"); + AnimationView::execute(_vm, "rexopen"); break; case CREDITS: @@ -369,12 +354,14 @@ void AdvertView::show() { uint32 expiryTime = g_system->getMillis() + 10 * 1000; _vm->_palette->resetGamePalette(4, 8); - + // Load the advert background onto the screen SceneInfo *sceneInfo = SceneInfo::init(_vm); sceneInfo->load(screenId, 0, Common::String(), 0, _vm->_game->_scene._depthSurface, _vm->_screen); _vm->_screen.copyRectToScreen(_vm->_screen.getBounds()); + _vm->_palette->setFullPalette(_vm->_palette->_mainPalette); + delete sceneInfo; EventsManager &events = *_vm->_events; @@ -390,6 +377,7 @@ void AdvertView::show() { events.setEventTarget(nullptr); _vm->quitGame(); + events.pollEvents(); } bool AdvertView::onEvent(Common::Event &event) { @@ -403,375 +391,16 @@ bool AdvertView::onEvent(Common::Event &event) { /*------------------------------------------------------------------------*/ -char TextView::_resourceName[100]; -#define TEXTVIEW_LINE_SPACING 2 -#define TEXT_ANIMATION_DELAY 100 -#define TV_NUM_FADE_STEPS 40 -#define TV_FADE_DELAY_MILLI 50 - -void TextView::execute(MADSEngine *vm, const Common::String &resName) { - assert(resName.size() < 100); - strncpy(_resourceName, resName.c_str(), sizeof(_resourceName)); - vm->_dialogs->_pendingDialog = DIALOG_TEXTVIEW; -} - -TextView::TextView(MADSEngine *vm) : MenuView(vm), - _textSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT + _vm->_font->getHeight()) { - _animating = false; - _panSpeed = 0; - Common::fill(&_spareScreens[0], &_spareScreens[10], 0); - _spareScreen = nullptr; - _scrollCount = 0; - _lineY = -1; - _scrollTimeout = 0; - _panCountdown = 0; - _translationX = 0; -} - -TextView::~TextView() { - delete _spareScreen; -} - -void TextView::load() { - if (!_script.open(_resourceName)) - error("Could not open resource %s", _resourceName); - - processLines(); -} - -void TextView::processLines() { - if (_script.eos()) - error("Attempted to read past end of response file"); - - while (!_script.eos()) { - _script.readLine(_currentLine, 79); - - // Commented out line, so go loop for another - if (_currentLine[0] == '#') - continue; - - // Process the line - char *cStart = strchr(_currentLine, '['); - if (cStart) { - while (cStart) { - // Loop for possible multiple commands on one line - char *cEnd = strchr(_currentLine, ']'); - if (!cEnd) - error("Unterminated command '%s' in response file", _currentLine); - - *cEnd = '\0'; - processCommand(); - - // Copy rest of line (if any) to start of buffer - strncpy(_currentLine, cEnd + 1, sizeof(_currentLine)); - - cStart = strchr(_currentLine, '['); - } - - if (_currentLine[0]) { - processText(); - break; - } - - } else { - processText(); - break; - } - } -} - -void TextView::processCommand() { - Scene &scene = _vm->_game->_scene; - Common::String scriptLine(_currentLine + 1); - scriptLine.toUppercase(); - const char *paramP; - const char *commandStr = scriptLine.c_str(); - - if (!strncmp(commandStr, "BACKGROUND", 10)) { - // Set the background - paramP = commandStr + 10; - int screenId = getParameter(¶mP); - - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->load(screenId, 0, Common::String(), 0, scene._depthSurface, - scene._backgroundSurface); - - } else if (!strncmp(commandStr, "GO", 2)) { - _animating = true; - - // Grab what the final palete will be - byte destPalette[PALETTE_SIZE]; - _vm->_palette->grabPalette(destPalette, 0, 256); - - // Copy the loaded background, if any, to the view surface - //int yp = 22; - //scene._backgroundSurface.copyTo(this, 0, 22); - - // Handle fade-in - //byte srcPalette[768]; - //Common::fill(&srcPalette[0], &srcPalette[PALETTE_SIZE], 0); - //_vm->_palette->fadeIn(srcPalette, destPalette, 0, PALETTE_COUNT, 0, 0, - // TV_FADE_DELAY_MILLI, TV_NUM_FADE_STEPS); - _vm->_game->_fx = kTransitionFadeIn; - - } else if (!strncmp(commandStr, "PAN", 3)) { - // Set panning values - paramP = commandStr + 3; - int panX = getParameter(¶mP); - int panY = getParameter(¶mP); - int panSpeed = getParameter(¶mP); - - if ((panX != 0) || (panY != 0)) { - _pan = Common::Point(panX, panY); - _panSpeed = panSpeed; - } - - } else if (!strncmp(commandStr, "DRIVER", 6)) { - // Set the driver to use - paramP = commandStr + 6; - int driverNum = getParameter(¶mP); - _vm->_sound->init(driverNum); - - } else if (!strncmp(commandStr, "SOUND", 5)) { - // Set sound number - paramP = commandStr + 5; - int soundId = getParameter(¶mP); - _vm->_sound->command(soundId); - - } else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || - (commandStr[5] == '1'))) { - // Set the text colors - int index = commandStr[5] - '0'; - paramP = commandStr + 6; - - byte palEntry[3]; - palEntry[0] = getParameter(¶mP) << 2; - palEntry[1] = getParameter(¶mP) << 2; - palEntry[2] = getParameter(¶mP) << 2; - _vm->_palette->setPalette(&palEntry[0], 5 + index, 1); - - } else if (!strncmp(commandStr, "SPARE", 5)) { - // Sets a secondary background number that can be later switched in with a PAGE command - paramP = commandStr + 6; - int spareIndex = commandStr[5] - '0'; - if ((spareIndex >= 0) && (spareIndex <= 9)) { - int screenId = getParameter(¶mP); - - _spareScreens[spareIndex] = screenId; - } - - } else if (!strncmp(commandStr, "PAGE", 4)) { - // Signals to change to a previous specified secondary background - paramP = commandStr + 4; - int spareIndex = getParameter(¶mP); - - // Only allow background switches if one isn't currently in progress - if (!_spareScreen && (_spareScreens[spareIndex] != 0)) { - _spareScreen = new MSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); - //_spareScreen->loadBackground(_spareScreens[spareIndex], &_bgSpare); - - _translationX = 0; - } - - } else { - error("Unknown response command: '%s'", commandStr); - } -} - -int TextView::getParameter(const char **paramP) { - if ((**paramP != '=') && (**paramP != ',')) - return 0; - - int result = 0; - ++*paramP; - while ((**paramP >= '0') && (**paramP <= '9')) { - result = result * 10 + (**paramP - '0'); - ++*paramP; - } - - return result; -} - -void TextView::processText() { - int lineWidth, xStart; - - if (!strcmp(_currentLine, "***")) { - // Special signifier for end of script - _scrollCount = _vm->_font->getHeight() * 13; - _lineY = -1; - return; - } - - _lineY = 0; - - // Lines are always centered, except if line contains a '@', in which case the - // '@' marks the position that must be horizontally centered - char *centerP = strchr(_currentLine, '@'); - if (centerP) { - *centerP = '\0'; - xStart = (MADS_SCREEN_WIDTH / 2) - _vm->_font->getWidth(_currentLine); - - // Delete the @ character and shift back the remainder of the string - char *p = centerP + 1; - if (*p == ' ') ++p; - strcpy(centerP, p); - - } else { - lineWidth = _vm->_font->getWidth(_currentLine); - xStart = (MADS_SCREEN_WIDTH - lineWidth) / 2; - } - - // Copy the text line onto the bottom of the textSurface surface, which will allow it - // to gradually scroll onto the screen - int yp = _textSurface.h - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING; - _textSurface.fillRect(Common::Rect(0, yp, MADS_SCREEN_WIDTH, _textSurface.h), 0); - _vm->_font->writeString(&_textSurface, _currentLine, Common::Point(xStart, yp)); -} - -/*------------------------------------------------------------------------*/ - -char AnimationView::_resourceName[100]; - -void AnimationView::execute(MADSEngine *vm, const Common::String &resName) { - assert(resName.size() < 100); - strncpy(_resourceName, resName.c_str(), sizeof(_resourceName)); - vm->_dialogs->_pendingDialog = DIALOG_ANIMVIEW; -} - -AnimationView::AnimationView(MADSEngine *vm) : MenuView(vm) { - _soundDriverLoaded = false; - _previousUpdate = 0; -} - -void AnimationView::load() { - Common::String resName(_resourceName); - if (!resName.hasSuffix(".")) - resName += ".res"; - - if (!_script.open(resName)) - error("Could not open resource %s", resName.c_str()); - - processLines(); -} +void RexAnimationView::scriptDone() { + AnimationView::scriptDone(); -bool AnimationView::onEvent(Common::Event &event) { - // Wait for the Escape key or a mouse press - if (((event.type == Common::EVENT_KEYDOWN) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) || - (event.type == Common::EVENT_RBUTTONUP)) { - scriptDone(); - return true; - } - - return false; -} - -void AnimationView::doFrame() { - Scene &scene = _vm->_game->_scene; - int bgNumber = 0; - - // Only update state if wait period has expired - if (_previousUpdate > 0) { - if (g_system->getMillis() - _previousUpdate < 3000) { - return; - } else { - // time for an update - _previousUpdate = g_system->getMillis(); - } - } else { - _previousUpdate = g_system->getMillis(); - return; - } - - char bgFile[10]; - strncpy(bgFile, _currentFile, 5); - bgFile[0] = bgFile[2]; - bgFile[1] = bgFile[3]; - bgFile[2] = bgFile[4]; - bgFile[3] = '\0'; - bgNumber = atoi(bgFile); - sprintf(bgFile, "rm%i.art", bgNumber); - - // Not all scenes have a background. If there is one, refresh it - if (Common::File::exists(bgFile)) { - _vm->_palette->resetGamePalette(4, 8); - SceneInfo *sceneInfo = SceneInfo::init(_vm); - sceneInfo->load(bgNumber, 0, Common::String(), 0, scene._depthSurface, - scene._backgroundSurface); - } - - // Read next line - processLines(); -} - -void AnimationView::scriptDone() { - _breakFlag = true; - _vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU; -} - -void AnimationView::processLines() { - if (_script.eos()) { - // end of script, end animation - scriptDone(); - return; - } - - while (!_script.eos()) { - _script.readLine(_currentLine, 79); - - // Process the line - char *cStart = strchr(_currentLine, '-'); - if (cStart) { - while (cStart) { - // Loop for possible multiple commands on one line - char *cEnd = strchr(_currentLine, ' '); - if (!cEnd) - error("Unterminated command '%s' in response file", _currentLine); - - *cEnd = '\0'; - processCommand(); - - // Copy rest of line (if any) to start of buffer - // Don't use strcpy() here, because if the - // rest of the line is the longer of the two - // strings, the memory areas will overlap. - memmove(_currentLine, cEnd + 1, strlen(cEnd + 1) + 1); - - cStart = strchr(_currentLine, '-'); - } - - if (_currentLine[0]) { - sprintf(_currentFile, "%s", _currentLine); - //printf("File: %s\n", _currentLine); - break; - } - - } else { - sprintf(_currentFile, "%s", _currentLine); - warning("File: %s\n", _currentLine); - break; - } - } -} - -void AnimationView::processCommand() { - Common::String commandLine(_currentLine + 1); - commandLine.toUppercase(); - const char *commandStr = commandLine.c_str(); - const char *param = commandStr; - - if (!strncmp(commandStr, "X", 1)) { - //printf("X "); - } else if (!strncmp(commandStr, "W", 1)) { - //printf("W "); - } else if (!strncmp(commandStr, "R", 1)) { - param = param + 2; - //printf("R:%s ", param); - } else if (!strncmp(commandStr, "O", 1)) { - // Set the transition effect - param = param + 2; - _vm->_game->_fx = (ScreenTransition)atoi(param); - } else { - error("Unknown response command: '%s'", commandStr); + Common::String s = getResourceName(); + if (s == "rexend1") { + TextView::execute(_vm, "ending1"); + } else if (s == "rexend2") { + TextView::execute(_vm, "ending2"); + } else if (s == "rexend3") { + TextView::execute(_vm, "credits"); } } diff --git a/engines/mads/nebular/menu_nebular.h b/engines/mads/nebular/menu_nebular.h index 6e877a8a24..77b8b6fc6e 100644 --- a/engines/mads/nebular/menu_nebular.h +++ b/engines/mads/nebular/menu_nebular.h @@ -25,6 +25,7 @@ #include "common/scummsys.h" #include "mads/game.h" +#include "mads/menu_views.h" #include "mads/msurface.h" #include "mads/nebular/dialogs_nebular.h" @@ -36,22 +37,6 @@ namespace Nebular { enum MADSGameAction { START_GAME, RESUME_GAME, SHOW_INTRO, CREDITS, QUOTES, EXIT }; -class MenuView: public FullScreenDialog { -protected: - bool _breakFlag; - bool _redrawFlag; - - virtual void doFrame() = 0; - - virtual void display(); -public: - MenuView(MADSEngine *vm); - - virtual ~MenuView() {} - - virtual void show(); -}; - class MainMenu: public MenuView { private: SpriteAsset *_menuItems[7]; @@ -95,6 +80,8 @@ private: * Add a sprite slot for the current menuitem frame */ void addSpriteSlot(); + + bool shouldShowQuotes(); protected: /** * Display the menu @@ -143,95 +130,16 @@ public: void show(); }; -/** - * Scrolling text view - */ -class TextView : public MenuView { -private: - static char _resourceName[100]; - - bool _animating; - Common::Point _pan; - int _panSpeed; - int _spareScreens[10]; - int _scrollCount; - int _lineY; - uint32 _scrollTimeout; - int _panCountdown; - int _translationX; - Common::File _script; - char _currentLine[80]; - MSurface _textSurface; - MSurface *_spareScreen; -private: - /** - * Load the text resource - */ - void load(); - - /** - * Process the lines - */ - void processLines(); - - /** - * Process a command from the script file - */ - void processCommand(); - - /** - * Process text from the script file - */ - void processText(); - - /** - * Get a parameter from line - */ - int getParameter(const char **paramP); +class RexAnimationView : public AnimationView { +protected: + virtual void scriptDone(); public: - /** - * Queue the given text resource for display - */ - static void execute(MADSEngine *vm, const Common::String &resName); - - TextView(MADSEngine *vm); - - virtual ~TextView(); + RexAnimationView(MADSEngine *vm) : AnimationView(vm) {} }; -/** -* Animation cutscene view -*/ -class AnimationView : public MenuView { -private: - static char _resourceName[100]; - - Common::File _script; - uint32 _previousUpdate; - char _currentLine[80]; - char _currentFile[10]; - bool _soundDriverLoaded; -private: - void load(); - - void processLines(); - - void processCommand(); - - void scriptDone(); - - void doFrame(); -protected: - virtual bool onEvent(Common::Event &event); +class RexTextView : public TextView { public: - /** - * Queue the given text resource for display - */ - static void execute(MADSEngine *vm, const Common::String &resName); - - AnimationView(MADSEngine *vm); - - virtual ~AnimationView() {} + RexTextView(MADSEngine *vm) : TextView(vm) {} }; } // End of namespace Nebular diff --git a/engines/mads/nebular/nebular_scenes.cpp b/engines/mads/nebular/nebular_scenes.cpp index c71512ed4c..b5e2491624 100644 --- a/engines/mads/nebular/nebular_scenes.cpp +++ b/engines/mads/nebular/nebular_scenes.cpp @@ -331,7 +331,7 @@ void SceneInfoNebular::loadCodes(MSurface &depthSurface, Common::SeekableReadStr byte runValue = stream->readByte(); // Write out the run length - Common::fill(destP, destP + runLength, runValue); + Common::fill(destP, MIN(endP, destP + runLength), runValue); destP += runLength; // Get the next run length diff --git a/engines/mads/nebular/nebular_scenes5.cpp b/engines/mads/nebular/nebular_scenes5.cpp index 5a67d1541f..66d8294fc6 100644 --- a/engines/mads/nebular/nebular_scenes5.cpp +++ b/engines/mads/nebular/nebular_scenes5.cpp @@ -2847,8 +2847,6 @@ void Scene551::actions() { _vm->_dialogs->show(55113); else if (_action.isAction(VERB_LOOK, NOUN_TELEPORTER)) _vm->_dialogs->show(55114); - else if (_action.isAction(VERB_LOOK, NOUN_BUILDING)) - _vm->_dialogs->show(55115); else if (_action.isAction(VERB_LOOK, NOUN_SIDEWALK_TO_WEST)) { if (_game._visitedScenes.exists(505)) _vm->_dialogs->show(55116); diff --git a/engines/mads/nebular/nebular_scenes6.cpp b/engines/mads/nebular/nebular_scenes6.cpp index d33675c578..679039535f 100644 --- a/engines/mads/nebular/nebular_scenes6.cpp +++ b/engines/mads/nebular/nebular_scenes6.cpp @@ -643,7 +643,8 @@ void Scene603::actions() { _game._player._visible = true; _game._player._stepEnabled = true; } - } + } else + _vm->_dialogs->show(60323); } else if (_action._lookFlag) _vm->_dialogs->show(60310); else if (_action.isAction(VERB_LOOK, NOUN_BED)) @@ -670,8 +671,6 @@ void Scene603::actions() { _vm->_dialogs->show(60321); else if (_action.isAction(VERB_TAKE, NOUN_PERFUME)) _vm->_dialogs->show(60322); - else if (_action.isAction(VERB_TAKE, NOUN_NOTE)) - _vm->_dialogs->show(60323); else if (_action.isAction(VERB_LOOK, NOUN_NOTE)) { if (_game._objects[OBJ_NOTE]._roomNumber == _scene->_currentSceneId) _vm->_dialogs->show(60324); @@ -3156,7 +3155,7 @@ bool Scene611::check2ChargedBatteries() { } bool Scene611::check4ChargedBatteries() { - if (_game._objects.isInInventory(OBJ_DURAFAIL_CELLS) && _game._objects.isInInventory(OBJ_PHONE_CELLS) + if (_game._objects.isInInventory(OBJ_DURAFAIL_CELLS) && _game._objects.isInInventory(OBJ_PHONE_CELLS) && _globals[kDurafailRecharged]) return true; diff --git a/engines/mads/nebular/nebular_scenes7.cpp b/engines/mads/nebular/nebular_scenes7.cpp index 930bb7c250..0f019c4b19 100644 --- a/engines/mads/nebular/nebular_scenes7.cpp +++ b/engines/mads/nebular/nebular_scenes7.cpp @@ -2616,7 +2616,7 @@ void Scene752::actions() { default: break; } - } else if (_action.isAction(VERB_TAKE, NOUN_BONES) && (_action._savedFields._mainObjectSource == CAT_HOTSPOT) && + } else if (_action.isAction(VERB_TAKE, NOUN_BONES) && (_action._savedFields._mainObjectSource == CAT_HOTSPOT) && (!_game._objects.isInInventory(OBJ_BONES) || _game._trigger)) { switch (_game._trigger) { case 0: diff --git a/engines/mads/nebular/nebular_scenes8.cpp b/engines/mads/nebular/nebular_scenes8.cpp index 14f36756de..62a1a262b0 100644 --- a/engines/mads/nebular/nebular_scenes8.cpp +++ b/engines/mads/nebular/nebular_scenes8.cpp @@ -1098,7 +1098,7 @@ void Scene804::actions() { _action.isAction(VERB_OPEN, NOUN_SERVICE_PANEL)) { _scene->_nextSceneId = 805; } else if ((_action.isAction(VERB_ACTIVATE, NOUN_REMOTE)) && _globals[kTopButtonPushed]) { - if (!_globals[kInSpace]) { + if (!_globals[kInSpace]) { // Top button pressed on panel in hanger control if (!_globals[kBeamIsUp]) { _globals[kFromCockpit] = true; diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp index fc2755db2f..0a054440b2 100644 --- a/engines/mads/nebular/sound_nebular.cpp +++ b/engines/mads/nebular/sound_nebular.cpp @@ -24,6 +24,7 @@ #include "audio/decoders/raw.h" #include "common/algorithm.h" #include "common/debug.h" +#include "common/md5.h" #include "common/memstream.h" #include "mads/sound.h" #include "mads/nebular/sound_nebular.h" @@ -149,7 +150,7 @@ AdlibSample::AdlibSample(Common::SeekableReadStream &s) { /*-----------------------------------------------------------------------*/ -ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset) { +ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset) { // Open up the appropriate sound file if (!_soundFile.open(filename)) error("Could not open file - %s", filename.c_str()); @@ -197,8 +198,7 @@ ASound::ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffs // Store passed parameters, and setup OPL _dataOffset = dataOffset; _mixer = mixer; - _opl = OPL::Config::create(); - assert(_opl); + _opl = opl; _opl->init(getRate()); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, @@ -217,7 +217,32 @@ ASound::~ASound() { delete[] (*i)._data; _mixer->stopHandle(_soundHandle); - delete _opl; +} + +void ASound::validate() { + Common::File f; + static const char *const MD5[] = { + "205398468de2c8873b7d4d73d5be8ddc", + "f9b2d944a2fb782b1af5c0ad592306d3", + "7431f8dad77d6ddfc24e6f3c0c4ac7df", + "eb1f3f5a4673d3e73d8ac1818c957cf4", + "f936dd853073fa44f3daac512e91c476", + "3dc139d3e02437a6d9b732072407c366", + "af0edab2934947982e9a405476702e03", + "8cbc25570b50ba41c9b5361cad4fbedc", + "a31e4783e098f633cbb6689adb41dd4f" + }; + + for (int i = 1; i <= 9; ++i) { + Common::String filename = Common::String::format("ASOUND.00%d", i); + if (!f.open(filename)) + error("Could not process - %s", filename.c_str()); + Common::String md5str = Common::computeStreamMD5AsString(f, 8192); + f.close(); + + if (md5str != MD5[i - 1]) + error("Invalid sound file - %s", filename.c_str()); + } } void ASound::adlibInit() { @@ -941,8 +966,8 @@ const ASound1::CommandPtr ASound1::_commandList[42] = { &ASound1::command40, &ASound1::command41 }; -ASound1::ASound1(Audio::Mixer *mixer) - : ASound(mixer, "asound.001", 0x1520) { +ASound1::ASound1(Audio::Mixer *mixer, FM_OPL *opl) + : ASound(mixer, opl, "asound.001", 0x1520) { _cmd23Toggle = false; // Load sound samples @@ -1242,7 +1267,7 @@ const ASound2::CommandPtr ASound2::_commandList[44] = { &ASound2::command40, &ASound2::command41, &ASound2::command42, &ASound2::command43 }; -ASound2::ASound2(Audio::Mixer *mixer) : ASound(mixer, "asound.002", 0x15E0) { +ASound2::ASound2(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { _command12Param = 0xFD; // Load sound samples @@ -1613,7 +1638,7 @@ const ASound3::CommandPtr ASound3::_commandList[61] = { &ASound3::command60 }; -ASound3::ASound3(Audio::Mixer *mixer) : ASound(mixer, "asound.003", 0x15B0) { +ASound3::ASound3(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.003", 0x15B0) { _command39Flag = false; // Load sound samples @@ -2017,7 +2042,7 @@ const ASound4::CommandPtr ASound4::_commandList[61] = { &ASound4::command60 }; -ASound4::ASound4(Audio::Mixer *mixer) : ASound(mixer, "asound.004", 0x14F0) { +ASound4::ASound4(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.004", 0x14F0) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 210; ++i) @@ -2273,7 +2298,7 @@ const ASound5::CommandPtr ASound5::_commandList[42] = { &ASound5::command40, &ASound5::command41 }; -ASound5::ASound5(Audio::Mixer *mixer) : ASound(mixer, "asound.002", 0x15E0) { +ASound5::ASound5(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { // Load sound samples _soundFile.seek(_dataOffset + 0x144); for (int i = 0; i < 164; ++i) @@ -2514,7 +2539,7 @@ const ASound6::CommandPtr ASound6::_commandList[30] = { &ASound6::nullCommand, &ASound6::command29 }; -ASound6::ASound6(Audio::Mixer *mixer) : ASound(mixer, "asound.006", 0x1390) { +ASound6::ASound6(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.006", 0x1390) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 200; ++i) @@ -2670,7 +2695,7 @@ const ASound7::CommandPtr ASound7::_commandList[38] = { &ASound7::command36, &ASound7::command37 }; -ASound7::ASound7(Audio::Mixer *mixer) : ASound(mixer, "asound.007", 0x1460) { +ASound7::ASound7(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.007", 0x1460) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 214; ++i) @@ -2876,7 +2901,7 @@ const ASound8::CommandPtr ASound8::_commandList[38] = { &ASound8::command36, &ASound8::command37 }; -ASound8::ASound8(Audio::Mixer *mixer) : ASound(mixer, "asound.008", 0x1490) { +ASound8::ASound8(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.008", 0x1490) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 174; ++i) @@ -3114,6 +3139,313 @@ int ASound8::command37() { return 0; } +/*-----------------------------------------------------------------------*/ + +const ASound9::CommandPtr ASound9::_commandList[52] = { + &ASound9::command0, &ASound9::command1, &ASound9::command2, &ASound9::command3, + &ASound9::command4, &ASound9::command5, &ASound9::command6, &ASound9::command7, + &ASound9::command8, &ASound9::command9, &ASound9::command10, &ASound9::command11, + &ASound9::command12, &ASound9::command13, &ASound9::command14, &ASound9::command15, + &ASound9::command16, &ASound9::command17, &ASound9::command18, &ASound9::command19, + &ASound9::command20, &ASound9::command21, &ASound9::command22, &ASound9::command23, + &ASound9::command24, &ASound9::command25, &ASound9::command26, &ASound9::command27, + &ASound9::command28, &ASound9::command29, &ASound9::command30, &ASound9::command31, + &ASound9::command32, &ASound9::command33, &ASound9::command34, &ASound9::command35, + &ASound9::command36, &ASound9::command37, &ASound9::command38, &ASound9::command39, + &ASound9::command40, &ASound9::command41, &ASound9::command42, &ASound9::command43, + &ASound9::command44_46, &ASound9::command45, &ASound9::command44_46, &ASound9::command47, + &ASound9::command48, &ASound9::command49, &ASound9::command50, &ASound9::command51 +}; + +ASound9::ASound9(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.009", 0x16F0) { + _v1 = _v2 = 0; + _soundPtr = nullptr; + + // Load sound samples + _soundFile.seek(_dataOffset + 0x50); + for (int i = 0; i < 94; ++i) + _samples.push_back(AdlibSample(_soundFile)); +} + +int ASound9::command(int commandId, int param) { + if (commandId > 51) + return 0; + + _commandParam = param; + _frameCounter = 0; + return (this->*_commandList[commandId])(); +} + +int ASound9::command9() { + _v1 = 1848; + _v2 = 84; + _channels[0].load(loadData(0xAA4, 470)); + _channels[1].load(loadData(0xE4C, 450)); + _channels[2].load(loadData(0x1466, 702)); + _channels[3].load(loadData(0x137E, 232)); + _channels[4].load(loadData(0x1014, 65)); + _channels[5].load(loadData(0x11C4, 44)); + _channels[6].load(loadData(0XC7A, 466)); + return 0; +} + +int ASound9::command10() { + _channels[0].load(loadData(0x1724, 24)); + _channels[1].load(loadData(0x173C, 24)); + _channels[2].load(loadData(0x1754, 20)); + _channels[3].load(loadData(0x1768, 20)); + _channels[4].load(loadData(0x177C, 20)); + _channels[5].load(loadData(0x1790, 20)); + return 0; +} + +int ASound9::command11() { + playSound(0x8232, 168); + playSound(0x82DA, 170); + return 0; +} + +int ASound9::command12() { + playSound(0x80DA, 12); + playSound(0x80E6, 12); + return 0; +} + +int ASound9::command13() { + playSound(0x80F2, 38); + playSound(0x8118, 42); + return 0; +} + +int ASound9::command14() { + playSound(0x81F6, 22); + return 0; +} + +int ASound9::command15() { + playSound(0x818A, 32); + playSound(0x81AA, 32); + return 0; +} + +int ASound9::command16() { + playSound(0x8022, 36); + playSound(0x8046, 42); + return 0; +} + +int ASound9::command17() { + command29(); + playSound(0x858C, 11); + return 0; +} + +int ASound9::command18() { + playSound(0x80C2, 24); + return 0; +} + +int ASound9::command19() { + playSound(0x80A0, 34); + return 0; +} + +int ASound9::command20() { + int v = (getRandomNumber() & 0x10) | 0x4D; + byte *pData = loadData(0x8142, 8); + pData[4] = v & 0x7F; + playSoundData(pData); + return 0; +} + +int ASound9::command21() { + playSound(0x815A, 16); + return 0; +} + +int ASound9::command22() { + playSound(0x816A, 16); + return 0; +} + +int ASound9::command23() { + playSound(0x814A, 16); + return 0; +} + +int ASound9::command24() { + playSound(0x7FE2, 34); + return 0; +} + +int ASound9::command25() { + playSound(0x8004, 30); + return 0; +} + +int ASound9::command26() { + _channels[6].load(loadData(0x8384, 156)); + _channels[7].load(loadData(0x8420, 160)); + return 0; +} + +int ASound9::command27() { + playSound(0x84C0, 140); + return 0; +} + +int ASound9::command28() { + playSound(0x81CA, 10); + return 0; +} + +int ASound9::command29() { + playSound(0x81D4, 10); + return 0; +} + +int ASound9::command30() { + playSound(0x817A, 16); + return 0; +} + +int ASound9::command31() { + playSound(0x820C, 14); + playSound(0x821A, 24); + return 0; +} + +int ASound9::command32() { + playSound(0x8070, 8); + return 0; +} + +int ASound9::command33() { + playSound(0x8078, 16); + playSound(0x8088, 16); + return 0; +} + +int ASound9::command34() { + // Skipped stuff in original + _channels[0].load(loadData(0x17A4, 24)); + _channels[1].load(loadData(0x1CDE, 62)); + _channels[2].load(loadData(0x2672, 980)); + _channels[3].load(loadData(0x3336, 1000)); + _channels[4].load(loadData(0x469E, 176)); + _channels[5].load(loadData(0x57F2, 138)); + + return 0; +} + +int ASound9::command35() { + playSound(0x854C, 64); + return 0; +} + +int ASound9::command36() { + playSound(0x81DE, 10); + playSound(0x81E8, 14); + return 0; +} + +int ASound9::command37() { + byte *pData = loadData(0x8098, 8); + int v = getRandomNumber(); + if ((v &= 0x40) != 0) + v |= 8; + else + v += 0x4A; + + pData[6] = v; + playSoundData(pData); + return 0; +} + +int ASound9::command38() { + playSound(0x100E, 6); + return 0; +} + +int ASound9::command39() { + _soundPtr = loadData(0x1055, 128); + return 0; +} + +int ASound9::command40() { + _soundPtr = loadData(0x118C, 50); + return 0; +} + +int ASound9::command41() { + _soundPtr = loadData(0x11BE, 6); + return 0; +} + +int ASound9::command42() { + _soundPtr = loadData(0x11F0, 50); + return 0; +} + +int ASound9::command43() { + _v1 = _v2 = 80; + _channels[0].load(loadData(0x626A, 90)); + _channels[1].load(loadData(0x67F2, 92)); + _channels[2].load(loadData(0x6CFE, 232)); + _channels[3].load(loadData(0x7146, 236)); + + return 0; +} + +int ASound9::command44_46() { + _soundPtr = loadData(0x10D5, 38); + return 0; +} + +int ASound9::command45() { + _soundPtr = loadData(0x10FB, 38); + return 0; +} + +int ASound9::command47() { + _soundPtr = loadData(0x1121, 107); + return 0; +} + +int ASound9::command48() { + playSound(0x7FD0, 8); + playSound(0x7FD8, 10); + return 0; +} + +int ASound9::command49() { + _channels[0].load(loadData(0x7AD6, 92)); + _channels[1].load(loadData(0x7B32, 90)); + _channels[2].load(loadData(0x7B8C, 738)); + _channels[3].load(loadData(0x7E6E, 28)); + _channels[4].load(loadData(0x7E8A, 30)); + _channels[5].load(loadData(0x7EA8, 30)); + _channels[6].load(loadData(0x7EC6, 195)); + return 0; +} + +int ASound9::command50() { + _soundPtr = loadData(0x1222, 348); + return 0; +} + +int ASound9::command51() { + // Skipped stuff in original + _channels[0].load(loadData(0x17BC, 1282)); + _channels[1].load(loadData(0x1CFC, 2422)); + _channels[2].load(loadData(0x2A46, 2288)); + _channels[3].load(loadData(0x371E, 3964)); + _channels[4].load(loadData(0x474E, 1863)); + _channels[5].load(loadData(0x587C, 2538)); + return 0; +} + + } // End of namespace Nebular } // End of namespace MADS diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h index c485bd7955..ccfd40ad52 100644 --- a/engines/mads/nebular/sound_nebular.h +++ b/engines/mads/nebular/sound_nebular.h @@ -305,10 +305,12 @@ public: public: /** * Constructor + * @param mixer Mixer + * @param opl OPL * @param filename Specifies the adlib sound player file to use * @param dataOffset Offset in the file of the data segment */ - ASound(Audio::Mixer *mixer, const Common::String &filename, int dataOffset); + ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset); /** * Destructor @@ -316,6 +318,11 @@ public: virtual ~ASound(); /** + * Validates the Adlib sound files + */ + static void validate(); + + /** * Execute a player command. Most commands represent sounds to play, but some * low number commands also provide control operations. * @param commandId Player ommand to execute. @@ -408,7 +415,7 @@ private: void command111213(); int command2627293032(); public: - ASound1(Audio::Mixer *mixer); + ASound1(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -460,7 +467,7 @@ private: void command9Randomize(); void command9Apply(byte *data, int val, int incr); public: - ASound2(Audio::Mixer *mixer); + ASound2(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -520,7 +527,7 @@ private: void command9Randomize(); void command9Apply(byte *data, int val, int incr); public: - ASound3(Audio::Mixer *mixer); + ASound3(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -558,7 +565,7 @@ private: void method1(); public: - ASound4(Audio::Mixer *mixer); + ASound4(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -604,7 +611,7 @@ private: int command42(); int command43(); public: - ASound5(Audio::Mixer *mixer); + ASound5(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -633,7 +640,7 @@ private: int command25(); int command29(); public: - ASound6(Audio::Mixer *mixer); + ASound6(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -665,7 +672,7 @@ private: int command36(); int command37(); public: - ASound7(Audio::Mixer *mixer); + ASound7(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; @@ -708,7 +715,66 @@ private: void method1(byte *pData); void adjustRange(byte *pData, byte v, int incr); public: - ASound8(Audio::Mixer *mixer); + ASound8(Audio::Mixer *mixer, FM_OPL *opl); + + virtual int command(int commandId, int param); +}; + +class ASound9 : public ASound { +private: + int _v1, _v2; + byte *_soundPtr; + + typedef int (ASound9::*CommandPtr)(); + static const CommandPtr _commandList[52]; + + int command9(); + int command10(); + int command11(); + int command12(); + int command13(); + int command14(); + int command15(); + int command16(); + int command17(); + int command18(); + int command19(); + int command20(); + int command21(); + int command22(); + int command23(); + int command24(); + int command25(); + int command26(); + int command27(); + int command28(); + int command29(); + int command30(); + int command31(); + int command32(); + int command33(); + int command34(); + int command35(); + int command36(); + int command37(); + int command38(); + int command39(); + int command40(); + int command41(); + int command42(); + int command43(); + int command44_46(); + int command45(); + int command47(); + int command48(); + int command49(); + int command50(); + int command51(); + int command57(); + int command59(); + int command60(); +public: + ASound9(Audio::Mixer *mixer, FM_OPL *opl); virtual int command(int commandId, int param); }; diff --git a/engines/mads/palette.cpp b/engines/mads/palette.cpp index eedbf36ddd..836d04f7c0 100644 --- a/engines/mads/palette.cpp +++ b/engines/mads/palette.cpp @@ -143,7 +143,7 @@ int PaletteUsage::process(Common::Array<RGB6> &palette, uint flags) { for (uint palIndex = 0; palIndex < palette.size(); ++palIndex) { bool changed = false; - int newPalIndex = -1; + int newPalIndex = 0xFF; int v1 = palRange[palIndex]._v2; if (palette[v1]._flags & 8) { @@ -229,8 +229,11 @@ int PaletteUsage::process(Common::Array<RGB6> &palette, uint flags) { // In at least scene 318, when the doctor knocks you with the blackjack, // the changed flag can be false //assert(changed); - assert(newPalIndex != -1); - + + // CHECKME: When pressing on F1 in the first screen, newPalIndex is set to 0xFF at this point + // which is a valid value for the index. Maybe a better check would be "< 256" ? + //assert(newPalIndex != -1); + int var52 = (noUsageFlag && palette[palIndex]._u2) ? 2 : 0; _vm->_palette->_palFlags[newPalIndex] |= var52 | rgbMask; @@ -314,6 +317,62 @@ int PaletteUsage::rgbFactor(byte *palEntry, RGB6 &pal6) { return total; } +int PaletteUsage::checkRGB(const byte *rgb, int palStart, bool flag, int *palIndex) { + Palette &palette = *_vm->_palette; + bool match = false; + int result; + if (palStart >= 0) { + result = palStart; + } else { + result = -1; + for (int i = 0; i < palette._highRange; ++i) { + if (!palette._rgbList[i]) { + result = i; + break; + } + } + } + + if (result >= 0) { + int mask = 1 << result; + byte *palP = &palette._mainPalette[0]; + uint32 *flagsP = &palette._palFlags[0]; + + for (; flagsP < &palette._palFlags[PALETTE_COUNT]; ++flagsP, ++result) { + if ((!(*flagsP & 1) || flag) && !(*flagsP & 2)) { + if (!memcmp(palP, rgb, 3)) { + *flagsP |= mask; + + if (palIndex) + *palIndex = result; + match = true; + break; + } + } + } + + if (!match) { + palP = &palette._mainPalette[0]; + flagsP = &palette._palFlags[0]; + + for (int i = 0; i < PALETTE_COUNT; ++i, palP += 3, ++flagsP) { + if (!*flagsP) { + Common::copy(rgb, rgb + 3, palP); + *flagsP |= mask; + + if (palIndex) + *palIndex = i; + match = true; + break; + } + } + } + } + + assert(match); + return result; +} + /*------------------------------------------------------------------------*/ void RGBList::clear() { diff --git a/engines/mads/palette.h b/engines/mads/palette.h index 9b8b7146db..27d25f266b 100644 --- a/engines/mads/palette.h +++ b/engines/mads/palette.h @@ -136,6 +136,8 @@ public: void updateUsage(Common::Array<int> &usageList, int sceneUsageIndex); void resetPalFlags(int idx); + + int checkRGB(const byte *rgb, int palStart, bool flag, int *palIndex); }; class RGBList { diff --git a/engines/mads/phantom/game_phantom.cpp b/engines/mads/phantom/game_phantom.cpp index ba2179fcbf..0b2531ef65 100644 --- a/engines/mads/phantom/game_phantom.cpp +++ b/engines/mads/phantom/game_phantom.cpp @@ -50,11 +50,12 @@ void GamePhantom::startGame() { } void GamePhantom::initializeGlobals() { - //int count, count2; - //int bad; - _globals.reset(); - //_globals[kTalkInanimateCount] = 8; + + warning("TODO: sub_316DA()"); + + _player._facing = FACING_NORTH; + _player._turnToFacing = FACING_NORTH; /* Section #1 variables */ // TODO @@ -74,11 +75,7 @@ void GamePhantom::initializeGlobals() { /* Section #9 variables */ // TODO - _player._facing = FACING_NORTH; - _player._turnToFacing = FACING_NORTH; - - //Player::preloadSequences("RXM", 1); - //Player::preloadSequences("ROX", 1); + Player::preloadSequences("RAL", 1); } void GamePhantom::setSectionHandler() { diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp index 1f95749fd8..18ceb3c813 100644 --- a/engines/mads/scene.cpp +++ b/engines/mads/scene.cpp @@ -63,8 +63,7 @@ Scene::Scene(MADSEngine *vm) _paletteUsageF.push_back(PaletteUsage::UsageEntry(0xF)); // Set up a scene surface that maps to our physical screen drawing surface - _sceneSurface.init(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH, - _vm->_screen.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); + restrictScene(); // Set up the verb list _verbList.push_back(VerbInit(VERB_LOOK, VERB_THAT, PREP_NONE)); @@ -82,6 +81,12 @@ Scene::Scene(MADSEngine *vm) Scene::~Scene() { delete _sceneLogic; delete _sceneInfo; + delete _animationData; +} + +void Scene::restrictScene() { + _sceneSurface.init(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH, + _vm->_screen.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); } void Scene::clearVocab() { @@ -355,6 +360,9 @@ void Scene::loop() { if (_vm->_dialogs->_pendingDialog != DIALOG_NONE && !_vm->_game->_trigger && _vm->_game->_player._stepEnabled) _reloadSceneFlag = true; + + if (_vm->_game->_winStatus) + break; } } @@ -510,7 +518,7 @@ void Scene::drawElements(ScreenTransition transitionType, bool surfaceFlag) { _vm->_sound->startQueuedCommands(); } else { // Copy dirty areas to the screen - _dirtyAreas.copyToScreen(_vm->_screen._offset); + _dirtyAreas.copyToScreen(); } _spriteSlots.cleanUp(); diff --git a/engines/mads/scene.h b/engines/mads/scene.h index 407d70dc85..9fd99ad8e5 100644 --- a/engines/mads/scene.h +++ b/engines/mads/scene.h @@ -52,11 +52,6 @@ private: */ void loadVocabStrings(); - /* - * Initializes the data for palette animation within the scene - */ - void initPaletteAnimation(Common::Array<PaletteCycle> &palCycles, bool animFlag); - /** * Handles a single frame within the game scene */ @@ -142,6 +137,8 @@ public: */ ~Scene(); + void restrictScene(); + /** * Clear the vocabulary list */ @@ -202,6 +199,11 @@ public: */ void drawElements(ScreenTransition transitionType, bool surfaceFlag); + /* + * Initializes the data for palette animation within the scene + */ + void initPaletteAnimation(Common::Array<PaletteCycle> &palCycles, bool animFlag); + /** * Handles cycling palette colors for the scene */ diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp index e874468345..b5e219ed04 100644 --- a/engines/mads/scene_data.cpp +++ b/engines/mads/scene_data.cpp @@ -217,7 +217,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, int width = _width; int height = _height; - if (!bgSurface.getPixels()) { + if (!bgSurface.getPixels() || (bgSurface.w != width) || (bgSurface.h != height)) { bgSurface.setSize(width, height); } @@ -232,11 +232,11 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, infoFile.close(); if (_vm->getGameID() == GType_RexNebular) { - loadMadsV1Background(sceneId, resName, flags, bgSurface); - loadPalette(sceneId, _artFileNum, resName, flags, bgSurface); + loadMadsV1Background(_artFileNum, resName, flags, bgSurface); + loadPalette(_sceneId, _artFileNum, resName, flags, bgSurface); } else { - loadMadsV2Background(sceneId, resName, flags, bgSurface); - loadPalette(sceneId, sceneId, resName, flags, bgSurface); + loadMadsV2Background(_sceneId, resName, flags, bgSurface); + loadPalette(_sceneId, _sceneId, resName, flags, bgSurface); } Common::Array<SpriteAsset *> spriteSets; @@ -264,7 +264,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, assert(asset && _depthStyle != 2); MSprite *spr = asset->getFrame(asset->getCount() - 1); - bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface, + bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface, si._scale, spr->getTransparencyIndex()); } @@ -299,6 +299,7 @@ void SceneInfo::loadPalette(int sceneId, int artFileNum, const Common::String &r delete stream; // Copy out the palette animation data + _paletteCycles.clear(); for (uint i = 0; i < artHeader._paletteCycles.size(); ++i) _paletteCycles.push_back(artHeader._paletteCycles[i]); @@ -333,7 +334,7 @@ void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, // Get the ART resource if (sceneFlag) { - resourceName = Resources::formatName(RESPREFIX_RM, _artFileNum, ".ART"); + resourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".ART"); } else { resourceName = "*" + Resources::formatResource(resName, resName); } @@ -342,13 +343,33 @@ void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, File artFile(resourceName); MadsPack artResource(&artFile); - // Read in the background surface data - assert(_width == bgSurface.w && _height == bgSurface.h); + // Read inhh the background surface data + assert(_width && _height == bgSurface.h); stream = artResource.getItemStream(1); stream->read(bgSurface.getPixels(), bgSurface.w * bgSurface.h); + delete stream; + + if (flags & SCENEFLAG_TRANSLATE) { + // Load in the palette and translate it + Common::SeekableReadStream *palStream = artResource.getItemStream(0); + Common::Array<RGB6> palette; + + _width = palStream->readUint16LE(); + _height = palStream->readUint16LE(); + + int numColors = palStream->readUint16LE(); + assert(numColors <= 252); + palette.resize(numColors); + for (int i = 0; i < numColors; ++i) + palette[i].load(palStream); + delete palStream; + + // Translate the surface + _vm->_palette->_paletteUsage.process(palette, 0); + bgSurface.translate(palette); + } // Close the ART file - delete stream; artFile.close(); } diff --git a/engines/mads/scene_data.h b/engines/mads/scene_data.h index 783a9ab8a9..41e094b8f5 100644 --- a/engines/mads/scene_data.h +++ b/engines/mads/scene_data.h @@ -55,7 +55,8 @@ class SpriteSlot; enum { SCENEFLAG_DITHER = 0x01, // Dither to 16 colors - SCENEFLAG_LOAD_SHADOW = 0x10 // Load hard shadows + SCENEFLAG_LOAD_SHADOW = 0x10, // Load hard shadows + SCENEFLAG_TRANSLATE = 0x10000 // Translate palette of loaded background }; class VerbInit { diff --git a/engines/mads/screen.cpp b/engines/mads/screen.cpp index ab5dff56ff..c9a0863d85 100644 --- a/engines/mads/screen.cpp +++ b/engines/mads/screen.cpp @@ -212,8 +212,7 @@ void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common: Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y, srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y); - Common::Point destPos(bounds.left + _vm->_screen._offset.x, - bounds.top + _vm->_screen._offset.y); + Common::Point destPos(srcBounds.left, srcBounds.top); if ((*this)[i]._active && bounds.isValidRect()) { srcSurface->copyTo(destSurface, bounds, destPos); @@ -221,17 +220,14 @@ void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common: } } -void DirtyAreas::copyToScreen(const Common::Point &posAdjust) { +void DirtyAreas::copyToScreen() { for (uint i = 0; i < size(); ++i) { - const Common::Rect &srcBounds = (*this)[i]._bounds; + const Common::Rect &bounds = (*this)[i]._bounds; // Check if this is a sane rectangle before attempting to create it - if (srcBounds.left >= srcBounds.right || srcBounds.top >= srcBounds.bottom) + if (bounds.left >= bounds.right || bounds.top >= bounds.bottom) continue; - Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y, - srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y); - if ((*this)[i]._active && (*this)[i]._bounds.isValidRect()) { _vm->_screen.copyRectToScreen(bounds); } @@ -561,23 +557,32 @@ void ScreenObjects::synchronize(Common::Serializer &s) { ScreenSurface::ScreenSurface() { _shakeCountdown = -1; _random = 0x4D2; + _surfacePixels = nullptr; } void ScreenSurface::init() { - setSize(g_system->getWidth(), g_system->getHeight()); -} + // Set the size for the screen + setSize(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); -void ScreenSurface::copyRectToScreen(const Common::Point &destPos, - const Common::Rect &bounds) { - const byte *buf = getBasePtr(destPos.x, destPos.y); + // Store a copy of the raw pixels pointer for the screen, since the surface + // itself may be later changed to only a subset of the screen + _surfacePixels = (byte *)getPixels(); + _freeFlag = false; +} - if (bounds.width() != 0 && bounds.height() != 0) - g_system->copyRectToScreen(buf, this->pitch, bounds.left, bounds.top, - bounds.width(), bounds.height()); +ScreenSurface::~ScreenSurface() { + delete[] _surfacePixels; } void ScreenSurface::copyRectToScreen(const Common::Rect &bounds) { - copyRectToScreen(Common::Point(bounds.left, bounds.top), bounds); + const byte *buf = getBasePtr(bounds.left, bounds.top); + + Common::Rect destBounds = bounds; + destBounds.translate(_clipBounds.left, _clipBounds.top); + + if (bounds.width() != 0 && bounds.height() != 0) + g_system->copyRectToScreen(buf, this->pitch, destBounds.left, destBounds.top, + destBounds.width(), destBounds.height()); } void ScreenSurface::updateScreen() { @@ -659,4 +664,15 @@ void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag } } +void ScreenSurface::setClipBounds(const Common::Rect &r) { + _clipBounds = r; + setPixels(_surfacePixels + pitch * r.top + r.left, r.width(), r.height()); + this->pitch = MADS_SCREEN_WIDTH; +} + +void ScreenSurface::resetClipBounds() { + setClipBounds(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); +} + + } // End of namespace MADS diff --git a/engines/mads/screen.h b/engines/mads/screen.h index 7937e15456..9d01ca82e3 100644 --- a/engines/mads/screen.h +++ b/engines/mads/screen.h @@ -117,8 +117,8 @@ public: /** * Use the lsit of dirty areas to copy areas of the screen surface to * the physical screen - * @param posAdjust Position adjustment */ - void copyToScreen(const Common::Point &posAdjust); + */ + void copyToScreen(); void reset(); }; @@ -205,8 +205,9 @@ public: class ScreenSurface : public MSurface { private: uint16 _random; + byte *_surfacePixels; + Common::Rect _clipBounds; public: - Common::Point _offset; int _shakeCountdown; public: /** @@ -215,17 +216,14 @@ public: ScreenSurface(); /** - * Initialize the surface + * Destructor */ - void init(); + ~ScreenSurface(); /** - * Copys an area of the screen surface to a given destination position on - * the ScummVM physical screen buffer - * @param destPos Destination position - * @param bounds Area of screen surface to copy + * Initialize the surface */ - void copyRectToScreen(const Common::Point &destPos, const Common::Rect &bounds); + void init(); /** * Copys an area of the screen surface to the ScmmVM physical screen buffer @@ -239,6 +237,12 @@ public: void updateScreen(); void transition(ScreenTransition transitionType, bool surfaceFlag); + + void setClipBounds(const Common::Rect &r); + + void resetClipBounds(); + + const Common::Rect &getClipBounds() { return _clipBounds; } }; } // End of namespace MADS diff --git a/engines/mads/sequence.cpp b/engines/mads/sequence.cpp index 07b1451718..05f00afb5a 100644 --- a/engines/mads/sequence.cpp +++ b/engines/mads/sequence.cpp @@ -473,7 +473,7 @@ int SequenceList::startReverseCycle(int srcSpriteIndex, bool flipped, int numTic int depth = _vm->_game->_scene._depthSurface.getDepth(Common::Point( frame->_offset.x + frame->w / 2, frame->_offset.y + frame->h / 2)); - return add(srcSpriteIndex, flipped, sprites->getCount(), triggerCountdown, timeoutTicks, + return add(srcSpriteIndex, flipped, sprites->getCount(), triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, true, 100, depth - 1, -1, ANIMTYPE_REVERSIBLE, 0, 0); } diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp index bd99aed2f4..1652550ba3 100644 --- a/engines/mads/sound.cpp +++ b/engines/mads/sound.cpp @@ -36,10 +36,27 @@ SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) { _pollSoundEnabled = false; _soundPollFlag = false; _newSoundsPaused = false; + + _opl = OPL::Config::create(); + _opl->init(11025); + + // Validate sound files + switch (_vm->getGameID()) { + case GType_RexNebular: + Nebular::ASound::validate(); + break; + default: + break; + } } SoundManager::~SoundManager() { - delete _driver; + if (_driver) { + _driver->stop(); + delete _driver; + } + + delete _opl; } void SoundManager::init(int sectionNumber) { @@ -49,31 +66,32 @@ void SoundManager::init(int sectionNumber) { case GType_RexNebular: switch (sectionNumber) { case 1: - _driver = new Nebular::ASound1(_mixer); + _driver = new Nebular::ASound1(_mixer, _opl); break; case 2: - _driver = new Nebular::ASound2(_mixer); + _driver = new Nebular::ASound2(_mixer, _opl); break; case 3: - _driver = new Nebular::ASound3(_mixer); + _driver = new Nebular::ASound3(_mixer, _opl); break; case 4: - _driver = new Nebular::ASound4(_mixer); + _driver = new Nebular::ASound4(_mixer, _opl); break; case 5: - _driver = new Nebular::ASound5(_mixer); + _driver = new Nebular::ASound5(_mixer, _opl); break; case 6: - _driver = new Nebular::ASound6(_mixer); + _driver = new Nebular::ASound6(_mixer, _opl); break; case 7: - _driver = new Nebular::ASound7(_mixer); + _driver = new Nebular::ASound7(_mixer, _opl); break; case 8: - _driver = new Nebular::ASound8(_mixer); + _driver = new Nebular::ASound8(_mixer, _opl); break; case 9: - error("Sound driver 9 not implemented"); + _driver = new Nebular::ASound9(_mixer, _opl); + break; default: _driver = nullptr; break; diff --git a/engines/mads/sound.h b/engines/mads/sound.h index 9a251f9dd0..72bb21a812 100644 --- a/engines/mads/sound.h +++ b/engines/mads/sound.h @@ -37,6 +37,7 @@ class SoundManager { private: MADSEngine *_vm; Audio::Mixer *_mixer; + FM_OPL *_opl; Nebular::ASound *_driver; bool _pollSoundEnabled; bool _soundPollFlag; diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp index 2bf13eeb5a..fd73930475 100644 --- a/engines/mads/sprites.cpp +++ b/engines/mads/sprites.cpp @@ -262,7 +262,7 @@ void SpriteSlots::drawBackground() { scene._backgroundSurface.copyFrom(frame, pt, spriteSlot._depth, &scene._depthSurface, -1, false, frame->getTransparencyIndex()); } else { - error("Unsupported depth style"); + frame->copyTo(&scene._backgroundSurface, pt, frame->getTransparencyIndex()); } } } @@ -331,8 +331,6 @@ void SpriteSlots::drawSprites(MSurface *s) { xp = slot._position.x - (sprite->w / 2) - scene._posAdjust.x; yp = slot._position.y - sprite->h - scene._posAdjust.y + 1; } - xp += _vm->_screen._offset.x; - yp += _vm->_screen._offset.y; if (slot._depth > 1) { // Draw the frame with depth processing @@ -406,9 +404,9 @@ void SpriteSets::remove(int idx) { delete (*this)[idx]; (*this)[idx] = nullptr; } else { - while (size() > 0 && (*this)[size() - 1] == nullptr) { + do { remove_at(size() - 1); - } + } while (size() > 0 && (*this)[size() - 1] == nullptr); } if (_assetCount > 0) diff --git a/engines/mads/user_interface.h b/engines/mads/user_interface.h index f251441e40..89044c9bf1 100644 --- a/engines/mads/user_interface.h +++ b/engines/mads/user_interface.h @@ -225,7 +225,7 @@ public: /** * Loads an interface from a specified resource */ - void load(const Common::String &resName); + virtual void load(const Common::String &resName); /** * Set up the interface diff --git a/engines/mortevielle/detection_tables.h b/engines/mortevielle/detection_tables.h index 0aa27b89eb..d244d15365 100644 --- a/engines/mortevielle/detection_tables.h +++ b/engines/mortevielle/detection_tables.h @@ -75,7 +75,7 @@ static const MortevielleGameDescription MortevielleGameDescriptions[] = { // DOS English version doesn't exist. Technically, they are French or German versions, // using English strings stored mort.dat - + // English on top of French version { { diff --git a/engines/mortevielle/mortevielle.h b/engines/mortevielle/mortevielle.h index c3d1e4ae8b..5f7f175c26 100644 --- a/engines/mortevielle/mortevielle.h +++ b/engines/mortevielle/mortevielle.h @@ -421,7 +421,7 @@ public: byte *_curPict; byte *_curAnim; byte *_rightFramePict; - + PaletteManager _paletteManager; GfxSurface _backgroundSurface; Common::RandomSource _randomSource; diff --git a/engines/mortevielle/utils.cpp b/engines/mortevielle/utils.cpp index d5dec6a286..40136ad78b 100644 --- a/engines/mortevielle/utils.cpp +++ b/engines/mortevielle/utils.cpp @@ -3097,7 +3097,7 @@ void MortevielleEngine::putObject() { */ void MortevielleEngine::addObjectToInventory(int objectId) { int i; - + for (i = 1; (i <= 5) && (_coreVar._inventory[i] != 0); i++) ; diff --git a/engines/neverhood/console.cpp b/engines/neverhood/console.cpp index 91ab3e767a..7ee6b30311 100644 --- a/engines/neverhood/console.cpp +++ b/engines/neverhood/console.cpp @@ -55,7 +55,7 @@ bool Console::Cmd_Scene(int argc, const char **argv) { const char *sceneTypes[] = { "normal", "smacker", "navigation" }; - debugPrintf("Current module: %d, previous module: %d, scene %d (%s scene)\n", currentModule, previousModule, scenenNum, sceneTypes[sceneType]); + debugPrintf("Current module: %d, previous module: %d, scene %d (%s scene)\n", currentModule, previousModule, scenenNum, sceneTypes[sceneType]); if (sceneType == kSceneTypeNormal) { Scene *scene = (Scene *)((GameModule *)_vm->_gameModule->_childObject)->_childObject; diff --git a/engines/neverhood/sound.cpp b/engines/neverhood/sound.cpp index d53243d4ba..b15bea4a64 100644 --- a/engines/neverhood/sound.cpp +++ b/engines/neverhood/sound.cpp @@ -560,7 +560,7 @@ int NeverhoodAudioStream::readBuffer(int16 *buffer, const int numSamples) { } else { while (samplesRead--) { *buffer++ = READ_LE_UINT16(src); - src += 2; + src += 2; } } diff --git a/engines/pegasus/energymonitor.cpp b/engines/pegasus/energymonitor.cpp index 40e54afb89..d3cc208e41 100644 --- a/engines/pegasus/energymonitor.cpp +++ b/engines/pegasus/energymonitor.cpp @@ -68,7 +68,9 @@ void Blinker::timeChanged(const TimeValue time) { } } -static const NotificationFlags kEnergyExpiredFlag = 1; +enum { + kEnergyExpiredFlag = 1 +}; EnergyMonitor *g_energyMonitor = 0; diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp index e1b7e25cd5..73c319bd8b 100644 --- a/engines/pegasus/input.cpp +++ b/engines/pegasus/input.cpp @@ -57,7 +57,7 @@ InputDeviceManager::InputDeviceManager() { _keyMap[Common::KEYCODE_p] = false; _keyMap[Common::KEYCODE_TILDE] = false; _keyMap[Common::KEYCODE_BACKQUOTE] = false; - _keyMap[Common::KEYCODE_KP7] = false; + _keyMap[Common::KEYCODE_KP7] = false; _keyMap[Common::KEYCODE_BACKSPACE] = false; _keyMap[Common::KEYCODE_KP_MULTIPLY] = false; _keyMap[Common::KEYCODE_KP9] = false; diff --git a/engines/pegasus/interaction.cpp b/engines/pegasus/interaction.cpp new file mode 100644 index 0000000000..143bdebaba --- /dev/null +++ b/engines/pegasus/interaction.cpp @@ -0,0 +1,38 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/interaction.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +GameInteraction::GameInteraction(const InteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler(nextHandler) { + _isInteracting = false; + _savedHandler = 0; + _owner = nextHandler; +} + +} // End of namespace Pegasus + diff --git a/engines/pegasus/interaction.h b/engines/pegasus/interaction.h index 293ee6be83..ca168b4cb7 100644 --- a/engines/pegasus/interaction.h +++ b/engines/pegasus/interaction.h @@ -37,11 +37,7 @@ class Neighborhood; class GameInteraction : public IDObject, public InputHandler { public: - GameInteraction(const InteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler((InputHandler *)nextHandler) { - _isInteracting = false; - _savedHandler = 0; - _owner = nextHandler; - } + GameInteraction(const InteractionID id, Neighborhood *nextHandler); // If the interaction is open (_isInteracting == true), it's too late to do anything // about it here. diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk index cb44a04171..6d69d6ea58 100644 --- a/engines/pegasus/module.mk +++ b/engines/pegasus/module.mk @@ -12,6 +12,7 @@ MODULE_OBJS = \ graphics.o \ hotspot.o \ input.o \ + interaction.o \ interface.o \ menu.o \ movie.o \ diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp index 9d2d6723a9..ed52851338 100644 --- a/engines/pegasus/neighborhood/caldoria/caldoria.cpp +++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp @@ -42,109 +42,119 @@ namespace Pegasus { -static const int16 kVidPhoneAngle = 30; -static const int16 kReplicatorAngle = 50; -static const int16 kDrawersAngle = -30; -static const int16 kCaldoria53Angle = 45; -static const int16 kCaldoria55Angle = -45; +enum { + kVidPhoneAngle = 30, + kReplicatorAngle = 50, + kDrawersAngle = -30, + kCaldoria53Angle = 45, + kCaldoria55Angle = -45 +}; -static const TimeValue kSinclairInterruptionTime1 = 2955; -static const TimeValue kSinclairInterruptionTime2 = 6835; -static const TimeValue kSinclairInterruptionTime3 = 9835; -static const TimeValue kSinclairInterruptionTime4 = 12555; +enum { + kSinclairInterruptionTime1 = 2955, + kSinclairInterruptionTime2 = 6835, + kSinclairInterruptionTime3 = 9835, + kSinclairInterruptionTime4 = 12555 +}; -static const InputBits kPullbackInterruptFilter = kFilterAllInput; -static const InputBits kRecalibrationInterruptFilter = kFilterAllInput; +enum { + kPullbackInterruptFilter = kFilterAllInput, + kRecalibrationInterruptFilter = kFilterAllInput +}; -static const TimeValue kCaldoriaReplicatorIntroIn = 4933; -static const TimeValue kCaldoriaReplicatorIntroOut = 6557; +enum { + kCaldoriaReplicatorIntroIn = 4933, + kCaldoriaReplicatorIntroOut = 6557, -static const TimeValue kCaldoriaReplicatorWrongChoiceIn = 6557; -static const TimeValue kCaldoriaReplicatorWrongChoiceOut = 8586; + kCaldoriaReplicatorWrongChoiceIn = 6557, + kCaldoriaReplicatorWrongChoiceOut = 8586, -static const TimeValue kCaldoriaReplicatorOJChoiceIn = 8586; -static const TimeValue kCaldoriaReplicatorOJChoiceOut = 11687; + kCaldoriaReplicatorOJChoiceIn = 8586, + kCaldoriaReplicatorOJChoiceOut = 11687, -static const TimeValue kCaldoriaMessagesIntroIn = 11687; -static const TimeValue kCaldoriaMessagesIntroOut = 13641; + kCaldoriaMessagesIntroIn = 11687, + kCaldoriaMessagesIntroOut = 13641, -static const TimeValue kCaldoriaFirstMessageIn = 13641; -static const TimeValue kCaldoriaFirstMessageOut = 14203; + kCaldoriaFirstMessageIn = 13641, + kCaldoriaFirstMessageOut = 14203, -static const TimeValue kCaldoriaSecondMessageIn = 14203; -static const TimeValue kCaldoriaSecondMessageOut = 14750; + kCaldoriaSecondMessageIn = 14203, + kCaldoriaSecondMessageOut = 14750, -static const TimeValue kCaldoriaDoorCloseIn = 14750; -static const TimeValue kCaldoriaDoorCloseOut = 15472; + kCaldoriaDoorCloseIn = 14750, + kCaldoriaDoorCloseOut = 15472, -static const TimeValue kCaldoriaElevatorCloseIn = 15472; -static const TimeValue kCaldoriaElevatorCloseOut = 16336; + kCaldoriaElevatorCloseIn = 15472, + kCaldoriaElevatorCloseOut = 16336, -static const TimeValue kCaldoriaShowerCloseIn = 16336; -static const TimeValue kCaldoriaShowerCloseOut = 17101; + kCaldoriaShowerCloseIn = 16336, + kCaldoriaShowerCloseOut = 17101, -static const TimeValue kCaldoriaGTDoorCloseIn = 17101; -static const TimeValue kCaldoriaGTDoorCloseOut = 18523; + kCaldoriaGTDoorCloseIn = 17101, + kCaldoriaGTDoorCloseOut = 18523, -static const TimeValue kCaldoriaNobodyHomeIn = 18523; -static const TimeValue kCaldoriaNobodyHomeOut = 21469; + kCaldoriaNobodyHomeIn = 18523, + kCaldoriaNobodyHomeOut = 21469, -static const TimeValue kCaldoriaNoOtherFloorIn = 21469; -static const TimeValue kCaldoriaNoOtherFloorOut = 28013; + kCaldoriaNoOtherFloorIn = 21469, + kCaldoriaNoOtherFloorOut = 28013, -static const TimeValue kCaldoria4DInstructionsIn = 28013; -static const TimeValue kCaldoria4DInstructionsOut = 29730; + kCaldoria4DInstructionsIn = 28013, + kCaldoria4DInstructionsOut = 29730, -static const TimeValue kCaldoriaDrinkOJIn = 33910; -static const TimeValue kCaldoriaDrinkOJOut = 35846; + kCaldoriaDrinkOJIn = 33910, + kCaldoriaDrinkOJOut = 35846, -static const TimeValue kCaldoriaNoOtherDestinationIn = 35846; -static const TimeValue kCaldoriaNoOtherDestinationOut = 37877; + kCaldoriaNoOtherDestinationIn = 35846, + kCaldoriaNoOtherDestinationOut = 37877, -static const TimeValue kCaldoriaUhghIn = 37877; -static const TimeValue kCaldoriaUhghOut = 38025; + kCaldoriaUhghIn = 37877, + kCaldoriaUhghOut = 38025, -static const TimeValue kCaldoriaSinclairShootsOSIn = 38025; -static const TimeValue kCaldoriaSinclairShootsOSOut = 40649; + kCaldoriaSinclairShootsOSIn = 38025, + kCaldoriaSinclairShootsOSOut = 40649, -static const TimeValue kCaldoriaScreamingAfterIn = 40649; -static const TimeValue kCaldoriaScreamingAfterOut = 47661; + kCaldoriaScreamingAfterIn = 40649, + kCaldoriaScreamingAfterOut = 47661 +}; -static const TimeValue k4FloorTime = 0; +enum { + k4FloorTime = 0, -static const TimeValue k4To1Start = 40; -static const TimeValue k4To1Stop = 7720; + k4To1Start = 40, + k4To1Stop = 7720, -static const TimeValue k4To5Start = 7720; -static const TimeValue k4To5Stop = 10280; + k4To5Start = 7720, + k4To5Stop = 10280, -static const TimeValue k4To2Time = 10280; + k4To2Time = 10280, -static const TimeValue k4To3Time = 10320; + k4To3Time = 10320, -static const TimeValue k1FloorTime = 10360; + k1FloorTime = 10360, -static const TimeValue k1To4Start = 10400; -static const TimeValue k1To4Stop = 18080; + k1To4Start = 10400, + k1To4Stop = 18080, -static const TimeValue k1To5Start = 18080; -static const TimeValue k1To5Stop = 28320; + k1To5Start = 18080, + k1To5Stop = 28320, -static const TimeValue k1To2Time = 28320; + k1To2Time = 28320, -static const TimeValue k1To3Time = 28360; + k1To3Time = 28360, -static const TimeValue k5FloorTime = 28400; + k5FloorTime = 28400, -static const TimeValue k5To1Start = 28440; -static const TimeValue k5To1Stop = 38680; + k5To1Start = 28440, + k5To1Stop = 38680, -static const TimeValue k5To4Start = 38680; -static const TimeValue k5To4Stop = 41240; + k5To4Start = 38680, + k5To4Stop = 41240, -static const TimeValue k5To2Time = 41240; + k5To2Time = 41240, -static const TimeValue k5To3Time = 41280; + k5To3Time = 41280 +}; // FuseFunction functions... diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp index 0494753661..688fb7860d 100644 --- a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp @@ -30,34 +30,36 @@ namespace Pegasus { -static const TimeValue kSwitchableSlop = 3 * kCaldoriaFrameDuration; -// Two seconds - some slop -static const TimeValue kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop; -// Twelve frames + some slop -static const TimeValue kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop; +enum { + kSwitchableSlop = 3 * kCaldoriaFrameDuration, + // Two seconds - some slop + kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop, + // Twelve frames + some slop + kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop, -static const TimeValue kSwitchable1Start = 0; -static const TimeValue kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration; + kSwitchable1Start = 0, + kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration, -static const TimeValue kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration; -static const TimeValue kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration; + kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration, + kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration, -static const TimeValue kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration; -static const TimeValue kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration; + kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration, + kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration, -static const NotificationFlags kVidPhoneDoneFlag = 1; + kVidPhoneDoneFlag = 1, -static const TimeValue kRockMusicLoopIn = 0; -static const TimeValue kRockMusicLoopOut = 2088; + kRockMusicLoopIn = 0, + kRockMusicLoopOut = 2088, -static const TimeValue kOrchestralMusicLoopIn = 2088; -static const TimeValue kOrchestralMusicLoopOut = 4985; + kOrchestralMusicLoopIn = 2088, + kOrchestralMusicLoopOut = 4985, -static const TimeValue kRhythmsMusicLoopIn = 4985; -static const TimeValue kRhythmsMusicLoopOut = 6824; + kRhythmsMusicLoopIn = 4985, + kRhythmsMusicLoopOut = 6824, -static const TimeValue kAcousticMusicLoopIn = 6824; -static const TimeValue kAcousticMusicLoopOut = 9387; + kAcousticMusicLoopIn = 6824, + kAcousticMusicLoopOut = 9387 +}; enum { k4DVideoMenu, diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.cpp b/engines/pegasus/neighborhood/mars/shuttlehud.cpp index 11e826278b..2d894f7b95 100644 --- a/engines/pegasus/neighborhood/mars/shuttlehud.cpp +++ b/engines/pegasus/neighborhood/mars/shuttlehud.cpp @@ -30,26 +30,28 @@ namespace Pegasus { -static const CoordType kHUDTargetGridLeft = kShuttleWindowLeft + 16; -static const CoordType kHUDTargetGridTop = kShuttleWindowTop + 8; -static const CoordType kHUDTargetGridWidth = 328; -static const CoordType kHUDTargetGridHeight = 206; - -static const CoordType kHUDRS232Left = kHUDTargetGridLeft + 264; -static const CoordType kHUDRS232Top = kHUDTargetGridTop + 2; - -static const CoordType kHUDLockLeft = kShuttleWindowLeft + 101; -static const CoordType kHUDLockTop = kShuttleWindowTop + 49; -static const CoordType kHUDLockWidth = 145; -static const CoordType kHUDLockHeight = 124; - -static const CoordType kTractorLockWidth = 50; -static const CoordType kTractorLockHeight = 30; - -static const CoordType kTractorLockLeft = kShuttleWindowMidH - kTractorLockWidth / 2; -static const CoordType kTractorLockTop = kShuttleWindowMidV - kTractorLockHeight / 2; -static const CoordType kTractorLockRight = kTractorLockLeft + kTractorLockWidth; -static const CoordType kTractorLockBottom = kTractorLockTop + kTractorLockHeight; +enum { + kHUDTargetGridLeft = kShuttleWindowLeft + 16, + kHUDTargetGridTop = kShuttleWindowTop + 8, + kHUDTargetGridWidth = 328, + kHUDTargetGridHeight = 206, + + kHUDRS232Left = kHUDTargetGridLeft + 264, + kHUDRS232Top = kHUDTargetGridTop + 2, + + kHUDLockLeft = kShuttleWindowLeft + 101, + kHUDLockTop = kShuttleWindowTop + 49, + kHUDLockWidth = 145, + kHUDLockHeight = 124, + + kTractorLockWidth = 50, + kTractorLockHeight = 30, + + kTractorLockLeft = kShuttleWindowMidH - kTractorLockWidth / 2, + kTractorLockTop = kShuttleWindowMidV - kTractorLockHeight / 2, + kTractorLockRight = kTractorLockLeft + kTractorLockWidth, + kTractorLockBottom = kTractorLockTop + kTractorLockHeight +}; static const uint16 s_RS232Data[] = { 0xF0E1, 0xCE70, diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp index e2a0267231..1478a74744 100644 --- a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp @@ -45,7 +45,9 @@ static const TimeValue kSection2Start = 26; static const TimeValue kSection2Stop = 1000; // Seems to be a good value for a 20 second pan. -static const CoordType kPanPixelsPerFrame = 8; +enum { + kPanPixelsPerFrame = 8 +}; // Interesting times are in seconds. static const TimeValue s_ECRInterestingTimes[] = { diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp index 3491f161c7..e85a3e699f 100644 --- a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp @@ -65,64 +65,66 @@ static const ItemID kCO2Item = 10000; static const ItemID kHeItem = 10001; // Interactive points. -static const TimeValue kFSPowerUpStartStart = 0; -static const TimeValue kFSPowerUpStartStop = 600; -static const TimeValue kFSSplashStart = 600; -static const TimeValue kFSSplashStop = 7800; -static const TimeValue kFSSplashIntakeStart = 7800; -static const TimeValue kFSSplashIntakeStop = 18600; - -static const TimeValue kFSMainMenu = 18600; -static const TimeValue kFSIntakeHiliteStart = 19200; -static const TimeValue kFSIntakeHiliteStop = 19800; -static const TimeValue kFSDispenseHiliteStart = 19800; -static const TimeValue kFSDispenseHiliteStop = 20400; - -static const TimeValue kFSDispenseMenu = 20400; - -static const TimeValue kFSArHiliteStart = 21000; -static const TimeValue kFSArHiliteStop = 21600; -static const TimeValue kFSArAttach = 21600; -static const TimeValue kFSArFilledStart = 22200; -static const TimeValue kFSArFilledStop = 25200; -static const TimeValue kFSArIncompatibleStart = 25200; -static const TimeValue kFSArIncompatibleStop = 30000; - -static const TimeValue kFSCO2HiliteStart = 30000; -static const TimeValue kFSCO2HiliteStop = 30600; -static const TimeValue kFSCO2Attach = 30600; -static const TimeValue kFSCO2FilledStart = 31200; -static const TimeValue kFSCO2FilledStop = 34200; -static const TimeValue kFSCO2IncompatibleStart = 34200; -static const TimeValue kFSCO2IncompatibleStop = 39000; - -static const TimeValue kFSHeHiliteStart = 39000; -static const TimeValue kFSHeHiliteStop = 39600; -static const TimeValue kFSHeAttach = 39600; -static const TimeValue kFSHeFilledStart = 40200; -static const TimeValue kFSHeFilledStop = 43200; -static const TimeValue kFSHeIncompatibleStart = 43200; -static const TimeValue kFSHeIncompatibleStop = 48000; - -static const TimeValue kFSOHiliteStart = 48000; -static const TimeValue kFSOHiliteStop = 48600; -static const TimeValue kFSOAttach = 48600; -static const TimeValue kFSOFilledStart = 49200; -static const TimeValue kFSOFilledStop = 52200; -static const TimeValue kFSOIncompatibleStart = 52200; -static const TimeValue kFSOIncompatibleStop = 57000; - -static const TimeValue kFSNHiliteStart = 57000; -static const TimeValue kFSNHiliteStop = 57600; -static const TimeValue kFSNAttach = 57600; -static const TimeValue kFSNFilledStart = 58200; -static const TimeValue kFSNFilledStop = 61200; -static const TimeValue kFSNIncompatibleStart = 61200; -static const TimeValue kFSNIncompatibleStop = 66000; - -static const TimeValue kFSIntakeMenu = 66000; -static const TimeValue kFSIntakeInProgressStart = 66600; -static const TimeValue kFSIntakeInProgressStop = 69600; +enum { + kFSPowerUpStartStart = 0, + kFSPowerUpStartStop = 600, + kFSSplashStart = 600, + kFSSplashStop = 7800, + kFSSplashIntakeStart = 7800, + kFSSplashIntakeStop = 18600, + + kFSMainMenu = 18600, + kFSIntakeHiliteStart = 19200, + kFSIntakeHiliteStop = 19800, + kFSDispenseHiliteStart = 19800, + kFSDispenseHiliteStop = 20400, + + kFSDispenseMenu = 20400, + + kFSArHiliteStart = 21000, + kFSArHiliteStop = 21600, + kFSArAttach = 21600, + kFSArFilledStart = 22200, + kFSArFilledStop = 25200, + kFSArIncompatibleStart = 25200, + kFSArIncompatibleStop = 30000, + + kFSCO2HiliteStart = 30000, + kFSCO2HiliteStop = 30600, + kFSCO2Attach = 30600, + kFSCO2FilledStart = 31200, + kFSCO2FilledStop = 34200, + kFSCO2IncompatibleStart = 34200, + kFSCO2IncompatibleStop = 39000, + + kFSHeHiliteStart = 39000, + kFSHeHiliteStop = 39600, + kFSHeAttach = 39600, + kFSHeFilledStart = 40200, + kFSHeFilledStop = 43200, + kFSHeIncompatibleStart = 43200, + kFSHeIncompatibleStop = 48000, + + kFSOHiliteStart = 48000, + kFSOHiliteStop = 48600, + kFSOAttach = 48600, + kFSOFilledStart = 49200, + kFSOFilledStop = 52200, + kFSOIncompatibleStart = 52200, + kFSOIncompatibleStop = 57000, + + kFSNHiliteStart = 57000, + kFSNHiliteStop = 57600, + kFSNAttach = 57600, + kFSNFilledStart = 58200, + kFSNFilledStop = 61200, + kFSNIncompatibleStart = 61200, + kFSNIncompatibleStop = 66000, + + kFSIntakeMenu = 66000, + kFSIntakeInProgressStart = 66600, + kFSIntakeInProgressStop = 69600 +}; NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner), _rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, ((PegasusEngine *)g_engine)) { diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp index 5c321a8e8a..9ea3036024 100644 --- a/engines/pegasus/neighborhood/norad/delta/globegame.cpp +++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp @@ -394,20 +394,22 @@ static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished | kGlobeTimerExpired | kMaxDeactivatedFinished; -static const int16 kSplash1End = 4; -static const int16 kSplash2End = 5; -static const int16 kSplash3Start = 8; -static const int16 kSplash3Stop = 9; -static const int16 kSplash4Start = 9; -static const int16 kSplash4Stop = 10; -static const int16 kNewLaunchSiloTime = 10; -static const int16 kSiloDeactivatedTime = 11; -static const int16 kMissileLaunchedTime = 12; -static const int16 kMaxDeactivatedStart = 13; -static const int16 kMaxDeactivatedStop = 23; - -static const int16 kGamePlaying = 1; -static const int16 kGameOver = 2; +enum { + kSplash1End = 4, + kSplash2End = 5, + kSplash3Start = 8, + kSplash3Stop = 9, + kSplash4Start = 9, + kSplash4Stop = 10, + kNewLaunchSiloTime = 10, + kSiloDeactivatedTime = 11, + kMissileLaunchedTime = 12, + kMaxDeactivatedStart = 13, + kMaxDeactivatedStop = 23, + + kGamePlaying = 1, + kGameOver = 2 +}; enum { kGameIntro, diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp index d48481e925..1b14c529d8 100644 --- a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp @@ -34,110 +34,113 @@ namespace Pegasus { // Right Monitor times -static const TimeValue kAlphaClawSplashStart = 0; -static const TimeValue kAlphaClawSplashStop = 4000; - -static const TimeValue kDeltaClawSplashStart = 4000; -static const TimeValue kDeltaClawSplashStop = 8000; - -static const TimeValue kClawAtATime = 8000; -static const TimeValue kClawAtAPinchedTime = 8600; -static const TimeValue kClawAtATurnedTime = 9200; -static const TimeValue kClawAtAWithRobotPinchedTime = 9800; - -static const TimeValue kClawAtBTime = 10400; -static const TimeValue kClawAtBPinchedTime = 11000; -static const TimeValue kClawAtBTurnedTime = 11600; -static const TimeValue kClawAtBWithRobotTime = 12200; -static const TimeValue kClawAtBWithRobotPinchedTime = 12800; - -static const TimeValue kClawAtCTime = 13400; -static const TimeValue kClawAtCPinchedTime = 14000; -static const TimeValue kClawAtCTurnedTime = 14600; - -static const TimeValue kClawAtDTime = 15200; -static const TimeValue kClawAtDPinchedTime = 15800; -static const TimeValue kClawAtDTurnedTime = 16400; - -static const TimeValue kAToBStart = 17000; -static const TimeValue kAToBStop = 18680; -static const TimeValue kAPinchStart = 18680; -static const TimeValue kAPinchStop = 20200; -static const TimeValue kACCWStart = 20200; -static const TimeValue kACCWStop = 21600; -static const TimeValue kACWStart = 21600; -static const TimeValue kACWStop = 23000; - -static const TimeValue kBToAStart = 23000; -static const TimeValue kBToAStop = 24680; -static const TimeValue kBToCStart = 24680; -static const TimeValue kBToCStop = 26520; -static const TimeValue kBToDStart = 26520; -static const TimeValue kBToDStop = 28320; -static const TimeValue kBPinchStart = 28320; -static const TimeValue kBPinchStop = 29680; -static const TimeValue kBCCWStart = 29680; -static const TimeValue kBCCWStop = 31200; -static const TimeValue kBCWStart = 31200; -static const TimeValue kBCWStop = 32720; - -static const TimeValue kCToBStart = 32720; -static const TimeValue kCToBStop = 34560; -static const TimeValue kCPinchStart = 34560; -static const TimeValue kCPinchStop = 36400; -static const TimeValue kCCCWStart = 36400; -static const TimeValue kCCCWStop = 37840; -static const TimeValue kCCWStart = 37840; -static const TimeValue kCCWStop = 39280; - -static const TimeValue kDToBStart = 39280; -static const TimeValue kDToBStop = 41080; -static const TimeValue kDPinchStart = 41080; -static const TimeValue kDPinchStop = 42600; -static const TimeValue kDCCWStart = 42600; -static const TimeValue kDCCWStop = 44000; -static const TimeValue kDCWStart = 44000; -static const TimeValue kDCWStop = 45400; - -static const TimeValue kRobotApproachStart = 45400; -static const TimeValue kRobotApproachStop = 56800; - -static const TimeValue kCToBWithRobotStart = 56800; -static const TimeValue kCToBWithRobotStop = 58600; - -static const TimeValue kBPinchWithRobotStart = 58600; -static const TimeValue kBPinchWithRobotStop = 60400; -static const TimeValue kBToAWithRobotStart = 60400; -static const TimeValue kBToAWithRobotStop = 62240; +enum { + kAlphaClawSplashStart = 0, + kAlphaClawSplashStop = 4000, + + kDeltaClawSplashStart = 4000, + kDeltaClawSplashStop = 8000, + + kClawAtATime = 8000, + kClawAtAPinchedTime = 8600, + kClawAtATurnedTime = 9200, + kClawAtAWithRobotPinchedTime = 9800, + + kClawAtBTime = 10400, + kClawAtBPinchedTime = 11000, + kClawAtBTurnedTime = 11600, + kClawAtBWithRobotTime = 12200, + kClawAtBWithRobotPinchedTime = 12800, + + kClawAtCTime = 13400, + kClawAtCPinchedTime = 14000, + kClawAtCTurnedTime = 14600, + + kClawAtDTime = 15200, + kClawAtDPinchedTime = 15800, + kClawAtDTurnedTime = 16400, + + kAToBStart = 17000, + kAToBStop = 18680, + kAPinchStart = 18680, + kAPinchStop = 20200, + kACCWStart = 20200, + kACCWStop = 21600, + kACWStart = 21600, + kACWStop = 23000, + + kBToAStart = 23000, + kBToAStop = 24680, + kBToCStart = 24680, + kBToCStop = 26520, + kBToDStart = 26520, + kBToDStop = 28320, + kBPinchStart = 28320, + kBPinchStop = 29680, + kBCCWStart = 29680, + kBCCWStop = 31200, + kBCWStart = 31200, + kBCWStop = 32720, + + kCToBStart = 32720, + kCToBStop = 34560, + kCPinchStart = 34560, + kCPinchStop = 36400, + kCCCWStart = 36400, + kCCCWStop = 37840, + kCCWStart = 37840, + kCCWStop = 39280, + + kDToBStart = 39280, + kDToBStop = 41080, + kDPinchStart = 41080, + kDPinchStop = 42600, + kDCCWStart = 42600, + kDCCWStop = 44000, + kDCWStart = 44000, + kDCWStop = 45400, + + kRobotApproachStart = 45400, + kRobotApproachStop = 56800, + + kCToBWithRobotStart = 56800, + kCToBWithRobotStop = 58600, + + kBPinchWithRobotStart = 58600, + kBPinchWithRobotStop = 60400, + kBToAWithRobotStart = 60400, + kBToAWithRobotStop = 62240 +}; // As usual, times here are in seconds. // Left monitor times. +enum { + kAlphaSplashStart = 0, + kAlphaSplashStop = 2, -static const TimeValue kAlphaSplashStart = 0; -static const TimeValue kAlphaSplashStop = 2; - -static const TimeValue kMainMenuTime = 2; -static const TimeValue kLaunchPrepRolloverTime = 3; -static const TimeValue kLaunchPrepHighlightStart = 4; -static const TimeValue kLaunchPrepHighlightStop = 5; -static const TimeValue kClawControlRolloverTime = 5; -static const TimeValue kClawControlHighlightStart = 6; -static const TimeValue kClawControlHighlightStop = 7; + kMainMenuTime = 2, + kLaunchPrepRolloverTime = 3, + kLaunchPrepHighlightStart = 4, + kLaunchPrepHighlightStop = 5, + kClawControlRolloverTime = 5, + kClawControlHighlightStart = 6, + kClawControlHighlightStop = 7, -static const TimeValue kAlphaLaunchPrepStart = 7; -static const TimeValue kAlphaLaunchPrepStop = 17; + kAlphaLaunchPrepStart = 7, + kAlphaLaunchPrepStop = 17, -static const TimeValue kClawMenuStart = 17; -static const TimeValue kClawMenuStop = 18; + kClawMenuStart = 17, + kClawMenuStop = 18, -static const TimeValue kClawMenuTime = 18; + kClawMenuTime = 18, -static const TimeValue kDeltaSplashStart = 19; -static const TimeValue kDeltaSplashStop = 21; + kDeltaSplashStart = 19, + kDeltaSplashStop = 21, -static const TimeValue kDeltaLaunchPrepStart = 21; -static const TimeValue kDeltaLaunchPrepStop = 30; + kDeltaLaunchPrepStart = 21, + kDeltaLaunchPrepStop = 30 +}; // Right monitor times. diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp index 92b79c038e..f7996fabf5 100644 --- a/engines/pegasus/neighborhood/tsa/fulltsa.cpp +++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp @@ -41,41 +41,45 @@ namespace Pegasus { // TSA PICTs: -static const ResIDType kTBPCloseBoxPICTID = 800; -static const ResIDType kTBPRewindPICTID = 801; -static const ResIDType kUnresolvedPICTID = 802; -static const ResIDType kResolvedPICTID = 803; -static const ResIDType kJumpMenuPICTID = 804; -static const ResIDType kJumpMenuHilitedPICTID = 805; -static const ResIDType kExitPICTID = 806; -static const ResIDType kExitHilitedPICTID = 807; -static const ResIDType kLeftRipPICTID = 808; -static const ResIDType kComparisonCloseBoxPICTID = 809; -static const ResIDType kComparisonLeftRewindPICTID = 810; -static const ResIDType kComparisonRightRewindPICTID = 811; -static const ResIDType kComparisonHiliteNoradPICTID = 812; -static const ResIDType kComparisonHiliteMarsPICTID = 813; -static const ResIDType kComparisonHiliteCaldoriaPICTID = 814; -static const ResIDType kComparisonHiliteWSCPICTID = 815; -static const ResIDType kComparisonChancesNoradPICTID = 816; -static const ResIDType kComparisonChancesMarsPICTID = 817; -static const ResIDType kComparisonChancesCaldoriaPICTID = 818; -static const ResIDType kComparisonChancesWSCPICTID = 819; -static const ResIDType kRedirectionCCRolloverPICTID = 820; -static const ResIDType kRedirectionRRRolloverPICTID = 821; -static const ResIDType kRedirectionFDRolloverPICTID = 822; -static const ResIDType kRedirectionCCDoorPICTID = 823; -static const ResIDType kRedirectionRRDoorPICTID = 824; -static const ResIDType kRedirectionFDDoorPICTID = 825; -static const ResIDType kRedirectionSecuredPICTID = 826; -static const ResIDType kRedirectionNewTargetPICTID = 827; -static const ResIDType kRedirectionClosePICTID = 828; +enum { + kTBPCloseBoxPICTID = 800, + kTBPRewindPICTID = 801, + kUnresolvedPICTID = 802, + kResolvedPICTID = 803, + kJumpMenuPICTID = 804, + kJumpMenuHilitedPICTID = 805, + kExitPICTID = 806, + kExitHilitedPICTID = 807, + kLeftRipPICTID = 808, + kComparisonCloseBoxPICTID = 809, + kComparisonLeftRewindPICTID = 810, + kComparisonRightRewindPICTID = 811, + kComparisonHiliteNoradPICTID = 812, + kComparisonHiliteMarsPICTID = 813, + kComparisonHiliteCaldoriaPICTID = 814, + kComparisonHiliteWSCPICTID = 815, + kComparisonChancesNoradPICTID = 816, + kComparisonChancesMarsPICTID = 817, + kComparisonChancesCaldoriaPICTID = 818, + kComparisonChancesWSCPICTID = 819, + kRedirectionCCRolloverPICTID = 820, + kRedirectionRRRolloverPICTID = 821, + kRedirectionFDRolloverPICTID = 822, + kRedirectionCCDoorPICTID = 823, + kRedirectionRRDoorPICTID = 824, + kRedirectionFDDoorPICTID = 825, + kRedirectionSecuredPICTID = 826, + kRedirectionNewTargetPICTID = 827, + kRedirectionClosePICTID = 828 +}; static const int16 kCompassShift = 15; -static const TimeScale kFullTSAMovieScale = 600; -static const TimeScale kFullTSAFramesPerSecond = 15; -static const TimeScale kFullTSAFrameDuration = 40; +enum { + kFullTSAMovieScale = 600, + kFullTSAFramesPerSecond = 15, + kFullTSAFrameDuration = 40 +}; // Alternate IDs. static const AlternateID kAltTSANormal = 0; @@ -84,416 +88,425 @@ static const AlternateID kAltTSARobotsAtFrontDoor = 2; static const AlternateID kAltTSARedAlert = 3; // Room IDs. -static const RoomID kTSA01 = 1; -static const RoomID kTSA02 = 2; -static const RoomID kTSA03 = 3; -static const RoomID kTSA04 = 4; -static const RoomID kTSA05 = 5; -static const RoomID kTSA0A = 6; -static const RoomID kTSA06 = 7; -static const RoomID kTSA07 = 8; -static const RoomID kTSA08 = 9; -static const RoomID kTSA09 = 10; -static const RoomID kTSA10 = 11; -static const RoomID kTSA11 = 12; -static const RoomID kTSA12 = 13; -static const RoomID kTSA13 = 14; -static const RoomID kTSA14 = 15; -static const RoomID kTSA15 = 16; -static const RoomID kTSA16 = 17; -static const RoomID kTSA17 = 18; -static const RoomID kTSA18 = 19; -static const RoomID kTSA19 = 20; -static const RoomID kTSA0B = 21; -static const RoomID kTSA21Cyan = 22; -static const RoomID kTSA22Cyan = 23; -static const RoomID kTSA23Cyan = 24; -static const RoomID kTSA24Cyan = 25; -static const RoomID kTSA25Cyan = 26; -static const RoomID kTSA21Red = 27; -static const RoomID kTSA23Red = 29; -static const RoomID kTSA24Red = 30; -static const RoomID kTSA25Red = 31; -static const RoomID kTSA26 = 32; -static const RoomID kTSA27 = 33; -static const RoomID kTSA28 = 34; -static const RoomID kTSA29 = 35; -static const RoomID kTSA30 = 36; -static const RoomID kTSA31 = 37; -static const RoomID kTSA32 = 38; -static const RoomID kTSA33 = 39; -static const RoomID kTSA34 = 40; -static const RoomID kTSA35 = 41; -static const RoomID kTSADeathRoom = 43; +enum { + kTSA01 = 1, + kTSA02 = 2, + kTSA03 = 3, + kTSA04 = 4, + kTSA05 = 5, + kTSA0A = 6, + kTSA06 = 7, + kTSA07 = 8, + kTSA08 = 9, + kTSA09 = 10, + kTSA10 = 11, + kTSA11 = 12, + kTSA12 = 13, + kTSA13 = 14, + kTSA14 = 15, + kTSA15 = 16, + kTSA16 = 17, + kTSA17 = 18, + kTSA18 = 19, + kTSA19 = 20, + kTSA0B = 21, + kTSA21Cyan = 22, + kTSA22Cyan = 23, + kTSA23Cyan = 24, + kTSA24Cyan = 25, + kTSA25Cyan = 26, + kTSA21Red = 27, + kTSA23Red = 29, + kTSA24Red = 30, + kTSA25Red = 31, + kTSA26 = 32, + kTSA27 = 33, + kTSA28 = 34, + kTSA29 = 35, + kTSA30 = 36, + kTSA31 = 37, + kTSA32 = 38, + kTSA33 = 39, + kTSA34 = 40, + kTSA35 = 41, + kTSADeathRoom = 43 +}; // Hot Spot Activation IDs. -static const HotSpotActivationID kActivateTSAReadyForCard = 1; -static const HotSpotActivationID kActivateTSAReadyToTransport = 2; -static const HotSpotActivationID kActivateTSARobotsAwake = 3; -static const HotSpotActivationID kActivateTSA0BZoomedOut = 4; -static const HotSpotActivationID kActivateTSA0BZoomedIn = 5; -static const HotSpotActivationID kActivateTSA0BComparisonVideo = 6; -static const HotSpotActivationID kActivationLogReaderOpen = 7; -static const HotSpotActivationID kActivateTSA0BTBPVideo = 8; -static const HotSpotActivationID kActivationDoesntHaveKey = 9; -static const HotSpotActivationID kActivationKeyVaultOpen = 10; -static const HotSpotActivationID kActivationDoesntHaveChips = 11; -static const HotSpotActivationID kActivationChipVaultOpen = 12; -static const HotSpotActivationID kActivationJumpToPrehistoric = 13; -static const HotSpotActivationID kActivationJumpToNorad = 14; -static const HotSpotActivationID kActivationJumpToMars = 15; -static const HotSpotActivationID kActivationJumpToWSC = 16; -static const HotSpotActivationID kActivationReadyToExit = 17; -static const HotSpotActivationID kActivationReadyForJumpMenu = 18; -static const HotSpotActivationID kActivationMainJumpMenu = 19; +enum { + kActivateTSAReadyForCard = 1, + kActivateTSAReadyToTransport = 2, + kActivateTSARobotsAwake = 3, + kActivateTSA0BZoomedOut = 4, + kActivateTSA0BZoomedIn = 5, + kActivateTSA0BComparisonVideo = 6, + kActivationLogReaderOpen = 7, + kActivateTSA0BTBPVideo = 8, + kActivationDoesntHaveKey = 9, + kActivationKeyVaultOpen = 10, + kActivationDoesntHaveChips = 11, + kActivationChipVaultOpen = 12, + kActivationJumpToPrehistoric = 13, + kActivationJumpToNorad = 14, + kActivationJumpToMars = 15, + kActivationJumpToWSC = 16, + kActivationReadyToExit = 17, + kActivationReadyForJumpMenu = 18, + kActivationMainJumpMenu = 19 +}; // Hot Spot IDs. -static const HotSpotID kTSAGTCardDropSpotID = 5000; -static const HotSpotID kTSAGTTokyoSpotID = 5001; -static const HotSpotID kTSAGTCaldoriaSpotID = 5002; -static const HotSpotID kTSAGTBeachSpotID = 5003; -static const HotSpotID kTSAGTOtherSpotID = 5004; -static const HotSpotID kTSA02DoorSpotID = 5005; -static const HotSpotID kTSA03EastJimenezSpotID = 5006; -static const HotSpotID kTSA03WestCrenshawSpotID = 5007; -static const HotSpotID kTSA04EastMatsumotoSpotID = 5008; -static const HotSpotID kTSA04WestCastilleSpotID = 5009; -static const HotSpotID kTSA05EastSinclairSpotID = 5010; -static const HotSpotID kTSA05WestWhiteSpotID = 5011; -static const HotSpotID kTSA0AEastSpotID = 5012; -static const HotSpotID kTSA0AWastSpotID = 5013; -static const HotSpotID kTSA0BEastMonitorSpotID = 5014; -static const HotSpotID kTSA0BEastMonitorOutSpotID = 5015; -static const HotSpotID kTSA0BEastCompareNoradSpotID = 5016; -static const HotSpotID kTSA0BEastCompareMarsSpotID = 5017; -static const HotSpotID kTSA0BEastCompareCaldoriaSpotID = 5018; -static const HotSpotID kTSA0BEastCompareWSCSpotID = 5019; -static const HotSpotID kTSA0BEastLeftRewindSpotID = 5020; -static const HotSpotID kTSA0BEastLeftPlaySpotID = 5021; -static const HotSpotID kTSA0BEastRightRewindSpotID = 5022; -static const HotSpotID kTSA0BEastRightPlaySpotID = 5023; -static const HotSpotID kTSA0BEastCloseVideoSpotID = 5024; -static const HotSpotID kTSA0BNorthMonitorSpotID = 5025; -static const HotSpotID kTSA0BNorthMonitorOutSpotID = 5026; -static const HotSpotID kTSA0BNorthHistLogSpotID = 5027; -static const HotSpotID kTSA0BNorthRobotsToCommandCenterSpotID = 5028; -static const HotSpotID kTSA0BNorthRobotsToReadyRoomSpotID = 5029; -static const HotSpotID kTSA0BNorthRobotsToFrontDoorSpotID = 5030; -static const HotSpotID kTSA0BWestMonitorSpotID = 5031; -static const HotSpotID kTSA0BWestMonitorOutSpotID = 5032; -static const HotSpotID kTSA0BWestTheorySpotID = 5033; -static const HotSpotID kTSA0BWestBackgroundSpotID = 5034; -static const HotSpotID kTSA0BWestProcedureSpotID = 5035; -static const HotSpotID kTSA0BWestCloseVideoSpotID = 5036; -static const HotSpotID kTSA0BWestPlayVideoSpotID = 5037; -static const HotSpotID kTSA0BWestRewindVideoSpotID = 5038; -static const HotSpotID kTSA22EastMonitorSpotID = 5039; -static const HotSpotID kTSA22EastKeySpotID = 5040; -static const HotSpotID kTSA23WestMonitorSpotID = 5041; -static const HotSpotID kTSA23WestChipsSpotID = 5042; -static const HotSpotID kTSA34NorthDoorSpotID = 5043; -static const HotSpotID kTSA37NorthJumpToPrehistoricSpotID = 5044; -static const HotSpotID kTSA37NorthJumpToNoradSpotID = 5045; -static const HotSpotID kTSA37NorthCancelNoradSpotID = 5046; -static const HotSpotID kTSA37NorthJumpToMarsSpotID = 5047; -static const HotSpotID kTSA37NorthCancelMarsSpotID = 5048; -static const HotSpotID kTSA37NorthJumpToWSCSpotID = 5049; -static const HotSpotID kTSA37NorthCancelWSCSpotID = 5050; -static const HotSpotID kTSA37NorthExitSpotID = 5051; -static const HotSpotID kTSA37NorthJumpMenuSpotID = 5052; -static const HotSpotID kTSA37NorthNoradMenuSpotID = 5053; -static const HotSpotID kTSA37NorthMarsMenuSpotID = 5054; -static const HotSpotID kTSA37NorthWSCMenuSpotID = 5055; +enum { + kTSAGTCardDropSpotID = 5000, + kTSAGTTokyoSpotID = 5001, + kTSAGTCaldoriaSpotID = 5002, + kTSAGTBeachSpotID = 5003, + kTSAGTOtherSpotID = 5004, + kTSA02DoorSpotID = 5005, + kTSA03EastJimenezSpotID = 5006, + kTSA03WestCrenshawSpotID = 5007, + kTSA04EastMatsumotoSpotID = 5008, + kTSA04WestCastilleSpotID = 5009, + kTSA05EastSinclairSpotID = 5010, + kTSA05WestWhiteSpotID = 5011, + kTSA0AEastSpotID = 5012, + kTSA0AWastSpotID = 5013, + kTSA0BEastMonitorSpotID = 5014, + kTSA0BEastMonitorOutSpotID = 5015, + kTSA0BEastCompareNoradSpotID = 5016, + kTSA0BEastCompareMarsSpotID = 5017, + kTSA0BEastCompareCaldoriaSpotID = 5018, + kTSA0BEastCompareWSCSpotID = 5019, + kTSA0BEastLeftRewindSpotID = 5020, + kTSA0BEastLeftPlaySpotID = 5021, + kTSA0BEastRightRewindSpotID = 5022, + kTSA0BEastRightPlaySpotID = 5023, + kTSA0BEastCloseVideoSpotID = 5024, + kTSA0BNorthMonitorSpotID = 5025, + kTSA0BNorthMonitorOutSpotID = 5026, + kTSA0BNorthHistLogSpotID = 5027, + kTSA0BNorthRobotsToCommandCenterSpotID = 5028, + kTSA0BNorthRobotsToReadyRoomSpotID = 5029, + kTSA0BNorthRobotsToFrontDoorSpotID = 5030, + kTSA0BWestMonitorSpotID = 5031, + kTSA0BWestMonitorOutSpotID = 5032, + kTSA0BWestTheorySpotID = 5033, + kTSA0BWestBackgroundSpotID = 5034, + kTSA0BWestProcedureSpotID = 5035, + kTSA0BWestCloseVideoSpotID = 5036, + kTSA0BWestPlayVideoSpotID = 5037, + kTSA0BWestRewindVideoSpotID = 5038, + kTSA22EastMonitorSpotID = 5039, + kTSA22EastKeySpotID = 5040, + kTSA23WestMonitorSpotID = 5041, + kTSA23WestChipsSpotID = 5042, + kTSA34NorthDoorSpotID = 5043, + kTSA37NorthJumpToPrehistoricSpotID = 5044, + kTSA37NorthJumpToNoradSpotID = 5045, + kTSA37NorthCancelNoradSpotID = 5046, + kTSA37NorthJumpToMarsSpotID = 5047, + kTSA37NorthCancelMarsSpotID = 5048, + kTSA37NorthJumpToWSCSpotID = 5049, + kTSA37NorthCancelWSCSpotID = 5050, + kTSA37NorthExitSpotID = 5051, + kTSA37NorthJumpMenuSpotID = 5052, + kTSA37NorthNoradMenuSpotID = 5053, + kTSA37NorthMarsMenuSpotID = 5054, + kTSA37NorthWSCMenuSpotID = 5055 +}; // Extra sequence IDs. -static const ExtraID kTSATransporterArrowLoop = 0; -static const ExtraID kTSAArriveFromCaldoria = 1; -static const ExtraID kTSAGTOtherChoice = 2; -static const ExtraID kTSAGTCardSwipe = 3; -static const ExtraID kTSAGTSelectCaldoria = 4; -static const ExtraID kTSAGTGoToCaldoria = 5; -static const ExtraID kTSAGTSelectBeach = 6; -static const ExtraID kTSAGTGoToBeach = 7; -static const ExtraID kTSAGTArriveAtBeach = 8; -static const ExtraID kTSAGTSelectTokyo = 9; -static const ExtraID kTSAGTGoToTokyo = 10; -static const ExtraID kTSAGTArriveAtTokyo = 11; -static const ExtraID kTSA02NorthZoomIn = 12; -static const ExtraID kTSA02NorthTenSecondDoor = 13; -static const ExtraID kTSA02NorthZoomOut = 14; -static const ExtraID kTSA02NorthDoorWithAgent3 = 15; -static const ExtraID kTSA03JimenezZoomIn = 16; -static const ExtraID kTSA03JimenezSpeech = 17; -static const ExtraID kTSA03JimenezZoomOut = 18; -static const ExtraID kTSA03CrenshawZoomIn = 19; -static const ExtraID kTSA03CrenshawSpeech = 20; -static const ExtraID kTSA03CrenshawZoomOut = 21; -static const ExtraID kTSA03SouthRobotDeath = 22; -static const ExtraID kTSA04NorthRobotGreeting = 23; -static const ExtraID kTSA04MatsumotoZoomIn = 24; -static const ExtraID kTSA04MatsumotoSpeech = 25; -static const ExtraID kTSA04MatsumotoZoomOut = 26; -static const ExtraID kTSA04CastilleZoomIn = 27; -static const ExtraID kTSA04CastilleSpeech = 28; -static const ExtraID kTSA04CastilleZoomOut = 29; -static const ExtraID kTSA05SinclairZoomIn = 30; -static const ExtraID kTSA05SinclairSpeech = 31; -static const ExtraID kTSA05SinclairZoomOut = 32; -static const ExtraID kTSA05WhiteZoomIn = 33; -static const ExtraID kTSA05WhiteSpeech = 34; -static const ExtraID kTSA05WhiteZoomOut = 35; -static const ExtraID kTSA0AEastRobot = 36; -static const ExtraID kTSA0AWestRobot = 37; -static const ExtraID kTSA16NorthRobotDeath = 38; -static const ExtraID kTSA0BEastZoomIn = 39; -static const ExtraID kTSA0BEastZoomedView = 40; -static const ExtraID kTSA0BEastZoomOut = 41; -static const ExtraID kTSA0BEastTurnLeft = 42; -static const ExtraID kTSA0BComparisonStartup = 43; -static const ExtraID kTSA0BComparisonView0000 = 44; -static const ExtraID kTSA0BComparisonView0002 = 45; -static const ExtraID kTSA0BComparisonView0020 = 46; -static const ExtraID kTSA0BComparisonView0022 = 47; -static const ExtraID kTSA0BComparisonView0200 = 48; -static const ExtraID kTSA0BComparisonView0202 = 49; -static const ExtraID kTSA0BComparisonView0220 = 50; -static const ExtraID kTSA0BComparisonView0222 = 51; -static const ExtraID kTSA0BComparisonView2000 = 52; -static const ExtraID kTSA0BComparisonView2002 = 53; -static const ExtraID kTSA0BComparisonView2020 = 54; -static const ExtraID kTSA0BComparisonView2022 = 55; -static const ExtraID kTSA0BComparisonView2200 = 56; -static const ExtraID kTSA0BComparisonView2202 = 57; -static const ExtraID kTSA0BComparisonView2220 = 58; -static const ExtraID kTSA0BComparisonView2222 = 59; -static const ExtraID kTSA0BNoradComparisonView = 60; -static const ExtraID kTSA0BNoradUnaltered = 61; -static const ExtraID kTSA0BNoradAltered = 62; -static const ExtraID kTSA0BMarsComparisonView = 63; -static const ExtraID kTSA0BMarsUnaltered = 64; -static const ExtraID kTSA0BMarsAltered = 65; -static const ExtraID kTSA0BWSCComparisonView = 66; -static const ExtraID kTSA0BWSCUnaltered = 67; -static const ExtraID kTSA0BWSCAltered = 68; -static const ExtraID kTSA0BCaldoriaComparisonView = 69; -static const ExtraID kTSA0BCaldoriaUnaltered = 70; -static const ExtraID kTSA0BCaldoriaAltered = 71; -static const ExtraID kTSA0BNorthZoomIn = 72; -static const ExtraID kTSA0BNorthZoomedView = 73; -static const ExtraID kTSA0BNorthZoomOut = 74; -static const ExtraID kTSA0BNorthTurnLeft = 75; -static const ExtraID kTSA0BNorthTurnRight = 76; -static const ExtraID kTSA0BNorthHistLogOpen = 77; -static const ExtraID kTSA0BNorthHistLogClose = 78; -static const ExtraID kTSA0BNorthHistLogCloseWithLog = 79; -static const ExtraID kTSA0BNorthCantChangeHistory = 80; -static const ExtraID kTSA0BNorthYoureBusted = 81; -static const ExtraID kTSA0BNorthFinallyHappened = 82; -static const ExtraID kTSA0BShowRip1 = 83; -static const ExtraID kTSA0BNorthRipView1 = 84; -static const ExtraID kTSA0BShowRip2 = 85; -static const ExtraID kTSA0BShowGuardRobots = 86; -static const ExtraID kTSA0BAIInterruption = 87; -static const ExtraID kTSA0BRobotsToCommandCenter = 88; -static const ExtraID kTSA0BNorthRobotsAtCCView = 89; -static const ExtraID kTSA0BNorthRobotsAtRRView = 90; -static const ExtraID kTSA0BNorthRobotsAtFDView = 91; -static const ExtraID kTSA0BRobotsFromCommandCenterToReadyRoom = 92; -static const ExtraID kTSA0BRobotsFromReadyRoomToCommandCenter = 93; -static const ExtraID kTSA0BRobotsFromCommandCenterToFrontDoor = 94; -static const ExtraID kTSA0BRobotsFromFrontDoorToCommandCenter = 95; -static const ExtraID kTSA0BRobotsFromFrontDoorToReadyRoom = 96; -static const ExtraID kTSA0BRobotsFromReadyRoomToFrontDoor = 97; -static const ExtraID kTSA0BWestZoomIn = 98; -static const ExtraID kTSA0BWestZoomedView = 99; -static const ExtraID kTSA0BWestZoomOut = 100; -static const ExtraID kTSA0BWestTurnRight = 101; -static const ExtraID kTSA0BTBPTheoryHighlight = 102; -static const ExtraID kTSA0BTBPBackgroundHighlight = 103; -static const ExtraID kTSA0BTBPProcedureHighlight = 104; -static const ExtraID kTSA0BTBPTheory = 105; -static const ExtraID kTSA0BTBPBackground = 106; -static const ExtraID kTSA0BTBPProcedure = 107; -static const ExtraID kTSA0BRipAlarmScreen = 108; -static const ExtraID kTSA22RedEastZoomInSequence = 109; -static const ExtraID kTSA22RedEastVaultViewWithKey = 110; -static const ExtraID kTSA22RedEastVaultViewNoKey = 111; -static const ExtraID kTSA23RedWestVaultZoomInSequence = 112; -static const ExtraID kTSA23RedWestVaultViewWithChips = 113; -static const ExtraID kTSA23RedWestVaultViewNoChips = 114; -static const ExtraID kTSA25NorthDeniedNoKey = 115; -static const ExtraID kTSA25NorthDeniedNoChip = 116; -static const ExtraID kTSA25NorthPutOnSuit = 117; -static const ExtraID kTSA25NorthAlreadyHaveSuit = 118; -static const ExtraID kTSA25NorthDescending1 = 119; -static const ExtraID kTSA25NorthDescending2 = 120; -static const ExtraID kTSA37HorseToAI1 = 121; -static const ExtraID kTSA37PegasusAI1 = 122; -static const ExtraID kTSA37AI1ToCommissioner1 = 123; -static const ExtraID kTSA37Commissioner1 = 124; -static const ExtraID kTSA37Commissioner1ToZoom = 125; -static const ExtraID kTSA37ZoomToPrehistoric = 126; -static const ExtraID kTSA37PrehistoricToAI2 = 127; -static const ExtraID kTSA37PegasusAI2 = 128; -static const ExtraID kTSA37AI2ToPrehistoric = 129; -static const ExtraID kTSA37PrehistoricToDepart = 130; -static const ExtraID kTSA37PegasusDepart = 131; -static const ExtraID kTSA37TimeJumpToPegasus = 132; -static const ExtraID kTSA37RecallToDownload = 133; -static const ExtraID kTSA37DownloadToColonel1 = 134; -static const ExtraID kTSA37Colonel1 = 135; -static const ExtraID kTSA37Colonel1ToReviewRequired = 136; -static const ExtraID kTSA37ReviewRequiredToExit = 137; -static const ExtraID kTSA37ExitHilited = 138; -static const ExtraID kTSA37ExitToHorse = 139; -static const ExtraID kTSA37HorseToColonel2 = 140; -static const ExtraID kTSA37Colonel2 = 141; -static const ExtraID kTSA37PegasusAI3 = 142; -static const ExtraID kTSA37AI3ToHorse = 143; -static const ExtraID kTSA37HorseToZoom = 144; -static const ExtraID kTSA37ZoomToMainMenu = 145; -static const ExtraID kTSA37MainMenuToAI4 = 146; -static const ExtraID kTSA37PegasusAI4 = 147; -static const ExtraID kTSA37AI4ToMainMenu = 148; -static const ExtraID kTSA37JumpMenu000 = 149; -static const ExtraID kTSA37JumpMenu001 = 150; -static const ExtraID kTSA37JumpMenu010 = 151; -static const ExtraID kTSA37JumpMenu011 = 152; -static const ExtraID kTSA37JumpMenu100 = 153; -static const ExtraID kTSA37JumpMenu101 = 154; -static const ExtraID kTSA37JumpMenu110 = 155; -static const ExtraID kTSA37JumpMenu111 = 156; -static const ExtraID kTSA37JumpToWSCMenu = 157; -static const ExtraID kTSA37CancelWSC = 158; -static const ExtraID kTSA37JumpToWSC = 159; -static const ExtraID kTSA37WSCToAI5 = 160; -static const ExtraID kTSA37PegasusAI5 = 161; -static const ExtraID kTSA37AI5ToWSC = 162; -static const ExtraID kTSA37WSCToDepart = 163; -static const ExtraID kTSA37JumpToMarsMenu = 164; -static const ExtraID kTSA37CancelMars = 165; -static const ExtraID kTSA37JumpToMars = 166; -static const ExtraID kTSA37MarsToAI6 = 167; -static const ExtraID kTSA37PegasusAI6 = 168; -static const ExtraID kTSA37AI6ToMars = 169; -static const ExtraID kTSA37MarsToDepart = 170; -static const ExtraID kTSA37JumpToNoradMenu = 171; -static const ExtraID kTSA37CancelNorad = 172; -static const ExtraID kTSA37JumpToNorad = 173; -static const ExtraID kTSA37NoradToAI7 = 174; -static const ExtraID kTSA37PegasusAI7 = 175; -static const ExtraID kTSA37AI7ToNorad = 176; -static const ExtraID kTSA37NoradToDepart = 177; -static const ExtraID kTSA37EnvironmentalScan = 178; -static const ExtraID kTSA37DownloadToMainMenu = 179; -static const ExtraID kTSA37DownloadToOpMemReview = 180; -static const ExtraID kTSA37OpMemReviewToMainMenu = 181; -static const ExtraID kTSA37OpMemReviewToAllClear = 182; -static const ExtraID kTSA37AllClearToCongratulations = 183; -static const ExtraID kTSA37Congratulations = 184; -static const ExtraID kTSA37CongratulationsToExit = 185; +enum { + kTSATransporterArrowLoop = 0, + kTSAArriveFromCaldoria = 1, + kTSAGTOtherChoice = 2, + kTSAGTCardSwipe = 3, + kTSAGTSelectCaldoria = 4, + kTSAGTGoToCaldoria = 5, + kTSAGTSelectBeach = 6, + kTSAGTGoToBeach = 7, + kTSAGTArriveAtBeach = 8, + kTSAGTSelectTokyo = 9, + kTSAGTGoToTokyo = 10, + kTSAGTArriveAtTokyo = 11, + kTSA02NorthZoomIn = 12, + kTSA02NorthTenSecondDoor = 13, + kTSA02NorthZoomOut = 14, + kTSA02NorthDoorWithAgent3 = 15, + kTSA03JimenezZoomIn = 16, + kTSA03JimenezSpeech = 17, + kTSA03JimenezZoomOut = 18, + kTSA03CrenshawZoomIn = 19, + kTSA03CrenshawSpeech = 20, + kTSA03CrenshawZoomOut = 21, + kTSA03SouthRobotDeath = 22, + kTSA04NorthRobotGreeting = 23, + kTSA04MatsumotoZoomIn = 24, + kTSA04MatsumotoSpeech = 25, + kTSA04MatsumotoZoomOut = 26, + kTSA04CastilleZoomIn = 27, + kTSA04CastilleSpeech = 28, + kTSA04CastilleZoomOut = 29, + kTSA05SinclairZoomIn = 30, + kTSA05SinclairSpeech = 31, + kTSA05SinclairZoomOut = 32, + kTSA05WhiteZoomIn = 33, + kTSA05WhiteSpeech = 34, + kTSA05WhiteZoomOut = 35, + kTSA0AEastRobot = 36, + kTSA0AWestRobot = 37, + kTSA16NorthRobotDeath = 38, + kTSA0BEastZoomIn = 39, + kTSA0BEastZoomedView = 40, + kTSA0BEastZoomOut = 41, + kTSA0BEastTurnLeft = 42, + kTSA0BComparisonStartup = 43, + kTSA0BComparisonView0000 = 44, + kTSA0BComparisonView0002 = 45, + kTSA0BComparisonView0020 = 46, + kTSA0BComparisonView0022 = 47, + kTSA0BComparisonView0200 = 48, + kTSA0BComparisonView0202 = 49, + kTSA0BComparisonView0220 = 50, + kTSA0BComparisonView0222 = 51, + kTSA0BComparisonView2000 = 52, + kTSA0BComparisonView2002 = 53, + kTSA0BComparisonView2020 = 54, + kTSA0BComparisonView2022 = 55, + kTSA0BComparisonView2200 = 56, + kTSA0BComparisonView2202 = 57, + kTSA0BComparisonView2220 = 58, + kTSA0BComparisonView2222 = 59, + kTSA0BNoradComparisonView = 60, + kTSA0BNoradUnaltered = 61, + kTSA0BNoradAltered = 62, + kTSA0BMarsComparisonView = 63, + kTSA0BMarsUnaltered = 64, + kTSA0BMarsAltered = 65, + kTSA0BWSCComparisonView = 66, + kTSA0BWSCUnaltered = 67, + kTSA0BWSCAltered = 68, + kTSA0BCaldoriaComparisonView = 69, + kTSA0BCaldoriaUnaltered = 70, + kTSA0BCaldoriaAltered = 71, + kTSA0BNorthZoomIn = 72, + kTSA0BNorthZoomedView = 73, + kTSA0BNorthZoomOut = 74, + kTSA0BNorthTurnLeft = 75, + kTSA0BNorthTurnRight = 76, + kTSA0BNorthHistLogOpen = 77, + kTSA0BNorthHistLogClose = 78, + kTSA0BNorthHistLogCloseWithLog = 79, + kTSA0BNorthCantChangeHistory = 80, + kTSA0BNorthYoureBusted = 81, + kTSA0BNorthFinallyHappened = 82, + kTSA0BShowRip1 = 83, + kTSA0BNorthRipView1 = 84, + kTSA0BShowRip2 = 85, + kTSA0BShowGuardRobots = 86, + kTSA0BAIInterruption = 87, + kTSA0BRobotsToCommandCenter = 88, + kTSA0BNorthRobotsAtCCView = 89, + kTSA0BNorthRobotsAtRRView = 90, + kTSA0BNorthRobotsAtFDView = 91, + kTSA0BRobotsFromCommandCenterToReadyRoom = 92, + kTSA0BRobotsFromReadyRoomToCommandCenter = 93, + kTSA0BRobotsFromCommandCenterToFrontDoor = 94, + kTSA0BRobotsFromFrontDoorToCommandCenter = 95, + kTSA0BRobotsFromFrontDoorToReadyRoom = 96, + kTSA0BRobotsFromReadyRoomToFrontDoor = 97, + kTSA0BWestZoomIn = 98, + kTSA0BWestZoomedView = 99, + kTSA0BWestZoomOut = 100, + kTSA0BWestTurnRight = 101, + kTSA0BTBPTheoryHighlight = 102, + kTSA0BTBPBackgroundHighlight = 103, + kTSA0BTBPProcedureHighlight = 104, + kTSA0BTBPTheory = 105, + kTSA0BTBPBackground = 106, + kTSA0BTBPProcedure = 107, + kTSA0BRipAlarmScreen = 108, + kTSA22RedEastZoomInSequence = 109, + kTSA22RedEastVaultViewWithKey = 110, + kTSA22RedEastVaultViewNoKey = 111, + kTSA23RedWestVaultZoomInSequence = 112, + kTSA23RedWestVaultViewWithChips = 113, + kTSA23RedWestVaultViewNoChips = 114, + kTSA25NorthDeniedNoKey = 115, + kTSA25NorthDeniedNoChip = 116, + kTSA25NorthPutOnSuit = 117, + kTSA25NorthAlreadyHaveSuit = 118, + kTSA25NorthDescending1 = 119, + kTSA25NorthDescending2 = 120, + kTSA37HorseToAI1 = 121, + kTSA37PegasusAI1 = 122, + kTSA37AI1ToCommissioner1 = 123, + kTSA37Commissioner1 = 124, + kTSA37Commissioner1ToZoom = 125, + kTSA37ZoomToPrehistoric = 126, + kTSA37PrehistoricToAI2 = 127, + kTSA37PegasusAI2 = 128, + kTSA37AI2ToPrehistoric = 129, + kTSA37PrehistoricToDepart = 130, + kTSA37PegasusDepart = 131, + kTSA37TimeJumpToPegasus = 132, + kTSA37RecallToDownload = 133, + kTSA37DownloadToColonel1 = 134, + kTSA37Colonel1 = 135, + kTSA37Colonel1ToReviewRequired = 136, + kTSA37ReviewRequiredToExit = 137, + kTSA37ExitHilited = 138, + kTSA37ExitToHorse = 139, + kTSA37HorseToColonel2 = 140, + kTSA37Colonel2 = 141, + kTSA37PegasusAI3 = 142, + kTSA37AI3ToHorse = 143, + kTSA37HorseToZoom = 144, + kTSA37ZoomToMainMenu = 145, + kTSA37MainMenuToAI4 = 146, + kTSA37PegasusAI4 = 147, + kTSA37AI4ToMainMenu = 148, + kTSA37JumpMenu000 = 149, + kTSA37JumpMenu001 = 150, + kTSA37JumpMenu010 = 151, + kTSA37JumpMenu011 = 152, + kTSA37JumpMenu100 = 153, + kTSA37JumpMenu101 = 154, + kTSA37JumpMenu110 = 155, + kTSA37JumpMenu111 = 156, + kTSA37JumpToWSCMenu = 157, + kTSA37CancelWSC = 158, + kTSA37JumpToWSC = 159, + kTSA37WSCToAI5 = 160, + kTSA37PegasusAI5 = 161, + kTSA37AI5ToWSC = 162, + kTSA37WSCToDepart = 163, + kTSA37JumpToMarsMenu = 164, + kTSA37CancelMars = 165, + kTSA37JumpToMars = 166, + kTSA37MarsToAI6 = 167, + kTSA37PegasusAI6 = 168, + kTSA37AI6ToMars = 169, + kTSA37MarsToDepart = 170, + kTSA37JumpToNoradMenu = 171, + kTSA37CancelNorad = 172, + kTSA37JumpToNorad = 173, + kTSA37NoradToAI7 = 174, + kTSA37PegasusAI7 = 175, + kTSA37AI7ToNorad = 176, + kTSA37NoradToDepart = 177, + kTSA37EnvironmentalScan = 178, + kTSA37DownloadToMainMenu = 179, + kTSA37DownloadToOpMemReview = 180, + kTSA37OpMemReviewToMainMenu = 181, + kTSA37OpMemReviewToAllClear = 182, + kTSA37AllClearToCongratulations = 183, + kTSA37Congratulations = 184, + kTSA37CongratulationsToExit = 185 +}; const DisplayOrder kRipTimerOrder = kMonitorLayer; +enum { + kUnresolvedLeft = kNavAreaLeft + 14, + kUnresolvedTop = kNavAreaTop + 236, -const CoordType kUnresolvedLeft = kNavAreaLeft + 14; -const CoordType kUnresolvedTop = kNavAreaTop + 236; - -const CoordType kResolvedLeft = kNavAreaLeft + 36; -const CoordType kResolvedTop = kNavAreaTop + 236; + kResolvedLeft = kNavAreaLeft + 36, + kResolvedTop = kNavAreaTop + 236, -const CoordType kJumpMenuLeft = kNavAreaLeft + 360; -const CoordType kJumpMenuTop = kNavAreaTop + 202; + kJumpMenuLeft = kNavAreaLeft + 360, + kJumpMenuTop = kNavAreaTop + 202, -const CoordType kJumpMenuHilitedLeft = kNavAreaLeft + 354; -const CoordType kJumpMenuHilitedTop = kNavAreaTop + 196; + kJumpMenuHilitedLeft = kNavAreaLeft + 354, + kJumpMenuHilitedTop = kNavAreaTop + 196, -const CoordType kExitLeft = kNavAreaLeft + 360; -const CoordType kExitTop = kNavAreaTop + 216; + kExitLeft = kNavAreaLeft + 360, + kExitTop = kNavAreaTop + 216, -const CoordType kExitHilitedLeft = kNavAreaLeft + 354; -const CoordType kExitHilitedTop = kNavAreaTop + 210; + kExitHilitedLeft = kNavAreaLeft + 354, + kExitHilitedTop = kNavAreaTop + 210, -const CoordType kRipTimerLeft = kNavAreaLeft + 95; -const CoordType kRipTimerTop = kNavAreaTop + 87; + kRipTimerLeft = kNavAreaLeft + 95, + kRipTimerTop = kNavAreaTop + 87, -const CoordType kTBPCloseLeft = kNavAreaLeft + 30; -const CoordType kTBPCloseTop = kNavAreaTop + 16; + kTBPCloseLeft = kNavAreaLeft + 30, + kTBPCloseTop = kNavAreaTop + 16, -const CoordType kTBPRewindLeft = kNavAreaLeft + 86; -const CoordType kTBPRewindTop = kNavAreaTop + 218; + kTBPRewindLeft = kNavAreaLeft + 86, + kTBPRewindTop = kNavAreaTop + 218, -const CoordType kComparisonCloseLeft = kNavAreaLeft + 50; -const CoordType kComparisonCloseTop = kNavAreaTop + 14; + kComparisonCloseLeft = kNavAreaLeft + 50, + kComparisonCloseTop = kNavAreaTop + 14, -const CoordType kComparisonLeftRewindLeft = kNavAreaLeft + 96; -const CoordType kComparisonLeftRewindTop = kNavAreaTop + 190; + kComparisonLeftRewindLeft = kNavAreaLeft + 96, + kComparisonLeftRewindTop = kNavAreaTop + 190, -const CoordType kComparisonRightRewindLeft = kNavAreaLeft + 282; -const CoordType kComparisonRightRewindTop = kNavAreaTop + 190; + kComparisonRightRewindLeft = kNavAreaLeft + 282, + kComparisonRightRewindTop = kNavAreaTop + 190, -const CoordType kComparisonHiliteSpriteLeft = kNavAreaLeft + 45; -const CoordType kComparisonHiliteSpriteTop = kNavAreaTop + 65; + kComparisonHiliteSpriteLeft = kNavAreaLeft + 45, + kComparisonHiliteSpriteTop = kNavAreaTop + 65, -const CoordType kComparisonHiliteNoradLeft = kNavAreaLeft + 45; -const CoordType kComparisonHiliteNoradTop = kNavAreaTop + 65; + kComparisonHiliteNoradLeft = kNavAreaLeft + 45, + kComparisonHiliteNoradTop = kNavAreaTop + 65, -const CoordType kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4; -const CoordType kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23; + kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4, + kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23, -const CoordType kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7; -const CoordType kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46; + kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7, + kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46, -const CoordType kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11; -const CoordType kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68; + kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11, + kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68, -const CoordType kComparisonChancesSpriteLeft = kNavAreaLeft + 148; -const CoordType kComparisonChancesSpriteTop = kNavAreaTop + 162; + kComparisonChancesSpriteLeft = kNavAreaLeft + 148, + kComparisonChancesSpriteTop = kNavAreaTop + 162, -const CoordType kComparisonChancesNoradLeft = kNavAreaLeft + 148; -const CoordType kComparisonChancesNoradTop = kNavAreaTop + 162; + kComparisonChancesNoradLeft = kNavAreaLeft + 148, + kComparisonChancesNoradTop = kNavAreaTop + 162, -const CoordType kComparisonChancesMarsLeft = kNavAreaLeft + 148; -const CoordType kComparisonChancesMarsTop = kNavAreaTop + 162; + kComparisonChancesMarsLeft = kNavAreaLeft + 148, + kComparisonChancesMarsTop = kNavAreaTop + 162, -const CoordType kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148; -const CoordType kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1; + kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148, + kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1, -const CoordType kComparisonChancesWSCLeft = kNavAreaLeft + 148; -const CoordType kComparisonChancesWSCTop = kNavAreaTop + 162; + kComparisonChancesWSCLeft = kNavAreaLeft + 148, + kComparisonChancesWSCTop = kNavAreaTop + 162, -const CoordType kRedirectionSprite1Left = kNavAreaLeft + 58; -const CoordType kRedirectionSprite1Top = kNavAreaTop + 16; + kRedirectionSprite1Left = kNavAreaLeft + 58, + kRedirectionSprite1Top = kNavAreaTop + 16, -const CoordType kRedirectionSprite2Left = kNavAreaLeft + 36; -const CoordType kRedirectionSprite2Top = kNavAreaTop + 166; + kRedirectionSprite2Left = kNavAreaLeft + 36, + kRedirectionSprite2Top = kNavAreaTop + 166, -const CoordType kRedirectionCCRolloverLeft = kNavAreaLeft + 58; -const CoordType kRedirectionCCRolloverTop = kNavAreaTop + 16; + kRedirectionCCRolloverLeft = kNavAreaLeft + 58, + kRedirectionCCRolloverTop = kNavAreaTop + 16, -const CoordType kRedirectionRRRolloverLeft = kNavAreaLeft + 430; -const CoordType kRedirectionRRRolloverTop = kNavAreaTop + 30; + kRedirectionRRRolloverLeft = kNavAreaLeft + 430, + kRedirectionRRRolloverTop = kNavAreaTop + 30, -const CoordType kRedirectionFDRolloverLeft = kNavAreaLeft + 278; -const CoordType kRedirectionFDRolloverTop = kNavAreaTop + 160; + kRedirectionFDRolloverLeft = kNavAreaLeft + 278, + kRedirectionFDRolloverTop = kNavAreaTop + 160, -const CoordType kRedirectionCCDoorLeft = kNavAreaLeft + 174; -const CoordType kRedirectionCCDoorTop = kNavAreaTop + 36; + kRedirectionCCDoorLeft = kNavAreaLeft + 174, + kRedirectionCCDoorTop = kNavAreaTop + 36, -const CoordType kRedirectionRRDoorLeft = kNavAreaLeft + 418; -const CoordType kRedirectionRRDoorTop = kNavAreaTop + 32; + kRedirectionRRDoorLeft = kNavAreaLeft + 418, + kRedirectionRRDoorTop = kNavAreaTop + 32, -const CoordType kRedirectionFDDoorLeft = kNavAreaLeft + 298; -const CoordType kRedirectionFDDoorTop = kNavAreaTop + 240; + kRedirectionFDDoorLeft = kNavAreaLeft + 298, + kRedirectionFDDoorTop = kNavAreaTop + 240, -const CoordType kRedirectionSecuredLeft = kNavAreaLeft + 36; -const CoordType kRedirectionSecuredTop = kNavAreaTop + 166; + kRedirectionSecuredLeft = kNavAreaLeft + 36, + kRedirectionSecuredTop = kNavAreaTop + 166, -const CoordType kRedirectionNewTargetLeft = kNavAreaLeft + 36; -const CoordType kRedirectionNewTargetTop = kNavAreaTop + 166; + kRedirectionNewTargetLeft = kNavAreaLeft + 36, + kRedirectionNewTargetTop = kNavAreaTop + 166, -const CoordType kRedirectionCloseLeft = kNavAreaLeft + 56; -const CoordType kRedirectionCloseTop = kNavAreaTop + 220; + kRedirectionCloseLeft = kNavAreaLeft + 56, + kRedirectionCloseTop = kNavAreaTop + 220 +}; static const TimeValue kTSABumpIntoWallIn = 0; static const TimeValue kTSABumpIntoWallOut = 148; @@ -516,10 +529,12 @@ static const TimeValue kTSAVaultCloseOut = 5388; static const TimeValue kTSAPegasusDoorCloseIn = 5388; static const TimeValue kTSAPegasusDoorCloseOut = 6457; -static const bool kPegasusUnresolved = false; -static const bool kPegasusResolved = true; -static const bool kPegasusCantExit = false; -static const bool kPegasusCanExit = true; +enum { + kPegasusUnresolved = false, + kPegasusResolved = true, + kPegasusCantExit = false, + kPegasusCanExit = true +}; // Monitor modes enum { @@ -582,15 +597,17 @@ static const ExtraID s_historicalLogViews[16] = { kTSA0BComparisonView2222 }; -static const int kRedirectionCCRolloverSprite = 0; -static const int kRedirectionRRRolloverSprite = 1; -static const int kRedirectionFDRolloverSprite = 2; -static const int kRedirectionCCDoorSprite = 3; -static const int kRedirectionRRDoorSprite = 4; -static const int kRedirectionFDDoorSprite = 5; -static const int kRedirectionCloseSprite = 6; -static const int kRedirectionSecuredSprite = 0; -static const int kRedirectionNewTargetSprite = 1; +enum { + kRedirectionCCRolloverSprite = 0, + kRedirectionRRRolloverSprite = 1, + kRedirectionFDRolloverSprite = 2, + kRedirectionCCDoorSprite = 3, + kRedirectionRRDoorSprite = 4, + kRedirectionFDDoorSprite = 5, + kRedirectionCloseSprite = 6, + kRedirectionSecuredSprite = 0, + kRedirectionNewTargetSprite = 1 +}; void RipTimer::initImage() { _middle = -1; diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp index c808325b0f..0c29e06f41 100644 --- a/engines/pegasus/neighborhood/tsa/tinytsa.cpp +++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp @@ -38,71 +38,81 @@ namespace Pegasus { static const int16 kCompassShift = 30; -static const TimeScale kTinyTSAMovieScale = 600; -static const TimeScale kTinyTSAFramesPerSecond = 15; -static const TimeScale kTinyTSAFrameDuration = 40; +enum { + kTinyTSAMovieScale = 600, + kTinyTSAFramesPerSecond = 15, + kTinyTSAFrameDuration = 40 +}; // Alternate IDs. -static const AlternateID kAltTinyTSANormal = 0; +enum { + kAltTinyTSANormal = 0 +}; // Hot Spot Activation IDs. -static const HotSpotActivationID kActivationTinyTSAJumpToNorad = 1; -static const HotSpotActivationID kActivationTinyTSAJumpToMars = 2; -static const HotSpotActivationID kActivationTinyTSAJumpToWSC = 3; -static const HotSpotActivationID kActivationTinyTSAReadyForJumpMenu = 4; -static const HotSpotActivationID kActivationTinyTSAMainJumpMenu = 5; +enum { + kActivationTinyTSAJumpToNorad = 1, + kActivationTinyTSAJumpToMars = 2, + kActivationTinyTSAJumpToWSC = 3, + kActivationTinyTSAReadyForJumpMenu = 4, + kActivationTinyTSAMainJumpMenu = 5 +}; // Hot Spot IDs. -static const HotSpotID kTinyTSA37NorthJumpToNoradSpotID = 5000; -static const HotSpotID kTinyTSA37NorthCancelNoradSpotID = 5001; -static const HotSpotID kTinyTSA37NorthJumpToMarsSpotID = 5002; -static const HotSpotID kTinyTSA37NorthCancelMarsSpotID = 5003; -static const HotSpotID kTinyTSA37NorthJumpToWSCSpotID = 5004; -static const HotSpotID kTinyTSA37NorthCancelWSCSpotID = 5005; -static const HotSpotID kTinyTSA37NorthJumpMenuSpotID = 5006; -static const HotSpotID kTinyTSA37NorthNoradMenuSpotID = 5007; -static const HotSpotID kTinyTSA37NorthMarsMenuSpotID = 5008; -static const HotSpotID kTinyTSA37NorthWSCMenuSpotID = 5009; +enum { + kTinyTSA37NorthJumpToNoradSpotID = 5000, + kTinyTSA37NorthCancelNoradSpotID = 5001, + kTinyTSA37NorthJumpToMarsSpotID = 5002, + kTinyTSA37NorthCancelMarsSpotID = 5003, + kTinyTSA37NorthJumpToWSCSpotID = 5004, + kTinyTSA37NorthCancelWSCSpotID = 5005, + kTinyTSA37NorthJumpMenuSpotID = 5006, + kTinyTSA37NorthNoradMenuSpotID = 5007, + kTinyTSA37NorthMarsMenuSpotID = 5008, + kTinyTSA37NorthWSCMenuSpotID = 5009 +}; // Extra sequence IDs. -static const ExtraID kTinyTSA37PegasusDepart = 0; -static const ExtraID kTinyTSA37TimeJumpToPegasus = 1; -static const ExtraID kTinyTSA37RecallToDownload = 2; -static const ExtraID kTinyTSA37ExitHilited = 3; -static const ExtraID kTinyTSA37ExitToHorse = 4; -static const ExtraID kTinyTSA37JumpMenu000 = 5; -static const ExtraID kTinyTSA37JumpMenu001 = 6; -static const ExtraID kTinyTSA37JumpMenu010 = 7; -static const ExtraID kTinyTSA37JumpMenu011 = 8; -static const ExtraID kTinyTSA37JumpMenu100 = 9; -static const ExtraID kTinyTSA37JumpMenu101 = 10; -static const ExtraID kTinyTSA37JumpMenu110 = 11; -static const ExtraID kTinyTSA37JumpMenu111 = 12; -static const ExtraID kTinyTSA37JumpToWSCMenu = 13; -static const ExtraID kTinyTSA37CancelWSC = 14; -static const ExtraID kTinyTSA37JumpToWSC = 15; -static const ExtraID kTinyTSA37WSCToAI5 = 16; -static const ExtraID kTinyTSA37PegasusAI5 = 17; -static const ExtraID kTinyTSA37AI5ToWSC = 18; -static const ExtraID kTinyTSA37WSCToDepart = 19; -static const ExtraID kTinyTSA37JumpToMarsMenu = 20; -static const ExtraID kTinyTSA37CancelMars = 21; -static const ExtraID kTinyTSA37JumpToMars = 22; -static const ExtraID kTinyTSA37MarsToAI6 = 23; -static const ExtraID kTinyTSA37PegasusAI6 = 24; -static const ExtraID kTinyTSA37AI6ToMars = 25; -static const ExtraID kTinyTSA37MarsToDepart = 26; -static const ExtraID kTinyTSA37JumpToNoradMenu = 27; -static const ExtraID kTinyTSA37CancelNorad = 28; -static const ExtraID kTinyTSA37JumpToNorad = 29; -static const ExtraID kTinyTSA37NoradToAI7 = 30; -static const ExtraID kTinyTSA37PegasusAI7 = 31; -static const ExtraID kTinyTSA37AI7ToNorad = 32; -static const ExtraID kTinyTSA37NoradToDepart = 33; -static const ExtraID kTinyTSA37EnvironmentalScan = 34; -static const ExtraID kTinyTSA37DownloadToMainMenu = 35; -static const ExtraID kTinyTSA37DownloadToOpMemReview = 36; -static const ExtraID kTinyTSA37OpMemReviewToMainMenu = 37; +enum { + kTinyTSA37PegasusDepart = 0, + kTinyTSA37TimeJumpToPegasus = 1, + kTinyTSA37RecallToDownload = 2, + kTinyTSA37ExitHilited = 3, + kTinyTSA37ExitToHorse = 4, + kTinyTSA37JumpMenu000 = 5, + kTinyTSA37JumpMenu001 = 6, + kTinyTSA37JumpMenu010 = 7, + kTinyTSA37JumpMenu011 = 8, + kTinyTSA37JumpMenu100 = 9, + kTinyTSA37JumpMenu101 = 10, + kTinyTSA37JumpMenu110 = 11, + kTinyTSA37JumpMenu111 = 12, + kTinyTSA37JumpToWSCMenu = 13, + kTinyTSA37CancelWSC = 14, + kTinyTSA37JumpToWSC = 15, + kTinyTSA37WSCToAI5 = 16, + kTinyTSA37PegasusAI5 = 17, + kTinyTSA37AI5ToWSC = 18, + kTinyTSA37WSCToDepart = 19, + kTinyTSA37JumpToMarsMenu = 20, + kTinyTSA37CancelMars = 21, + kTinyTSA37JumpToMars = 22, + kTinyTSA37MarsToAI6 = 23, + kTinyTSA37PegasusAI6 = 24, + kTinyTSA37AI6ToMars = 25, + kTinyTSA37MarsToDepart = 26, + kTinyTSA37JumpToNoradMenu = 27, + kTinyTSA37CancelNorad = 28, + kTinyTSA37JumpToNorad = 29, + kTinyTSA37NoradToAI7 = 30, + kTinyTSA37PegasusAI7 = 31, + kTinyTSA37AI7ToNorad = 32, + kTinyTSA37NoradToDepart = 33, + kTinyTSA37EnvironmentalScan = 34, + kTinyTSA37DownloadToMainMenu = 35, + kTinyTSA37DownloadToOpMemReview = 36, + kTinyTSA37OpMemReviewToMainMenu = 37 +}; TinyTSA::TinyTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Tiny TSA", kTinyTSAID) { } diff --git a/engines/pegasus/neighborhood/wsc/wsc.cpp b/engines/pegasus/neighborhood/wsc/wsc.cpp index f009b35cdc..5e35d8ccc1 100644 --- a/engines/pegasus/neighborhood/wsc/wsc.cpp +++ b/engines/pegasus/neighborhood/wsc/wsc.cpp @@ -87,81 +87,85 @@ static const int kTimerEventPlasmaHit = 0; static const int kTimerEventPlayerGawkingAtRobot = 1; static const int kTimerEventPlayerGawkingAtRobot2 = 2; -static const TimeValue kWSCMolecule1In = 0; -static const TimeValue kWSCMolecule1Out = 937; +enum { + kWSCMolecule1In = 0, + kWSCMolecule1Out = 937, -static const TimeValue kWSCMolecule2In = 937; -static const TimeValue kWSCMolecule2Out = 1864; + kWSCMolecule2In = 937, + kWSCMolecule2Out = 1864, -static const TimeValue kWSCMolecule3In = 1864; -static const TimeValue kWSCMolecule3Out = 2790; + kWSCMolecule3In = 1864, + kWSCMolecule3Out = 2790, -static const TimeValue kWSCClick1In = 2790; -static const TimeValue kWSCClick1Out = 2890; + kWSCClick1In = 2790, + kWSCClick1Out = 2890, -static const TimeValue kWSCClick2In = 2890; -static const TimeValue kWSCClick2Out = 3059; + kWSCClick2In = 2890, + kWSCClick2Out = 3059, -static const TimeValue kWSCClick3In = 3059; -static const TimeValue kWSCClick3Out = 3156; + kWSCClick3In = 3059, + kWSCClick3Out = 3156, -static const TimeValue kWSCFlashlightClickIn = 3156; -static const TimeValue kWSCFlashlightClickOut = 3211; + kWSCFlashlightClickIn = 3156, + kWSCFlashlightClickOut = 3211, -static const TimeValue kWSCBumpIntoWallIn = 3211; -static const TimeValue kWSCBumpIntoWallOut = 3514; + kWSCBumpIntoWallIn = 3211, + kWSCBumpIntoWallOut = 3514, -static const TimeValue kWSCCantTransportIn = 3514; -static const TimeValue kWSCCantTransportOut = 7791; + kWSCCantTransportIn = 3514, + kWSCCantTransportOut = 7791, -static const TimeValue kHernandezNotHomeIn = 7791; -static const TimeValue kHernandezNotHomeOut = 10199; + kHernandezNotHomeIn = 7791, + kHernandezNotHomeOut = 10199, -static const TimeValue kWashingtonNotHomeIn = 10199; -static const TimeValue kWashingtonNotHomeOut = 12649; + kWashingtonNotHomeIn = 10199, + kWashingtonNotHomeOut = 12649, -static const TimeValue kSullivanNotHomeIn = 12649; -static const TimeValue kSullivanNotHomeOut = 15031; + kSullivanNotHomeIn = 12649, + kSullivanNotHomeOut = 15031, -static const TimeValue kNakamuraNotHomeIn = 15031; -static const TimeValue kNakamuraNotHomeOut = 17545; + kNakamuraNotHomeIn = 15031, + kNakamuraNotHomeOut = 17545, -static const TimeValue kGrailisNotHomeIn = 17545; -static const TimeValue kGrailisNotHomeOut = 19937; + kGrailisNotHomeIn = 17545, + kGrailisNotHomeOut = 19937, -static const TimeValue kTheriaultNotHomeIn = 19937; -static const TimeValue kTheriaultNotHomeOut = 22395; + kTheriaultNotHomeIn = 19937, + kTheriaultNotHomeOut = 22395, -static const TimeValue kGlennerNotHomeIn = 22395; -static const TimeValue kGlennerNotHomeOut = 24770; + kGlennerNotHomeIn = 22395, + kGlennerNotHomeOut = 24770, -static const TimeValue kSinclairNotHomeIn = 24770; -static const TimeValue kSinclairNotHomeOut = 27328; + kSinclairNotHomeIn = 24770, + kSinclairNotHomeOut = 27328, -static const TimeValue kWSCLabClosedIn = 27328; -static const TimeValue kWSCLabClosedOut = 28904; + kWSCLabClosedIn = 27328, + kWSCLabClosedOut = 28904, -static const TimeValue kSlidingDoorCloseIn = 28904; -static const TimeValue kSlidingDoorCloseOut = 29295; + kSlidingDoorCloseIn = 28904, + kSlidingDoorCloseOut = 29295, -static const TimeValue kSlimyDoorCloseIn = 29295; -static const TimeValue kSlimyDoorCloseOut = 29788; + kSlimyDoorCloseIn = 29295, + kSlimyDoorCloseOut = 29788, -static const TimeValue kPaging1In = 29788; -static const TimeValue kPaging1Out = 32501; + kPaging1In = 29788, + kPaging1Out = 32501, -static const TimeValue kPaging2In = 32501; -static const TimeValue kPaging2Out = 34892; + kPaging2In = 32501, + kPaging2Out = 34892, -static const TimeValue kCheckInIn = 34892; -static const TimeValue kCheckInOut = 37789; + kCheckInIn = 34892, + kCheckInOut = 37789, -static const TimeValue kDrinkAntidoteIn = 37789; -static const TimeValue kDrinkAntidoteOut = 39725; + kDrinkAntidoteIn = 37789, + kDrinkAntidoteOut = 39725 +}; -static const TimeScale kWSCMovieScale = 600; -static const TimeScale kWSCFramesPerSecond = 15; -static const TimeScale kWSCFrameDuration = 40; +enum { + kWSCMovieScale = 600, + kWSCFramesPerSecond = 15, + kWSCFrameDuration = 40 +}; // Alternate IDs. static const AlternateID kAltWSCNormal = 0; @@ -170,304 +174,312 @@ static const AlternateID kAltWSCW0ZDoorOpen = 2; static const AlternateID kAltWSCPeopleAtW19North = 3; // Room IDs. -static const RoomID kWSC02 = 1; -static const RoomID kWSC03 = 4; -static const RoomID kWSC04 = 5; -static const RoomID kWSC06 = 6; -static const RoomID kWSC07 = 7; -static const RoomID kWSC08 = 8; -static const RoomID kWSC09 = 9; -static const RoomID kWSC10 = 10; -static const RoomID kWSC11 = 11; -static const RoomID kWSC13 = 12; -static const RoomID kWSC14 = 13; -static const RoomID kWSC15 = 14; -static const RoomID kWSC16 = 15; -static const RoomID kWSC17 = 16; -static const RoomID kWSC18 = 17; -static const RoomID kWSC19 = 18; -static const RoomID kWSC20 = 19; -static const RoomID kWSC21 = 20; -static const RoomID kWSC22 = 21; -static const RoomID kWSC23 = 22; -static const RoomID kWSC24 = 23; -static const RoomID kWSC25 = 24; -static const RoomID kWSC26 = 25; -static const RoomID kWSC27 = 26; -static const RoomID kWSC28 = 27; -static const RoomID kWSC29 = 28; -static const RoomID kWSC31 = 29; -static const RoomID kWSC32 = 30; -static const RoomID kWSC33 = 31; -static const RoomID kWSC34 = 32; -static const RoomID kWSC35 = 33; -static const RoomID kWSC36 = 34; -static const RoomID kWSC37 = 35; -static const RoomID kWSC38 = 36; -static const RoomID kWSC39 = 37; -static const RoomID kWSC40 = 38; -static const RoomID kWSC41 = 39; -static const RoomID kWSC42 = 40; -static const RoomID kWSC43 = 41; -static const RoomID kWSC44 = 42; -static const RoomID kWSC45 = 43; -static const RoomID kWSC46 = 44; -static const RoomID kWSC47 = 45; -static const RoomID kWSC48 = 46; -static const RoomID kWSC49 = 47; -static const RoomID kWSC50 = 48; -static const RoomID kWSC52 = 49; -static const RoomID kWSC53 = 50; -static const RoomID kWSC54 = 51; -static const RoomID kWSC55 = 52; -static const RoomID kWSC56 = 53; -static const RoomID kWSC57 = 54; -static const RoomID kWSC58 = 55; -static const RoomID kWSC60 = 56; -static const RoomID kWSC60East = 57; -static const RoomID kWSC60North = 58; -static const RoomID kWSC61 = 59; -static const RoomID kWSC61South = 60; -static const RoomID kWSC61West = 61; -static const RoomID kWSC63 = 63; -static const RoomID kWSC64 = 64; -static const RoomID kWSC65 = 65; -static const RoomID kWSC65Screen = 66; -static const RoomID kWSC66 = 67; -static const RoomID kWSC67 = 68; -static const RoomID kWSC68 = 69; -static const RoomID kWSC69 = 70; -static const RoomID kWSC70 = 71; -static const RoomID kWSC71 = 72; -static const RoomID kWSC72 = 73; -static const RoomID kWSC73 = 74; -static const RoomID kWSC74 = 75; -static const RoomID kWSC75 = 76; -static const RoomID kWSC0Z = 77; -static const RoomID kWSC76 = 78; -static const RoomID kWSC77 = 79; -static const RoomID kWSC78 = 80; -static const RoomID kWSC79 = 81; -static const RoomID kWSC80 = 82; -static const RoomID kWSC81 = 83; -static const RoomID kWSC82 = 84; -static const RoomID kWSC83 = 85; -static const RoomID kWSC84 = 86; -static const RoomID kWSC85 = 87; -static const RoomID kWSC86 = 88; -static const RoomID kWSC87 = 89; -static const RoomID kWSC88 = 90; -static const RoomID kWSC89 = 91; -static const RoomID kWSC90 = 92; -static const RoomID kWSC91 = 93; -static const RoomID kWSC92 = 94; -static const RoomID kWSC93 = 95; -static const RoomID kWSC94 = 96; -static const RoomID kWSC95 = 97; -static const RoomID kWSC96 = 98; -static const RoomID kWSC97 = 99; -static const RoomID kWSC98 = 100; -static const RoomID kWSCDeathRoom = 101; +enum { + kWSC02 = 1, + kWSC03 = 4, + kWSC04 = 5, + kWSC06 = 6, + kWSC07 = 7, + kWSC08 = 8, + kWSC09 = 9, + kWSC10 = 10, + kWSC11 = 11, + kWSC13 = 12, + kWSC14 = 13, + kWSC15 = 14, + kWSC16 = 15, + kWSC17 = 16, + kWSC18 = 17, + kWSC19 = 18, + kWSC20 = 19, + kWSC21 = 20, + kWSC22 = 21, + kWSC23 = 22, + kWSC24 = 23, + kWSC25 = 24, + kWSC26 = 25, + kWSC27 = 26, + kWSC28 = 27, + kWSC29 = 28, + kWSC31 = 29, + kWSC32 = 30, + kWSC33 = 31, + kWSC34 = 32, + kWSC35 = 33, + kWSC36 = 34, + kWSC37 = 35, + kWSC38 = 36, + kWSC39 = 37, + kWSC40 = 38, + kWSC41 = 39, + kWSC42 = 40, + kWSC43 = 41, + kWSC44 = 42, + kWSC45 = 43, + kWSC46 = 44, + kWSC47 = 45, + kWSC48 = 46, + kWSC49 = 47, + kWSC50 = 48, + kWSC52 = 49, + kWSC53 = 50, + kWSC54 = 51, + kWSC55 = 52, + kWSC56 = 53, + kWSC57 = 54, + kWSC58 = 55, + kWSC60 = 56, + kWSC60East = 57, + kWSC60North = 58, + kWSC61 = 59, + kWSC61South = 60, + kWSC61West = 61, + kWSC63 = 63, + kWSC64 = 64, + kWSC65 = 65, + kWSC65Screen = 66, + kWSC66 = 67, + kWSC67 = 68, + kWSC68 = 69, + kWSC69 = 70, + kWSC70 = 71, + kWSC71 = 72, + kWSC72 = 73, + kWSC73 = 74, + kWSC74 = 75, + kWSC75 = 76, + kWSC0Z = 77, + kWSC76 = 78, + kWSC77 = 79, + kWSC78 = 80, + kWSC79 = 81, + kWSC80 = 82, + kWSC81 = 83, + kWSC82 = 84, + kWSC83 = 85, + kWSC84 = 86, + kWSC85 = 87, + kWSC86 = 88, + kWSC87 = 89, + kWSC88 = 90, + kWSC89 = 91, + kWSC90 = 92, + kWSC91 = 93, + kWSC92 = 94, + kWSC93 = 95, + kWSC94 = 96, + kWSC95 = 97, + kWSC96 = 98, + kWSC97 = 99, + kWSC98 = 100, + kWSCDeathRoom = 101 +}; // Hot Spot Activation IDs. -static const HotSpotActivationID kActivationZoomedInToAnalyzer = 1; -static const HotSpotActivationID kActivationShotByRobot = 2; -static const HotSpotActivationID kActivationWarnedAboutPoison = 3; -static const HotSpotActivationID kActivationMorphScreenOff = 4; -static const HotSpotActivationID kActivationReadyForMorph = 5; -static const HotSpotActivationID kActivationMorphLooping = 6; -static const HotSpotActivationID kActivationMorphInterrupted = 7; -static const HotSpotActivationID kActivationW03NorthOff = 8; -static const HotSpotActivationID kActivationW03NorthReadyForInstructions = 9; -static const HotSpotActivationID kActivationW03NorthSawInstructions = 10; -static const HotSpotActivationID kActivationW03NorthInGame = 11; -static const HotSpotActivationID kActivationReadyForSynthesis = 12; -static const HotSpotActivationID kActivationSynthesizerLooping = 13; -static const HotSpotActivationID kActivationReadyForMap = 14; -static const HotSpotActivationID kActivationSinclairOfficeLocked = 15; -static const HotSpotActivationID kActivationW58SouthDoorLocked = 16; -static const HotSpotActivationID kActivationW61SouthOff = 17; -static const HotSpotActivationID kActivationW61SouthOn = 18; -static const HotSpotActivationID kActivationW61MessagesOff = 19; -static const HotSpotActivationID kActivationW61MessagesOn = 20; -static const HotSpotActivationID kActivationWSCRobotHeadOpen = 21; -static const HotSpotActivationID kActivationRobotTurning = 22; -static const HotSpotActivationID kActivationRobotDead = 23; -static const HotSpotActivationID kActivationRobotGone = 24; +enum { + kActivationZoomedInToAnalyzer = 1, + kActivationShotByRobot = 2, + kActivationWarnedAboutPoison = 3, + kActivationMorphScreenOff = 4, + kActivationReadyForMorph = 5, + kActivationMorphLooping = 6, + kActivationMorphInterrupted = 7, + kActivationW03NorthOff = 8, + kActivationW03NorthReadyForInstructions = 9, + kActivationW03NorthSawInstructions = 10, + kActivationW03NorthInGame = 11, + kActivationReadyForSynthesis = 12, + kActivationSynthesizerLooping = 13, + kActivationReadyForMap = 14, + kActivationSinclairOfficeLocked = 15, + kActivationW58SouthDoorLocked = 16, + kActivationW61SouthOff = 17, + kActivationW61SouthOn = 18, + kActivationW61MessagesOff = 19, + kActivationW61MessagesOn = 20, + kActivationWSCRobotHeadOpen = 21, + kActivationRobotTurning = 22, + kActivationRobotDead = 23, + kActivationRobotGone = 24 +}; // Hot Spot IDs. -static const HotSpotID kWSCDropDartSpotID = 5000; -static const HotSpotID kWSCTurnOnAnalyzerSpotID = 5001; -static const HotSpotID kWSCAnalyzerScreenSpotID = 5002; -static const HotSpotID kWSCSpinRobotSpotID = 5003; -static const HotSpotID kWSC01YesSpotID = 5004; -static const HotSpotID kWSC01NoSpotID = 5005; -static const HotSpotID kWSC01AcknowledgeWarningSpotID = 5006; -static const HotSpotID kWSC02SouthMorphSpotID = 5007; -static const HotSpotID kWSC02SouthMessagesSpotID = 5008; -static const HotSpotID kWSC02SouthMorphOutSpotID = 5009; -static const HotSpotID kWSC02ActivateMorphScreenSpotID = 5010; -static const HotSpotID kWSC02SouthStartMorphSpotID = 5011; -static const HotSpotID kWSC02SouthInterruptMorphSpotID = 5012; -static const HotSpotID kWSC02SouthMorphFinishedSpotID = 5013; -static const HotSpotID kWSC02SouthTakeArgonSpotID = 5014; -static const HotSpotID kWSC02SouthMessagesOutSpotID = 5015; -static const HotSpotID kWSC02SouthTakeNitrogenSpotID = 5016; -static const HotSpotID kWSC02SouthPlayMessagesSpotID = 5017; -static const HotSpotID kWSC03NorthActivateScreenSpotID = 5018; -static const HotSpotID kWSC03NorthBuildMoleculeSpotID = 5019; -static const HotSpotID kWSC03NorthProceedSpotID = 5020; -static const HotSpotID kWSC03NorthMolecule1SpotID = 5021; -static const HotSpotID kWSC03NorthMolecule2SpotID = 5022; -static const HotSpotID kWSC03NorthMolecule3SpotID = 5023; -static const HotSpotID kWSC03NorthMolecule4SpotID = 5024; -static const HotSpotID kWSC03NorthMolecule5SpotID = 5025; -static const HotSpotID kWSC03NorthMolecule6SpotID = 5026; -static const HotSpotID kWSC03SouthActivateSynthesizerSpotID = 5027; -static const HotSpotID kWSC03SouthPickUpAntidoteSpotID = 5028; -static const HotSpotID kWSC07SouthMapSpotID = 5029; -static const HotSpotID kW42EastUnlockDoorSpotID = 5030; -static const HotSpotID kW56NorthMapSpotID = 5031; -static const HotSpotID kW58SouthPryDoorSpotID = 5032; -static const HotSpotID kWSC60EastSpotID = 5033; -static const HotSpotID kWSC60NorthSpotID = 5034; -static const HotSpotID kWSC60EastOutSpotID = 5035; -static const HotSpotID kWSC60NorthOutSpotID = 5036; -static const HotSpotID kWSC61EastSpotID = 5037; -static const HotSpotID kWSC61SouthSpotID = 5038; -static const HotSpotID kW61SouthMachineGunSpotID = 5039; -static const HotSpotID kW61SouthDropMachineGunSpotID = 5040; -static const HotSpotID kWSC61WestSpotID = 5041; -static const HotSpotID kWSC61SouthOutSpotID = 5042; -static const HotSpotID kW61SouthActivateSpotID = 5043; -static const HotSpotID kW61SmartAlloysSpotID = 5044; -static const HotSpotID kW61MorphingSpotID = 5045; -static const HotSpotID kW61TimeBendingSpotID = 5046; -static const HotSpotID kWSC61WestOutSpotID = 5047; -static const HotSpotID kW61TurnOnMessagesSpotID = 5048; -static const HotSpotID kW61WhiteMessageSpotID = 5049; -static const HotSpotID kW61WalchekMessageSpotID = 5050; -static const HotSpotID kWSC65SouthScreenSpotID = 5051; -static const HotSpotID kWSC65SouthScreenOutSpotID = 5052; -static const HotSpotID kW98RetinalChipSpotID = 5053; -static const HotSpotID kW98MapChipSpotID = 5054; -static const HotSpotID kW98OpticalChipSpotID = 5055; -static const HotSpotID kW98DropArgonSpotID = 5056; -static const HotSpotID kW98GrabCableSpotID = 5057; -static const HotSpotID kW98OpenRobotSpotID = 5058; -static const HotSpotID kW98StunGunSpotID = 5059; +enum { + kWSCDropDartSpotID = 5000, + kWSCTurnOnAnalyzerSpotID = 5001, + kWSCAnalyzerScreenSpotID = 5002, + kWSCSpinRobotSpotID = 5003, + kWSC01YesSpotID = 5004, + kWSC01NoSpotID = 5005, + kWSC01AcknowledgeWarningSpotID = 5006, + kWSC02SouthMorphSpotID = 5007, + kWSC02SouthMessagesSpotID = 5008, + kWSC02SouthMorphOutSpotID = 5009, + kWSC02ActivateMorphScreenSpotID = 5010, + kWSC02SouthStartMorphSpotID = 5011, + kWSC02SouthInterruptMorphSpotID = 5012, + kWSC02SouthMorphFinishedSpotID = 5013, + kWSC02SouthTakeArgonSpotID = 5014, + kWSC02SouthMessagesOutSpotID = 5015, + kWSC02SouthTakeNitrogenSpotID = 5016, + kWSC02SouthPlayMessagesSpotID = 5017, + kWSC03NorthActivateScreenSpotID = 5018, + kWSC03NorthBuildMoleculeSpotID = 5019, + kWSC03NorthProceedSpotID = 5020, + kWSC03NorthMolecule1SpotID = 5021, + kWSC03NorthMolecule2SpotID = 5022, + kWSC03NorthMolecule3SpotID = 5023, + kWSC03NorthMolecule4SpotID = 5024, + kWSC03NorthMolecule5SpotID = 5025, + kWSC03NorthMolecule6SpotID = 5026, + kWSC03SouthActivateSynthesizerSpotID = 5027, + kWSC03SouthPickUpAntidoteSpotID = 5028, + kWSC07SouthMapSpotID = 5029, + kW42EastUnlockDoorSpotID = 5030, + kW56NorthMapSpotID = 5031, + kW58SouthPryDoorSpotID = 5032, + kWSC60EastSpotID = 5033, + kWSC60NorthSpotID = 5034, + kWSC60EastOutSpotID = 5035, + kWSC60NorthOutSpotID = 5036, + kWSC61EastSpotID = 5037, + kWSC61SouthSpotID = 5038, + kW61SouthMachineGunSpotID = 5039, + kW61SouthDropMachineGunSpotID = 5040, + kWSC61WestSpotID = 5041, + kWSC61SouthOutSpotID = 5042, + kW61SouthActivateSpotID = 5043, + kW61SmartAlloysSpotID = 5044, + kW61MorphingSpotID = 5045, + kW61TimeBendingSpotID = 5046, + kWSC61WestOutSpotID = 5047, + kW61TurnOnMessagesSpotID = 5048, + kW61WhiteMessageSpotID = 5049, + kW61WalchekMessageSpotID = 5050, + kWSC65SouthScreenSpotID = 5051, + kWSC65SouthScreenOutSpotID = 5052, + kW98RetinalChipSpotID = 5053, + kW98MapChipSpotID = 5054, + kW98OpticalChipSpotID = 5055, + kW98DropArgonSpotID = 5056, + kW98GrabCableSpotID = 5057, + kW98OpenRobotSpotID = 5058, + kW98StunGunSpotID = 5059 +}; // Extra sequence IDs. -static const ExtraID kWSCArrivalFromTSA = 0; -static const ExtraID kWSCShotByRobot = 1; -static const ExtraID kWSCDartScan1 = 2; -static const ExtraID kWSCDartScan2 = 3; -static const ExtraID kWSCDartScanNo = 4; -static const ExtraID kWSCDartScan3 = 5; -static const ExtraID kWSCAnalyzerPowerUp = 6; -static const ExtraID kWSCAnalyzerPowerUpWithDart = 7; -static const ExtraID kWSCDropDartIntoAnalyzer = 8; -static const ExtraID kWSCAnalyzeDart = 9; -static const ExtraID kWSCZoomOutFromAnalyzer = 10; -static const ExtraID kWSCSpinRobot = 11; -static const ExtraID kWSC02MorphZoomNoArgon = 12; -static const ExtraID kWSC02MessagesZoomNoNitrogen = 13; -static const ExtraID kWSC02ZoomOutNoArgon = 14; -static const ExtraID kWSC02TurnOnMorphScreen = 15; -static const ExtraID kWSC02DropToMorphExperiment = 16; -static const ExtraID kWSC02MorphLoop = 17; -static const ExtraID kWSC02MorphInterruption = 18; -static const ExtraID kWSC02MorphFinished = 19; -static const ExtraID kWSC02TurnOffMorphScreen = 20; -static const ExtraID kWSC02SouthViewNoArgon = 21; -static const ExtraID kMessagesMovedToOffice = 22; -static const ExtraID kMessagesOff = 23; -static const ExtraID kMessagesZoomOutNoNitrogen = 24; -static const ExtraID kMessagesMovedToOfficeNoNitrogen = 25; -static const ExtraID kMessagesOffNoNitrogen = 26; -static const ExtraID kMessagesViewNoNitrogen = 27; -static const ExtraID kMessagesViewMachineOnNoNitrogen = 28; -static const ExtraID kW03NorthActivate = 29; -static const ExtraID kW03NorthGetData = 30; -static const ExtraID kW03NorthInstructions = 31; -static const ExtraID kW03NorthPrepMolecule1 = 32; -static const ExtraID kW03NorthPrepMolecule2 = 33; -static const ExtraID kW03NorthPrepMolecule3 = 34; -static const ExtraID kW03NorthFinishSynthesis = 35; -static const ExtraID kW03SouthCreateAntidote = 36; -static const ExtraID kW03SouthAntidoteLoop = 37; -static const ExtraID kW03SouthDeactivate = 38; -static const ExtraID kW03SouthViewNoAntidote = 39; -static const ExtraID kWSC07SouthMap = 40; -static const ExtraID kW17WestPeopleCrossing = 41; -static const ExtraID kW17WestPeopleCrossingView = 42; -static const ExtraID kW21SouthPeopleCrossing = 43; -static const ExtraID kW24SouthPeopleCrossing = 44; -static const ExtraID kW34EastPeopleCrossing = 45; -static const ExtraID kW36WestPeopleCrossing = 46; -static const ExtraID kW38NorthPeopleCrossing = 47; -static const ExtraID kW46SouthPeopleCrossing = 48; -static const ExtraID kW49NorthPeopleCrossing = 49; -static const ExtraID kW49NorthPeopleCrossingView = 50; -static const ExtraID kWSC56SouthMap = 51; -static const ExtraID kNerdAtTheDoor1 = 52; -static const ExtraID kNerdAtTheDoor2 = 53; -static const ExtraID kW61SouthZoomInNoGun = 54; -static const ExtraID kW61Brochure = 55; -static const ExtraID kW61SouthScreenOnWithGun = 56; -static const ExtraID kW61SouthScreenOffWithGun = 57; -static const ExtraID kW61SouthSmartAlloysWithGun = 58; -static const ExtraID kW61SouthMorphingWithGun = 59; -static const ExtraID kW61SouthTimeBendingWithGun = 60; -static const ExtraID kW61SouthZoomOutNoGun = 61; -static const ExtraID kW61SouthScreenOnNoGun = 62; -static const ExtraID kW61SouthScreenOffNoGun = 63; -static const ExtraID kW61SouthSmartAlloysNoGun = 64; -static const ExtraID kW61SouthMorphingNoGun = 65; -static const ExtraID kW61SouthTimeBendingNoGun = 66; -static const ExtraID kW61MessagesOn = 67; -static const ExtraID kW61MessagesOff = 68; -static const ExtraID kW61WhiteMessage = 69; -static const ExtraID kW61WalchekMessage = 70; -static const ExtraID kW61WalchekEasterEgg1 = 71; -static const ExtraID kW62SouthPlasmaRobotAppears = 72; -static const ExtraID kW62ZoomToRobot = 73; -static const ExtraID kW62ZoomOutFromRobot = 74; -static const ExtraID kW62PlasmaDodgeSurvive = 75; -static const ExtraID kW62PlasmaDodgeDie = 76; -static const ExtraID kW65SouthSinclairLecture = 77; -static const ExtraID kW73WestPeopleCrossing = 78; -static const ExtraID kW73WestPeopleCrossingView = 79; -static const ExtraID kW0ZSpottedByWomen = 80; -static const ExtraID kW95RobotShoots = 81; -static const ExtraID kW98MorphsToRobot = 82; -static const ExtraID kW98RobotShoots = 83; -static const ExtraID kW98RobotShocked = 84; -static const ExtraID kW98RobotGassed = 85; -static const ExtraID kW98RobotHeadOpensDark = 86; -static const ExtraID kW98RobotHead000Dark = 87; -static const ExtraID kW98RobotHead001Dark = 88; -static const ExtraID kW98RobotHead010Dark = 89; -static const ExtraID kW98RobotHead011Dark = 90; -static const ExtraID kW98RobotHead100Dark = 91; -static const ExtraID kW98RobotHead101Dark = 92; -static const ExtraID kW98RobotHead110Dark = 93; -static const ExtraID kW98RobotHead111Dark = 94; -static const ExtraID kW98RobotHeadClosesDark = 95; -static const ExtraID kW98WestViewWithGunDark = 96; -static const ExtraID kW98WestViewNoGunDark = 97; -static const ExtraID kW98RobotHeadOpensLight = 98; -static const ExtraID kW98RobotHead000Light = 99; -static const ExtraID kW98RobotHead001Light = 100; -static const ExtraID kW98RobotHead010Light = 101; -static const ExtraID kW98RobotHead011Light = 102; -static const ExtraID kW98RobotHead100Light = 103; -static const ExtraID kW98RobotHead101Light = 104; -static const ExtraID kW98RobotHead110Light = 105; -static const ExtraID kW98RobotHead111Light = 106; -static const ExtraID kW98RobotHeadClosesLight = 107; -static const ExtraID kW98WestViewWithGunLight = 108; -static const ExtraID kW98WestViewNoGunLight = 109; +enum { + kWSCArrivalFromTSA = 0, + kWSCShotByRobot = 1, + kWSCDartScan1 = 2, + kWSCDartScan2 = 3, + kWSCDartScanNo = 4, + kWSCDartScan3 = 5, + kWSCAnalyzerPowerUp = 6, + kWSCAnalyzerPowerUpWithDart = 7, + kWSCDropDartIntoAnalyzer = 8, + kWSCAnalyzeDart = 9, + kWSCZoomOutFromAnalyzer = 10, + kWSCSpinRobot = 11, + kWSC02MorphZoomNoArgon = 12, + kWSC02MessagesZoomNoNitrogen = 13, + kWSC02ZoomOutNoArgon = 14, + kWSC02TurnOnMorphScreen = 15, + kWSC02DropToMorphExperiment = 16, + kWSC02MorphLoop = 17, + kWSC02MorphInterruption = 18, + kWSC02MorphFinished = 19, + kWSC02TurnOffMorphScreen = 20, + kWSC02SouthViewNoArgon = 21, + kMessagesMovedToOffice = 22, + kMessagesOff = 23, + kMessagesZoomOutNoNitrogen = 24, + kMessagesMovedToOfficeNoNitrogen = 25, + kMessagesOffNoNitrogen = 26, + kMessagesViewNoNitrogen = 27, + kMessagesViewMachineOnNoNitrogen = 28, + kW03NorthActivate = 29, + kW03NorthGetData = 30, + kW03NorthInstructions = 31, + kW03NorthPrepMolecule1 = 32, + kW03NorthPrepMolecule2 = 33, + kW03NorthPrepMolecule3 = 34, + kW03NorthFinishSynthesis = 35, + kW03SouthCreateAntidote = 36, + kW03SouthAntidoteLoop = 37, + kW03SouthDeactivate = 38, + kW03SouthViewNoAntidote = 39, + kWSC07SouthMap = 40, + kW17WestPeopleCrossing = 41, + kW17WestPeopleCrossingView = 42, + kW21SouthPeopleCrossing = 43, + kW24SouthPeopleCrossing = 44, + kW34EastPeopleCrossing = 45, + kW36WestPeopleCrossing = 46, + kW38NorthPeopleCrossing = 47, + kW46SouthPeopleCrossing = 48, + kW49NorthPeopleCrossing = 49, + kW49NorthPeopleCrossingView = 50, + kWSC56SouthMap = 51, + kNerdAtTheDoor1 = 52, + kNerdAtTheDoor2 = 53, + kW61SouthZoomInNoGun = 54, + kW61Brochure = 55, + kW61SouthScreenOnWithGun = 56, + kW61SouthScreenOffWithGun = 57, + kW61SouthSmartAlloysWithGun = 58, + kW61SouthMorphingWithGun = 59, + kW61SouthTimeBendingWithGun = 60, + kW61SouthZoomOutNoGun = 61, + kW61SouthScreenOnNoGun = 62, + kW61SouthScreenOffNoGun = 63, + kW61SouthSmartAlloysNoGun = 64, + kW61SouthMorphingNoGun = 65, + kW61SouthTimeBendingNoGun = 66, + kW61MessagesOn = 67, + kW61MessagesOff = 68, + kW61WhiteMessage = 69, + kW61WalchekMessage = 70, + kW61WalchekEasterEgg1 = 71, + kW62SouthPlasmaRobotAppears = 72, + kW62ZoomToRobot = 73, + kW62ZoomOutFromRobot = 74, + kW62PlasmaDodgeSurvive = 75, + kW62PlasmaDodgeDie = 76, + kW65SouthSinclairLecture = 77, + kW73WestPeopleCrossing = 78, + kW73WestPeopleCrossingView = 79, + kW0ZSpottedByWomen = 80, + kW95RobotShoots = 81, + kW98MorphsToRobot = 82, + kW98RobotShoots = 83, + kW98RobotShocked = 84, + kW98RobotGassed = 85, + kW98RobotHeadOpensDark = 86, + kW98RobotHead000Dark = 87, + kW98RobotHead001Dark = 88, + kW98RobotHead010Dark = 89, + kW98RobotHead011Dark = 90, + kW98RobotHead100Dark = 91, + kW98RobotHead101Dark = 92, + kW98RobotHead110Dark = 93, + kW98RobotHead111Dark = 94, + kW98RobotHeadClosesDark = 95, + kW98WestViewWithGunDark = 96, + kW98WestViewNoGunDark = 97, + kW98RobotHeadOpensLight = 98, + kW98RobotHead000Light = 99, + kW98RobotHead001Light = 100, + kW98RobotHead010Light = 101, + kW98RobotHead011Light = 102, + kW98RobotHead100Light = 103, + kW98RobotHead101Light = 104, + kW98RobotHead110Light = 105, + kW98RobotHead111Light = 106, + kW98RobotHeadClosesLight = 107, + kW98WestViewWithGunLight = 108, + kW98WestViewNoGunLight = 109 +}; static const CoordType kMoleculesMovieLeft = kNavAreaLeft + 112; static const CoordType kMoleculesMovieTop = kNavAreaTop + 40; diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp index 0010180d8d..4262ad4c12 100644 --- a/engines/pegasus/pegasus.cpp +++ b/engines/pegasus/pegasus.cpp @@ -979,7 +979,7 @@ void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) { resetIntroTimer(); break; case kMenuCmdPauseSave: - result = showSaveDialog(); + result = showSaveDialog(); if (result.getCode() != Common::kUserCanceled) { if (result.getCode() != Common::kNoError) @@ -2514,7 +2514,7 @@ void PegasusEngine::initKeymap() { { Common::KEYCODE_t, "TMA", _("Toggle Center Data Display") }, { Common::KEYCODE_i, "TIN", _("Display/Hide Info Screen") }, { Common::KEYCODE_ESCAPE, "PM", _("Display/Hide Pause Menu") }, - { Common::KEYCODE_e, "WTF", _("???") } // easter egg key (without being completely upfront about it) + { Common::KEYCODE_e, "WTF", "???" } // easter egg key (without being completely upfront about it) }; for (uint i = 0; i < ARRAYSIZE(keyActionEntries); i++) { diff --git a/engines/prince/animation.cpp b/engines/prince/animation.cpp new file mode 100644 index 0000000000..aabdd7a623 --- /dev/null +++ b/engines/prince/animation.cpp @@ -0,0 +1,178 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/animation.h" +#include "prince/decompress.h" + +#include "common/debug.h" +#include "common/endian.h" + +namespace Prince { + +Animation::Animation() : _loopCount(0), _phaseCount(0), _frameCount(0), _baseX(0), _baseY(0) +{ +} + +Animation::~Animation() { + clear(); +} + +void Animation::clear() { + _phaseList.clear(); + for (int i = 0; i < _frameCount; i++) { + _frameList[i]._surface->free(); + delete _frameList[i]._surface; + _frameList[i]._surface = nullptr; + if (_frameList[i]._compressedData != nullptr) { + free(_frameList[i]._compressedData); + _frameList[i]._compressedData = nullptr; + } + } +} + +bool Animation::loadStream(Common::SeekableReadStream &stream) { + stream.skip(2); // skip not used x and y coord diff + _loopCount = stream.readUint16LE(); + _phaseCount = stream.readUint16LE(); + stream.skip(2); // skip _frameCount here + _baseX = stream.readUint16LE(); + _baseY = stream.readUint16LE(); + uint32 phaseTableOffset = stream.readUint32LE(); + uint32 tableOfFrameOffsets = stream.pos(); + + stream.seek(phaseTableOffset); + Phase tempPhase; + _frameCount = 0; + for (int phase = 0; phase < _phaseCount; phase++) { + tempPhase._phaseOffsetX = stream.readSint16LE(); + tempPhase._phaseOffsetY = stream.readSint16LE(); + tempPhase._phaseToFrameIndex = stream.readUint16LE(); + if (tempPhase._phaseToFrameIndex > _frameCount) { + _frameCount = tempPhase._phaseToFrameIndex; + } + _phaseList.push_back(tempPhase); + stream.skip(2); + } + if (_phaseCount) { + _frameCount++; + } + + Frame tempFrame; + for (int frame = 0; frame < _frameCount; frame++) { + stream.seek(tableOfFrameOffsets + frame * 4); + uint32 frameInfoOffset = stream.readUint32LE(); + stream.seek(frameInfoOffset); + uint16 frameWidth = stream.readUint16LE(); + uint16 frameHeight = stream.readUint16LE(); + uint32 frameDataPos = stream.pos(); + uint32 frameDataOffset = stream.readUint32BE(); + + tempFrame._surface = new Graphics::Surface(); + tempFrame._surface->create(frameWidth, frameHeight, Graphics::PixelFormat::createFormatCLUT8()); + if (frameDataOffset == MKTAG('m', 'a', 's', 'm')) { + tempFrame._isCompressed = true; + tempFrame._dataSize = stream.readUint32LE(); + tempFrame._compressedData = (byte *)malloc(tempFrame._dataSize); + stream.read(tempFrame._compressedData, tempFrame._dataSize); + } else { + tempFrame._isCompressed = false; + tempFrame._dataSize = 0; + tempFrame._compressedData = nullptr; + stream.seek(frameDataPos); + for (uint16 i = 0; i < frameHeight; i++) { + stream.read(tempFrame._surface->getBasePtr(0, i), frameWidth); + } + } + _frameList.push_back(tempFrame); + } + + return true; +} + +int16 Animation::getLoopCount() const { + return _loopCount; +} + +int32 Animation::getPhaseCount() const { + return _phaseCount; +} + +int32 Animation::getFrameCount() const { + return _frameCount; +} + +int16 Animation::getBaseX() const { + return _baseX; +} + +int16 Animation::getBaseY() const { + return _baseY; +} + +int16 Animation::getPhaseOffsetX(int phaseIndex) const { + if (phaseIndex < _phaseCount) { + return _phaseList[phaseIndex]._phaseOffsetX; + } else { + error("getPhaseOffsetX() phaseIndex: %d, phaseCount: %d", phaseIndex, _phaseCount); + } +} + +int16 Animation::getPhaseOffsetY(int phaseIndex) const { + if (phaseIndex < _phaseCount) { + return _phaseList[phaseIndex]._phaseOffsetY; + } else { + error("getPhaseOffsetY() phaseIndex: %d, phaseCount: %d", phaseIndex, _phaseCount); + } +} + +int16 Animation::getPhaseFrameIndex(int phaseIndex) const { + if (phaseIndex < _phaseCount) { + return _phaseList[phaseIndex]._phaseToFrameIndex; + } else { + error("getPhaseFrameIndex() phaseIndex: %d, phaseCount: %d", phaseIndex, _phaseCount); + } +} + +Graphics::Surface *Animation::getFrame(int frameIndex) { + if (frameIndex < _frameCount) { + if (_frameList[frameIndex]._isCompressed) { + Decompressor dec; + byte *ddata = (byte *)malloc(_frameList[frameIndex]._dataSize); + dec.decompress(_frameList[frameIndex]._compressedData, ddata, _frameList[frameIndex]._dataSize); + int frameHeight = _frameList[frameIndex]._surface->h; + int frameWidth = _frameList[frameIndex]._surface->w; + for (uint16 i = 0; i < frameHeight; i++) { + memcpy(_frameList[frameIndex]._surface->getBasePtr(0, i), ddata + frameWidth * i, frameWidth); + } + free(ddata); + free(_frameList[frameIndex]._compressedData); + _frameList[frameIndex]._compressedData = nullptr; + _frameList[frameIndex]._dataSize = 0; + _frameList[frameIndex]._isCompressed = false; + } + return _frameList[frameIndex]._surface; + } else { + error("getFrame() frameIndex: %d, frameCount: %d", frameIndex, _frameCount); + } +} + +} // End of namespace Prince diff --git a/engines/prince/animation.h b/engines/prince/animation.h new file mode 100644 index 0000000000..3471ffa158 --- /dev/null +++ b/engines/prince/animation.h @@ -0,0 +1,73 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_ANIMATION_H +#define PRINCE_ANIMATION_H + +#include "common/array.h" +#include "common/stream.h" + +#include "graphics/surface.h" + +namespace Prince { + +class Animation { +public: + Animation(); + ~Animation(); + bool loadStream(Common::SeekableReadStream &stream); + + int16 getLoopCount() const; + int32 getPhaseCount() const; + int32 getFrameCount() const; + int16 getBaseX() const; + int16 getBaseY() const; + int16 getPhaseOffsetX(int phaseIndex) const; + int16 getPhaseOffsetY(int phaseIndex) const; + int16 getPhaseFrameIndex(int phaseIndex) const; + Graphics::Surface *getFrame(int frameIndex); + void clear(); + +private: + struct Phase { + int16 _phaseOffsetX; + int16 _phaseOffsetY; + uint16 _phaseToFrameIndex; + }; + struct Frame { + bool _isCompressed; + uint32 _dataSize; + byte *_compressedData; + Graphics::Surface *_surface; + }; + Common::Array<Frame> _frameList; + Common::Array<Phase> _phaseList; + int16 _loopCount; + int16 _phaseCount; + int32 _frameCount; + int16 _baseX; + int16 _baseY; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/archive.cpp b/engines/prince/archive.cpp new file mode 100644 index 0000000000..a01f824df8 --- /dev/null +++ b/engines/prince/archive.cpp @@ -0,0 +1,166 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/archive.h" +#include "prince/decompress.h" + +#include "common/stream.h" +#include "common/debug.h" +#include "common/memstream.h" + +namespace Prince { + +PtcArchive::PtcArchive() : _stream(nullptr) { +} + +PtcArchive::~PtcArchive() { + close(); +} + +static void decrypt(byte *buffer, uint32 size) { + uint32 key = 0xDEADF00D; + while (size--) { + *buffer++ += key & 0xFF; + key ^= 0x2E84299A; + key += MKTAG('B', 'L', 'A', 'H'); + key = ((key & 1) << 31) | (key >> 1); + } +} + +bool PtcArchive::open(const Common::String &filename) { + _stream = SearchMan.createReadStreamForMember(filename); + if (!_stream) + return false; + + _stream->readUint32LE(); // magic + uint32 fileTableOffset = _stream->readUint32LE() ^ 0x4D4F4B2D; // MOK- + uint32 fileTableSize = _stream->readUint32LE() ^ 0x534F4654; // SOFT + + //debug("fileTableOffset : %08X", fileTableOffset); + //debug("fileTableSize: %08X", fileTableSize); + + _stream->seek(fileTableOffset); + + byte *fileTable = (byte *)malloc(fileTableSize); + byte *fileTableEnd = fileTable + fileTableSize; + _stream->read(fileTable, fileTableSize); + decrypt(fileTable, fileTableSize); + + for (byte *fileItem = fileTable; fileItem < fileTableEnd; fileItem += 32) { + FileEntry item; + Common::String name = (const char*)fileItem; + item._offset = READ_LE_UINT32(fileItem + 24); + item._size = READ_LE_UINT32(fileItem + 28); + //debug("%12s %8X %d", name.c_str(), item._offset, item._size); + _items[name] = item; + } + + free(fileTable); + + return true; +} + +bool PtcArchive::openTranslation(const Common::String &filename) { + _stream = SearchMan.createReadStreamForMember(filename); + if (!_stream) + return false; + + Common::Array<Common::String> translationNames; + Common::String translationFileName; + const int kTranslationFiles = 5; + for (int i = 0; i < kTranslationFiles; i++) { + translationFileName = _stream->readLine(); + translationNames.push_back(translationFileName); + } + FileEntry item; + for (int i = 0; i < kTranslationFiles; i++) { + item._offset = _stream->readUint32LE(); + item._size = _stream->readUint32LE(); + _items[translationNames[i]] = item; + } + + return true; +} + +void PtcArchive::close() { + delete _stream; + _stream = nullptr; + _items.clear(); +} + +bool PtcArchive::hasFile(const Common::String &name) const { + // TODO: check if path matching should be added + return _items.contains(name); +} + +int PtcArchive::listMembers(Common::ArchiveMemberList &list) const { + int matches = 0; + + for (FileMap::const_iterator it = _items.begin(); it != _items.end(); ++it) { + list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(it->_key, this))); + matches++; + } + + return matches; +} + +const Common::ArchiveMemberPtr PtcArchive::getMember(const Common::String &name) const { + if (!_items.contains(name)) { + Common::ArchiveMemberPtr(); + } + return Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *PtcArchive::createReadStreamForMember(const Common::String &name) const { + if (!_items.contains(name)) { + return 0; + } + + const FileEntry &entryHeader = _items[name]; + + if (entryHeader._size < 4) + return 0; + + uint32 size = entryHeader._size; + + _stream->seek(entryHeader._offset); + + // This *HAS* to be malloc (not new[]) because MemoryReadStream uses free() to free the memory + byte *buffer = (byte *)malloc(size); + _stream->read(buffer, size); + + if (READ_BE_UINT32(buffer) == MKTAG('M', 'A', 'S', 'M')) { + Decompressor dec; + uint32 decompLen = READ_BE_UINT32(buffer + 14); + byte *decompData = (byte *)malloc(decompLen); + dec.decompress(buffer + 18, decompData, decompLen); + free(buffer); + size = decompLen; + buffer = decompData; + } + + //debug("PtcArchive::createReadStreamForMember name %s", name.c_str()); + + return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES); +} + +} // End of namespace Prince diff --git a/engines/prince/archive.h b/engines/prince/archive.h new file mode 100644 index 0000000000..a640b77911 --- /dev/null +++ b/engines/prince/archive.h @@ -0,0 +1,62 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_ARCHIVE_H +#define PRINCE_ARCHIVE_H + +#include "common/archive.h" +#include "common/hashmap.h" +#include "common/hash-str.h" + +namespace Prince { + +class PtcArchive : public Common::Archive { +public: + PtcArchive(); + ~PtcArchive(); + + bool open(const Common::String &filename); + bool openTranslation(const Common::String &filename); + void close(); + bool isOpen() const { return _stream != 0; } + + // Common::Archive API implementation + bool hasFile(const Common::String &name) const; + int listMembers(Common::ArchiveMemberList &list) const; + const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + +private: + struct FileEntry { + uint32 _offset; + uint32 _size; + }; + + Common::SeekableReadStream *_stream; + + typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; + FileMap _items; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/common.h b/engines/prince/common.h new file mode 100644 index 0000000000..c846f9a751 --- /dev/null +++ b/engines/prince/common.h @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_COMMON_H +#define PRINCE_COMMON_H + +namespace Prince { + +enum Direction { + kDirLD, + kDirL, + kDirLU, + kDirRD, + kDirR, + kDirRU, + kDirUL, + kDirU, + kDirUR, + kDirDL, + kDirD, + kDirDR +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/configure.engine b/engines/prince/configure.engine new file mode 100644 index 0000000000..50740d9f41 --- /dev/null +++ b/engines/prince/configure.engine @@ -0,0 +1,3 @@ +# This file is included from the main "configure" script +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine prince "The Prince and The Coward" no diff --git a/engines/prince/cursor.cpp b/engines/prince/cursor.cpp new file mode 100644 index 0000000000..ddcabbd28f --- /dev/null +++ b/engines/prince/cursor.cpp @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/cursor.h" + +#include "common/debug.h" + +namespace Prince { + +Cursor::Cursor() : _surface(nullptr) { +} + +Cursor::~Cursor() { + if (_surface != nullptr) { + _surface->free(); + delete _surface; + _surface = nullptr; + } +} + +bool Cursor::loadStream(Common::SeekableReadStream &stream) { + stream.skip(4); + uint16 width = stream.readUint16LE(); + uint16 height = stream.readUint16LE(); + + _surface = new Graphics::Surface(); + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + for (int h = 0; h < height; h++) { + stream.read(_surface->getBasePtr(0, h), width); + } + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/cursor.h b/engines/prince/cursor.h new file mode 100644 index 0000000000..fb07d01729 --- /dev/null +++ b/engines/prince/cursor.h @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CURSOR_PRINCE_H +#define CURSOR_PRINCE_H + +#include "graphics/surface.h" + +#include "common/stream.h" + +namespace Prince { + +class Cursor { +public: + Cursor(); + ~Cursor(); + + bool loadStream(Common::SeekableReadStream &stream); + const Graphics::Surface *getSurface() const { return _surface; } + +private: + Graphics::Surface *_surface; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/curve_values.h b/engines/prince/curve_values.h new file mode 100644 index 0000000000..d72f11fd36 --- /dev/null +++ b/engines/prince/curve_values.h @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +namespace Prince { + +const int curveValues[17][4] = { + { 32768, 0, 0, 0 }, + { 25200, 7200, 480, -112 }, + { 18816, 12544, 1792, -384 }, + { 13520, 16224, 3744, -720 }, + { 9216, 18432, 6144, -1024 }, + { 5808, 19360, 8800, -1200 }, + { 3200, 19200, 11520, -1152 }, + { 1296, 18144, 14112, -784 }, + { 0, 16384, 16384, 0 }, + { -784, 14112, 18144, 1296 }, + { -1152, 11520, 19200, 3200 }, + { -1200, 8800, 19360, 5808 }, + { -1024, 6144, 18432, 9216 }, + { -720, 3744, 16224, 13520 }, + { -384, 1792, 12544, 18816 }, + { -112, 480, 7200, 25200 }, + { 0, 0, 0, 32768 } +}; + +} // End of namespace Prince diff --git a/engines/prince/debugger.cpp b/engines/prince/debugger.cpp new file mode 100644 index 0000000000..fc216e0cfb --- /dev/null +++ b/engines/prince/debugger.cpp @@ -0,0 +1,174 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/debugger.h" +#include "prince/prince.h" +#include "prince/flags.h" +#include "prince/script.h" + +namespace Prince { + +Debugger::Debugger(PrinceEngine *vm, InterpreterFlags *flags) : GUI::Debugger(), _vm(vm), _locationNr(0), _flags(flags) { + registerCmd("continue", WRAP_METHOD(Debugger, cmdExit)); + registerCmd("level", WRAP_METHOD(Debugger, Cmd_DebugLevel)); + registerCmd("setflag", WRAP_METHOD(Debugger, Cmd_SetFlag)); + registerCmd("getflag", WRAP_METHOD(Debugger, Cmd_GetFlag)); + registerCmd("clearflag", WRAP_METHOD(Debugger, Cmd_ClearFlag)); + registerCmd("viewflc", WRAP_METHOD(Debugger, Cmd_ViewFlc)); + registerCmd("initroom", WRAP_METHOD(Debugger, Cmd_InitRoom)); + registerCmd("changecursor", WRAP_METHOD(Debugger, Cmd_ChangeCursor)); + registerCmd("additem", WRAP_METHOD(Debugger, Cmd_AddItem)); +} + +static int strToInt(const char *s) { + if (!*s) + // No string at all + return 0; + else if (toupper(s[strlen(s) - 1]) != 'H') + // Standard decimal string + return atoi(s); + + // Hexadecimal string + uint tmp = 0; + int read = sscanf(s, "%xh", &tmp); + if (read < 1) + error("strToInt failed on string \"%s\"", s); + return (int)tmp; +} + +bool Debugger::Cmd_DebugLevel(int argc, const char **argv) { + if (argc == 1) { + debugPrintf("Debugging is currently set at level %d\n", gDebugLevel); + } else { // set level + gDebugLevel = atoi(argv[1]); + if (0 <= gDebugLevel && gDebugLevel < 11) { + debugPrintf("Debug level set to level %d\n", gDebugLevel); + } else if (gDebugLevel < 0) { + debugPrintf("Debugging is now disabled\n"); + } else + debugPrintf("Not a valid debug level (0 - 10)\n"); + } + + return true; +} + +/* + * This command sets a flag + */ +bool Debugger::Cmd_SetFlag(int argc, const char **argv) { + // Check for a flag to set + if (argc != 3) { + debugPrintf("Usage: %s <flag number> <value>\n", argv[0]); + return true; + } + + int flagNum = strToInt(argv[1]); + uint16 value = strToInt(argv[2]); + _flags->setFlagValue((Flags::Id)flagNum, value); + return true; +} + +/* + * This command gets the value of a flag + */ +bool Debugger::Cmd_GetFlag(int argc, const char **argv) { + // Check for an flag to display + if (argc != 2) { + debugPrintf("Usage: %s <flag number>\n", argv[0]); + return true; + } + + int flagNum = strToInt(argv[1]); + debugPrintf("Value: %d\n", _flags->getFlagValue((Flags::Id)flagNum)); + return true; +} + +/* + * This command clears a flag + */ +bool Debugger::Cmd_ClearFlag(int argc, const char **argv) { + // Check for a flag to clear + if (argc != 2) { + debugPrintf("Usage: %s <flag number>\n", argv[0]); + return true; + } + + int flagNum = strToInt(argv[1]); + _flags->setFlagValue((Flags::Id)flagNum, 0); + return true; +} + +/* + * This command starts new flc anim + */ +bool Debugger::Cmd_ViewFlc(int argc, const char **argv) { + // Check for a flag to clear + if (argc != 2) { + debugPrintf("Usage: %s <anim number>\n", argv[0]); + return true; + } + + int flagNum = strToInt(argv[1]); + _vm->loadAnim(flagNum, false); + return true; +} + +bool Debugger::Cmd_InitRoom(int argc, const char **argv) { + // Check for a flag to clear + if (argc != 2) { + debugPrintf("Usage: %s <anim number>\n", argv[0]); + return true; + } + + _locationNr = strToInt(argv[1]); + return true; +} + +bool Debugger::Cmd_ChangeCursor(int argc, const char **argv) { + // Check for a flag to clear + if (argc != 2) { + debugPrintf("Usage: %s <curId>\n", argv[0]); + return true; + } + + _cursorNr = strToInt(argv[1]); + + return true; +} + +bool Debugger::Cmd_AddItem(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Usage: %s <itemId>\n", argv[0]); + return true; + } + if (!strcmp(argv[1], "map")) { + _vm->addInv(0, 29, true); + _vm->_flags->setFlagValue(Flags::MapaUsable, 1); + } else { + int itemId = strToInt(argv[1]); + _vm->addInv(0, itemId, true); + } + + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/debugger.h b/engines/prince/debugger.h new file mode 100644 index 0000000000..a4467e63d5 --- /dev/null +++ b/engines/prince/debugger.h @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_DEBUGGER_H +#define PRINCE_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace Prince { + +class PrinceEngine; +class InterpreterFlags; + +class Debugger : public GUI::Debugger { +public: + Debugger(PrinceEngine *vm, InterpreterFlags *flags); + virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ + + uint8 _locationNr; + uint8 _cursorNr; + +private: + bool Cmd_DebugLevel(int argc, const char **argv); + bool Cmd_SetFlag(int argc, const char **argv); + bool Cmd_GetFlag(int argc, const char **argv); + bool Cmd_ClearFlag(int argc, const char **argv); + bool Cmd_ViewFlc(int argc, const char **argv); + bool Cmd_InitRoom(int argc, const char **argv); + bool Cmd_ChangeCursor(int argc, const char **argv); + bool Cmd_AddItem(int argc, const char **argv); + + PrinceEngine *_vm; + InterpreterFlags *_flags; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/decompress.cpp b/engines/prince/decompress.cpp new file mode 100644 index 0000000000..7fba179541 --- /dev/null +++ b/engines/prince/decompress.cpp @@ -0,0 +1,171 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// John_Doe's implementation + +#include "prince/decompress.h" + +namespace Prince { + +static const uint16 table1[] = { + 0x8000, 0x0002, + 0x4000, 0x0004, + 0x2000, 0x0008, + 0x1000, 0x0010, + 0x0800, 0x0020, + 0x0400, 0x0040, + 0x0200, 0x0080, + 0x0100, 0x0100, + 0x0080, 0x0200, + 0x0040, 0x0400 +}; + +static const uint32 table2[] = { + 0x0000F000, + 0x0020FC00, + 0x00A0FF00, + 0x02A0FF80, + 0x06A0FFC0, + 0x0EA0FFE0, + 0x1EA0FFF0, + 0x3EA0FFF8 +}; + +static const uint16 table3[] = { + 0x8000, 0x0000, + 0x4000, 0x0002, + 0x2000, 0x0006, + 0x1000, 0x000E, + 0x0800, 0x001E, + 0x0400, 0x003E, + 0x0200, 0x007E, + 0x0100, 0x00FE, + 0x0080, 0x01FE, + 0x0040, 0x03FE, + 0x0020, 0x07FE, + 0x0010, 0x0FFE, + 0x0008, 0x1FFE, + 0x0004, 0x3FFE, + 0x0002, 0x7FFE, + 0x0001, 0xFFFE +}; + +void Decompressor::decompress(byte *source, byte *dest, uint32 destSize) { + byte *destEnd = dest + destSize; + int more; + _src = source; + _dst = dest; + _bitBuffer = 0x80; + while (_dst < destEnd) { + uint32 ebp; + uint16 offset, length; + if (getBit()) { + if (getBit()) { + if (getBit()) { + if (getBit()) { + if (getBit()) { + if (getBit()) { + uint32 tableIndex = 0; + while (getBit()) + tableIndex++; + length = table3[tableIndex * 2 + 0]; + do { + more = !(length & 0x8000); + length = (length << 1) | getBit(); + } while (more); + length += table3[tableIndex * 2 + 1]; + length++; + memcpy(_dst, _src, length); + _src += length; + _dst += length; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + *_dst++ = *_src++; + } + if (!getBit()) { + if (getBit()) { + uint32 tableIndex = getBit(); + tableIndex = (tableIndex << 1) | getBit(); + tableIndex = (tableIndex << 1) | getBit(); + ebp = table2[tableIndex]; + length = 1; + } else { + ebp = 0x0000FF00; + length = 0; + } + } else { + uint32 tableIndex = 0; + while (getBit()) + tableIndex++; + length = table1[tableIndex * 2 + 0]; + do { + more = !(length & 0x8000); + length = (length << 1) | getBit(); + } while (more); + length += table1[tableIndex * 2 + 1]; + tableIndex = getBit(); + tableIndex = (tableIndex << 1) | getBit(); + tableIndex = (tableIndex << 1) | getBit(); + ebp = table2[tableIndex]; + } + offset = ebp & 0xFFFF; + do { + if (_bitBuffer == 0x80) { + if (offset >= 0xFF00) { + offset = (offset << 8) | *_src++; + } + } + more = offset & 0x8000; + offset = (offset << 1) | getBit(); + } while (more); + offset += (ebp >> 16); + length += 2; + while (length--) { + if (_dst >= destEnd) { + return; + } + *_dst = *(_dst - offset); + _dst++; + } + } +} + +int Decompressor::getBit() { + int bit = (_bitBuffer & 0x80) >> 7; + _bitBuffer <<= 1; + if (_bitBuffer == 0) { + _bitBuffer = *_src++; + bit = (_bitBuffer & 0x80) >> 7; + _bitBuffer <<= 1; + _bitBuffer |= 1; + } + return bit; +} + +} // End of namespace Prince diff --git a/engines/prince/decompress.h b/engines/prince/decompress.h new file mode 100644 index 0000000000..ef495db65e --- /dev/null +++ b/engines/prince/decompress.h @@ -0,0 +1,44 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// John_Doe's implementation + +#ifndef PRINCE_DECOMPRESS_H +#define PRINCE_DECOMPRESS_H + +#include "engines/util.h" + +namespace Prince { + +class Decompressor { +public: + void decompress(byte *source, byte *dest, uint32 destSize); +protected: + byte *_src, *_dst; + byte _bitBuffer; + int _bitsLeft; + int getBit(); +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp new file mode 100644 index 0000000000..3fe7993fdb --- /dev/null +++ b/engines/prince/detection.cpp @@ -0,0 +1,75 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/detection.h" + +namespace Prince { + +int PrinceEngine::getGameType() const { + return _gameDescription->gameType; +} + +const char *PrinceEngine::getGameId() const { + return _gameDescription->desc.gameid; +} + +uint32 PrinceEngine::getFeatures() const { + return _gameDescription->desc.flags; +} + +Common::Language PrinceEngine::getLanguage() const { + return _gameDescription->desc.language; +} + +bool PrinceMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + using namespace Prince; + const PrinceGameDescription *gd = (const PrinceGameDescription *)desc; + if (gd) { + *engine = new PrinceEngine(syst, gd); + } + return gd != 0; +} + +bool PrinceMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup); +} + +bool Prince::PrinceEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime) || + (f == kSupportsRTL); +} + +} // End of namespace Prince + +#if PLUGIN_ENABLED_DYNAMIC(PRINCE) +REGISTER_PLUGIN_DYNAMIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine); +#else +REGISTER_PLUGIN_STATIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine); +#endif diff --git a/engines/prince/detection.h b/engines/prince/detection.h new file mode 100644 index 0000000000..7e5bdd6b7b --- /dev/null +++ b/engines/prince/detection.h @@ -0,0 +1,130 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_DETECTION_H +#define PRINCE_DETECTION_H + +#include "prince/prince.h" +#include "engines/advancedDetector.h" + +namespace Prince { + +enum PrinceGameType { + kPrinceDataUNK, + kPrinceDataDE, + kPrinceDataPL +}; + +struct PrinceGameDescription { + ADGameDescription desc; + PrinceGameType gameType; +}; + +static const PlainGameDescriptor princeGames[] = { + {"prince", "Prince Game"}, + {0, 0} +}; + +static const PrinceGameDescription gameDescriptions[] = { + { + { + "prince", + "Galador", + AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031), + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_TESTING, + GUIO1(GUIO_NONE) + }, + kPrinceDataDE + }, + { + { + "prince", + "Ksiaze i Tchorz", + AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298), + Common::PL_POL, + Common::kPlatformWindows, + ADGF_TESTING, + GUIO1(GUIO_NONE) + }, + kPrinceDataPL + }, + { + { + "prince", + "The Prince and the Coward", + AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_TESTING, + GUIO1(GUIO_NONE) + }, + kPrinceDataDE + }, + { + { + "prince", + "The Prince and the Coward", + AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_TESTING, + GUIO1(GUIO_NONE) + }, + kPrinceDataPL + }, + { AD_TABLE_END_MARKER, kPrinceDataUNK } +}; + +const static char *directoryGlobs[] = { + "all", + 0 +}; + +class PrinceMetaEngine : public AdvancedMetaEngine { +public: + PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) { + _singleid = "prince"; + _maxScanDepth = 2; + _directoryGlobs = directoryGlobs; + } + + virtual const char *getName() const { + return "Prince Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "The Prince and the Coward (C) 1996-97 Metropolis"; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual bool hasFeature(MetaEngineFeature f) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + virtual void removeSaveState(const char *target, int slot) const; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/flags.cpp b/engines/prince/flags.cpp new file mode 100644 index 0000000000..f1a05bd4df --- /dev/null +++ b/engines/prince/flags.cpp @@ -0,0 +1,420 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/flags.h" +#include "prince/script.h" + +namespace Prince { + +const char *Flags::getFlagName(uint16 flagId) { + FlagDebug *flagd = nullptr; + flagd = (FlagDebug *)bsearch(&flagId, _flagNames, kFlagDebugAmount, sizeof(FlagDebug), Flags::compareFlagDebug); + if (flagd != nullptr) { + return flagd->flagName; + } else { + return "unknown_flag"; + } +} + +int Flags::compareFlagDebug(const void *a, const void *b) { + const uint32 *flagId = (const uint32 *)a; + const FlagDebug *entry = (const FlagDebug *)b; + if (*flagId < (uint32)entry->id) { + return -1; + } else if (*flagId > (uint32)entry->id) { + return 1; + } + return 0; +} + +const Flags::FlagDebug Flags::_flagNames[Flags::kFlagDebugAmount] = { + { Flags::FLAGA1, "FLAGA1" }, + { Flags::FLAGA2, "FLAGA2" }, + { Flags::FLAGA3, "FLAGA3" }, + { Flags::DESTX, "DESTX" }, + { Flags::DESTY, "DESTY" }, + { Flags::DESTD, "DESTD" }, + { Flags::DwarfDone, "DwarfDone" }, + { Flags::GRABARZCOUNTER, "GRABARZCOUNTER" }, + { Flags::KIERUNEK, "KIERUNEK" }, + { Flags::BACKFLAG1, "BACKFLAG1" }, + { Flags::BACKFLAG2, "BACKFLAG2" }, + { Flags::BACKFLAG3, "BACKFLAG3" }, + { Flags::BACKFLAG4, "BACKFLAG4" }, + { Flags::MACROFLAG1, "MACROFLAG1" }, + { Flags::MACROFLAG2, "MACROFLAG2" }, + { Flags::MACROFLAG3, "MACROFLAG3" }, + { Flags::HEROLDDONE, "HEROLDDONE" }, + { Flags::BRIDGESET, "BRIDGESET" }, + { Flags::U_BT_1, "U_BT_1" }, + { Flags::U_BT_2, "U_BT_2" }, + { Flags::U_BT_3, "U_BT_3" }, + { Flags::U_BT_4, "U_BT_4" }, + { Flags::U_BT_5, "U_BT_5" }, + { Flags::U_BT_6, "U_BT_6" }, + { Flags::U_BT_7, "U_BT_7" }, + { Flags::U_BT_8, "U_BT_8" }, + { Flags::U_BT_9, "U_BT_9" }, + { Flags::U_BT_COUNTER, "U_BT_COUNTER" }, + { Flags::ARIVALDALIVE, "ARIVALDALIVE" }, + { Flags::TALKCHAR1, "TALKCHAR1" }, + { Flags::TalkType1, "TalkType1" }, + { Flags::TALKROUT1, "TALKROUT1" }, + { Flags::TALKROUT2, "TALKROUT2" }, + { Flags::TALKROUT3, "TALKROUT3" }, + { Flags::TALKROUT4, "TALKROUT4" }, + { Flags::TALKANIM1, "TALKANIM1" }, + { Flags::TALKANIM2, "TALKANIM2" }, + { Flags::TALKCOLOR1, "TALKCOLOR1" }, + { Flags::TALKCOLOR2, "TALKCOLOR2" }, + { Flags::KapciuchTaken, "KapciuchTaken" }, + { Flags::CurrentBeggarA, "CurrentBeggarA" }, + { Flags::TempKapc, "TempKapc" }, + { Flags::HomTaken, "HomTaken" }, + { Flags::WizardTalk, "WizardTalk" }, + { Flags::SunlordTalk, "SunlordTalk" }, + { Flags::HermitTalk, "HermitTalk" }, + { Flags::RunyMode, "RunyMode" }, + { Flags::FatMerchantTalk, "FatMerchantTalk" }, + { Flags::HotDogTalk, "HotDogTalk" }, + { Flags::ThiefTalk, "ThiefTalk" }, + { Flags::BeggarTalk, "BeggarTalk" }, + { Flags::MonkTalk, "MonkTalk" }, + { Flags::BardTalk, "BardTalk" }, + { Flags::BarmanTalk, "BarmanTalk" }, + { Flags::LeftPlayerTalk, "LeftPlayerTalk" }, + { Flags::OczySowy, "OczySowy" }, + { Flags::CzachySpeed1, "CzachySpeed1" }, + { Flags::CzachySpeed2, "CzachySpeed2" }, + { Flags::CzachySpeed3, "CzachySpeed3" }, + { Flags::CzachySlowDown1, "CzachySlowDown1" }, + { Flags::CzachySlowDown2, "CzachySlowDown2" }, + { Flags::CzachySlowDown3, "CzachySlowDown3" }, + { Flags::FjordDane, "FjordDane" }, + { Flags::GKopany1, "GKopany1" }, + { Flags::GKopany2, "GKopany2" }, + { Flags::GKopany3, "GKopany3" }, + { Flags::GKopany4, "GKopany4" }, + { Flags::KnowGodWord, "KnowGodWord" }, + { Flags::TALKROUT21, "TALKROUT21" }, + { Flags::TALKROUT22, "TALKROUT22" }, + { Flags::TALKROUT23, "TALKROUT23" }, + { Flags::TALKROUT24, "TALKROUT24" }, + { Flags::TalkType2, "TalkType2" }, + { Flags::GrabarzTalk, "GrabarzTalk" }, + { Flags::LastTalker, "LastTalker" }, + { Flags::MapaPustelniaEnabled, "MapaPustelniaEnabled" }, + { Flags::MapaTempleEnabled, "MapaTempleEnabled" }, + { Flags::MapaFjordEnabled, "MapaFjordEnabled" }, + { Flags::MapaSilmanionaEnabled, "MapaSilmanionaEnabled" }, + { Flags::MapaKurhanEnabled, "MapaKurhanEnabled" }, + { Flags::MapaDragonEnabled, "MapaDragonEnabled" }, + { Flags::MapaMillEnabled, "MapaMillEnabled" }, + { Flags::DwarfRunning, "DwarfRunning" }, + { Flags::DwarfTalk, "DwarfTalk" }, + { Flags::CurseLift, "CurseLift" }, + { Flags::KosciSwapped, "KosciSwapped" }, + { Flags::BookStolen, "BookStolen" }, + { Flags::MapaUsable, "MapaUsable" }, + { Flags::FjordBoss, "FjordBoss" }, + { Flags::FjordHotDog, "FjordHotDog" }, + { Flags::FjordLewy, "FjordLewy" }, + { Flags::FjordPrawy, "FjordPrawy" }, + { Flags::TalkArivald, "TalkArivald" }, + { Flags::ShootDone, "ShootDone" }, + { Flags::ShootRunning, "ShootRunning" }, + { Flags::ShootKnow, "ShootKnow" }, + { Flags::MirrorKnow, "MirrorKnow" }, + { Flags::Gar1stTime, "Gar1stTime" }, + { Flags::KosciTaken, "KosciTaken" }, + { Flags::ArivGotSpell, "ArivGotSpell" }, + { Flags::BookGiven, "BookGiven" }, + { Flags::Wywieszka, "Wywieszka" }, + { Flags::TalkSheila, "TalkSheila" }, + { Flags::TalkSheila2, "TalkSheila2" }, + { Flags::BackHuman, "BackHuman" }, + { Flags::SkarbiecOpen, "SkarbiecOpen" }, + { Flags::LustroTaken, "LustroTaken" }, + { Flags::GargoyleHom, "GargoyleHom" }, + { Flags::GargoyleBroken, "GargoyleBroken" }, + { Flags::FjordDzien, "FjordDzien" }, + { Flags::GargoyleHom2, "GargoyleHom2" }, + { Flags::RunMonstersRunning, "RunMonstersRunning" }, + { Flags::FoundPaperInCoffin, "FoundPaperInCoffin" }, + { Flags::KnowSunlord, "KnowSunlord" }, + { Flags::KnowSunlordTalk, "KnowSunlordTalk" }, + { Flags::ArivaldCzyta, "ArivaldCzyta" }, + { Flags::TelepX, "TelepX" }, + { Flags::TelepY, "TelepY" }, + { Flags::TelepDir, "TelepDir" }, + { Flags::TelepRoom, "TelepRoom" }, + { Flags::ListStolen, "ListStolen" }, + { Flags::WifeInDoor, "WifeInDoor" }, + { Flags::TalkWifeFlag, "TalkWifeFlag" }, + { Flags::LetterGiven, "LetterGiven" }, + { Flags::LutniaTaken, "LutniaTaken" }, + { Flags::BardHomeOpen, "BardHomeOpen" }, + { Flags::FjordNoMonsters, "FjordNoMonsters" }, + { Flags::ShandriaWallTalking, "ShandriaWallTalking" }, + { Flags::ShandriaWallCounter, "ShandriaWallCounter" }, + { Flags::ShandriaWallDone, "ShandriaWallDone" }, + { Flags::FutureDone, "FutureDone" }, + { Flags::TalkButch, "TalkButch" }, + { Flags::GotSzalik, "GotSzalik" }, + { Flags::GotCzosnek, "GotCzosnek" }, + { Flags::BearDone, "BearDone" }, + { Flags::NekrVisited, "NekrVisited" }, + { Flags::SunRiddle, "SunRiddle" }, + { Flags::PtaszekAway, "PtaszekAway" }, + { Flags::KotGadanie, "KotGadanie" }, + { Flags::SzlafmycaTaken, "SzlafmycaTaken" }, + { Flags::BabkaTalk, "BabkaTalk" }, + { Flags::SellerTalk, "SellerTalk" }, + { Flags::CzosnekDone, "CzosnekDone" }, + { Flags::PriestCounter, "PriestCounter" }, + { Flags::PriestGest1, "PriestGest1" }, + { Flags::PriestGest2, "PriestGest2" }, + { Flags::PriestGest3, "PriestGest3" }, + { Flags::PriestGest4, "PriestGest4" }, + { Flags::PriestAnim, "PriestAnim" }, + { Flags::HolyWaterTaken, "HolyWaterTaken" }, + { Flags::AxeTaken, "AxeTaken" }, + { Flags::BadylTaken1, "BadylTaken1" }, + { Flags::BadylTaken2, "BadylTaken2" }, + { Flags::BadylSharpened, "BadylSharpened" }, + { Flags::PorwanieSmoka, "PorwanieSmoka" }, + { Flags::ShopReOpen, "ShopReOpen" }, + { Flags::LuskaShown, "LuskaShown" }, + { Flags::CudKnow, "CudKnow" }, + { Flags::VampireDead, "VampireDead" }, + { Flags::MapaVisible1, "MapaVisible1" }, + { Flags::MapaVisible2, "MapaVisible2" }, + { Flags::MapaVisible3, "MapaVisible3" }, + { Flags::MapaVisible4, "MapaVisible4" }, + { Flags::MapaVisible5, "MapaVisible5" }, + { Flags::MapaVisible6, "MapaVisible6" }, + { Flags::MapaVisible7, "MapaVisible7" }, + { Flags::MapaVisible8, "MapaVisible8" }, + { Flags::MapaVisible9, "MapaVisible9" }, + { Flags::MapaX, "MapaX" }, + { Flags::MapaY, "MapaY" }, + { Flags::MapaD, "MapaD" }, + { Flags::OldMapaX, "OldMapaX" }, + { Flags::OldMapaY, "OldMapaY" }, + { Flags::OldMapaD, "OldMapaD" }, + { Flags::MovingBack, "MovingBack" }, + { Flags::MapaCount, "MapaCount" }, + { Flags::Pustelnia1st, "Pustelnia1st" }, + { Flags::CzarnePole1st, "CzarnePole1st" }, + { Flags::TalkArivNum, "TalkArivNum" }, + { Flags::Pfui, "Pfui" }, + { Flags::MapaSunlordEnabled, "MapaSunlordEnabled" }, + { Flags::WebDone, "WebDone" }, + { Flags::DragonDone, "DragonDone" }, + { Flags::KanPlay, "KanPlay" }, + { Flags::OldKanPlay, "OldKanPlay" }, + { Flags::LapkiWait, "LapkiWait" }, + { Flags::WebNoCheck, "WebNoCheck" }, + { Flags::Perfumeria, "Perfumeria" }, + { Flags::SmokNoCheck, "SmokNoCheck" }, + { Flags::IluzjaBroken, "IluzjaBroken" }, + { Flags::IluzjaWorking, "IluzjaWorking" }, + { Flags::IluzjaCounter, "IluzjaCounter" }, + { Flags::KurhanOpen1, "KurhanOpen1" }, + { Flags::KastetTaken, "KastetTaken" }, + { Flags::KastetDown, "KastetDown" }, + { Flags::KurhanDone, "KurhanDone" }, + { Flags::SkelCounter, "SkelCounter" }, + { Flags::SkelDial1, "SkelDial1" }, + { Flags::SkelDial2, "SkelDial2" }, + { Flags::SkelDial3, "SkelDial3" }, + { Flags::SkelDial4, "SkelDial4" }, + { Flags::SameTalker, "SameTalker" }, + { Flags::RunMonstersText, "RunMonstersText" }, + { Flags::PiwnicaChecked, "PiwnicaChecked" }, + { Flags::DragonTalked, "DragonTalked" }, + { Flags::ToldAboutBook, "ToldAboutBook" }, + { Flags::SilmanionaDone, "SilmanionaDone" }, + { Flags::ToldBookCount, "ToldBookCount" }, + { Flags::SmrodNoCheck, "SmrodNoCheck" }, + { Flags::RopeTaken, "RopeTaken" }, + { Flags::RopeTime, "RopeTime" }, + { Flags::LaskaFree, "LaskaFree" }, + { Flags::ShanSmokTalked, "ShanSmokTalked" }, + { Flags::SwordTaken, "SwordTaken" }, + { Flags::Mill1st, "Mill1st" }, + { Flags::SawRat, "SawRat" }, + { Flags::KnowRat, "KnowRat" }, + { Flags::DziuraTimer, "DziuraTimer" }, + { Flags::LaskaInside, "LaskaInside" }, + { Flags::HoleBig, "HoleBig" }, + { Flags::EnableWiedzmin, "EnableWiedzmin" }, + { Flags::EnableTrucizna, "EnableTrucizna" }, + { Flags::KnowPoison, "KnowPoison" }, + { Flags::KufelTaken, "KufelTaken" }, + { Flags::BojkaEnabled, "BojkaEnabled" }, + { Flags::BitwaNot1st, "BitwaNot1st" }, + { Flags::BojkaTimer, "BojkaTimer" }, + { Flags::BojkaGirl, "BojkaGirl" }, + { Flags::Look1st, "Look1st" }, + { Flags::RatTaken, "RatTaken" }, + { Flags::LaskaTalkedGr, "LaskaTalkedGr" }, + { Flags::RatusGivus, "RatusGivus" }, + { Flags::MamObole, "MamObole" }, + { Flags::Speed1st, "Speed1st" }, + { Flags::SpeedTimer, "SpeedTimer" }, + { Flags::ProveIt, "ProveIt" }, + { Flags::Proven, "Proven" }, + { Flags::ShowWoalka, "ShowWoalka" }, + { Flags::PoisonTaken, "PoisonTaken" }, + { Flags::HellOpened, "HellOpened" }, + { Flags::HellNoCheck, "HellNoCheck" }, + { Flags::TalAn1, "TalAn1" }, + { Flags::TalAn2, "TalAn2" }, + { Flags::TalAn3, "TalAn3" }, + { Flags::TalkDevilGuard, "TalkDevilGuard" }, + { Flags::Sword1st, "Sword1st" }, + { Flags::IluzjaNoCheck, "IluzjaNoCheck" }, + { Flags::RozdzielniaNumber, "RozdzielniaNumber" }, + { Flags::JailChecked, "JailChecked" }, + { Flags::JailTalked, "JailTalked" }, + { Flags::TrickFailed, "TrickFailed" }, + { Flags::WegielVisible, "WegielVisible" }, + { Flags::WegielTimer1, "WegielTimer1" }, + { Flags::RandomSample, "RandomSample" }, + { Flags::RandomSampleTimer, "RandomSampleTimer" }, + { Flags::SampleTimer, "SampleTimer" }, + { Flags::ZonaSample, "ZonaSample" }, + { Flags::HoleTryAgain, "HoleTryAgain" }, + { Flags::TeleportTimer, "TeleportTimer" }, + { Flags::RozLezy, "RozLezy" }, + { Flags::UdkoTimer, "UdkoTimer" }, + { Flags::ZaworZatkany, "ZaworZatkany" }, + { Flags::ZaworOpened, "ZaworOpened" }, + { Flags::DoorExploded, "DoorExploded" }, + { Flags::SkoraTaken, "SkoraTaken" }, + { Flags::CiezkieByl, "CiezkieByl" }, + { Flags::MamWegiel, "MamWegiel" }, + { Flags::SwiecaAway, "SwiecaAway" }, + { Flags::ITSAVE, "ITSAVE" }, + { Flags::RozpadlSie, "RozpadlSie" }, + { Flags::WegielFullTimer, "WegielFullTimer" }, + { Flags::WegielDown, "WegielDown" }, + { Flags::WegielDownTimer, "WegielDownTimer" }, + { Flags::PaliSie, "PaliSie" }, + { Flags::DiabGuardTalked, "DiabGuardTalked" }, + { Flags::GuardsNoCheck, "GuardsNoCheck" }, + { Flags::TalkedPowloka, "TalkedPowloka" }, + { Flags::JailOpen, "JailOpen" }, + { Flags::PrzytulTimer, "PrzytulTimer" }, + { Flags::JailDone, "JailDone" }, + { Flags::MamMonety, "MamMonety" }, + { Flags::LotTimer, "LotTimer" }, + { Flags::LotObj, "LotObj" }, + { Flags::PtakTimer, "PtakTimer" }, + { Flags::BookTimer, "BookTimer" }, + { Flags::BookGiba, "BookGiba" }, + { Flags::PtakLata, "PtakLata" }, + { Flags::Podej, "Podej" }, + { Flags::GotHint, "GotHint" }, + { Flags::LawaLeci, "LawaLeci" }, + { Flags::PowerKlik, "PowerKlik" }, + { Flags::LucekBad, "LucekBad" }, + { Flags::LucekBad1st, "LucekBad1st" }, + { Flags::IntroDial1, "IntroDial1" }, + { Flags::IntroDial2, "IntroDial2" }, + { Flags::ItsOutro, "ItsOutro" }, + { Flags::KamienComment, "KamienComment" }, + { Flags::KamienSkip, "KamienSkip" }, + { Flags::TesterFlag, "TesterFlag" }, + { Flags::RememberLine, "RememberLine" }, + { Flags::OpisLapek, "OpisLapek" }, + { Flags::TalWait, "TalWait" }, + { Flags::OpisKamienia, "OpisKamienia" }, + { Flags::JumpBox, "JumpBox" }, + { Flags::JumpBox1, "JumpBox1" }, + { Flags::JumpBox2, "JumpBox2" }, + { Flags::JumpBox3, "JumpBox3" }, + { Flags::SpecPiesek, "SpecPiesek" }, + { Flags::SpecPiesekCount, "SpecPiesekCount" }, + { Flags::SpecPiesekGadanie, "SpecPiesekGadanie" }, + { Flags::ZnikaFlag, "ZnikaFlag" }, + { Flags::ZnikaTimer, "ZnikaTimer" }, + { Flags::SowaTimer, "SowaTimer" }, + { Flags::MamrotanieOff, "MamrotanieOff" }, + { Flags::CURRMOB, "CURRMOB" }, + { Flags::KOLOR, "KOLOR" }, + { Flags::MBFLAG, "MBFLAG" }, + { Flags::MXFLAG, "MXFLAG" }, + { Flags::MYFLAG, "MYFLAG" }, + { Flags::SCROLLTYPE, "SCROLLTYPE" }, + { Flags::SCROLLVALUE, "SCROLLVALUE" }, + { Flags::SCROLLVALUE2, "SCROLLVALUE2" }, + { Flags::TALKEXITCODE, "TALKEXITCODE" }, + { Flags::SPECROUTFLAG1, "SPECROUTFLAG1" }, + { Flags::SPECROUTFLAG2, "SPECROUTFLAG2" }, + { Flags::SPECROUTFLAG3, "SPECROUTFLAG3" }, + { Flags::TALKFLAGCODE, "TALKFLAGCODE" }, + { Flags::CURRROOM, "CURRROOM" }, + { Flags::Talker1Init, "Talker1Init" }, + { Flags::Talker2Init, "Talker2Init" }, + { Flags::RESTOREROOM, "RESTOREROOM" }, + { Flags::INVALLOWED, "INVALLOWED" }, + { Flags::BOXSEL, "BOXSEL" }, + { Flags::CURSEBLINK, "CURSEBLINK" }, + { Flags::EXACTMOVE, "EXACTMOVE" }, + { Flags::MOVEDESTX, "MOVEDESTX" }, + { Flags::MOVEDESTY, "MOVEDESTY" }, + { Flags::NOANTIALIAS, "NOANTIALIAS" }, + { Flags::ESCAPED, "ESCAPED" }, + { Flags::ALLOW1OPTION, "ALLOW1OPTION" }, + { Flags::VOICE_H_LINE, "VOICE_H_LINE" }, + { Flags::VOICE_A_LINE, "VOICE_A_LINE" }, + { Flags::VOICE_B_LINE, "VOICE_B_LINE" }, + { Flags::VOICE_C_LINE, "VOICE_C_LINE" }, + { Flags::NOHEROATALL, "NOHEROATALL" }, + { Flags::MOUSEENABLED, "MOUSEENABLED" }, + { Flags::DIALINES, "DIALINES" }, + { Flags::SHANWALK, "SHANWALK" }, + { Flags::SHANDOG, "SHANDOG" }, + { Flags::GETACTIONBACK, "GETACTIONBACK" }, + { Flags::GETACTIONDATA, "GETACTIONDATA" }, + { Flags::GETACTION, "GETACTION" }, + { Flags::HEROFAST, "HEROFAST" }, + { Flags::SELITEM, "SELITEM" }, + { Flags::LMOUSE, "LMOUSE" }, + { Flags::MINMX, "MINMX" }, + { Flags::MAXMX, "MAXMX" }, + { Flags::MINMY, "MINMY" }, + { Flags::MAXMY, "MAXMY" }, + { Flags::TORX1, "TORX1" }, + { Flags::TORY1, "TORY1" }, + { Flags::TORX2, "TORX2" }, + { Flags::TORY2, "TORY2" }, + { Flags::POWER, "POWER" }, + { Flags::POWERENABLED, "POWERENABLED" }, + { Flags::FLCRESTORE, "FLCRESTORE" }, + { Flags::NOCLSTEXT, "NOCLSTEXT" }, + { Flags::ESCAPED2, "ESCAPED2" }, +}; + +} // End of namespace Prince diff --git a/engines/prince/flags.h b/engines/prince/flags.h new file mode 100644 index 0000000000..8337f82a95 --- /dev/null +++ b/engines/prince/flags.h @@ -0,0 +1,421 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_FLAGS_H +#define PRINCE_FLAGS_H + +#include "common/scummsys.h" + +namespace Prince { + +class Flags { +public: + static int compareFlagDebug(const void *a, const void *b); + static const char *getFlagName(uint16 flagId); + + enum Id { + FLAGA1 = 0x8000, + FLAGA2 = 0x8002, + FLAGA3 = 0x8004, + DESTX = 0x8006, + DESTY = 0x8008, + DESTD = 0x800A, + DwarfDone = 0x800C, + GRABARZCOUNTER = 0x800E, + KIERUNEK = 0x8010, + BACKFLAG1 = 0x8012, + BACKFLAG2 = 0x8014, + BACKFLAG3 = 0x8016, + BACKFLAG4 = 0x8018, + MACROFLAG1 = 0x801A, + MACROFLAG2 = 0x801C, + MACROFLAG3 = 0x801E, + HEROLDDONE = 0x8020, + BRIDGESET = 0x8022, + U_BT_1 = 0x8024, + U_BT_2 = 0x8026, + U_BT_3 = 0x8028, + U_BT_4 = 0x802A, + U_BT_5 = 0x802C, + U_BT_6 = 0x802E, + U_BT_7 = 0x8030, + U_BT_8 = 0x8032, + U_BT_9 = 0x8034, + U_BT_COUNTER = 0x8036, + ARIVALDALIVE = 0x8038, + TALKCHAR1 = 0x803A, + TalkType1 = 0x803C, + TALKROUT1 = 0x803E, + TALKROUT2 = 0x8042, + TALKROUT3 = 0x8046, + TALKROUT4 = 0x804A, + TALKANIM1 = 0x804E, + TALKANIM2 = 0x8050, + TALKCOLOR1 = 0x8052, + TALKCOLOR2 = 0x8054, + KapciuchTaken = 0x8056, + CurrentBeggarA = 0x8058, + TempKapc = 0x805A, + HomTaken = 0x805C, + WizardTalk = 0x805E, + SunlordTalk = 0x8060, + HermitTalk = 0x8062, + RunyMode = 0x8064, + FatMerchantTalk = 0x8066, + HotDogTalk = 0x8068, + ThiefTalk = 0x806A, + BeggarTalk = 0x806C, + // DwarfTalk = 0x806E, // Redefinition + MonkTalk = 0x8070, + BardTalk = 0x8072, + BarmanTalk = 0x8074, + LeftPlayerTalk = 0x8076, + OczySowy = 0x8078, + CzachySpeed1 = 0x807A, + CzachySpeed2 = 0x807C, + CzachySpeed3 = 0x807E, + CzachySlowDown1 = 0x8080, + CzachySlowDown2 = 0x8082, + CzachySlowDown3 = 0x8084, + FjordDane = 0x8086, + GKopany1 = 0x8088, + GKopany2 = 0x808A, + GKopany3 = 0x808C, + GKopany4 = 0x808E, + KnowGodWord = 0x8090, + TALKROUT21 = 0x8092, + TALKROUT22 = 0x8096, + TALKROUT23 = 0x809A, + TALKROUT24 = 0x809E, + TalkType2 = 0x80A2, + GrabarzTalk = 0x80A4, + LastTalker = 0x80A6, + MapaPustelniaEnabled = 0x80A8, + MapaTempleEnabled = 0x80AA, + MapaFjordEnabled = 0x80AC, + MapaSilmanionaEnabled = 0x80AE, + MapaKurhanEnabled = 0x80B0, + MapaDragonEnabled = 0x80B2, + MapaMillEnabled = 0x80B4, + DwarfRunning = 0x80B6, + DwarfTalk = 0x80B8, + CurseLift = 0x80BA, + KosciSwapped = 0x80BC, + BookStolen = 0x80BE, + MapaUsable = 0x80C0, + FjordBoss = 0x80C2, + FjordHotDog = 0x80C4, + FjordLewy = 0x80C6, + FjordPrawy = 0x80C8, + TalkArivald = 0x80CA, + ShootDone = 0x80CC, + ShootRunning = 0x80CE, + ShootKnow = 0x80D0, + MirrorKnow = 0x80D2, + Gar1stTime = 0x80D4, + KosciTaken = 0x80D6, + ArivGotSpell = 0x80D8, + BookGiven = 0x80DA, + Wywieszka = 0x80DC, + TalkSheila = 0x80DE, + TalkSheila2 = 0x80E0, + BackHuman = 0x80E2, + SkarbiecOpen = 0x80E4, + LustroTaken = 0x80E6, + GargoyleHom = 0x80E8, + GargoyleBroken = 0x80EA, + FjordDzien = 0x80EC, + GargoyleHom2 = 0x80EE, + RunMonstersRunning = 0x80F0, + FoundPaperInCoffin = 0x80F2, + KnowSunlord = 0x80F4, + KnowSunlordTalk = 0x80F6, + ArivaldCzyta = 0x80F8, + TelepX = 0x80FA, + TelepY = 0x80FC, + TelepDir = 0x80FE, + TelepRoom = 0x8100, + ListStolen = 0x8102, + WifeInDoor = 0x8104, + TalkWifeFlag = 0x8106, + LetterGiven = 0x8108, + LutniaTaken = 0x810A, + BardHomeOpen = 0x810C, + FjordNoMonsters = 0x810E, + ShandriaWallTalking = 0x8110, + ShandriaWallCounter = 0x8112, + ShandriaWallDone = 0x8114, + FutureDone = 0x8116, + TalkButch = 0x8118, + GotSzalik = 0x811A, + GotCzosnek = 0x811C, + BearDone = 0x811E, + NekrVisited = 0x8120, + SunRiddle = 0x8122, + PtaszekAway = 0x8124, + KotGadanie = 0x8126, + SzlafmycaTaken = 0x8128, + BabkaTalk = 0x812A, + SellerTalk = 0x812C, + CzosnekDone = 0x812E, + PriestCounter = 0x8130, + PriestGest1 = 0x8132, + PriestGest2 = 0x8134, + PriestGest3 = 0x8136, + PriestGest4 = 0x8138, + PriestAnim = 0x813A, + HolyWaterTaken = 0x813C, + AxeTaken = 0x813E, + BadylTaken1 = 0x8140, + BadylTaken2 = 0x8142, + BadylSharpened = 0x8144, + PorwanieSmoka = 0x8146, + ShopReOpen = 0x8148, + LuskaShown = 0x814A, + CudKnow = 0x814C, + VampireDead = 0x814E, + MapaVisible1 = 0x8150, + MapaVisible2 = 0x8152, + MapaVisible3 = 0x8154, + MapaVisible4 = 0x8156, + MapaVisible5 = 0x8158, + MapaVisible6 = 0x815A, + MapaVisible7 = 0x815C, + MapaVisible8 = 0x815E, + MapaVisible9 = 0x8160, + MapaX = 0x8162, + MapaY = 0x8164, + MapaD = 0x8166, + OldMapaX = 0x8168, + OldMapaY = 0x816A, + OldMapaD = 0x816C, + MovingBack = 0x816E, + MapaCount = 0x8170, + Pustelnia1st = 0x8172, + CzarnePole1st = 0x8174, + TalkArivNum = 0x8176, + Pfui = 0x8178, + MapaSunlordEnabled = 0x817A, + WebDone = 0x817C, + DragonDone = 0x817E, + KanPlay = 0x8180, + OldKanPlay = 0x8182, + LapkiWait = 0x8184, + WebNoCheck = 0x8186, + Perfumeria = 0x8188, + SmokNoCheck = 0x818A, + IluzjaBroken = 0x818C, + IluzjaWorking = 0x818E, + IluzjaCounter = 0x8190, + KurhanOpen1 = 0x8192, + KastetTaken = 0x8194, + KastetDown = 0x8196, + KurhanDone = 0x8198, + SkelCounter = 0x819A, + SkelDial1 = 0x819C, + SkelDial2 = 0x819E, + SkelDial3 = 0x81A0, + SkelDial4 = 0x81A2, + SameTalker = 0x81A4, + RunMonstersText = 0x81A6, + PiwnicaChecked = 0x81A8, + DragonTalked = 0x81AA, + ToldAboutBook = 0x81AC, + SilmanionaDone = 0x81AE, + ToldBookCount = 0x81B0, + SmrodNoCheck = 0x81B2, + RopeTaken = 0x81B4, + RopeTime = 0x81B6, + LaskaFree = 0x81B8, + ShanSmokTalked = 0x81BA, + SwordTaken = 0x81BC, + Mill1st = 0x81BE, + SawRat = 0x81C0, + KnowRat = 0x81C2, + DziuraTimer = 0x81C4, + LaskaInside = 0x81C6, + HoleBig = 0x81C8, + EnableWiedzmin = 0x81CA, + EnableTrucizna = 0x81CC, + KnowPoison = 0x81CE, + KufelTaken = 0x81D0, + BojkaEnabled = 0x81D2, + BitwaNot1st = 0x81D4, + BojkaTimer = 0x81D6, + BojkaGirl = 0x81D8, + Look1st = 0x81DA, + RatTaken = 0x81DC, + LaskaTalkedGr = 0x81DE, + RatusGivus = 0x81E0, + MamObole = 0x81E2, + Speed1st = 0x81E4, + SpeedTimer = 0x81E6, + ProveIt = 0x81E8, + Proven = 0x81EA, + ShowWoalka = 0x81EC, + PoisonTaken = 0x81EE, + HellOpened = 0x81F0, + HellNoCheck = 0x81F2, + TalAn1 = 0x81F4, + TalAn2 = 0x81F6, + TalAn3 = 0x81F8, + TalkDevilGuard = 0x81fA, + Sword1st = 0x81FC, + IluzjaNoCheck = 0x81FE, + RozdzielniaNumber = 0x8200, + JailChecked = 0x8202, + JailTalked = 0x8204, + TrickFailed = 0x8206, + WegielVisible = 0x8208, + WegielTimer1 = 0x820A, + RandomSample = 0x820C, + RandomSampleTimer = 0x820E, + SampleTimer = 0x8210, + ZonaSample = 0x8212, + HoleTryAgain = 0x8214, + TeleportTimer = 0x8216, + RozLezy = 0x8218, + UdkoTimer = 0x821A, + ZaworZatkany = 0x821C, + ZaworOpened = 0x821E, + DoorExploded = 0x8220, + SkoraTaken = 0x8222, + CiezkieByl = 0x8224, + MamWegiel = 0x8226, + SwiecaAway = 0x8228, + ITSAVE = 0x822A, + RozpadlSie = 0x822C, + WegielFullTimer = 0x822E, + WegielDown = 0x8230, + WegielDownTimer = 0x8232, + PaliSie = 0x8234, + DiabGuardTalked = 0x8236, + GuardsNoCheck = 0x8238, + TalkedPowloka = 0x823A, + JailOpen = 0x823C, + PrzytulTimer = 0x823E, + JailDone = 0x8240, + MamMonety = 0x8242, + LotTimer = 0x8244, + LotObj = 0x8246, + PtakTimer = 0x8248, + BookTimer = 0x824A, + BookGiba = 0x824C, + PtakLata = 0x824E, + Podej = 0x8250, + GotHint = 0x8252, + LawaLeci = 0x8254, + PowerKlik = 0x8258, + LucekBad = 0x825A, + LucekBad1st = 0x825C, + IntroDial1 = 0x825E, + IntroDial2 = 0x8260, + ItsOutro = 0x8262, + KamienComment = 0x8264, + KamienSkip = 0x8266, + TesterFlag = 0x8268, + RememberLine = 0x826A, + OpisLapek = 0x826C, + //OpisKamienia = 0x826E, // Redefinition + TalWait = 0x8270, + OpisKamienia = 0x8272, + JumpBox = 0x8274, + JumpBox1 = 0x8276, + JumpBox2 = 0x8278, + JumpBox3 = 0x827A, + SpecPiesek = 0x827C, + SpecPiesekCount = 0x827E, + SpecPiesekGadanie = 0x8282, + ZnikaFlag = 0x8284, + ZnikaTimer = 0x8286, + SowaTimer = 0x8288, + MamrotanieOff = 0x828A, + // System flags controlled by script + CURRMOB = 0x8400, + KOLOR = 0x8402, + MBFLAG = 0x8404, + MXFLAG = 0x8406, + MYFLAG = 0x8408, + SCROLLTYPE = 0x840A, + SCROLLVALUE = 0x840C, + SCROLLVALUE2 = 0x840E, + TALKEXITCODE = 0x8410, + SPECROUTFLAG1 = 0x8412, + SPECROUTFLAG2 = 0x8414, + SPECROUTFLAG3 = 0x8416, + TALKFLAGCODE = 0x8418, + CURRROOM = 0x841A, + Talker1Init = 0x841C, + Talker2Init = 0x841E, + RESTOREROOM = 0x8420, + INVALLOWED = 0x8422, + BOXSEL = 0x8424, + CURSEBLINK = 0x8426, + EXACTMOVE = 0x8428, + MOVEDESTX = 0x842A, + MOVEDESTY = 0x842C, + NOANTIALIAS = 0x842E, + ESCAPED = 0x8430, + ALLOW1OPTION = 0x8432, + VOICE_H_LINE = 0x8434, + VOICE_A_LINE = 0x8436, + VOICE_B_LINE = 0x8438, + VOICE_C_LINE = 0x843A, + NOHEROATALL = 0x843C, + MOUSEENABLED = 0x843E, + DIALINES = 0x8440, + //SELITEM = 0x8442, // Redefinition + SHANWALK = 0x8444, + SHANDOG = 0x8446, + GETACTIONBACK = 0x8448, + GETACTIONDATA = 0x844C, + GETACTION = 0x8450, + HEROFAST = 0x8452, + SELITEM = 0x8454, + LMOUSE = 0x8456, + MINMX = 0x8458, + MAXMX = 0x845A, + MINMY = 0x845C, + MAXMY = 0x845E, + TORX1 = 0x8460, + TORY1 = 0x8462, + TORX2 = 0x8464, + TORY2 = 0x8466, + POWER = 0x8468, + POWERENABLED = 0x846A, + FLCRESTORE = 0x846C, + NOCLSTEXT = 0x846E, + ESCAPED2 = 0x8470 + }; + + struct FlagDebug { + Id id; + char flagName[30]; + }; + + static const int kFlagDebugAmount = 368; + static const FlagDebug _flagNames[kFlagDebugAmount]; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/font.cpp b/engines/prince/font.cpp new file mode 100644 index 0000000000..e81a93d1a1 --- /dev/null +++ b/engines/prince/font.cpp @@ -0,0 +1,94 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/archive.h" +#include "common/debug.h" +#include "common/stream.h" + +#include "prince/font.h" +#include "prince/prince.h" + +namespace Prince { + +Font::Font() : _fontData(nullptr) { +} + +Font::~Font() { + if (_fontData != nullptr) { + free(_fontData); + _fontData = nullptr; + } +} + +bool Font::loadStream(Common::SeekableReadStream &stream) { + stream.seek(0); + uint32 dataSize = stream.size(); + _fontData = (byte *)malloc(dataSize); + stream.read(_fontData, stream.size()); + return true; +} + +int Font::getFontHeight() const { + return _fontData[5]; +} + +int Font::getMaxCharWidth() const { + return 0; +} + +Font::ChrData Font::getChrData(byte chr) const { + chr -= 32; + uint16 chrOffset = 4 * chr + 6; + + ChrData chrData; + chrData._width = _fontData[chrOffset + 2]; + chrData._height = _fontData[chrOffset + 3]; + chrData._pixels = _fontData + READ_LE_UINT16(_fontData + chrOffset); + + return chrData; +} + +int Font::getCharWidth(uint32 chr) const { + return getChrData(chr)._width; +} + +void Font::drawChar(Graphics::Surface *dst, uint32 chr, int posX, int posY, uint32 color) const { + const ChrData chrData = getChrData(chr); + Common::Rect screenRect(0, 0, PrinceEngine::kNormalWidth, PrinceEngine::kNormalHeight); + + for (int y = 0; y < chrData._height; y++) { + for (int x = 0; x < chrData._width; x++) { + byte d = chrData._pixels[x + (chrData._width * y)]; + if (d == 0) d = 255; + else if (d == 1) d = 0; + else if (d == 2) d = color; + else if (d == 3) d = 0; + if (d != 255) { + if (screenRect.contains(posX + x, posY + y)) { + *(byte *)dst->getBasePtr(posX + x, posY + y) = d; + } + } + } + } +} + +} // End of namespace Prince diff --git a/engines/prince/font.h b/engines/prince/font.h new file mode 100644 index 0000000000..b03849a4aa --- /dev/null +++ b/engines/prince/font.h @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PRINCE_FONT_H +#define PRINCE_FONT_H + +#include "graphics/font.h" +#include "graphics/surface.h" + +#include "common/str.h" +#include "common/rect.h" + +namespace Prince { + +class Font : public Graphics::Font { +public: + Font(); + virtual ~Font(); + + bool loadStream(Common::SeekableReadStream &stream); + + virtual int getFontHeight() const override; + + virtual int getMaxCharWidth() const override; + + virtual int getCharWidth(uint32 chr) const override; + + virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override; + + virtual int getKerningOffset(uint32 left, uint32 right) const override { return -2; } + +private: + struct ChrData { + byte *_pixels; + byte _width; + byte _height; + }; + + ChrData getChrData(byte chr) const; + + byte *_fontData; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/graphics.cpp b/engines/prince/graphics.cpp new file mode 100644 index 0000000000..f556d81eab --- /dev/null +++ b/engines/prince/graphics.cpp @@ -0,0 +1,474 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/graphics.h" +#include "prince/prince.h" +#include "prince/mhwanh.h" + +#include "graphics/palette.h" + +#include "common/memstream.h" + +namespace Prince { + +GraphicsMan::GraphicsMan(PrinceEngine *vm) : _vm(vm), _changed(false) { + initGraphics(640, 480, true); + + _frontScreen = new Graphics::Surface(); + _frontScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); + + _screenForInventory = new Graphics::Surface(); + _screenForInventory->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); + + _mapScreen = new Graphics::Surface(); + _mapScreen->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); + + _shadowTable70 = (byte *)malloc(256); + _shadowTable50 = (byte *)malloc(256); +} + +GraphicsMan::~GraphicsMan() { + _frontScreen->free(); + delete _frontScreen; + + _screenForInventory->free(); + delete _screenForInventory; + + _mapScreen->free(); + delete _mapScreen; + + free(_shadowTable70); + free(_shadowTable50); +} + +void GraphicsMan::update(Graphics::Surface *screen) { + if (_changed) { + _vm->_system->copyRectToScreen((byte *)screen->getBasePtr(0, 0), 640, 0, 0, 640, 480); + + _vm->_system->updateScreen(); + _changed = false; + } +} + +void GraphicsMan::setPalette(const byte *palette) { + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); +} + +void GraphicsMan::change() { + _changed = true; +} + +void GraphicsMan::draw(Graphics::Surface *screen, const Graphics::Surface *s) { + uint16 w = MIN(screen->w, s->w); + const byte *src = (const byte *)s->getBasePtr(0, 0); + byte *dst = (byte *)screen->getBasePtr(0, 0); + for (uint y = 0; y < s->h; y++) { + if (y < screen->h) { + memcpy(dst, src, w); + } + src += s->pitch; + dst += screen->pitch; + } + change(); +} + +// Black (value = 0) as a primary transparent color, fix for FLC animations +void GraphicsMan::drawTransparentSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, int secondTransColor) { + const byte *src1 = (const byte *)s->getBasePtr(0, 0); + byte *dst1 = (byte *)screen->getBasePtr(posX, posY); + for (int y = 0; y < s->h; y++) { + if (y + posY < screen->h && y + posY >= 0) { + const byte *src2 = src1; + byte *dst2 = dst1; + for (int x = 0; x < s->w; x++, src2++, dst2++) { + if (*src2 && *src2 != secondTransColor) { + if (x + posX < screen->w && x + posX >= 0) { + *dst2 = *src2; + } + } + } + } + src1 += s->pitch; + dst1 += screen->pitch; + } + change(); +} + +/** + * Similar to drawTransparentSurface but with use of shadowTable for color recalculation + * and kShadowColor (191) as a transparent color. + */ +void GraphicsMan::drawAsShadowSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, byte *shadowTable) { + const byte *src1 = (const byte *)s->getBasePtr(0, 0); + byte *dst1 = (byte *)screen->getBasePtr(posX, posY); + for (int y = 0; y < s->h; y++) { + if (y + posY < screen->h && y + posY >= 0) { + const byte *src2 = src1; + byte *dst2 = dst1; + for (int x = 0; x < s->w; x++, src2++, dst2++) { + if (*src2 == kShadowColor) { + if (x + posX < screen->w && x + posX >= 0) { + *dst2 = *(shadowTable + *dst2); + } + } + } + } + src1 += s->pitch; + dst1 += screen->pitch; + } +} + +/** + * Used in glowing animation for inventory items. Creates special blendTable array of colors, + * use black (0) as a transparent color. + */ +void GraphicsMan::drawTransparentWithBlendSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s) { + const byte *src1 = (const byte *)s->getBasePtr(0, 0); + byte *dst1 = (byte *)screen->getBasePtr(posX, posY); + byte *blendTable = (byte *)malloc(256); + for (int i = 0; i < 256; i++) { + blendTable[i] = 255; + } + for (int y = 0; y < s->h; y++) { + if (y + posY < screen->h && y + posY >= 0) { + const byte *src2 = src1; + byte *dst2 = dst1; + for (int x = 0; x < s->w; x++, src2++, dst2++) { + if (*src2) { + if (x + posX < screen->w && x + posX >= 0) { + *dst2 = getBlendTableColor(*src2, *dst2, blendTable); + } + } + } + } + src1 += s->pitch; + dst1 += screen->pitch; + } + free(blendTable); + change(); +} + +/** + * Similar to drawTransparentSurface but with with use of DrawNode as argument for Z axis sorting + * and white (255) as transparent color. + */ +void GraphicsMan::drawTransparentDrawNode(Graphics::Surface *screen, DrawNode *drawNode) { + byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0); + byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY); + for (int y = 0; y < drawNode->s->h; y++) { + if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) { + byte *src2 = src1; + byte *dst2 = dst1; + for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) { + if (*src2 != 255) { + if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) { + *dst2 = *src2; + } + } + } + } + src1 += drawNode->s->pitch; + dst1 += screen->pitch; + } +} + +/** + * Similar to drawTransparentDrawNode but with additional anti-aliasing code for sprite drawing. + * Edge smoothing is based on 256 x 256 table of colors transition. + * Algorithm is checking if currently drawing pixel is located next to the edge of sprite and if it makes jagged line. + * If it does then this pixel is set with color from transition table calculated of original background pixel color + * and sprite's edge pixel color. + */ +void GraphicsMan::drawTransparentWithTransDrawNode(Graphics::Surface *screen, DrawNode *drawNode) { + // pos of first pixel for each row of source sprite + byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0); + // pos of drawing first pixel for each row on screen surface + byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY); + // trasition table for calculating new color value + byte *transTableData = (byte *)drawNode->data; + for (int y = 0; y < drawNode->s->h; y++) { + if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) { + // current pixel in row of source sprite + byte *src2 = src1; + // current pixel in row of screen surface + byte *dst2 = dst1; + for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) { + if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) { + if (*src2 != 255) { + // if source sprite pixel is not mask color than draw it normally + *dst2 = *src2; + } else { + // check for making jagged line + if (x) { + // not first pixel in row + if (*(src2 - 1) == 255) { + // if it has mask color to the left - check right + if (x != drawNode->s->w - 1) { + // not last pixel in row + if (*(src2 + 1) == 255) { + // pixel to the right with mask color - no anti-alias + continue; + } + // it's not mask color to the right - we continue checking + } else { + // last pixel in row, no right check - no anti-alias + continue; + } + } + // it's not mask color to the left - we continue checking + } else if (x != drawNode->s->w - 1) { + // first pixel in row but not last - just right pixel checking + if (*(src2 + 1) == 255) { + // pixel to the right with mask color - no anti-alias + continue; + } + // it's not mask color to the right - we continue checking + } else { + // it's first and last pixel in row at the same time (width = 1) - no anti-alias + continue; + } + byte value = 0; + if (y != drawNode->s->h - 1) { + // not last row + // check pixel below of current src2 pixel + value = *(src2 + drawNode->s->pitch); + if (value == 255) { + // pixel below with mask color - check above + if (y) { + // not first row + value = *(src2 - drawNode->s->pitch); + if (value == 255) { + // pixel above with mask color - no anti-alias + continue; + } + // it's not mask color above - we draw as transition color + } else { + // first row - no anti-alias + continue; + } + } + // it's not mask color below - we draw as transition color + } else if (y) { + // last row - just check above + value = *(src2 - drawNode->s->pitch); + if (value == 255) { + // pixel above with mask color - no anti-alias + continue; + } + // it's not mask color above - we draw as transition color + } else { + // first and last row at the same time (height = 1) - no anti-alias + continue; + } + // new color value based on orginal screen surface color and sprite's edge pixel color + *dst2 = transTableData[*dst2 * 256 + value]; + } + } + } + } + // adding pitch to jump to next row of pixels + src1 += drawNode->s->pitch; + dst1 += screen->pitch; + } +} + +void GraphicsMan::drawMaskDrawNode(Graphics::Surface *screen, DrawNode *drawNode) { + byte *maskData = (byte *)drawNode->data; + byte *src1 = (byte *)drawNode->originalRoomSurface->getBasePtr(drawNode->posX, drawNode->posY); + byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY); + int maskWidth = drawNode->width >> 3; + int maskPostion = 0; + int maskCounter = 128; + for (int y = 0; y < drawNode->height; y++) { + if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) { + byte *src2 = src1; + byte *dst2 = dst1; + int tempMaskPostion = maskPostion; + for (int x = 0; x < drawNode->width; x++, src2++, dst2++) { + if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) { + if ((maskData[tempMaskPostion] & maskCounter) != 0) { + *dst2 = *src2; + } + } + maskCounter >>= 1; + if (maskCounter == 0) { + maskCounter = 128; + tempMaskPostion++; + } + } + } + src1 += drawNode->originalRoomSurface->pitch; + dst1 += screen->pitch; + maskPostion += maskWidth; + maskCounter = 128; + } +} + +void GraphicsMan::drawAsShadowDrawNode(Graphics::Surface *screen, DrawNode *drawNode) { + byte *shadowData = (byte *)drawNode->data; + byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0); + byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY); + for (int y = 0; y < drawNode->s->h; y++) { + if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) { + byte *src2 = src1; + byte *dst2 = dst1; + for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) { + if (*src2 == kShadowColor) { + if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) { + *dst2 = *(shadowData + *dst2); + } + } + } + } + src1 += drawNode->s->pitch; + dst1 += screen->pitch; + } +} + +void GraphicsMan::drawBackSpriteDrawNode(Graphics::Surface *screen, DrawNode *drawNode) { + byte *src1 = (byte *)drawNode->s->getBasePtr(0, 0); + byte *dst1 = (byte *)screen->getBasePtr(drawNode->posX, drawNode->posY); + for (int y = 0; y < drawNode->s->h; y++) { + if (y + drawNode->posY < screen->h && y + drawNode->posY >= 0) { + byte *src2 = src1; + byte *dst2 = dst1; + for (int x = 0; x < drawNode->s->w; x++, src2++, dst2++) { + if (*src2 != 255) { + if (x + drawNode->posX < screen->w && x + drawNode->posX >= 0) { + if (*dst2 == 255) { + *dst2 = *src2; + } + } + } + } + } + src1 += drawNode->s->pitch; + dst1 += screen->pitch; + } +} + +byte GraphicsMan::getBlendTableColor(byte pixelColor, byte backgroundPixelColor, byte *blendTable) { + int currColor = 0; + + if (blendTable[pixelColor] != 255) { + currColor = blendTable[pixelColor]; + } else { + const byte *originalPalette = _vm->_roomBmp->getPalette(); + + int redFirstOrg = originalPalette[pixelColor * 3] * _vm->_mst_shadow / 256; + CLIP(redFirstOrg, 0, 255); + if (_vm->_mst_shadow <= 256) { + int redFirstBack = originalPalette[backgroundPixelColor * 3] * (256 - _vm->_mst_shadow) / 256; + CLIP(redFirstBack, 0, 255); + redFirstOrg += redFirstBack; + CLIP(redFirstOrg, 0, 255); + } + + int greenFirstOrg = originalPalette[pixelColor * 3 + 1] * _vm->_mst_shadow / 256; + CLIP(greenFirstOrg, 0, 255); + if (_vm->_mst_shadow <= 256) { + int greenFirstBack = originalPalette[backgroundPixelColor * 3 + 1] * (256 - _vm->_mst_shadow) / 256; + CLIP(greenFirstBack, 0, 255); + greenFirstOrg += greenFirstBack; + CLIP(greenFirstOrg, 0, 255); + } + + int blueFirstOrg = originalPalette[pixelColor * 3 + 2] * _vm->_mst_shadow / 256; + CLIP(blueFirstOrg, 0, 255); + if (_vm->_mst_shadow <= 256) { + int blueFirstBack = originalPalette[backgroundPixelColor * 3 + 2] * (256 - _vm->_mst_shadow) / 256; + CLIP(blueFirstBack, 0, 255); + blueFirstOrg += blueFirstBack; + CLIP(blueFirstOrg, 0, 255); + } + + int bigValue = PrinceEngine::kIntMax; // infinity + for (int j = 0; j < 256; j++) { + int redSecondOrg = originalPalette[3 * j]; + int redNew = redFirstOrg - redSecondOrg; + redNew = redNew * redNew; + + int greenSecondOrg = originalPalette[3 * j + 1]; + int greenNew = greenFirstOrg - greenSecondOrg; + greenNew = greenNew * greenNew; + + int blueSecondOrg = originalPalette[3 * j + 2]; + int blueNew = blueFirstOrg - blueSecondOrg; + blueNew = blueNew * blueNew; + + int sumOfColorValues = redNew + greenNew + blueNew; + + if (sumOfColorValues < bigValue) { + bigValue = sumOfColorValues; + currColor = j; + } + + if (sumOfColorValues == 0) { + break; + } + } + blendTable[pixelColor] = currColor; + } + return currColor; +} + +void GraphicsMan::makeShadowTable(int brightness, byte *shadowPalette) { + int shadow = brightness * 256 / 100; + const byte *originalPalette = _vm->_roomBmp->getPalette(); + + for (int i = 0; i < 256; i++) { + int redFirstOrg = originalPalette[3 * i] * shadow / 256; + int greenFirstOrg = originalPalette[3 * i + 1] * shadow / 256; + int blueFirstOrg = originalPalette[3 * i + 2] * shadow / 256; + + int currColor = 0; + int bigValue = 999999999; // infinity + + for (int j = 0; j < 256; j++) { + int redSecondOrg = originalPalette[3 * j]; + int redNew = redFirstOrg - redSecondOrg; + redNew = redNew * redNew; + + int greenSecondOrg = originalPalette[3 * j + 1]; + int greenNew = greenFirstOrg - greenSecondOrg; + greenNew = greenNew * greenNew; + + int blueSecondOrg = originalPalette[3 * j + 2]; + int blueNew = blueFirstOrg - blueSecondOrg; + blueNew = blueNew * blueNew; + + int sumOfColorValues = redNew + greenNew + blueNew; + + if (sumOfColorValues < bigValue) { + bigValue = sumOfColorValues; + currColor = j; + } + + if (sumOfColorValues == 0) { + break; + } + } + shadowPalette[i] = currColor; + } +} + +} // End of namespace Prince diff --git a/engines/prince/graphics.h b/engines/prince/graphics.h new file mode 100644 index 0000000000..1a1737f976 --- /dev/null +++ b/engines/prince/graphics.h @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_GRAPHICS_H +#define PRINCE_GRAPHICS_H + +#include "graphics/surface.h" + +namespace Prince { + +class PrinceEngine; +class MhwanhDecoder; +struct DrawNode; + +class GraphicsMan { +public: + GraphicsMan(PrinceEngine *vm); + ~GraphicsMan(); + + void update(Graphics::Surface *screen); + + void change(); + + void setPalette(const byte *palette); + void makeShadowTable(int brightness, byte *shadowTable); + + void draw(Graphics::Surface *screen, const Graphics::Surface *s); + void drawTransparentSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, int secondTransColor = 0); + void drawAsShadowSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s, byte *shadowTable); + void drawTransparentWithBlendSurface(Graphics::Surface *screen, int32 posX, int32 posY, const Graphics::Surface *s); + + static void drawTransparentDrawNode(Graphics::Surface *screen, DrawNode *drawNode); + static void drawTransparentWithTransDrawNode(Graphics::Surface *screen, DrawNode *drawNode); + static void drawAsShadowDrawNode(Graphics::Surface *screen, DrawNode *drawNode); + static void drawMaskDrawNode(Graphics::Surface *screen, DrawNode *drawNode); + static void drawBackSpriteDrawNode(Graphics::Surface *screen, DrawNode *drawNode); + + byte getBlendTableColor(byte pixelColor, byte backgroundPixelColor, byte *blendTable); + + Graphics::Surface *_frontScreen; + Graphics::Surface *_screenForInventory; + Graphics::Surface *_mapScreen; + const Graphics::Surface *_roomBackground; + + byte *_shadowTable70; + byte *_shadowTable50; + + static const byte kShadowColor = 191; + +private: + PrinceEngine *_vm; + bool _changed; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/hero.cpp b/engines/prince/hero.cpp new file mode 100644 index 0000000000..b873e83360 --- /dev/null +++ b/engines/prince/hero.cpp @@ -0,0 +1,1002 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/random.h" + +#include "prince/hero.h" +#include "prince/hero_set.h" +#include "prince/animation.h" +#include "prince/resource.h" +#include "prince/prince.h" +#include "prince/graphics.h" +#include "prince/flags.h" +#include "prince/script.h" + +namespace Prince { + +Hero::Hero(PrinceEngine *vm, GraphicsMan *graph) : _vm(vm), _graph(graph), + _number(0), _visible(false), _state(kHeroStateStay), _middleX(0), _middleY(0), + _boreNum(1), _currHeight(0), _moveDelay(0), _shadMinus(0), _moveSetType(0), _zoomedHeroSurface(nullptr), + _lastDirection(kHeroDirDown), _destDirection(kHeroDirDown), _talkTime(0), _boredomTime(0), _phase(0), + _specAnim(nullptr), _drawX(0), _drawY(0), _drawZ(0), + _frameXSize(0), _frameYSize(0), _scaledFrameXSize(0), _scaledFrameYSize(0), _color(0), + _coords(nullptr), _dirTab(nullptr), _currCoords(nullptr), _currDirTab(nullptr), _step(0), + _maxBoredom(200), _leftRightMainDir(0), _upDownMainDir(0), _animSetNr(0) +{ +} + +Hero::~Hero() { + freeHeroAnim(); + freeOldMove(); + freeZoomedSurface(); +} + +bool Hero::loadAnimSet(uint32 animSetNr) { + _animSetNr = animSetNr; + + if (animSetNr >= ARRAYSIZE(heroSetTable)) { + return false; + } + + _shadMinus = heroSetBack[animSetNr]; + + for (uint32 i = 0; i < _moveSet.size(); i++) { + delete _moveSet[i]; + } + + const HeroSetAnimNames &animSet = *heroSetTable[animSetNr]; + + _moveSet.resize(kMoveSetSize); + for (uint32 i = 0; i < kMoveSetSize; i++) { + debug("Anim set item %d %s", i, animSet[i]); + Animation *anim = nullptr; + if (animSet[i] != nullptr) { + anim = new Animation(); + Resource::loadResource(anim, animSet[i], false); + } + _moveSet[i] = anim; + } + + return true; +} + +Graphics::Surface *Hero::getSurface() { + Animation *heroAnim = nullptr; + if (_specAnim != nullptr) { + heroAnim = _specAnim; + } else { + heroAnim = _moveSet[_moveSetType]; + } + + if (heroAnim != nullptr) { + int16 phaseFrameIndex = heroAnim->getPhaseFrameIndex(_phase); + Graphics::Surface *heroFrame = heroAnim->getFrame(phaseFrameIndex); + return heroFrame; + } + return nullptr; +} + +uint16 Hero::getData(AttrId dataId) { + switch (dataId) { + case kHeroLastDir: + return _lastDirection; + case kHeroAnimSet: + return _animSetNr; + default: + assert(false); + return 0; + } +} + +int Hero::getScaledValue(int size) { + int16 initScaleValue = _vm->_scaleValue; + if (_vm->_scaleValue != 10000) { + int newSize = 0; + for (int i = 0; i < size; i++) { + initScaleValue -= 100; + if (initScaleValue >= 0) { + newSize++; + } else { + initScaleValue += _vm->_scaleValue; + } + } + return newSize; + } else { + return size; + } +} + +Graphics::Surface *Hero::zoomSprite(Graphics::Surface *heroFrame) { + Graphics::Surface *zoomedFrame = new Graphics::Surface(); + zoomedFrame->create(_scaledFrameXSize, _scaledFrameYSize, Graphics::PixelFormat::createFormatCLUT8()); + + int sprZoomX; + int sprZoomY = _vm->_scaleValue; + uint xSource = 0; + uint ySource = 0; + uint xDest = 0; + uint yDest = 0; + + for (int i = 0; i < _scaledFrameYSize; i++) { + // linear_loop: + while (1) { + sprZoomY -= 100; + if (sprZoomY >= 0 || _vm->_scaleValue == 10000) { + // all_r_y + sprZoomX = _vm->_scaleValue; + break; // to loop_lin + } else { + sprZoomY += _vm->_scaleValue; + xSource = 0; + ySource++; + } + } + // loop_lin: + for (int j = 0; j < _scaledFrameXSize; j++) { + sprZoomX -= 100; + if (sprZoomX >= 0) { + // its_all_r + memcpy(zoomedFrame->getBasePtr(xDest, yDest), heroFrame->getBasePtr(xSource, ySource), 1); + xDest++; + } else { + sprZoomX += _vm->_scaleValue; + j--; + } + xSource++; + } + xDest = 0; + yDest++; + xSource = 0; + ySource++; + } + return zoomedFrame; +} + +void Hero::countDrawPosition() { + Animation *heroAnim = nullptr; + if (_specAnim != nullptr) { + heroAnim = _specAnim; + } else { + heroAnim = _moveSet[_moveSetType]; + } + if (heroAnim != nullptr) { + int phaseFrameIndex = heroAnim->getPhaseFrameIndex(_phase); + Graphics::Surface *heroSurface = heroAnim->getFrame(phaseFrameIndex); + + _frameXSize = heroSurface->w; + _frameYSize = heroSurface->h; + _scaledFrameXSize = getScaledValue(_frameXSize); + _scaledFrameYSize = getScaledValue(_frameYSize); + + if (_vm->_scaleValue != 10000) { + //notfullSize + _drawX = _middleX - _scaledFrameXSize / 2; + _drawY = _middleY + 1 - _scaledFrameYSize; + _vm->checkMasks(_drawX, _drawY - 1, _scaledFrameXSize, _scaledFrameYSize, _middleY); + } else { + //fullSize + _drawX = _middleX - _frameXSize / 2; + _drawY = _middleY + 1 - _frameYSize; + _vm->checkMasks(_drawX, _drawY - 1, _frameXSize, _frameYSize, _middleY); + } + _drawZ = _middleY; + } +} + +void Hero::showHeroShadow(Graphics::Surface *screen, DrawNode *drawNode) { + PrinceEngine *vm = (PrinceEngine *)drawNode->data; + int16 heroSurfaceWidth = drawNode->s->w; + int16 heroSurfaceHeight = drawNode->s->h; + + Graphics::Surface *makeShadow = new Graphics::Surface(); + makeShadow->create(heroSurfaceWidth, heroSurfaceHeight, Graphics::PixelFormat::createFormatCLUT8()); + + for (int y = 0; y < heroSurfaceHeight; y++) { + byte *src = (byte *)drawNode->s->getBasePtr(0, y); + byte *dst = (byte *)makeShadow->getBasePtr(0, y); + for (int x = 0; x < heroSurfaceWidth; x++, dst++, src++) { + if (*src != 0xFF) { + *dst = GraphicsMan::kShadowColor; + } else { + *dst = *src; + } + } + } + + if (drawNode->posY > 1 && drawNode->posY < PrinceEngine::kMaxPicHeight) { + int shadDirection; + if (vm->_lightY > drawNode->posY) { + shadDirection = 1; + } else { + shadDirection = 0; + } + + vm->_shadLineLen = 0; + Graphics::drawLine(vm->_lightX, vm->_lightY, drawNode->posX, drawNode->posY, 0, &vm->plotShadowLinePoint, vm); + + byte *sprShadow = vm->_graph->_shadowTable70; + + int shadDrawX = drawNode->posX - vm->_picWindowX; + int shadDrawY = drawNode->posY - vm->_picWindowY; + + int shadPosX = shadDrawX; + int shadPosY = shadDrawY; + int shadBitAddr = drawNode->posY * PrinceEngine::kMaxPicWidth / 8 + drawNode->posX / 8; + int shadBitMask = 128 >> (drawNode->posX % 8); + + int shadZoomY2 = vm->_shadScaleValue; + int shadZoomY = drawNode->scaleValue; + + int diffX = 0; + int diffY = 0; + + int shadowHeroX = 0; + int shadowHeroY = heroSurfaceHeight - 1; + + int shadLastY = 0; + + byte *shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY); // first pixel from last row of shadow hero + byte *background = (byte *)screen->getBasePtr(shadDrawX, shadDrawY); // pixel of background where shadow sprite starts + + // banked2 + byte *shadowLineStart = vm->_shadowLine + 8; + + int shadWallDown = 0; + int shadWallBitAddr = 0; + int shadWallBitMask = 0; + byte *shadWallDestAddr = 0; + int shadWallPosY = 0; + int shadWallSkipX = 0; + int shadWallModulo = 0; + + // linear_loop + for (int i = 0; i < heroSurfaceHeight; i++) { + int j; + //retry_line: + for (j = heroSurfaceHeight - i; j > 0; j--) { + shadZoomY -= 100; + if (shadZoomY < 0 && drawNode->scaleValue != 10000) { + shadZoomY += drawNode->scaleValue; + shadowHeroY--; + if (shadowHeroY < 0) { + break; + } + } else { + break; + } + } + if (!j) { + break; + } + if (shadowHeroY < 0) { + break; + } + + //line_y_ok + if (shadLastY != shadPosY && shadPosY >= 0 && shadPosY < 480 && shadPosX < 640) { + shadLastY = shadPosY; + bool skipLineFlag = false; + int shadSkipX = 0; + int ctLoop = 0; + int sprModulo = 0; + + if (shadPosX < 0) { + shadSkipX = -1 * shadPosX; + if (heroSurfaceWidth > shadSkipX) { + ctLoop = heroSurfaceWidth - shadSkipX; + shadowHeroX = shadSkipX; + } else { + //skip_line + skipLineFlag = true; + } + } else { + //x1_ok + if (shadPosX + heroSurfaceWidth > 640) { + ctLoop = 640 - shadPosX; + sprModulo = shadPosX + heroSurfaceWidth - 640; + } else { + //draw_line + ctLoop = heroSurfaceWidth; + } + } + + if (!skipLineFlag) { + //draw_line1 + //retry_line2 + int k; + for (k = j; k > 0; k--) { + shadZoomY2 -= 100; + if (shadZoomY2 < 0 && vm->_shadScaleValue != 10000) { + shadZoomY2 += vm->_shadScaleValue; + shadowHeroY--; + if (shadowHeroY < 0) { + break; + } + } else { + break; + } + } + if (shadowHeroY < 0) { + break; + } + if (!k) { + break; + } + //line_y_ok_2: + //copy_trans + bool shadWDFlag = false; + int shadZoomX = drawNode->scaleValue; + int backgroundDiff = shadSkipX; + int shadBitMaskCopyTrans = shadBitMask; + int shadBitAddrCopyTrans = shadBitAddr; + shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY); + background = (byte *)screen->getBasePtr(shadDrawX + diffX + backgroundDiff, shadDrawY + diffY); + + if (shadPosX < 0) { + if (heroSurfaceWidth > shadSkipX) { + shadBitAddrCopyTrans += shadSkipX / 8; + if ((shadSkipX % 8)) { + //loop_rotate: + for (int a = 0; a < (shadSkipX % 8); a++) { + if (shadBitMaskCopyTrans == 1) { + shadBitMaskCopyTrans = 128; + shadBitAddrCopyTrans++; + } else { + shadBitMaskCopyTrans >>= 1; + } + } + } + } + } + + //ct_loop: + for (int l = 0; l < ctLoop; l++) { + shadZoomX -= 100; + if (shadZoomX < 0 && drawNode->scaleValue != 10000) { + shadZoomX += drawNode->scaleValue; + } else { + if (*shadowHero == GraphicsMan::kShadowColor) { + if ((shadBitMaskCopyTrans & vm->_shadowBitmap[shadBitAddrCopyTrans])) { + if (shadWallDown == 0) { + if ((shadBitMaskCopyTrans & vm->_shadowBitmap[shadBitAddrCopyTrans + PrinceEngine::kShadowBitmapSize])) { + shadWDFlag = true; + //shadow + *background = *(sprShadow + *background); + } + } + } else { + //shadow + *background = *(sprShadow + *background); + } + } + //ct_next + if (shadBitMaskCopyTrans == 1) { + shadBitMaskCopyTrans = 128; + shadBitAddrCopyTrans++; + } else { + shadBitMaskCopyTrans >>= 1; + } + //okok + backgroundDiff++; + background = (byte *)screen->getBasePtr(shadDrawX + diffX + backgroundDiff, shadDrawY + diffY); + } + shadowHeroX++; + shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY); + } + //byebyebye + if (!shadWallDown && shadWDFlag) { + shadWallDown = shadPosX; + shadWallBitAddr = shadBitAddr; + shadWallDestAddr = (byte *)screen->getBasePtr(shadDrawX + diffX, shadDrawY + diffY); + shadWallBitMask = shadBitMask; + shadWallPosY = shadPosY; + shadWallSkipX = shadSkipX; + shadWallModulo = sprModulo; + } + //byebye + if (shadDirection && shadWallDown) { + int shadBitMaskWallCopyTrans = shadWallBitMask; + int shadBitAddrWallCopyTrans = shadWallBitAddr; + background = shadWallDestAddr; + shadowHero = (byte *)makeShadow->getBasePtr(shadWallSkipX, shadowHeroY); + + if (ctLoop > shadWallSkipX && ctLoop - shadWallSkipX > shadWallModulo) { + //WALL_copy_trans + shadWDFlag = false; + int shadZoomXWall = drawNode->scaleValue; + int backgroundDiffWall = 0; + int shadowHeroXWall = 0; + //ct_loop: + for (int m = 0; m < ctLoop; m++) { + shadZoomXWall -= 100; + if (shadZoomXWall < 0 && drawNode->scaleValue != 10000) { + shadZoomXWall += drawNode->scaleValue; + } else { + //point_ok: + if (*shadowHero == GraphicsMan::kShadowColor) { + if ((shadBitMaskWallCopyTrans & vm->_shadowBitmap[shadBitAddrWallCopyTrans + PrinceEngine::kShadowBitmapSize])) { + *background = *(sprShadow + *background); + } + } + //ct_next + if (shadBitMaskWallCopyTrans == 1) { + shadBitMaskWallCopyTrans = 128; + shadBitAddrWallCopyTrans++; + } else { + shadBitMaskWallCopyTrans >>= 1; + } + //okok + backgroundDiffWall++; + background = shadWallDestAddr + backgroundDiffWall; + } + shadowHeroXWall++; + shadowHero = (byte *)makeShadow->getBasePtr(shadWallSkipX + shadowHeroXWall, shadowHeroY); + } + } + //krap2 + shadWallDestAddr -= PrinceEngine::kNormalWidth; + shadWallBitAddr -= PrinceEngine::kMaxPicWidth / 8; + shadWallPosY--; + } + } + } + //skip_line + //next_line + if (READ_LE_UINT16(shadowLineStart + 2) < READ_LE_UINT16(shadowLineStart - 2)) { + //minus_y + shadBitAddr -= PrinceEngine::kMaxPicWidth / 8; + shadPosY--; + diffY--; + } else if (READ_LE_UINT16(shadowLineStart + 2) > READ_LE_UINT16(shadowLineStart - 2)) { + shadBitAddr += PrinceEngine::kMaxPicWidth / 8; + shadPosY++; + diffY++; + } + //no_change_y + if (READ_LE_UINT16(shadowLineStart) < READ_LE_UINT16(shadowLineStart - 4)) { + //minus_x + shadPosX--; + //rol + if (shadBitMask == 128) { + shadBitMask = 1; + shadBitAddr--; + } else { + shadBitMask <<= 1; + } + diffX--; + } else if (READ_LE_UINT16(shadowLineStart) > READ_LE_UINT16(shadowLineStart - 4)) { + shadPosX++; + //ror + if (shadBitMask == 1) { + shadBitMask = 128; + shadBitAddr++; + } else { + shadBitMask >>= 1; + } + diffX++; + } + //no_change_x + shadowLineStart += 4; + shadowHeroY--; + if (shadowHeroY < 0) { + break; + } + shadowHeroX = 0; + background = (byte *)screen->getBasePtr(shadDrawX + diffX, shadDrawY + diffY); + shadowHero = (byte *)makeShadow->getBasePtr(shadowHeroX, shadowHeroY); + } + //koniec_bajki - end_of_a_story + } + makeShadow->free(); + delete makeShadow; +} + +void Hero::setScale(int8 zoomBitmapValue) { + if (!zoomBitmapValue) { + _vm->_scaleValue = 10000; + } else { + _vm->_scaleValue = 10000 / zoomBitmapValue; + } +} + +void Hero::selectZoom() { + int8 zoomBitmapValue = *(_vm->_zoomBitmap + _middleY / 4 * _vm->kZoomBitmapWidth + _middleX / 4); + setScale(zoomBitmapValue); +} + +int Hero::rotateHero(int oldDirection, int newDirection) { + switch (oldDirection) { + case kHeroDirLeft: + switch (newDirection) { + case kHeroDirRight: + return kMove_MLR; + case kHeroDirUp: + return kMove_MLU; + case kHeroDirDown: + return kMove_MLD; + } + break; + case kHeroDirRight: + switch (newDirection) { + case kHeroDirLeft: + return kMove_MRL; + case kHeroDirUp: + return kMove_MRU; + case kHeroDirDown: + return kMove_MRD; + } + break; + case kHeroDirUp: + switch (newDirection) { + case kHeroDirLeft: + return kMove_MUL; + case kHeroDirRight: + return kMove_MUR; + case kHeroDirDown: + return kMove_MUD; + } + break; + case kHeroDirDown: + switch (newDirection) { + case kHeroDirLeft: + return kMove_MDL; + case kHeroDirRight: + return kMove_MDR; + case kHeroDirUp: + return kMove_MDU; + } + break; + } + error("rotateHero - wrong directions - old %d, new %d", oldDirection, newDirection); +} + +void Hero::heroStanding() { + _phase = 0; + switch (_lastDirection) { + case kHeroDirLeft: + _moveSetType = kMove_SL; + break; + case kHeroDirRight: + _moveSetType = kMove_SR; + break; + case kHeroDirUp: + _moveSetType = kMove_SU; + break; + case kHeroDirDown: + _moveSetType = kMove_SD; + break; + } +} + +void Hero::showHero() { + if (_visible && !_vm->_flags->getFlagValue(Flags::NOHEROATALL)) { + + if (_talkTime != 0) { + _talkTime--; + } + + // Scale of hero + selectZoom(); + + if (_state != kHeroStateStay) { + _boredomTime = 0; + } + + if (_state == kHeroStateSpec) { + if (_specAnim != nullptr) { + if (_phase < _specAnim->getPhaseCount() - 1) { + _phase++; + } else { + if (!_talkTime) { + _state = kHeroStateStay; + } else { + _state = kHeroStateTalk; + } + countDrawPosition(); + return; + } + } else { + _state = kHeroStateStay; + } + } else { + freeHeroAnim(); + } + + if (_state == kHeroStateTalk) { + if (_talkTime) { + switch (_lastDirection) { + case kHeroDirLeft: + _moveSetType = kMove_TL; + break; + case kHeroDirRight: + _moveSetType = kMove_TR; + break; + case kHeroDirUp: + _moveSetType = kMove_TU; + break; + case kHeroDirDown: + _moveSetType = kMove_TD; + break; + } + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 1) { + _phase++; + } else { + _phase = _moveSet[_moveSetType]->getLoopCount(); + } + } else { + _state = kHeroStateStay; + } + } + + if (_state == kHeroStateBore) { + switch (_boreNum) { + case 0: + _moveSetType = kMove_BORED1; + break; + case 1: + _moveSetType = kMove_BORED2; + break; + } + if (_moveSet[_moveSetType] != nullptr) { + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 1) { + _phase++; + } else { + _phase = 0; + _lastDirection = kHeroDirDown; + _state = kHeroStateStay; + } + } else { + _state = kHeroStateStay; + } + } + + if (_state == kHeroStateStay) { + if (!_vm->_optionsFlag) { + if (!_vm->_interpreter->getLastOPCode() || !_vm->_interpreter->getFgOpcodePC()) { + _boredomTime++; + if (_boredomTime == _maxBoredom) { + _boreNum =_vm->_randomSource.getRandomNumber(1); // rand one of two 'bored' animation + _phase = 0; + _state = kHeroStateBore; + if (_lastDirection == kHeroDirUp) { + _lastDirection = kHeroDirLeft; + } else { + _lastDirection = kHeroDirDown; + } + } + } else { + _boredomTime = 0; + } + } else { + _boredomTime = 0; + } + heroStanding(); + } + + if (_state == kHeroStateTurn) { + if (_destDirection && (_lastDirection != _destDirection)) { + _phase = 0; + int rotateDir = rotateHero(_lastDirection, _destDirection); + _lastDirection = _destDirection; + if (rotateDir) { + _moveSetType = rotateDir; + _state = kHeroStateTran; + } else { + _state = kHeroStateStay; + heroStanding(); + } + } else { + _state = kHeroStateStay; + heroStanding(); + } + } + + if (_state == kHeroStateTran) { + if (_moveSet[_moveSetType] != nullptr) { + // only in bear form + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) { + _phase += 2; + } else { + _state = kHeroStateStay; + heroStanding(); + } + } else { + _state = kHeroStateStay; + heroStanding(); + } + } + + if (_state == kHeroStateMvan) { + if (_moveSet[_moveSetType] != nullptr) { + // only in bear form + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) { + _phase += 2; + } else { + _state = kHeroStateMove; + } + } else { + _state = kHeroStateMove; + } + } + + if (_state == kHeroStateDelayMove) { + _moveDelay--; + if (!_moveDelay) { + _state = kHeroStateMove; + } + } + + if (_state == kHeroStateMove || _state == kHeroStateRun) { + //go_for_it: + while (1) { + if (_currCoords != nullptr) { + if (READ_LE_UINT32(_currCoords) != 0xFFFFFFFF) { + int x = READ_LE_UINT16(_currCoords); + int y = READ_LE_UINT16(_currCoords + 2); + _currCoords += 4; + int dir = *_currDirTab; + _currDirTab++; + if (_lastDirection != dir) { + _phase = 0; + int rotateDir = rotateHero(_lastDirection, dir); + _lastDirection = dir; + if (_moveSet[rotateDir] != nullptr) { + // only in bear form + _state = kHeroStateMvan; + _moveSetType = rotateDir; + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) { + _phase += 2; + break; + } else { + _state = kHeroStateMove; + continue; + } + } else { + continue; + } + } + //no_need_direction_change + if (dir == kHeroDirLeft) { + if (_middleX - x >= _step) { + heroMoveGotIt(x, y, dir); + break; + } + } else if (dir == kHeroDirRight) { + if (x - _middleX >= _step) { + heroMoveGotIt(x, y, dir); + break; + } + } else if (dir == kHeroDirUp) { + if (_middleY - y >= _step) { + heroMoveGotIt(x, y, dir); + break; + } + } else if (dir == kHeroDirDown) { + if (y - _middleY >= _step) { + heroMoveGotIt(x, y, dir); + break; + } + } + } else { + //finito + _middleX = READ_LE_UINT16(_currCoords - 4); + _middleY = READ_LE_UINT16(_currCoords - 2); + selectZoom(); + + if (_coords != nullptr) { + free(_coords); + _coords = nullptr; + _currCoords = nullptr; + } + + if (_dirTab != nullptr) { + free(_dirTab); + _dirTab = nullptr; + _currDirTab = nullptr; + } + + _boredomTime = 0; + _phase = 0; + _state = kHeroStateTurn; + + if (!_destDirection) { + _destDirection = _lastDirection; + } + + heroStanding(); + + break; + } + } else { + heroStanding(); + break; + } + } + } + countDrawPosition(); + } +} + +void Hero::drawHero() { + if (_visible && !_vm->_flags->getFlagValue(Flags::NOHEROATALL)) { + freeZoomedSurface(); + Graphics::Surface *mainHeroSurface = getSurface(); + if (mainHeroSurface) { + DrawNode newDrawNode; + newDrawNode.posX = _drawX; + newDrawNode.posY = _drawY; + newDrawNode.posZ = _drawZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = _vm->_transTable; + newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; + + if (_vm->_scaleValue != 10000) { + _zoomedHeroSurface = zoomSprite(mainHeroSurface); + newDrawNode.s = _zoomedHeroSurface; + } else { + newDrawNode.s = mainHeroSurface; + } + _vm->_drawNodeList.push_back(newDrawNode); + + drawHeroShadow(mainHeroSurface); + + } + } +} + +void Hero::drawHeroShadow(Graphics::Surface *heroFrame) { + DrawNode newDrawNode; + newDrawNode.posX = _middleX - _scaledFrameXSize / 2; + newDrawNode.posY = _middleY - _shadMinus - 1; + newDrawNode.posZ = kHeroShadowZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.scaleValue = _vm->_scaleValue; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = _vm; + newDrawNode.drawFunction = &showHeroShadow; + newDrawNode.s = heroFrame; + _vm->_drawNodeList.push_back(newDrawNode); +} + +void Hero::heroMoveGotIt(int x, int y, int dir) { + _middleX = x; + _middleY = y; + selectZoom(); + + switch (dir) { + case kHeroDirLeft: + _moveSetType = kMove_ML; + break; + case kHeroDirRight: + _moveSetType = kMove_MR; + break; + case kHeroDirUp: + _moveSetType = kMove_MU; + break; + case kHeroDirDown: + _moveSetType = kMove_MD; + break; + } + + if (_vm->_flags->getFlagValue(Flags::HEROFAST) || _state == kHeroStateRun) { + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 2) { + _phase += 2; + } else { + _phase = 0; + } + } else { + if (_phase < _moveSet[_moveSetType]->getPhaseCount() - 1) { + _phase++; + } else { + _phase = 0; + } + } + + _step = kStepLeftRight; + if (_moveSetType == kMove_MU || _moveSetType == kMove_MD) { + _step = kStepUpDown; + } + if (_vm->_flags->getFlagValue(Flags::HEROFAST)) { + _step *= 2.5; + } else if (_state == kHeroStateRun) { + _step *= 2; + } +} + +void Hero::scrollHero() { + int scrollType = _vm->_flags->getFlagValue(Flags::SCROLLTYPE); + int position = _middleX; + int scrollValue, scrollValue2; + + switch (scrollType) { + case 0: + position = _middleX; + break; + case 1: + scrollValue = _vm->_flags->getFlagValue(Flags::SCROLLVALUE); + position = _vm->_normAnimList[scrollValue]._currX + _vm->_normAnimList[scrollValue]._currW / 2; + break; + case 2: + scrollValue = _vm->_flags->getFlagValue(Flags::SCROLLVALUE); + scrollValue2 = _vm->_flags->getFlagValue(Flags::SCROLLVALUE2); + position = scrollValue; + if (scrollValue < scrollValue2) { + _vm->_flags->setFlagValue(Flags::SCROLLVALUE, 0); + } else { + _vm->_flags->setFlagValue(Flags::SCROLLVALUE, scrollValue - scrollValue2); + } + break; + } + + int locationWidth = _vm->_sceneWidth; + int difference = locationWidth - _vm->kNormalWidth / 2; + + int destValue = 0; + if (position > _vm->kNormalWidth / 2) { + destValue = difference - _vm->kNormalWidth / 2; + } + if (position < difference) { + destValue = position - _vm->kNormalWidth / 2; + } + + if(destValue < 0) { + destValue = 0; + } + _vm->_picWindowX = destValue; + _drawX -= destValue; +} + +void Hero::freeOldMove() { + if (_coords != nullptr) { + free(_coords); + _coords = nullptr; + } + if (_dirTab != nullptr) { + free(_dirTab); + _dirTab = nullptr; + } + _step = 0; + _phase = 0; + _moveDelay = 0; + _state = Hero::kHeroStateStay; +} + +void Hero::freeHeroAnim() { + if (_specAnim != nullptr) { + delete _specAnim; + _specAnim = nullptr; + } +} + +void Hero::freeZoomedSurface() { + if (_zoomedHeroSurface != nullptr) { + _zoomedHeroSurface->free(); + delete _zoomedHeroSurface; + _zoomedHeroSurface = nullptr; + } +} + +} // End of namespace Prince diff --git a/engines/prince/hero.h b/engines/prince/hero.h new file mode 100644 index 0000000000..703ef0650d --- /dev/null +++ b/engines/prince/hero.h @@ -0,0 +1,184 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_HERO_H +#define PRINCE_HERO_H + +#include "common/scummsys.h" +#include "common/array.h" +#include "common/memstream.h" + +#include "graphics/surface.h" +#include "graphics/primitives.h" + +namespace Prince { + +class Animation; +class PrinceEngine; +class GraphicsMan; +struct InventoryItem; +struct DrawNode; + +class Hero { +public: + static const uint32 kMoveSetSize = 26; + static const int16 kStepLeftRight = 8; + static const int16 kStepUpDown = 4; + static const int16 kHeroShadowZ = 2; + + enum State { + kHeroStateStay, + kHeroStateTurn, + kHeroStateMove, + kHeroStateBore, + kHeroStateSpec, + kHeroStateTalk, + kHeroStateMvan, + kHeroStateTran, + kHeroStateRun, + kHeroStateDelayMove + }; + + enum Direction { + kHeroDirLeft = 1, + kHeroDirRight = 2, + kHeroDirUp = 3, + kHeroDirDown = 4 + }; + + enum MoveSet { + kMove_SL, + kMove_SR, + kMove_SU, + kMove_SD, + kMove_ML, + kMove_MR, + kMove_MU, + kMove_MD, + kMove_TL, + kMove_TR, + kMove_TU, + kMove_TD, + kMove_MLU, + kMove_MLD, + kMove_MLR, + kMove_MRU, + kMove_MRD, + kMove_MRL, + kMove_MUL, + kMove_MUR, + kMove_MUD, + kMove_MDL, + kMove_MDR, + kMove_MDU, + kMove_BORED1, + kMove_BORED2 + }; + + // Used instead of offset in getData + enum AttrId { + kHeroLastDir = 26, + kHeroAnimSet = 120 + }; + + uint16 getData(AttrId dataId); + + Hero(PrinceEngine *vm, GraphicsMan *graph); + ~Hero(); + bool loadAnimSet(uint32 heroAnimNumber); + + Graphics::Surface *getSurface(); + + void setPos(int16 x, int16 y) { _middleX = x; _middleY = y; } + void setVisible(bool flag) { _visible = flag; } + + void showHero(); + void drawHero(); + void freeZoomedSurface(); + void heroStanding(); + void heroMoveGotIt(int x, int y, int dir); + int rotateHero(int oldDirection, int newDirection); + void scrollHero(); + void setScale(int8 zoomBitmapValue); + int getScaledValue(int size); + void selectZoom(); + void countDrawPosition(); + Graphics::Surface *zoomSprite(Graphics::Surface *heroFrame); + void line(int x1, int y1, int x2, int y2); + void plotPoint(int x, int y); + static void showHeroShadow(Graphics::Surface *screen, DrawNode *drawNode); + void drawHeroShadow(Graphics::Surface *heroFrame); + void freeOldMove(); + void freeHeroAnim(); + + uint16 _number; + uint16 _visible; + int16 _state; + int16 _middleX; // middle of X + int16 _middleY; // lower part of hero + int16 _moveSetType; + + int16 _frameXSize; + int16 _frameYSize; + int16 _scaledFrameXSize; + int16 _scaledFrameYSize; + int16 _drawX; + int16 _drawY; + int16 _drawZ; + + byte *_coords; // array of coordinates + byte *_dirTab; // array of directions + byte *_currCoords; // current coordinations + byte *_currDirTab; // current direction + int16 _lastDirection; // previous move direction + int16 _destDirection; + int16 _leftRightMainDir; // left or right - dominant direction + int16 _upDownMainDir; // up or down - dominant direction + int32 _phase; // Phase animation phase + int16 _step; // Step x/y step size depends on direction + int16 _maxBoredom; // stand still timeout + int16 _boredomTime; // Boredom current boredom time in frames + uint16 _boreNum; // Bore anim frame + int16 _talkTime; // TalkTime time of talk anim + Animation *_specAnim; // additional anim + Graphics::Surface *_zoomedHeroSurface; + + uint16 _currHeight; // height of current anim phase + + Common::Array<byte> _inventory; // Inventory array of items + Common::Array<byte> _inventory2; // Inventory2 array of items + // Font subtitiles font + int _color; // subtitles color + uint32 _animSetNr; // number of animation set + Common::Array<Animation *> _moveSet; // MoveAnims MoveSet + + uint32 _moveDelay; + uint32 _shadMinus; + +private: + PrinceEngine *_vm; + GraphicsMan *_graph; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/hero_set.h b/engines/prince/hero_set.h new file mode 100644 index 0000000000..dfe7e50268 --- /dev/null +++ b/engines/prince/hero_set.h @@ -0,0 +1,244 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" + +namespace Prince { + +const int heroSetBack[7] = { 0, 0, 10, 0, 6, 0, 0 }; + +typedef const char *HeroSetAnimNames[26]; + +static HeroSetAnimNames heroSet5 = { + "SL_DIAB.ANI", + "SR_DIAB.ANI", + "SU_DIAB.ANI", + "SD_DIAB.ANI", + nullptr, + nullptr, + "MU_DIAB.ANI", + "MD_DIAB.ANI", + "TL_DIAB.ANI", + "TR_DIAB.ANI", + "TU_DIAB.ANI", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr +}; + +static HeroSetAnimNames heroSet1 = { + "SL_HERO1.ANI", + "SR_HERO1.ANI", + "SU_HERO1.ANI", + "SD_HERO1.ANI", + "ML_HERO1.ANI", + "MR_HERO1.ANI", + "MU_HERO1.ANI", + "MD_HERO1.ANI", + "TL_HERO1.ANI", + "TR_HERO1.ANI", + "TU_HERO1.ANI", + "TD_HERO1.ANI", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "KSI_KURZ.ANI", + "KS_WLOSY.ANI" +}; + +static HeroSetAnimNames heroSet2 = { + "SL_HERO2.ANI", + "SR_HERO2.ANI", + "SU_HERO2.ANI", + "SD_HERO2.ANI", + "ML_HERO2.ANI", + "MR_HERO2.ANI", + "MU_HERO2.ANI", + "MD_HERO2.ANI", + "TL_HERO2.ANI", + "TR_HERO2.ANI", + "TU_HERO2.ANI", + "TD_HERO2.ANI", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "KSI_KU_S.ANI", + "KS_WLO_S.ANI" +}; + +static HeroSetAnimNames heroSet3 = { + "SL_BEAR.ANI", + "SR_BEAR.ANI", + "SU_BEAR.ANI", + "SD_BEAR.ANI", + "NIED-LEW.ANI", + "NIED-PRW.ANI", + "NIED-TYL.ANI", + "NIED-PRZ.ANI", + "SL_BEAR.ANI", + "SR_BEAR.ANI", + "SU_BEAR.ANI", + "SD_BEAR.ANI", + "N_LW-TYL.ANI", + "N_LW-PRZ.ANI", + "N_LW-PR.ANI", + "N_PR-TYL.ANI", + "N_PR-PRZ.ANI", + "N_PR-LW.ANI", + "N_TYL-LW.ANI", + "N_TYL-PR.ANI", + "N_TL-PRZ.ANI", + "N_PRZ-LW.ANI", + "N_PRZ-PR.ANI", + "N_PRZ-TL.ANI", + nullptr, + nullptr, +}; + +static HeroSetAnimNames shanSet1 = { + "SL_SHAN.ANI", + "SR_SHAN.ANI", + "SU_SHAN.ANI", + "SD_SHAN.ANI", + "ML_SHAN.ANI", + "MR_SHAN.ANI", + "MU_SHAN.ANI", + "MD_SHAN.ANI", + "TL_SHAN.ANI", + "TR_SHAN.ANI", + "TU_SHAN.ANI", + "TD_SHAN.ANI", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "B1_SHAN.ANI", + "B2_SHAN.ANI", +}; + +static HeroSetAnimNames shanSet2 = { + "SL_SHAN2.ANI", + "SR_SHAN2.ANI", + "SU_SHAN.ANI", + "SD_SHAN2.ANI", + "ML_SHAN2.ANI", + "MR_SHAN2.ANI", + "MU_SHAN.ANI", + "MD_SHAN2.ANI", + "TL_SHAN2.ANI", + "TR_SHAN2.ANI", + "TU_SHAN.ANI", + "TD_SHAN2.ANI", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "B1_SHAN2.ANI", + "B2_SHAN2.ANI" +}; + +static HeroSetAnimNames arivSet1 = { + "SL_ARIV.ANI", + "SR_ARIV.ANI", + "SU_ARIV.ANI", + "SD_ARIV.ANI", + "ML_ARIV.ANI", + "MR_ARIV.ANI", + "MU_ARIV.ANI", + "MD_ARIV.ANI", + "TL_ARIV.ANI", + "TR_ARIV.ANI", + "TU_ARIV.ANI", + "TD_ARIV.ANI", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr +}; + +const HeroSetAnimNames *heroSetTable[7] = { + &heroSet1, + &heroSet2, + &heroSet3, + &shanSet1, + &arivSet1, + &heroSet5, + &shanSet2, +}; + +} // End of namespace Prince diff --git a/engines/prince/mhwanh.cpp b/engines/prince/mhwanh.cpp new file mode 100644 index 0000000000..608ccc23d7 --- /dev/null +++ b/engines/prince/mhwanh.cpp @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "graphics/surface.h" + +#include "prince/mhwanh.h" + +namespace Prince { + +MhwanhDecoder::MhwanhDecoder() : _surface(nullptr), _palette(nullptr) { +} + +MhwanhDecoder::~MhwanhDecoder() { + destroy(); +} + +void MhwanhDecoder::destroy() { + if (_surface != nullptr) { + _surface->free(); + delete _surface; + _surface = nullptr; + } + if (_palette != nullptr) { + free(_palette); + _palette = nullptr; + } +} + +bool MhwanhDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + stream.seek(0); + stream.skip(0x20); + // Read the palette + _palette = (byte *)malloc(kPaletteColorCount * 3); + for (uint16 i = 0; i < kPaletteColorCount; i++) { + _palette[i * 3] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + _surface = new Graphics::Surface(); + _surface->create(640, 480, Graphics::PixelFormat::createFormatCLUT8()); + for (int h = 0; h < 480; h++) { + stream.read(_surface->getBasePtr(0, h), 640); + } + + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/mhwanh.h b/engines/prince/mhwanh.h new file mode 100644 index 0000000000..f8165a7666 --- /dev/null +++ b/engines/prince/mhwanh.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_MHWANH_H +#define PRINCE_MHWANH_H + +#include "image/image_decoder.h" +#include "image/bmp.h" + +#include "graphics/surface.h" + +#include "resource.h" + +namespace Prince { + +class MhwanhDecoder : public Image::ImageDecoder { +public: + MhwanhDecoder(); + virtual ~MhwanhDecoder(); + + // ImageDecoder API + void destroy(); + virtual bool loadStream(Common::SeekableReadStream &stream); + virtual Graphics::Surface *getSurface() const { return _surface; } + virtual const byte *getPalette() const { return _palette; } + uint16 getPaletteCount() const { return kPaletteColorCount; } + static const uint16 kPaletteColorCount = 256; + +private: + Graphics::Surface *_surface; + byte *_palette; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/mob.cpp b/engines/prince/mob.cpp new file mode 100644 index 0000000000..de825ef8b2 --- /dev/null +++ b/engines/prince/mob.cpp @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/mob.h" + +namespace Prince { + +bool Mob::loadFromStream(Common::SeekableReadStream &stream) { + int32 pos = stream.pos(); + + uint16 visible = stream.readUint16LE(); + + if (visible == 0xFFFF) + return false; + + _visible = visible; + _type = stream.readUint16LE(); + _rect.left = stream.readUint16LE(); + _rect.top = stream.readUint16LE(); + _rect.right = stream.readUint16LE(); + _rect.bottom = stream.readUint16LE(); + + _mask = stream.readUint16LE(); + + _examPosition.x = stream.readUint16LE(); + _examPosition.y = stream.readUint16LE(); + _examDirection = (Direction)stream.readUint16LE(); + + _usePosition.x = stream.readByte(); + _usePosition.y = stream.readByte(); + _useDirection = (Direction)stream.readUint16LE(); + + uint32 nameOffset = stream.readUint32LE(); + uint32 examTextOffset = stream.readUint32LE(); + + byte c; + stream.seek(nameOffset); + _name.clear(); + while ((c = stream.readByte())) + _name += c; + + stream.seek(examTextOffset); + _examText.clear(); + c = stream.readByte(); + if (c) { + _examText += c; + do { + c = stream.readByte(); + _examText += c; + } while (c != 255); + } + stream.seek(pos + 32); + + return true; +} + +void Mob::setData(AttrId dataId, uint16 value) { + switch (dataId) { + case kMobExamDir: + _examDirection = (Direction)value; + break; + case kMobExamX: + _examPosition.x = value; + break; + case kMobExamY: + _examPosition.y = value; + break; + default: + assert(false); + } +} + +uint16 Mob::getData(AttrId dataId) { + switch (dataId) { + case kMobVisible: + return _visible; + case kMobExamDir: + return _examDirection; + case kMobExamX: + return _examPosition.x; + case kMobExamY: + return _examPosition.y; + default: + assert(false); + return 0; + } +} + +} // End of namespace Prince diff --git a/engines/prince/mob.h b/engines/prince/mob.h new file mode 100644 index 0000000000..0ea610dd8f --- /dev/null +++ b/engines/prince/mob.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_MOB_H +#define PRINCE_MOB_H + +#include "common/scummsys.h" +#include "common/rect.h" +#include "common/str.h" +#include "common/stream.h" + +#include "prince/common.h" + +namespace Prince { + +class Mob { +public: + + Mob() : _name(""), _examText("") {} + + bool loadFromStream(Common::SeekableReadStream &stream); + + // Used instead of offset in setData and getData + enum AttrId { + kMobVisible = 0, + kMobExamX = 14, + kMobExamY = 16, + kMobExamDir = 18 + }; + + void setData(AttrId dataId, uint16 value); + uint16 getData(AttrId dataId); + + bool _visible; + uint16 _type; + uint16 _mask; + Common::Rect _rect; + + Common::Point _examPosition; + Direction _examDirection; + + Common::Point _usePosition; + Direction _useDirection; + + Common::String _name; + Common::String _examText; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/module.mk b/engines/prince/module.mk new file mode 100644 index 0000000000..b1be123d4b --- /dev/null +++ b/engines/prince/module.mk @@ -0,0 +1,30 @@ +MODULE := engines/prince + +MODULE_OBJS = \ + animation.o \ + archive.o \ + cursor.o \ + debugger.o \ + decompress.o \ + detection.o \ + flags.o \ + font.o \ + graphics.o \ + hero.o \ + mhwanh.o \ + mob.o \ + object.o \ + prince.o \ + pscr.o \ + saveload.o \ + script.o \ + sound.o \ + variatxt.o + +# This module can be built as a plugin +ifeq ($(ENABLE_PRINCE), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/prince/musNum.h b/engines/prince/musNum.h new file mode 100644 index 0000000000..c24cba6ad7 --- /dev/null +++ b/engines/prince/musNum.h @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +namespace Prince { + +enum RoomMus { + ROOM01MUS = 3, + ROOM02MUS = 9, + ROOM03MUS = 9, + ROOM04MUS = 9, + ROOM05MUS = 13, + ROOM06MUS = 9, + ROOM07MUS = 9, + ROOM08MUS = 9, + ROOM09MUS = 14, + ROOM10MUS = 9, + ROOM11MUS = 9, + ROOM12MUS = 9, + ROOM13MUS = 9, + ROOM14MUS = 9, + ROOM15MUS = 5, + ROOM16MUS = 5, + ROOM17MUS = 5, + ROOM18MUS = 5, + ROOM19MUS = 5, + ROOM20MUS = 12, + ROOM21MUS = 9, + ROOM22MUS = 9, + ROOM23MUS = 1, + ROOM24MUS = 1, + ROOM25MUS = 2, + ROOM26MUS = 10, + ROOM27MUS = 7, + ROOM28MUS = 10, + ROOM29MUS = 10, + ROOM30MUS = 11, + ROOM31MUS = 14, + ROOM32MUS = 11, + ROOM33MUS = 7, + ROOM34MUS = 7, + ROOM35MUS = 7, + ROOM36MUS = 7, + ROOM37MUS = 7, + ROOM38MUS = 7, + ROOM39MUS = 7, + ROOM40MUS = 7, + ROOM41MUS = 7, + ROOM42MUS = 7, + ROOM43MUS = 15, + ROOM46MUS = 100, + ROOM47MUS = 100, + ROOM48MUS = 100, + ROOM49MUS = 100, + ROOM50MUS = 100, + ROOM51MUS = 12, + ROOM52MUS = 9, + ROOM53MUS = 5, + ROOM54MUS = 11, + ROOM55MUS = 11, + ROOM56MUS = 11, + ROOM57MUS = 7, + ROOM58MUS = 13, + ROOM59MUS = 16, + ROOM60MUS = 4, + ROOM61MUS = 0 +}; + +} // End of namespace Prince diff --git a/engines/prince/object.cpp b/engines/prince/object.cpp new file mode 100644 index 0000000000..2dc5da68e7 --- /dev/null +++ b/engines/prince/object.cpp @@ -0,0 +1,113 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/archive.h" +#include "common/debug-channels.h" +#include "common/debug.h" +#include "common/stream.h" + +#include "graphics/surface.h" + +#include "prince/object.h" + +namespace Prince { + +Object::Object() : _surface(nullptr), _x(0), _y(0), _z(0), _flags(0), _width(0), + _height(0), _zoomTime(0), _zoomSurface(nullptr) +{ +} + +Object::~Object() { + if (_surface != nullptr) { + _surface->free(); + delete _surface; + _surface = nullptr; + } + if (_zoomSurface != nullptr) { + _zoomSurface->free(); + delete _zoomSurface; + _zoomSurface = nullptr; + } +} + +void Object::loadSurface(Common::SeekableReadStream &stream) { + stream.skip(4); + _width = stream.readUint16LE(); + _height = stream.readUint16LE(); + _surface = new Graphics::Surface(); + _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8()); + + for (int h = 0; h < _surface->h; h++) { + stream.read(_surface->getBasePtr(0, h), _surface->w); + } +} + +bool Object::loadFromStream(Common::SeekableReadStream &stream) { + int32 pos = stream.pos(); + uint16 x = stream.readUint16LE(); + if (x == 0xFFFF) { + return false; + } + _x = x; + _y = stream.readSint16LE(); // skull mini-game has some signed y coords + + const Common::String obStreamName = Common::String::format("OB%02d", stream.readUint16LE()); + Common::SeekableReadStream *obStream = SearchMan.createReadStreamForMember(obStreamName); + if (obStream) { + loadSurface(*obStream); + } + delete obStream; + + _flags = stream.readUint16LE(); + _z = stream.readUint16LE(); + + stream.seek(pos + 16); + + return true; +} + +void Object::setData(AttrId dataId, int32 value) { + switch (dataId) { + case kObjectX: + _x = value; + break; + case kObjectY: + _y = value; + break; + default: + assert(false); + } +} + +int32 Object::getData(AttrId dataId) { + switch (dataId) { + case kObjectX: + return _x; + case kObjectY: + return _y; + default: + assert(false); + return 0; + } +} + +} // End of namespace Prince diff --git a/engines/prince/object.h b/engines/prince/object.h new file mode 100644 index 0000000000..ff22a05805 --- /dev/null +++ b/engines/prince/object.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_OBJECT_H +#define PRINCE_OBJECT_H + +#include "image/image_decoder.h" +#include "graphics/surface.h" + +namespace Prince { + +class Object { +public: + Object(); + ~Object(); + + int32 _x; + int32 _y; + int32 _z; + uint16 _width; + uint16 _height; + int32 _flags; + int32 _zoomTime; + Graphics::Surface *_zoomSurface; + + // Used instead of offset in setData and getData + enum AttrId { + kObjectAddr = 0, + kObjectX = 4, + kObjectY = 6, + kObjectZ = 8, + kObjectFlags = 10, + kObjectZoomInSource = 12, + kObjectZoomInLen = 16, + kObjectZoomInAddr = 20, + kObjectZoomInTime = 24 + }; + + bool loadFromStream(Common::SeekableReadStream &stream); + Graphics::Surface *getSurface() const { return _surface; } + int32 getData(AttrId dataId); + void setData(AttrId dataId, int32 value); + +private: + void loadSurface(Common::SeekableReadStream &stream); + Graphics::Surface *_surface; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/option_text.h b/engines/prince/option_text.h new file mode 100644 index 0000000000..d4d214c98c --- /dev/null +++ b/engines/prince/option_text.h @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +namespace Prince { + +// PL - Mazovia coding +const char invOptionsTextPL[5][18] = { + "Obejrzyj", + "U\xa7""yj", + "Otw\xa2""rz/Pchnij", + "Zamknij/Poci\x86""gnij", + "Daj" +}; + +const char optionsTextPL[7][18] = { + "Podejd\xa6", + "Obejrzyj", + "Zabierz", + "U\xa7""yj", + "Otw\xa2""rz/Pchnij", + "Zamknij/Poci\x86""gnij", + "Porozmawiaj" +}; + +// DE - Other font then for PL + ISO 8859-2 or Windows-1250 +// + special letter values changing +// Normal value: 196, 214, 220, 223, 228, 246, 252 +// Prince change: 131, 132, 133, 127, 128, 129, 130 +char invOptionsTextDE[5][17] = { + "Anschauen", + "Benutzen", + "\x84""ffnen/Sto\x7f""en", + "Schlie\x7f""en/Ziehen", + "Geben" +}; + +const char optionsTextDE[7][17] = { + "Hingehen", + "Anschauen", + "Wegnehmen", + "Benutzen", + "\x84""ffnen/Sto\x7f""en", + "Schlie\x7f""en/Ziehen", + "Ansprechen" +}; + +// EN +const char *invOptionsTextEN[] = { + "Examine", + "Use", + "Open/Push", + "Close/Pull", + "Give" +}; + +const char *optionsTextEN[] = { + "Walk to", + "Examine", + "Pick up", + "Use", + "Open/Push", + "Close/Pull", + "Talk to" +}; + +} // End of namespace Prince diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp new file mode 100644 index 0000000000..55f12a6560 --- /dev/null +++ b/engines/prince/prince.cpp @@ -0,0 +1,4846 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" +#include "common/config-manager.h" +#include "common/debug-channels.h" +#include "common/debug.h" +#include "common/events.h" +#include "common/file.h" +#include "common/random.h" +#include "common/fs.h" +#include "common/keyboard.h" +#include "common/substream.h" +#include "common/str.h" + +#include "graphics/cursorman.h" +#include "graphics/surface.h" +#include "graphics/palette.h" +#include "graphics/pixelformat.h" + +#include "engines/util.h" +#include "engines/advancedDetector.h" + +#include "audio/audiostream.h" + +#include "prince/prince.h" +#include "prince/font.h" +#include "prince/graphics.h" +#include "prince/script.h" +#include "prince/debugger.h" +#include "prince/object.h" +#include "prince/mob.h" +#include "prince/sound.h" +#include "prince/variatxt.h" +#include "prince/flags.h" +#include "prince/font.h" +#include "prince/mhwanh.h" +#include "prince/cursor.h" +#include "prince/archive.h" +#include "prince/hero.h" +#include "prince/resource.h" +#include "prince/animation.h" +#include "prince/option_text.h" +#include "prince/curve_values.h" +#include "prince/detection.h" + +namespace Prince { + +void PrinceEngine::debugEngine(const char *s, ...) { + char buf[STRINGBUFLEN]; + va_list va; + + va_start(va, s); + vsnprintf(buf, STRINGBUFLEN, s, va); + va_end(va); + + debug("Prince::Engine %s", buf); +} + +PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) : + Engine(syst), _gameDescription(gameDesc), _graph(nullptr), _script(nullptr), _interpreter(nullptr), _flags(nullptr), + _locationNr(0), _debugger(nullptr), _midiPlayer(nullptr), _room(nullptr), + _cursor1(nullptr), _cursor2(nullptr), _cursor3(nullptr), _font(nullptr), + _suitcaseBmp(nullptr), _roomBmp(nullptr), _cursorNr(0), _picWindowX(0), _picWindowY(0), _randomSource("prince"), + _invLineX(134), _invLineY(176), _invLine(5), _invLines(3), _invLineW(70), _invLineH(76), _maxInvW(72), _maxInvH(76), + _invLineSkipX(2), _invLineSkipY(3), _showInventoryFlag(false), _inventoryBackgroundRemember(false), + _mst_shadow(0), _mst_shadow2(0), _candleCounter(0), _invX1(53), _invY1(18), _invWidth(536), _invHeight(438), + _invCurInside(false), _optionsFlag(false), _optionEnabled(0), _invExamY(120), _invMaxCount(2), _invCounter(0), + _optionsMob(-1), _currentPointerNumber(1), _selectedMob(-1), _selectedItem(0), _selectedMode(0), + _optionsWidth(210), _optionsHeight(170), _invOptionsWidth(210), _invOptionsHeight(130), _optionsStep(20), + _invOptionsStep(20), _optionsNumber(7), _invOptionsNumber(5), _optionsColor1(236), _optionsColor2(252), + _dialogWidth(600), _dialogHeight(0), _dialogLineSpace(10), _dialogColor1(220), _dialogColor2(223), + _dialogFlag(false), _dialogLines(0), _dialogText(nullptr), _mouseFlag(1), + _roomPathBitmap(nullptr), _roomPathBitmapTemp(nullptr), _coordsBufEnd(nullptr), _coordsBuf(nullptr), _coords(nullptr), + _traceLineLen(0), _rembBitmapTemp(nullptr), _rembBitmap(nullptr), _rembMask(0), _rembX(0), _rembY(0), _fpX(0), _fpY(0), + _checkBitmapTemp(nullptr), _checkBitmap(nullptr), _checkMask(0), _checkX(0), _checkY(0), _traceLineFirstPointFlag(false), + _tracePointFirstPointFlag(false), _coordsBuf2(nullptr), _coords2(nullptr), _coordsBuf3(nullptr), _coords3(nullptr), + _shanLen(0), _directionTable(nullptr), _currentMidi(0), _lightX(0), _lightY(0), _curveData(nullptr), _curvPos(0), + _creditsData(nullptr), _creditsDataSize(0), _currentTime(0), _zoomBitmap(nullptr), _shadowBitmap(nullptr), _transTable(nullptr), + _flcFrameSurface(nullptr), _shadScaleValue(0), _shadLineLen(0), _scaleValue(0), _dialogImage(nullptr), _mobTranslationData(nullptr), + _mobTranslationSize(0) { + + // Debug/console setup + DebugMan.addDebugChannel(DebugChannel::kScript, "script", "Prince Script debug channel"); + DebugMan.addDebugChannel(DebugChannel::kEngine, "engine", "Prince Engine debug channel"); + + DebugMan.enableDebugChannel("script"); + + memset(_audioStream, 0, sizeof(_audioStream)); + + gDebugLevel = 10; +} + +PrinceEngine::~PrinceEngine() { + DebugMan.clearAllDebugChannels(); + + delete _rnd; + delete _debugger; + delete _cursor1; + delete _cursor3; + delete _midiPlayer; + delete _script; + delete _flags; + delete _interpreter; + delete _font; + delete _roomBmp; + delete _suitcaseBmp; + delete _variaTxt; + free(_talkTxt); + free(_invTxt); + free(_dialogDat); + delete _graph; + delete _room; + + if (_cursor2 != nullptr) { + _cursor2->free(); + delete _cursor2; + } + + for (uint i = 0; i < _objList.size(); i++) { + delete _objList[i]; + } + _objList.clear(); + + free(_objSlot); + + for (uint32 i = 0; i < _pscrList.size(); i++) { + delete _pscrList[i]; + } + _pscrList.clear(); + + for (uint i = 0; i < _maskList.size(); i++) { + free(_maskList[i]._data); + } + _maskList.clear(); + + _drawNodeList.clear(); + + clearBackAnimList(); + _backAnimList.clear(); + + freeAllNormAnims(); + _normAnimList.clear(); + + for (uint i = 0; i < _allInvList.size(); i++) { + _allInvList[i]._surface->free(); + delete _allInvList[i]._surface; + } + _allInvList.clear(); + + _optionsPic->free(); + delete _optionsPic; + + _optionsPicInInventory->free(); + delete _optionsPicInInventory; + + for (uint i = 0; i < _mainHero->_moveSet.size(); i++) { + delete _mainHero->_moveSet[i]; + } + + for (uint i = 0; i < _secondHero->_moveSet.size(); i++) { + delete _secondHero->_moveSet[i]; + } + + delete _mainHero; + delete _secondHero; + + free(_roomPathBitmap); + free(_roomPathBitmapTemp); + free(_coordsBuf); + + _mobPriorityList.clear(); + + freeAllSamples(); + + free(_zoomBitmap); + free(_shadowBitmap); + free(_transTable); + + free(_curveData); + + free(_shadowLine); + + free(_creditsData); + + if (_dialogImage != nullptr) { + _dialogImage->free(); + delete _dialogImage; + } + + free(_mobTranslationData); +} + +GUI::Debugger *PrinceEngine::getDebugger() { + return _debugger; +} + +void PrinceEngine::init() { + + const Common::FSNode gameDataDir(ConfMan.get("path")); + + debugEngine("Adding all path: %s", gameDataDir.getPath().c_str()); + + PtcArchive *all = new PtcArchive(); + if (!all->open("all/databank.ptc")) + error("Can't open all/databank.ptc"); + + PtcArchive *voices = new PtcArchive(); + if (!voices->open("data/voices/databank.ptc")) + error("Can't open data/voices/databank.ptc"); + + PtcArchive *sound = new PtcArchive(); + if (!sound->open("sound/databank.ptc")) + error("Can't open sound/databank.ptc"); + + PtcArchive *translation = new PtcArchive(); + if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) { + if (!translation->openTranslation("all/prince_translation.dat")) + error("Can't open prince_translation.dat"); + } + + SearchMan.addSubDirectoryMatching(gameDataDir, "all"); + + SearchMan.add("all", all); + SearchMan.add("voices", voices); + SearchMan.add("sound", sound); + if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) { + SearchMan.add("translation", translation); + } + + _graph = new GraphicsMan(this); + + _rnd = new Common::RandomSource("prince"); + + _midiPlayer = new MusicPlayer(this); + + if (getLanguage() == Common::DE_DEU) { + _font = new Font(); + Resource::loadResource(_font, "font3.raw", true); + } else { + _font = new Font(); + Resource::loadResource(_font, "font1.raw", true); + } + + _suitcaseBmp = new MhwanhDecoder(); + Resource::loadResource(_suitcaseBmp, "walizka", true); + + _script = new Script(this); + Resource::loadResource(_script, "skrypt.dat", true); + + _flags = new InterpreterFlags(); + _interpreter = new Interpreter(this, _script, _flags); + + _debugger = new Debugger(this, _flags); + + _variaTxt = new VariaTxt(); + if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) { + Resource::loadResource(_variaTxt, "variatxt.dat", true); + } else { + Resource::loadResource(_variaTxt, "variatxt_translate.dat", true); + } + + _cursor1 = new Cursor(); + Resource::loadResource(_cursor1, "mouse1.cur", true); + + _cursor3 = new Cursor(); + Resource::loadResource(_cursor3, "mouse2.cur", true); + + Common::SeekableReadStream *talkTxtStream; + if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) { + talkTxtStream = SearchMan.createReadStreamForMember("talktxt.dat"); + } else { + talkTxtStream = SearchMan.createReadStreamForMember("talktxt_translate.dat"); + } + if (!talkTxtStream) { + error("Can't load talkTxtStream"); + return; + } + _talkTxtSize = talkTxtStream->size(); + _talkTxt = (byte *)malloc(_talkTxtSize); + talkTxtStream->read(_talkTxt, _talkTxtSize); + + delete talkTxtStream; + + Common::SeekableReadStream *invTxtStream; + if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) { + invTxtStream = SearchMan.createReadStreamForMember("invtxt.dat"); + } else { + invTxtStream = SearchMan.createReadStreamForMember("invtxt_translate.dat"); + } + if (!invTxtStream) { + error("Can't load invTxtStream"); + return; + } + _invTxtSize = invTxtStream->size(); + _invTxt = (byte *)malloc(_invTxtSize); + invTxtStream->read(_invTxt, _invTxtSize); + + delete invTxtStream; + + loadAllInv(); + + Common::SeekableReadStream *dialogDatStream = SearchMan.createReadStreamForMember("dialog.dat"); + if (!dialogDatStream) { + error("Can't load dialogDatStream"); + return; + } + _dialogDatSize = dialogDatStream->size(); + _dialogDat = (byte *)malloc(_dialogDatSize); + dialogDatStream->read(_dialogDat, _dialogDatSize); + + delete dialogDatStream; + + _optionsPic = new Graphics::Surface(); + _optionsPic->create(_optionsWidth, _optionsHeight, Graphics::PixelFormat::createFormatCLUT8()); + Common::Rect picRect(0, 0, _optionsWidth, _optionsHeight); + _optionsPic->fillRect(picRect, _graph->kShadowColor); + + _optionsPicInInventory = new Graphics::Surface(); + _optionsPicInInventory->create(_invOptionsWidth, _invOptionsHeight, Graphics::PixelFormat::createFormatCLUT8()); + Common::Rect invPicRect(0, 0, _invOptionsWidth, _invOptionsHeight); + _optionsPicInInventory->fillRect(invPicRect, _graph->kShadowColor); + + _roomBmp = new Image::BitmapDecoder(); + + _room = new Room(); + + _mainHero = new Hero(this, _graph); + _secondHero = new Hero(this, _graph); + _secondHero->_maxBoredom = 140; + _secondHero->loadAnimSet(3); + + _roomPathBitmap = (byte *)malloc(kPathBitmapLen); + _roomPathBitmapTemp = (byte *)malloc(kPathBitmapLen); + _coordsBuf = (byte *)malloc(kTracePts * 4); + _coords = _coordsBuf; + _coordsBufEnd = _coordsBuf + kTracePts * 4 - 4; + + BackgroundAnim tempBackAnim; + tempBackAnim._seq._currRelative = 0; + for (int i = 0; i < kMaxBackAnims; i++) { + _backAnimList.push_back(tempBackAnim); + } + + Anim tempAnim; + tempAnim._animData = nullptr; + tempAnim._shadowData = nullptr; + for (int i = 0; i < kMaxNormAnims; i++) { + _normAnimList.push_back(tempAnim); + } + + _objSlot = (uint16 *)malloc(kMaxObjects * sizeof(uint16)); + for (int i = 0; i < kMaxObjects; i++) { + _objSlot[i] = 0xFF; + } + + _zoomBitmap = (byte *)malloc(kZoomBitmapLen); + _shadowBitmap = (byte *)malloc(2 * kShadowBitmapSize); + _transTable = (byte *)malloc(kTransTableSize); + + _curveData = (int16 *)malloc(2 * kCurveLen * sizeof(int16)); + + _shadowLine = (byte *)malloc(kShadowLineArraySize); + + Common::SeekableReadStream *creditsDataStream; + if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) { + creditsDataStream = SearchMan.createReadStreamForMember("credits.dat"); + } else { + creditsDataStream = SearchMan.createReadStreamForMember("credits_translate.dat"); + } + if (!creditsDataStream) { + error("Can't load creditsDataStream"); + return; + } + _creditsDataSize = creditsDataStream->size(); + _creditsData = (byte *)malloc(_creditsDataSize); + creditsDataStream->read(_creditsData, _creditsDataSize); + delete creditsDataStream; + + if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) { + loadMobTranslationTexts(); + } +} + +void PrinceEngine::showLogo() { + MhwanhDecoder logo; + if (Resource::loadResource(&logo, "logo.raw", true)) { + loadSample(0, "LOGO.WAV"); + playSample(0, 0); + _graph->draw(_graph->_frontScreen, logo.getSurface()); + _graph->change(); + _graph->update(_graph->_frontScreen); + setPalette(logo.getPalette()); + + uint32 logoStart = _system->getMillis(); + while (_system->getMillis() < logoStart + 5000) { + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + if (event.kbd.keycode == Common::KEYCODE_ESCAPE) { + stopSample(0); + return; + } + break; + case Common::EVENT_LBUTTONDOWN: + stopSample(0); + return; + default: + break; + } + } + + if (shouldQuit()) { + return; + } + } + } +} + +Common::Error PrinceEngine::run() { + syncSoundSettings(); + int startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1; + init(); + if (startGameSlot == -1) { + showLogo(); + } else { + loadLocation(59); // load intro location - easiest way to set everything up + loadGame(startGameSlot); + } + mainLoop(); + return Common::kNoError; +} + +void PrinceEngine::pauseEngineIntern(bool pause) { + Engine::pauseEngineIntern(pause); + if (pause) { + _midiPlayer->pause(); + } + else { + _midiPlayer->resume(); + } +} + +bool AnimListItem::loadFromStream(Common::SeekableReadStream &stream) { + int32 pos = stream.pos(); + + uint16 type = stream.readUint16LE(); + if (type == 0xFFFF) { + return false; + } + _type = type; + _fileNumber = stream.readUint16LE(); + _startPhase = stream.readUint16LE(); + _endPhase = stream.readUint16LE(); + _loopPhase = stream.readUint16LE(); + _x = stream.readSint16LE(); + _y = stream.readSint16LE(); + _loopType = stream.readUint16LE(); + _nextAnim = stream.readUint16LE(); + _flags = stream.readUint16LE(); + + //debug("AnimListItem type %d, fileNumber %d, x %d, y %d, flags %d", _type, _fileNumber, _x, _y, _flags); + //debug("startPhase %d, endPhase %d, loopPhase %d", _startPhase, _endPhase, _loopPhase); + + // 32 byte aligment + stream.seek(pos + 32); + + return true; +} + +bool PrinceEngine::loadLocation(uint16 locationNr) { + + blackPalette(); + + _flicPlayer.close(); + + memset(_textSlots, 0, sizeof(_textSlots)); + freeAllSamples(); + + debugEngine("PrinceEngine::loadLocation %d", locationNr); + const Common::FSNode gameDataDir(ConfMan.get("path")); + SearchMan.remove(Common::String::format("%02d", _locationNr)); + + _locationNr = locationNr; + _debugger->_locationNr = locationNr; + + _flags->setFlagValue(Flags::CURRROOM, _locationNr); + _interpreter->stopBg(); + + changeCursor(0); + + const Common::String locationNrStr = Common::String::format("%02d", _locationNr); + debugEngine("loadLocation %s", locationNrStr.c_str()); + + PtcArchive *locationArchive = new PtcArchive(); + if (!locationArchive->open(locationNrStr + "/databank.ptc")) + error("Can't open location %s", locationNrStr.c_str()); + + SearchMan.add(locationNrStr, locationArchive); + + loadMusic(_locationNr); + + // load location background, replace old one + Resource::loadResource(_roomBmp, "room", true); + if (_roomBmp->getSurface()) { + _sceneWidth = _roomBmp->getSurface()->w; + } + + loadZoom(_zoomBitmap, kZoomBitmapLen, "zoom"); + loadShadow(_shadowBitmap, kShadowBitmapSize, "shadow", "shadow2"); + loadTrans(_transTable, "trans"); + loadPath("path"); + + for (uint32 i = 0; i < _pscrList.size(); i++) { + delete _pscrList[i]; + } + _pscrList.clear(); + Resource::loadResource(_pscrList, "pscr.lst", false); + + loadMobPriority("mobpri"); + + _mobList.clear(); + if (getGameType() == kPrinceDataDE) { + const Common::String mobLstName = Common::String::format("mob%02d.lst", _locationNr); + debug("name: %s", mobLstName.c_str()); + Resource::loadResource(_mobList, mobLstName.c_str(), false); + } else if (getGameType() == kPrinceDataPL) { + Resource::loadResource(_mobList, "mob.lst", false); + } + if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) { + // update Mob texts for translated version + setMobTranslationTexts(); + } + + _animList.clear(); + Resource::loadResource(_animList, "anim.lst", false); + + for (uint32 i = 0; i < _objList.size(); i++) { + delete _objList[i]; + } + _objList.clear(); + Resource::loadResource(_objList, "obj.lst", false); + + _room->loadRoom(_script->getRoomOffset(_locationNr)); + + for (uint i = 0; i < _maskList.size(); i++) { + free(_maskList[i]._data); + } + _maskList.clear(); + _script->loadAllMasks(_maskList, _room->_nak); + + _picWindowX = 0; + + _lightX = _script->getLightX(_locationNr); + _lightY = _script->getLightY(_locationNr); + setShadowScale(_script->getShadowScale(_locationNr)); + + for (uint i = 0; i < _mobList.size(); i++) { + _mobList[i]._visible = _script->getMobVisible(_room->_mobs, i); + } + + _script->installObjects(_room->_obj); + + freeAllNormAnims(); + + clearBackAnimList(); + _script->installBackAnims(_backAnimList, _room->_backAnim); + + _graph->makeShadowTable(70, _graph->_shadowTable70); + _graph->makeShadowTable(50, _graph->_shadowTable50); + + _mainHero->freeOldMove(); + _secondHero->freeOldMove(); + + _mainHero->scrollHero(); + + return true; +} + +void PrinceEngine::setShadowScale(int32 shadowScale) { + shadowScale = 100 - shadowScale; + if (!shadowScale) { + _shadScaleValue = 10000; + } else { + _shadScaleValue = 10000 / shadowScale; + } +} + +void PrinceEngine::plotShadowLinePoint(int x, int y, int color, void *data) { + PrinceEngine *vm = (PrinceEngine *)data; + WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4], x); + WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4 + 2], y); + vm->_shadLineLen++; +} + +void PrinceEngine::changeCursor(uint16 curId) { + _debugger->_cursorNr = curId; + _mouseFlag = curId; + _flags->setFlagValue(Flags::MOUSEENABLED, curId); + + const Graphics::Surface *curSurface = nullptr; + + switch (curId) { + case 0: + CursorMan.showMouse(false); + _optionsFlag = 0; + _selectedMob = -1; + return; + case 1: + curSurface = _cursor1->getSurface(); + break; + case 2: + curSurface = _cursor2; + break; + case 3: + curSurface = _cursor3->getSurface(); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639); + mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170); + _system->warpMouse(mousePos.x, mousePos.y); + break; + } + + CursorMan.replaceCursorPalette(_roomBmp->getPalette(), 0, 255); + CursorMan.replaceCursor( + curSurface->getBasePtr(0, 0), + curSurface->w, curSurface->h, + 0, 0, + 255, false, + &curSurface->format + ); + CursorMan.showMouse(true); +} + +void PrinceEngine::makeInvCursor(int itemNr) { + const Graphics::Surface *cur1Surface = _cursor1->getSurface(); + int cur1W = cur1Surface->w; + int cur1H = cur1Surface->h; + const Common::Rect cur1Rect(0, 0, cur1W, cur1H); + + const Graphics::Surface *itemSurface = _allInvList[itemNr].getSurface(); + int itemW = itemSurface->w; + int itemH = itemSurface->h; + + int cur2W = cur1W + itemW / 2; + int cur2H = cur1H + itemH / 2; + + if (_cursor2 != nullptr) { + _cursor2->free(); + delete _cursor2; + } + _cursor2 = new Graphics::Surface(); + _cursor2->create(cur2W, cur2H, Graphics::PixelFormat::createFormatCLUT8()); + Common::Rect cur2Rect(0, 0, cur2W, cur2H); + _cursor2->fillRect(cur2Rect, 255); + _cursor2->copyRectToSurface(*cur1Surface, 0, 0, cur1Rect); + + const byte *src1 = (const byte *)itemSurface->getBasePtr(0, 0); + byte *dst1 = (byte *)_cursor2->getBasePtr(cur1W, cur1H); + + if (itemH % 2) { + itemH--; + } + if (itemW % 2) { + itemW--; + } + + for (int y = 0; y < itemH; y++) { + const byte *src2 = src1; + byte *dst2 = dst1; + if (y % 2 == 0) { + for (int x = 0; x < itemW; x++, src2++) { + if (x % 2 == 0) { + if (*src2) { + *dst2 = *src2; + } else { + *dst2 = 255; + } + dst2++; + } + } + dst1 += _cursor2->pitch; + } + src1 += itemSurface->pitch; + } +} + +bool PrinceEngine::loadMusic(int musNumber) { + uint8 midiNumber = MusicPlayer::_musRoomTable[musNumber]; + if (midiNumber) { + if (midiNumber != 100) { + if (_currentMidi != midiNumber) { + _currentMidi = midiNumber; + const char *musName = MusicPlayer::_musTable[_currentMidi]; + _midiPlayer->loadMidi(musName); + } + } + } else { + stopMusic(); + } + return true; +} + +void PrinceEngine::stopMusic() { + if (_midiPlayer->isPlaying()) { + _midiPlayer->stop(); + } +} + +bool PrinceEngine::playNextFLCFrame() { + if (!_flicPlayer.isVideoLoaded()) + return false; + + const Graphics::Surface *s = _flicPlayer.decodeNextFrame(); + if (s) { + _graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, s, 255); + _graph->change(); + _flcFrameSurface = s; + } else if (_flicLooped) { + _flicPlayer.rewind(); + playNextFLCFrame(); + } else if (_flcFrameSurface) { + _graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, _flcFrameSurface, 255); + _graph->change(); + } + + return true; +} + +void PrinceEngine::playSample(uint16 sampleId, uint16 loopType) { + if (_audioStream[sampleId]) { + if (_mixer->isSoundIDActive(sampleId)) { + return; + } + _audioStream[sampleId]->rewind(); + if (sampleId < 28) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + } else { + _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); + } + } +} + +void PrinceEngine::stopSample(uint16 sampleId) { + _mixer->stopID(sampleId); +} + +void PrinceEngine::stopAllSamples() { + _mixer->stopAll(); +} + +void PrinceEngine::freeSample(uint16 sampleId) { + stopSample(sampleId); + if (_audioStream[sampleId] != nullptr) { + delete _audioStream[sampleId]; + _audioStream[sampleId] = nullptr; + } +} + +void PrinceEngine::freeAllSamples() { + for (int sampleId = 0; sampleId < kMaxSamples; sampleId++) { + freeSample(sampleId); + } +} + +bool PrinceEngine::loadSample(uint32 sampleSlot, const Common::String &streamName) { + // FIXME: This is just a workaround streamName is a path + // SOUND\\SCIERKA1.WAV for now only last path component is used + Common::String normalizedPath = lastPathComponent(streamName, '\\'); + + // WALKAROUND: Wrong name in script, not existing sound in data files + if (!normalizedPath.compareTo("9997BEKA.WAV")) { + return 0; + } + + debugEngine("loadSample slot %d, name %s", sampleSlot, normalizedPath.c_str()); + + freeSample(sampleSlot); + Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(normalizedPath); + if (sampleStream == nullptr) { + delete sampleStream; + error("Can't load sample %s to slot %d", normalizedPath.c_str(), sampleSlot); + } + _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO); + delete sampleStream; + return true; +} + +bool PrinceEngine::loadVoice(uint32 slot, uint32 sampleSlot, const Common::String &streamName) { + debugEngine("Loading wav %s slot %d", streamName.c_str(), slot); + + if (slot >= kMaxTexts) { + error("Text slot bigger than MAXTEXTS %d", kMaxTexts - 1); + return false; + } + + freeSample(sampleSlot); + Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(streamName); + if (sampleStream == nullptr) { + debug("Can't open %s", streamName.c_str()); + return false; + } + + uint32 id = sampleStream->readUint32LE(); + if (id != MKTAG('F', 'F', 'I', 'R')) { + error("It's not RIFF file %s", streamName.c_str()); + return false; + } + + sampleStream->skip(0x20); + id = sampleStream->readUint32LE(); + if (id != MKTAG('a', 't', 'a', 'd')) { + error("No data section in %s id %04x", streamName.c_str(), id); + return false; + } + + id = sampleStream->readUint32LE(); + debugEngine("SetVoice slot %d time %04x", slot, id); + id <<= 3; + id /= 22050; + id += 2; + + _textSlots[slot]._time = id; + if (!slot) { + _mainHero->_talkTime = id; + } else if (slot == 1) { + _secondHero->_talkTime = id; + } + + debugEngine("SetVoice slot %d time %04x", slot, id); + sampleStream->seek(SEEK_SET); + _audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO); + delete sampleStream; + return true; +} + +void PrinceEngine::setVoice(uint16 slot, uint32 sampleSlot, uint16 flag) { + Common::String sampleName; + uint32 currentString = _interpreter->getCurrentString(); + + if (currentString >= 80000) { + uint32 nr = currentString - 80000; + sampleName = Common::String::format("%02d0%02d-%02d.WAV", nr / 100, nr % 100, flag); + } else if (currentString >= 70000) { + sampleName = Common::String::format("inv%02d-01.WAV", currentString - 70000); + } else if (currentString >= 60000) { + sampleName = Common::String::format("M%04d-%02d.WAV", currentString - 60000, flag); + } else if (currentString >= 2000) { + return; + } else if (flag >= 100) { + sampleName = Common::String::format("%03d-%03d.WAV", currentString, flag); + } else { + sampleName = Common::String::format("%03d-%02d.WAV", currentString, flag); + } + + loadVoice(slot, sampleSlot, sampleName); +} + +bool PrinceEngine::loadAnim(uint16 animNr, bool loop) { + Common::String streamName = Common::String::format("AN%02d", animNr); + Common::SeekableReadStream *flicStream = SearchMan.createReadStreamForMember(streamName); + + if (!flicStream) { + error("Can't open %s", streamName.c_str()); + return false; + } + + if (!_flicPlayer.loadStream(flicStream)) { + error("Can't load flic stream %s", streamName.c_str()); + } + + debugEngine("%s loaded", streamName.c_str()); + _flicLooped = loop; + _flicPlayer.start(); + playNextFLCFrame(); + return true; +} + +bool PrinceEngine::loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + return false; + } + if (stream->read(zoomBitmap, dataSize) != dataSize) { + free(zoomBitmap); + delete stream; + return false; + } + delete stream; + return true; +} + +bool PrinceEngine::loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName1); + if (!stream) { + delete stream; + return false; + } + + if (stream->read(shadowBitmap, dataSize) != dataSize) { + free(shadowBitmap); + delete stream; + return false; + } + + Common::SeekableReadStream *stream2 = SearchMan.createReadStreamForMember(resourceName2); + if (!stream2) { + delete stream; + delete stream2; + return false; + } + + byte *shadowBitmap2 = shadowBitmap + dataSize; + if (stream2->read(shadowBitmap2, dataSize) != dataSize) { + free(shadowBitmap); + delete stream; + delete stream2; + return false; + } + + delete stream; + delete stream2; + return true; +} + +bool PrinceEngine::loadTrans(byte *transTable, const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + transTable[i * 256 + j] = j; + } + } + return true; + } + if (stream->read(transTable, kTransTableSize) != kTransTableSize) { + delete stream; + return false; + } + delete stream; + return true; +} + +bool PrinceEngine::loadPath(const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + return false; + } + if (stream->read(_roomPathBitmap, kPathBitmapLen) != kPathBitmapLen) { + delete stream; + return false; + } + delete stream; + return true; +} + +bool PrinceEngine::loadAllInv() { + for (int i = 0; i < kMaxInv; i++) { + InvItem tempInvItem; + + const Common::String invStreamName = Common::String::format("INV%02d", i); + Common::SeekableReadStream *invStream = SearchMan.createReadStreamForMember(invStreamName); + if (!invStream) { + delete invStream; + return true; + } + + tempInvItem._x = invStream->readUint16LE(); + tempInvItem._y = invStream->readUint16LE(); + int width = invStream->readUint16LE(); + int height = invStream->readUint16LE(); + tempInvItem._surface = new Graphics::Surface(); + tempInvItem._surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + for (int h = 0; h < tempInvItem._surface->h; h++) { + invStream->read(tempInvItem._surface->getBasePtr(0, h), tempInvItem._surface->w); + } + + _allInvList.push_back(tempInvItem); + delete invStream; + } + + return true; +} + +bool PrinceEngine::loadMobPriority(const char *resourceName) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName); + if (!stream) { + delete stream; + return false; + } + + _mobPriorityList.clear(); + uint mobId; + while (1) { + mobId = stream->readUint32LE(); + if (mobId == 0xFFFFFFFF) { + break; + } + _mobPriorityList.push_back(mobId); + } + delete stream; + return true; +} + +void PrinceEngine::loadMobTranslationTexts() { + Common::SeekableReadStream *mobTranslationStream = SearchMan.createReadStreamForMember("mob_translate.dat"); + if (!mobTranslationStream) { + error("Can't load mob_translate.dat"); + } + _mobTranslationSize = mobTranslationStream->size(); + _mobTranslationData = (byte *)malloc(_mobTranslationSize); + mobTranslationStream->read(_mobTranslationData, _mobTranslationSize); + delete mobTranslationStream; +} + +void PrinceEngine::setMobTranslationTexts() { + int locationOffset = READ_UINT16(_mobTranslationData + (_locationNr - 1) * 2); + if (locationOffset) { + byte *locationText = _mobTranslationData + locationOffset; + for (uint i = 0; i < _mobList.size(); i++) { + byte c; + locationText++; + _mobList[i]._name.clear(); + while ((c = *locationText)) { + _mobList[i]._name += c; + locationText++; + } + locationText++; + _mobList[i]._examText.clear(); + c = *locationText; + locationText++; + if (c) { + _mobList[i]._examText += c; + do { + c = *locationText; + _mobList[i]._examText += c; + locationText++; + } while (c != 255); + } + } + } +} + +void PrinceEngine::keyHandler(Common::Event event) { + uint16 nChar = event.kbd.keycode; + switch (nChar) { + case Common::KEYCODE_d: + if (event.kbd.hasFlags(Common::KBD_CTRL)) { + getDebugger()->attach(); + } + break; + case Common::KEYCODE_z: + if (_flags->getFlagValue(Flags::POWERENABLED)) { + _flags->setFlagValue(Flags::MBFLAG, 1); + } + break; + case Common::KEYCODE_x: + if (_flags->getFlagValue(Flags::POWERENABLED)) { + _flags->setFlagValue(Flags::MBFLAG, 2); + } + break; + case Common::KEYCODE_ESCAPE: + _flags->setFlagValue(Flags::ESCAPED2, 1); + break; + } +} + +int PrinceEngine::getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY) { + + Common::Point pointPos(posX, posY); + + int mobListSize; + if (usePriorityList) { + mobListSize = _mobPriorityList.size(); + } else { + mobListSize = mobList.size(); + } + + for (int mobNumber = 0; mobNumber < mobListSize; mobNumber++) { + Mob *mob = nullptr; + if (usePriorityList) { + mob = &mobList[_mobPriorityList[mobNumber]]; + } else { + mob = &mobList[mobNumber]; + } + + if (mob->_visible) { + continue; + } + + int type = mob->_type & 7; + switch (type) { + case 0: + case 1: + //normal_mob + if (!mob->_rect.contains(pointPos)) { + continue; + } + break; + case 3: + //mob_obj + if (mob->_mask < kMaxObjects) { + int nr = _objSlot[mob->_mask]; + if (nr != 0xFF) { + Object &obj = *_objList[nr]; + Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height); + if (objectRect.contains(pointPos)) { + Graphics::Surface *objSurface = obj.getSurface(); + byte *pixel = (byte *)objSurface->getBasePtr(posX - obj._x, posY - obj._y); + if (*pixel != 255) { + break; + } + } + } + } + continue; + break; + case 2: + case 5: + //check_ba_mob + if (!_backAnimList[mob->_mask].backAnims.empty()) { + int currentAnim = _backAnimList[mob->_mask]._seq._currRelative; + Anim &backAnim = _backAnimList[mob->_mask].backAnims[currentAnim]; + if (backAnim._animData != nullptr) { + if (!backAnim._state) { + Common::Rect backAnimRect(backAnim._currX, backAnim._currY, backAnim._currX + backAnim._currW, backAnim._currY + backAnim._currH); + if (backAnimRect.contains(pointPos)) { + int phase = backAnim._showFrame; + int phaseFrameIndex = backAnim._animData->getPhaseFrameIndex(phase); + Graphics::Surface *backAnimSurface = backAnim._animData->getFrame(phaseFrameIndex); + byte pixel = *(byte *)backAnimSurface->getBasePtr(posX - backAnim._currX, posY - backAnim._currY); + if (pixel != 255) { + if (type == 5) { + if (mob->_rect.contains(pointPos)) { + break; + } + } else { + break; + } + } + } + } + } + } + continue; + break; + default: + //not_part_ba + continue; + break; + } + + if (usePriorityList) { + return _mobPriorityList[mobNumber]; + } else { + return mobNumber; + } + } + return -1; +} + +int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList) { + if (_mouseFlag == 0 || _mouseFlag == 3) { + return -1; + } + Common::Point mousePos = _system->getEventManager()->getMousePos(); + int mobNumber = getMob(mobList, usePriorityList, mousePos.x + _picWindowX, mousePos.y); + + if (mobNumber != -1) { + Common::String mobName = mobList[mobNumber]._name; + + if (getLanguage() == Common::DE_DEU) { + for (uint i = 0; i < mobName.size(); i++) { + switch (mobName[i]) { + case '\xc4': + mobName.setChar('\x83', i); + break; + case '\xd6': + mobName.setChar('\x84', i); + break; + case '\xdc': + mobName.setChar('\x85', i); + break; + case '\xdf': + mobName.setChar('\x7f', i); + break; + case '\xe4': + mobName.setChar('\x80', i); + break; + case '\xf6': + mobName.setChar('\x81', i); + break; + case '\xfc': + mobName.setChar('\x82', i); + break; + } + } + } + + uint16 textW = getTextWidth(mobName.c_str()); + + uint16 x = mousePos.x - textW / 2; + if (x > screen->w) { + x = 0; + } + + if (x + textW > screen->w) { + x = screen->w - textW; + } + + uint16 y = mousePos.y - _font->getFontHeight(); + if (y > screen->h) { + y = _font->getFontHeight() - 2; + } + + _font->drawString(screen, mobName, x, y, screen->w, 216); + } + + return mobNumber; +} + +void PrinceEngine::printAt(uint32 slot, uint8 color, char *s, uint16 x, uint16 y) { + debugC(1, DebugChannel::kEngine, "PrinceEngine::printAt slot %d, color %d, x %02d, y %02d, str %s", slot, color, x, y, s); + + if (getLanguage() == Common::DE_DEU) + correctStringDEU(s); + + Text &text = _textSlots[slot]; + text._str = s; + text._x = x; + text._y = y; + text._color = color; + int lines = calcTextLines(s); + text._time = calcTextTime(lines); +} + +int PrinceEngine::calcTextLines(const char *s) { + int lines = 1; + while (*s) { + if (*s == '\n') { + lines++; + } + s++; + } + return lines; +} + +int PrinceEngine::calcTextTime(int numberOfLines) { + return numberOfLines * 30; +} + +void PrinceEngine::correctStringDEU(char *s) { + while (*s) { + switch (*s) { + case '\xc4': + *s = '\x83'; + break; + case '\xd6': + *s = '\x84'; + break; + case '\xdc': + *s = '\x85'; + break; + case '\xdf': + *s = '\x7f'; + break; + case '\xe4': + *s = '\x80'; + break; + case '\xf6': + *s = '\x81'; + break; + case '\xfc': + *s = '\x82'; + break; + } + s++; + } +} + +uint32 PrinceEngine::getTextWidth(const char *s) { + uint16 textW = 0; + while (*s) { + textW += _font->getCharWidth(*s) + _font->getKerningOffset(0, 0); + s++; + } + return textW; +} + +void PrinceEngine::showTexts(Graphics::Surface *screen) { + for (uint32 slot = 0; slot < kMaxTexts; slot++) { + + if (_showInventoryFlag && slot) { + // only slot 0 for inventory + break; + } + + Text& text = _textSlots[slot]; + if (!text._str && !text._time) { + continue; + } + + int x = text._x; + int y = text._y; + + if (!_showInventoryFlag) { + x -= _picWindowX; + y -= _picWindowY; + } + + Common::Array<Common::String> lines; + _font->wordWrapText(text._str, _graph->_frontScreen->w, lines); + + int wideLine = 0; + for (uint i = 0; i < lines.size(); i++) { + int textLen = getTextWidth(lines[i].c_str()); + if (textLen > wideLine) { + wideLine = textLen; + } + } + + int leftBorderText = 6; + if (x + wideLine / 2 > kNormalWidth - leftBorderText) { + x = kNormalWidth - leftBorderText - wideLine / 2; + } + + if (x - wideLine / 2 < leftBorderText) { + x = leftBorderText + wideLine / 2; + } + + int textSkip = 2; + for (uint i = 0; i < lines.size(); i++) { + int drawX = x - getTextWidth(lines[i].c_str()) / 2; + int drawY = y - 10 - (lines.size() - i) * (_font->getFontHeight() - textSkip); + if (drawX < 0) { + drawX = 0; + } + if (drawY < 0) { + drawY = 0; + } + _font->drawString(screen, lines[i], drawX, drawY, screen->w, text._color); + } + + text._time--; + if (!text._time) { + text._str = nullptr; + } + } +} + +bool PrinceEngine::spriteCheck(int sprWidth, int sprHeight, int destX, int destY) { + destX -= _picWindowX; + destY -= _picWindowY; + + // if x1 is on visible part of screen + if (destX < 0) { + if (destX + sprWidth < 1) { + //x2 is negative - out of window + return false; + } + } + // if x1 is outside of screen on right side + if (destX >= kNormalWidth) { + return false; + } + + if (destY < 0) { + if (destY + sprHeight < 1) { + //y2 is negative - out of window + return false; + } + } + if (destY >= kNormalHeight) { + return false; + } + + return true; +} + +// CheckNak +void PrinceEngine::checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z) { + int x2 = x1 + sprWidth - 1; + int y2 = y1 + sprHeight - 1; + if (x1 < 0) { + x1 = 0; + } + for (uint i = 0; i < _maskList.size(); i++) { + if (!_maskList[i]._state && !_maskList[i]._flags) { + if (_maskList[i]._z > z) { + if (_maskList[i]._x1 <= x2 && _maskList[i]._x2 >= x1) { + if (_maskList[i]._y1 <= y2 && _maskList[i]._y2 >= y1) { + _maskList[i]._state = 1; + } + } + } + } + } +} + +// ClsNak +void PrinceEngine::clsMasks() { + for (uint i = 0; i < _maskList.size(); i++) { + if (_maskList[i]._state) { + _maskList[i]._state = 0; + } + } +} + +// InsertNakladki +void PrinceEngine::insertMasks(Graphics::Surface *originalRoomSurface) { + for (uint i = 0; i < _maskList.size(); i++) { + if (_maskList[i]._state) { + if (_maskList[i]._data != nullptr) { + showMask(i, originalRoomSurface); + } else { + error("insertMasks() - Wrong mask data- nr %d", i); + } + } + } +} + +// ShowNak +void PrinceEngine::showMask(int maskNr, Graphics::Surface *originalRoomSurface) { + if (!_maskList[maskNr]._flags) { + if (spriteCheck(_maskList[maskNr]._width, _maskList[maskNr]._height, _maskList[maskNr]._x1, _maskList[maskNr]._y1)) { + int destX = _maskList[maskNr]._x1 - _picWindowX; + int destY = _maskList[maskNr]._y1 - _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = _maskList[maskNr]._z; + newDrawNode.width = _maskList[maskNr]._width; + newDrawNode.height = _maskList[maskNr]._height; + newDrawNode.s = nullptr; + newDrawNode.originalRoomSurface = originalRoomSurface; + newDrawNode.data = _maskList[maskNr].getMask(); + newDrawNode.drawFunction = &_graph->drawMaskDrawNode; + _drawNodeList.push_back(newDrawNode); + } + } +} + +void PrinceEngine::showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ) { + if (spriteCheck(spriteSurface->w, spriteSurface->h, destX, destY)) { + destX -= _picWindowX; + destY -= _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = destZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.s = spriteSurface; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = _transTable; + newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; + _drawNodeList.push_back(newDrawNode); + } +} + +void PrinceEngine::showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ) { + if (spriteCheck(shadowSurface->w, shadowSurface->h, destX, destY)) { + destX -= _picWindowX; + destY -= _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = destZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.s = shadowSurface; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = _graph->_shadowTable70; + newDrawNode.drawFunction = &_graph->drawAsShadowDrawNode; + _drawNodeList.push_back(newDrawNode); + } +} + +void PrinceEngine::showAnim(Anim &anim) { + //ShowFrameCode + //ShowAnimFrame + int phase = anim._showFrame; + int phaseFrameIndex = anim._animData->getPhaseFrameIndex(phase); + int x = anim._x + anim._animData->getPhaseOffsetX(phase); + int y = anim._y + anim._animData->getPhaseOffsetY(phase); + int animFlag = anim._flags; + int checkMaskFlag = (animFlag & 1); + int maxFrontFlag = (animFlag & 2); + int specialZFlag = anim._nextAnim; + int z = anim._nextAnim; + Graphics::Surface *animSurface = anim._animData->getFrame(phaseFrameIndex); + int frameWidth = animSurface->w; + int frameHeight = animSurface->h; + int shadowZ = 0; + + if (checkMaskFlag) { + if (!anim._nextAnim) { + z = y + frameHeight - 1; + } + checkMasks(x, y, frameWidth, frameHeight, z); + } + + if (specialZFlag) { + z = specialZFlag; + } else if (maxFrontFlag) { + z = kMaxPicHeight + 1; + } else { + z = y + frameHeight - 1; + } + shadowZ = z; + + anim._currX = x; + anim._currY = y; + anim._currW = frameWidth; + anim._currH = frameHeight; + showSprite(animSurface, x, y, z); + + // make_special_shadow + if ((anim._flags & 0x80)) { + if (animSurface) { + DrawNode newDrawNode; + newDrawNode.posX = x; + newDrawNode.posY = y + animSurface->h - anim._shadowBack; + newDrawNode.posZ = Hero::kHeroShadowZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.scaleValue = _scaleValue; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = this; + newDrawNode.drawFunction = &Hero::showHeroShadow; + newDrawNode.s = animSurface; + _drawNodeList.push_back(newDrawNode); + } + } + + //ShowFrameCodeShadow + //ShowAnimFrameShadow + if (anim._shadowData != nullptr) { + int shadowPhaseFrameIndex = anim._shadowData->getPhaseFrameIndex(phase); + int shadowX = anim._shadowData->getBaseX() + anim._shadowData->getPhaseOffsetX(phase); + int shadowY = anim._shadowData->getBaseY() + anim._shadowData->getPhaseOffsetY(phase); + Graphics::Surface *shadowSurface = anim._shadowData->getFrame(shadowPhaseFrameIndex); + int shadowFrameWidth = shadowSurface->w; + int shadowFrameHeight = shadowSurface->h; + + if (checkMaskFlag) { + checkMasks(shadowX, shadowY, shadowFrameWidth, shadowFrameHeight, shadowY + shadowFrameWidth - 1); + } + + if (!shadowZ) { + if (maxFrontFlag) { + shadowZ = kMaxPicHeight + 1; + } else { + shadowZ = shadowY + shadowFrameWidth - 1; + } + } + showSpriteShadow(shadowSurface, shadowX, shadowY, shadowZ); + } +} + +void PrinceEngine::showNormAnims() { + for (int i = 0; i < kMaxNormAnims; i++) { + Anim &anim = _normAnimList[i]; + if (anim._animData != nullptr) { + int phaseCount = anim._animData->getPhaseCount(); + if (!anim._state) { + if (anim._frame == anim._lastFrame - 1) { + if (anim._loopType) { + if (anim._loopType == 1) { + anim._frame = anim._loopFrame; + } else { + continue; + } + } + } else { + anim._frame++; + } + anim._showFrame = anim._frame; + if (anim._showFrame >= phaseCount) { + anim._showFrame = phaseCount - 1; + } + showAnim(anim); + } + } + } +} + +void PrinceEngine::setBackAnim(Anim &backAnim) { + int start = backAnim._basaData._start; + if (start != -1) { + backAnim._frame = start; + backAnim._showFrame = start; + backAnim._loopFrame = start; + } + int end = backAnim._basaData._end; + if (end != -1) { + backAnim._lastFrame = end; + } + backAnim._state = 0; +} + +void PrinceEngine::showBackAnims() { + for (int i = 0; i < kMaxBackAnims; i++) { + BAS &seq = _backAnimList[i]._seq; + int activeSubAnim = seq._currRelative; + if (!_backAnimList[i].backAnims.empty()) { + if (_backAnimList[i].backAnims[activeSubAnim]._animData != nullptr) { + if (!_backAnimList[i].backAnims[activeSubAnim]._state) { + seq._counter++; + if (seq._type == 2) { + if (!seq._currRelative) { + if (seq._counter >= seq._data) { + if (seq._anims > 2) { + seq._currRelative = _randomSource.getRandomNumber(seq._anims - 2) + 1; + activeSubAnim = seq._currRelative; + seq._current = _backAnimList[i].backAnims[activeSubAnim]._basaData._num; + } + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + seq._counter = 0; + } + } + } + + if (seq._type == 3) { + if (!seq._currRelative) { + if (seq._counter < seq._data2) { + continue; + } else { + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + } + } + } + + if (_backAnimList[i].backAnims[activeSubAnim]._frame == _backAnimList[i].backAnims[activeSubAnim]._lastFrame - 1) { + _backAnimList[i].backAnims[activeSubAnim]._frame = _backAnimList[i].backAnims[activeSubAnim]._loopFrame; + switch (seq._type) { + case 1: + if (seq._anims > 1) { + int rnd; + do { + rnd = _randomSource.getRandomNumber(seq._anims - 1); + } while (rnd == seq._currRelative); + seq._currRelative = rnd; + seq._current = _backAnimList[i].backAnims[rnd]._basaData._num; + activeSubAnim = rnd; + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + seq._counter = 0; + } + break; + case 2: + if (seq._currRelative) { + seq._currRelative = 0; + seq._current = _backAnimList[i].backAnims[0]._basaData._num; + activeSubAnim = 0; + setBackAnim(_backAnimList[i].backAnims[activeSubAnim]); + seq._counter = 0; + } + break; + case 3: + seq._currRelative = 0; + seq._current = _backAnimList[i].backAnims[0]._basaData._num; + seq._counter = 0; + seq._data2 = _randomSource.getRandomNumber(seq._data - 1); + continue; // for bug in original game + break; + } + } else { + _backAnimList[i].backAnims[activeSubAnim]._frame++; + } + _backAnimList[i].backAnims[activeSubAnim]._showFrame = _backAnimList[i].backAnims[activeSubAnim]._frame; + showAnim(_backAnimList[i].backAnims[activeSubAnim]); + } + } + } + } +} + +void PrinceEngine::removeSingleBackAnim(int slot) { + if (!_backAnimList[slot].backAnims.empty()) { + for (uint j = 0; j < _backAnimList[slot].backAnims.size(); j++) { + if (_backAnimList[slot].backAnims[j]._animData != nullptr) { + delete _backAnimList[slot].backAnims[j]._animData; + _backAnimList[slot].backAnims[j]._animData = nullptr; + } + if (_backAnimList[slot].backAnims[j]._shadowData != nullptr) { + delete _backAnimList[slot].backAnims[j]._shadowData; + _backAnimList[slot].backAnims[j]._shadowData = nullptr; + } + } + _backAnimList[slot].backAnims.clear(); + _backAnimList[slot]._seq._currRelative = 0; + } +} + +void PrinceEngine::clearBackAnimList() { + for (int i = 0; i < kMaxBackAnims; i++) { + removeSingleBackAnim(i); + } +} + +void PrinceEngine::grabMap() { + _graph->_frontScreen->copyFrom(*_roomBmp->getSurface()); + showObjects(); + runDrawNodes(); + _graph->_mapScreen->copyFrom(*_graph->_frontScreen); +} + +void PrinceEngine::initZoomIn(int slot) { + freeZoomObject(slot); + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *zoomSource = object->getSurface(); + if (zoomSource != nullptr) { + object->_flags |= 0x8000; + object->_zoomSurface = new Graphics::Surface(); + object->_zoomSurface->create(zoomSource->w, zoomSource->h, Graphics::PixelFormat::createFormatCLUT8()); + object->_zoomSurface->fillRect(Common::Rect(zoomSource->w, zoomSource->h), 0xFF); + object->_zoomTime = 20; + } + } +} + +void PrinceEngine::initZoomOut(int slot) { + freeZoomObject(slot); + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *zoomSource = object->getSurface(); + if (zoomSource != nullptr) { + object->_flags |= 0x4000; + object->_zoomSurface = new Graphics::Surface(); + object->_zoomSurface->copyFrom(*zoomSource); + object->_zoomTime = 10; + } + } +} + +void PrinceEngine::doZoomIn(int slot) { + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *orgSurface = object->getSurface(); + if (orgSurface != nullptr) { + byte *src1 = (byte *)orgSurface->getBasePtr(0, 0); + byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); + int x = 0; + int surfaceHeight = orgSurface->h; + for (int y = 0; y < surfaceHeight; y++) { + byte *src2 = src1; + byte *dst2 = dst1; + int w = orgSurface->w - x; + src2 += x; + dst2 += x; + while (w > 0) { + int randVal = _randomSource.getRandomNumber(zoomInStep - 1); + if (randVal < w) { + *(dst2 + randVal) = *(src2 + randVal); + src2 += zoomInStep; + dst2 += zoomInStep; + } else if (y + 1 != surfaceHeight) { + *(dst1 + orgSurface->pitch + randVal - w) = *(src1 + orgSurface->pitch + randVal - w); + } + w -= zoomInStep; + } + x = -1 * w; + src1 += orgSurface->pitch; + dst1 += orgSurface->pitch; + } + } + } +} + +void PrinceEngine::doZoomOut(int slot) { + Object *object = _objList[slot]; + if (object != nullptr) { + Graphics::Surface *orgSurface = object->getSurface(); + if (orgSurface != nullptr) { + byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0); + int x = 0; + int surfaceHeight = orgSurface->h; + for (int y = 0; y < surfaceHeight; y++) { + byte *dst2 = dst1; + int w = orgSurface->w - x; + dst2 += x; + while (w > 0) { + int randVal = _randomSource.getRandomNumber(zoomInStep - 1); + if (randVal < w) { + *(dst2 + randVal) = 255; + dst2 += zoomInStep; + } else if (y + 1 != surfaceHeight) { + *(dst1 + orgSurface->pitch + randVal - w) = 255; + } + w -= zoomInStep; + } + x = -1 * w; + dst1 += orgSurface->pitch; + } + } + } +} + +void PrinceEngine::freeZoomObject(int slot) { + Object *object = _objList[slot]; + if (object != nullptr) { + if (object->_zoomSurface != nullptr) { + object->_zoomSurface->free(); + delete object->_zoomSurface; + object->_zoomSurface = nullptr; + } + } +} + +void PrinceEngine::showObjects() { + for (int i = 0; i < kMaxObjects; i++) { + int nr = _objSlot[i]; + if (nr != 0xFF) { + Graphics::Surface *objSurface = nullptr; + if ((_objList[nr]->_flags & 0x8000)) { + _objList[nr]->_zoomTime--; + if (!_objList[nr]->_zoomTime) { + freeZoomObject(nr); + _objList[nr]->_flags &= 0x7FFF; + objSurface = _objList[nr]->getSurface(); + } else { + doZoomIn(nr); + objSurface = _objList[nr]->_zoomSurface; + } + } else if ((_objList[nr]->_flags & 0x4000)) { + _objList[nr]->_zoomTime--; + if (!_objList[nr]->_zoomTime) { + freeZoomObject(nr); + _objList[nr]->_flags &= 0xBFFF; + objSurface = _objList[nr]->getSurface(); + } else { + doZoomOut(nr); + objSurface = _objList[nr]->_zoomSurface; + } + } else { + objSurface = _objList[nr]->getSurface(); + } + + if (objSurface != nullptr) { + if (spriteCheck(objSurface->w, objSurface->h, _objList[nr]->_x, _objList[nr]->_y)) { + int destX = _objList[nr]->_x - _picWindowX; + int destY = _objList[nr]->_y - _picWindowY; + DrawNode newDrawNode; + newDrawNode.posX = destX; + newDrawNode.posY = destY; + newDrawNode.posZ = _objList[nr]->_z; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.s = objSurface; + newDrawNode.originalRoomSurface = nullptr; + if ((_objList[nr]->_flags & 0x2000)) { + newDrawNode.data = nullptr; + newDrawNode.drawFunction = &_graph->drawBackSpriteDrawNode; + } else { + newDrawNode.data = _transTable; + if (_flags->getFlagValue(Flags::NOANTIALIAS)) { + newDrawNode.drawFunction = &_graph->drawTransparentDrawNode; + } else { + newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode; + } + } + _drawNodeList.push_back(newDrawNode); + } + + if ((_objList[nr]->_flags & 1)) { + checkMasks(_objList[nr]->_x, _objList[nr]->_y, objSurface->w, objSurface->h, _objList[nr]->_z); + } + } + } + } +} + +void PrinceEngine::showParallax() { + if (!_pscrList.empty()) { + for (uint i = 0; i < _pscrList.size(); i++) { + Graphics::Surface *pscrSurface = _pscrList[i]->getSurface(); + if (pscrSurface != nullptr) { + int x = _pscrList[i]->_x - (_pscrList[i]->_step * _picWindowX / 4); + int y = _pscrList[i]->_y; + int z = PScr::kPScrZ; + if (spriteCheck(pscrSurface->w, pscrSurface->h, x, y)) { + showSprite(pscrSurface, x, y, z); + } + } + } + } +} + +bool PrinceEngine::compareDrawNodes(DrawNode d1, DrawNode d2) { + if (d1.posZ < d2.posZ) { + return true; + } + return false; +} + +void PrinceEngine::runDrawNodes() { + Common::sort(_drawNodeList.begin(), _drawNodeList.end(), compareDrawNodes); + + for (uint i = 0; i < _drawNodeList.size(); i++) { + (*_drawNodeList[i].drawFunction)(_graph->_frontScreen, &_drawNodeList[i]); + } + _graph->change(); +} + +void PrinceEngine::drawScreen() { + if (!_showInventoryFlag || _inventoryBackgroundRemember) { + clsMasks(); + + _mainHero->showHero(); + _mainHero->scrollHero(); + _mainHero->drawHero(); + + _secondHero->showHero(); + _secondHero->_drawX -= _picWindowX; + _secondHero->drawHero(); + + const Graphics::Surface *roomSurface; + if (_locationNr != 50) { + roomSurface = _roomBmp->getSurface(); + } else { + roomSurface = _graph->_mapScreen; + } + Graphics::Surface visiblePart; + if (roomSurface) { + visiblePart = roomSurface->getSubArea(Common::Rect(_picWindowX, 0, roomSurface->w, roomSurface->h)); + _graph->draw(_graph->_frontScreen, &visiblePart); + } + + showBackAnims(); + + showNormAnims(); + + playNextFLCFrame(); + + showObjects(); + + if (roomSurface) { + insertMasks(&visiblePart); + } + + showParallax(); + + runDrawNodes(); + + _drawNodeList.clear(); + + if (!_inventoryBackgroundRemember && !_dialogFlag) { + if (!_optionsFlag) { + _selectedMob = checkMob(_graph->_frontScreen, _mobList, true); + } + showTexts(_graph->_frontScreen); + checkOptions(); + } else { + _inventoryBackgroundRemember = false; + } + + showPower(); + + getDebugger()->onFrame(); + + } else { + displayInventory(); + } +} + +void PrinceEngine::blackPalette() { + byte *paletteBackup = (byte *)malloc(256 * 3); + byte *blackPalette1 = (byte *)malloc(256 * 3); + + int fadeStep = kFadeStep - 1; + for (int i = 0; i < kFadeStep; i++) { + _system->getPaletteManager()->grabPalette(paletteBackup, 0, 256); + for (int j = 0; j < 256; j++) { + blackPalette1[3 * j] = paletteBackup[3 * j] * fadeStep / 4; + blackPalette1[3 * j + 1] = paletteBackup[3 * j + 1] * fadeStep / 4; + blackPalette1[3 * j + 2] = paletteBackup[3 * j + 2] * fadeStep / 4; + } + fadeStep--; + _graph->setPalette(blackPalette1); + _system->updateScreen(); + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + free(paletteBackup); + free(blackPalette1); + return; + } + pausePrinceEngine(); + } + free(paletteBackup); + free(blackPalette1); +} + +void PrinceEngine::setPalette(const byte *palette) { + if (palette != nullptr) { + byte *blackPalette_ = (byte *)malloc(256 * 3); + int fadeStep = 0; + for (int i = 0; i <= kFadeStep; i++) { + for (int j = 0; j < 256; j++) { + blackPalette_[3 * j] = palette[3 * j] * fadeStep / 4; + blackPalette_[3 * j + 1] = palette[3 * j + 1] * fadeStep / 4; + blackPalette_[3 * j + 2] = palette[3 * j + 2] * fadeStep / 4; + } + fadeStep++; + _graph->setPalette(blackPalette_); + _system->updateScreen(); + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + _graph->setPalette(palette); + free(blackPalette_); + return; + } + pausePrinceEngine(); + } + _graph->setPalette(palette); + free(blackPalette_); + } +} + +void PrinceEngine::pausePrinceEngine(int fps) { + int delay = 1000 / fps - int32(_system->getMillis() - _currentTime); + delay = delay < 0 ? 0 : delay; + _system->delayMillis(delay); + _currentTime = _system->getMillis(); +} + +void PrinceEngine::addInv(int heroId, int item, bool addItemQuiet) { + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + if (hero != nullptr) { + if (hero->_inventory.size() < kMaxItems) { + if (item != 0x7FFF) { + hero->_inventory.push_back(item); + } + if (!addItemQuiet) { + addInvObj(); + } + _interpreter->setResult(0); + } else { + _interpreter->setResult(1); + } + } +} + +void PrinceEngine::remInv(int heroId, int item) { + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + if (hero != nullptr) { + for (uint i = 0; i < hero->_inventory.size(); i++) { + if (hero->_inventory[i] == item) { + hero->_inventory.remove_at(i); + _interpreter->setResult(0); + return; + } + } + } + _interpreter->setResult(1); +} + +void PrinceEngine::clearInv(int heroId) { + switch (heroId) { + case 0: + _mainHero->_inventory.clear(); + break; + case 1: + _secondHero->_inventory.clear(); + break; + default: + error("clearInv() - wrong hero slot"); + break; + } +} + +void PrinceEngine::swapInv(int heroId) { + Common::Array<int> tempInv; + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + if (hero != nullptr) { + for (uint i = 0; i < hero->_inventory.size(); i++) { + tempInv.push_back(hero->_inventory[i]); + } + hero->_inventory.clear(); + for (uint i = 0; i < hero->_inventory2.size(); i++) { + hero->_inventory.push_back(hero->_inventory2[i]); + } + hero->_inventory2.clear(); + for (uint i = 0; i < tempInv.size(); i++) { + hero->_inventory2.push_back(tempInv[i]); + } + tempInv.clear(); + } +} + +void PrinceEngine::addInvObj() { + changeCursor(0); + prepareInventoryToView(); + + _inventoryBackgroundRemember = true; + drawScreen(); + + Graphics::Surface *suitcase = _suitcaseBmp->getSurface(); + + if (!_flags->getFlagValue(Flags::CURSEBLINK)) { + + loadSample(27, "PRZEDMIO.WAV"); + playSample(27, 0); + + _mst_shadow2 = 1; + + while (_mst_shadow2 < 512) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 += 50; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + while (_mst_shadow2 > 256) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 -= 42; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + } else { + //CURSEBLINK: + for (int i = 0; i < 3; i++) { + _mst_shadow2 = 256; + while (_mst_shadow2 < 512) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 += 50; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + while (_mst_shadow2 > 256) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + _mst_shadow2 -= 50; + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } + } + } + _mst_shadow2 = 0; + for (int i = 0; i < 20; i++) { + rememberScreenInv(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + drawInvItems(); + _graph->update(_graph->_screenForInventory); + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + eventMan->pollEvent(event); + if (shouldQuit()) { + return; + } + pausePrinceEngine(); + } +} + +void PrinceEngine::rememberScreenInv() { + _graph->_screenForInventory->copyFrom(*_graph->_frontScreen); +} + +void PrinceEngine::inventoryFlagChange(bool inventoryState) { + if (inventoryState) { + _showInventoryFlag = true; + _inventoryBackgroundRemember = true; + } else { + _showInventoryFlag = false; + } +} + +void PrinceEngine::prepareInventoryToView() { + _invMobList.clear(); + int invItem = _mainHero->_inventory.size(); + _invLine = invItem / 3; + if (invItem % 3) { + _invLine++; + } + if (_invLine < 4) { + _invLine = 4; + } + _maxInvW = (374 - 2 * _invLine) / _invLine; + _invLineW = _maxInvW - 2; + + int currInvX = _invLineX; + int currInvY = _invLineY; + + Common::MemoryReadStream stream(_invTxt, _invTxtSize); + byte c; + + uint item = 0; + for (int i = 0; i < _invLines; i++) { + for (int j = 0; j < _invLine; j++) { + Mob tempMobItem; + if (item < _mainHero->_inventory.size()) { + int itemNr = _mainHero->_inventory[item]; + tempMobItem._visible = 0; + tempMobItem._mask = itemNr; + tempMobItem._rect = Common::Rect(currInvX + _picWindowX, currInvY, currInvX + _picWindowX + _invLineW - 1, currInvY + _invLineH - 1); + tempMobItem._type = 0; // to work with checkMob() + + tempMobItem._name = ""; + tempMobItem._examText = ""; + int txtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8]); + int examTxtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8 + 4]); + + stream.seek(txtOffset); + while ((c = stream.readByte())) { + tempMobItem._name += c; + } + + stream.seek(examTxtOffset); + while ((c = stream.readByte())) { + tempMobItem._examText += c; + } + _invMobList.push_back(tempMobItem); + } + currInvX += _invLineW + _invLineSkipX; + item++; + } + currInvX = _invLineX; + currInvY += _invLineSkipY + _invLineH; + } +} + +void PrinceEngine::drawInvItems() { + int currInvX = _invLineX; + int currInvY = _invLineY; + uint item = 0; + for (int i = 0; i < _invLines; i++) { + for (int j = 0; j < _invLine; j++) { + if (item < _mainHero->_inventory.size()) { + int itemNr = _mainHero->_inventory[item]; + _mst_shadow = 0; + if (_mst_shadow2) { + if (!_flags->getFlagValue(Flags::CURSEBLINK)) { + if (item + 1 == _mainHero->_inventory.size()) { // last item in inventory + _mst_shadow = 1; + } + } else if (itemNr == 1 || itemNr == 3 || itemNr == 4 || itemNr == 7) { + _mst_shadow = 1; + } + } + + int drawX = currInvX; + int drawY = currInvY; + Graphics::Surface *itemSurface = nullptr; + if (itemNr != 68) { + itemSurface = _allInvList[itemNr].getSurface(); + if (itemSurface->h < _maxInvH) { + drawY += (_maxInvH - itemSurface->h) / 2; + } + } else { + // candle item: + if (_candleCounter == 8) { + _candleCounter = 0; + } + itemNr = _candleCounter; + _candleCounter++; + itemNr &= 7; + itemNr += 71; + itemSurface = _allInvList[itemNr].getSurface(); + drawY += _allInvList[itemNr]._y + (_maxInvH - 76) / 2 - 200; + } + if (itemSurface->w < _maxInvW) { + drawX += (_maxInvW - itemSurface->w) / 2; + } + if (!_mst_shadow) { + _graph->drawTransparentSurface(_graph->_screenForInventory, drawX, drawY, itemSurface); + } else { + _mst_shadow = _mst_shadow2; + _graph->drawTransparentWithBlendSurface(_graph->_screenForInventory, drawX, drawY, itemSurface); + } + } + currInvX += _invLineW + _invLineSkipX; + item++; + } + currInvX = _invLineX; + currInvY += _invLineSkipY + _invLineH; + } +} + +void PrinceEngine::walkTo() { + if (_mainHero->_visible) { + _mainHero->freeHeroAnim(); + _mainHero->freeOldMove(); + _interpreter->storeNewPC(_script->_scriptInfo.usdCode); + int destX, destY; + if (_optionsMob != -1) { + destX = _mobList[_optionsMob]._examPosition.x; + destY = _mobList[_optionsMob]._examPosition.y; + _mainHero->_destDirection = _mobList[_optionsMob]._examDirection; + } else { + Common::Point mousePos = _system->getEventManager()->getMousePos(); + destX = mousePos.x + _picWindowX; + destY = mousePos.y + _picWindowY; + _mainHero->_destDirection = 0; + } + _mainHero->_coords = makePath(kMainHero, _mainHero->_middleX, _mainHero->_middleY, destX, destY); + if (_mainHero->_coords != nullptr) { + _mainHero->_currCoords = _mainHero->_coords; + _mainHero->_dirTab = _directionTable; + _mainHero->_currDirTab = _directionTable; + _directionTable = nullptr; + _mainHero->_state = Hero::kHeroStateMove; + moveShandria(); + } + } +} + +void PrinceEngine::moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag) { + Hero *hero = nullptr; + if (!heroId) { + hero = _mainHero; + } else if (heroId == 1) { + hero = _secondHero; + } + + if (hero != nullptr) { + if (dir) { + hero->_destDirection = dir; + } + if (x || y) { + hero->freeOldMove(); + hero->_coords = makePath(heroId, hero->_middleX, hero->_middleY, x, y); + if (hero->_coords != nullptr) { + hero->_currCoords = hero->_coords; + hero->_dirTab = _directionTable; + hero->_currDirTab = _directionTable; + _directionTable = nullptr; + if (runHeroFlag) { + hero->_state = Hero::kHeroStateRun; + } else { + hero->_state = Hero::kHeroStateMove; + } + if (heroId == kMainHero && _mouseFlag) { + moveShandria(); + } + } + } else { + hero->freeOldMove(); + hero->_state = Hero::kHeroStateTurn; + } + hero->freeHeroAnim(); + hero->_visible = 1; + } +} + +void PrinceEngine::leftMouseButton() { + _flags->setFlagValue(Flags::ESCAPED2, 1); // skip intro animation + _flags->setFlagValue(Flags::LMOUSE, 1); + if (_flags->getFlagValue(Flags::POWERENABLED)) { + _flags->setFlagValue(Flags::MBFLAG, 1); + } + if (_mouseFlag) { + int option = 0; + int optionEvent = -1; + + if (_optionsFlag) { + if (_optionEnabled < _optionsNumber && _optionEnabled != -1) { + option = _optionEnabled; + _optionsFlag = 0; + } else { + return; + } + } else { + _optionsMob = _selectedMob; + if (_optionsMob == -1) { + walkTo(); + return; + } + option = 0; + } + //do_option + if (_currentPointerNumber != 2) { + //skip_use_code + int optionScriptOffset = _room->getOptionOffset(option); + if (optionScriptOffset != 0) { + optionEvent = _script->scanMobEvents(_optionsMob, optionScriptOffset); + } + if (optionEvent == -1) { + if (!option) { + walkTo(); + return; + } else { + optionEvent = _script->getOptionStandardOffset(option); + } + } + } else if (_selectedMode) { + //give_item + if (_room->_itemGive) { + optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemGive, _selectedItem); + } + if (optionEvent == -1) { + //standard_giveitem + optionEvent = _script->_scriptInfo.stdGiveItem; + } + } else { + if (_room->_itemUse) { + optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemUse, _selectedItem); + _flags->setFlagValue(Flags::SELITEM, _selectedItem); + } + if (optionEvent == -1) { + //standard_useitem + optionEvent = _script->_scriptInfo.stdUseItem; + } + } + _interpreter->storeNewPC(optionEvent); + _flags->setFlagValue(Flags::CURRMOB, _selectedMob); + _selectedMob = -1; + _optionsMob = -1; + } else { + if (!_flags->getFlagValue(Flags::POWERENABLED)) { + if (!_flags->getFlagValue(Flags::NOCLSTEXT)) { + for (int slot = 0; slot < kMaxTexts; slot++) { + if (slot != 9) { + Text& text = _textSlots[slot]; + if (!text._str) { + continue; + } + text._str = 0; + text._time = 0; + } + } + _mainHero->_talkTime = 0; + _secondHero->_talkTime = 0; + } + } + } +} + +void PrinceEngine::rightMouseButton() { + if (_flags->getFlagValue(Flags::POWERENABLED)) { + _flags->setFlagValue(Flags::MBFLAG, 2); + } + if (_mouseFlag && _mouseFlag != 3) { + _mainHero->freeOldMove(); + _secondHero->freeOldMove(); + _interpreter->storeNewPC(0); + if (_currentPointerNumber < 2) { + enableOptions(true); + } else { + _currentPointerNumber = 1; + changeCursor(1); + } + } +} + +void PrinceEngine::inventoryLeftMouseButton() { + if (!_mouseFlag) { + _textSlots[0]._time = 0; + _textSlots[0]._str = nullptr; + stopSample(28); + } + + if (_optionsFlag == 1) { + if (_selectedMob != -1) { + if (_optionEnabled < _invOptionsNumber) { + _optionsFlag = 0; + } else { + return; + } + } else { + error("PrinceEngine::inventoryLeftMouseButton() - optionsFlag = 1, selectedMob = 0"); + if (_currentPointerNumber == 2) { + changeCursor(1); + _currentPointerNumber = 1; + _selectedMob = -1; + _optionsMob = -1; + return; + } else { + return; + } + } + } else { + if (_selectedMob != -1) { + if (_currentPointerNumber != 2) { + if (_invMobList[_selectedMob]._mask != 29) { + _optionEnabled = 0; + } else { + // map item + _optionEnabled = 1; + } + } else { + //use_item_on_item + int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem); + if (invObjUU == -1) { + int textNr = 80011; // "I can't do it." + if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) { + textNr = 80020; // "Nothing is happening." + } + _interpreter->setCurrentString(textNr); + printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100); + setVoice(0, 28, 1); + playSample(28, 0); + _selectedMob = -1; + _optionsMob = -1; + return; + } else { + _interpreter->storeNewPC(invObjUU); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } + } else { + return; + } + } + //do_option + if (_optionEnabled == 0) { + int invObjExamEvent = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjExam); + if (invObjExamEvent == -1) { + // do_standard + printAt(0, 216, (char *)_invMobList[_selectedMob]._examText.c_str(), kNormalWidth / 2, _invExamY); + _interpreter->setCurrentString(_invMobList[_selectedMob]._mask + 70000); + setVoice(0, 28, 1); + playSample(28, 0); + // disableuseuse + changeCursor(0); + _currentPointerNumber = 1; + } else { + _interpreter->storeNewPC(invObjExamEvent); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } else if (_optionEnabled == 1) { + // not_examine + int invObjUse = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUse); + if (invObjUse == -1) { + // do_standard_use + _selectedMode = 0; + _selectedItem = _invMobList[_selectedMob]._mask; + makeInvCursor(_invMobList[_selectedMob]._mask); + _currentPointerNumber = 2; + changeCursor(2); + } else { + _interpreter->storeNewPC(invObjUse); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } else if (_optionEnabled == 4) { + // do_standard_give + _selectedMode = 1; + _selectedItem = _invMobList[_selectedMob]._mask; + makeInvCursor(_invMobList[_selectedMob]._mask); + _currentPointerNumber = 2; + changeCursor(2); + } else { + // use_item_on_item + int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem); + if (invObjUU == -1) { + int textNr = 80011; // "I can't do it." + if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) { + textNr = 80020; // "Nothing is happening." + } + _interpreter->setCurrentString(textNr); + printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100); + setVoice(0, 28, 1); + playSample(28, 0); + } else { + _interpreter->storeNewPC(invObjUU); + _flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask); + _showInventoryFlag = false; + } + } + _selectedMob = -1; + _optionsMob = -1; +} + +void PrinceEngine::inventoryRightMouseButton() { + if (_textSlots[0]._str == nullptr) { + enableOptions(false); + } +} + +void PrinceEngine::enableOptions(bool checkType) { + if (_optionsFlag != 1) { + changeCursor(1); + _currentPointerNumber = 1; + if (_selectedMob != -1) { + if (checkType) { + if (_mobList[_selectedMob]._type & 0x100) { + return; + } + } + Common::Point mousePos = _system->getEventManager()->getMousePos(); + int x1 = mousePos.x - _optionsWidth / 2; + int x2 = mousePos.x + _optionsWidth / 2; + if (x1 < 0) { + x1 = 0; + x2 = _optionsWidth; + } else if (x2 >= kNormalWidth) { + x1 = kNormalWidth - _optionsWidth; + x2 = kNormalWidth; + } + int y1 = mousePos.y - 10; + if (y1 < 0) { + y1 = 0; + } + if (y1 + _optionsHeight >= kNormalHeight) { + y1 = kNormalHeight - _optionsHeight; + } + _optionsMob = _selectedMob; + _optionsX = x1; + _optionsY = y1; + _optionsFlag = 1; + } + } +} + +void PrinceEngine::checkOptions() { + if (_optionsFlag) { + Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _optionsWidth, _optionsY + _optionsHeight); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + if (!optionsRect.contains(mousePos)) { + _optionsFlag = 0; + _selectedMob = -1; + return; + } + _graph->drawAsShadowSurface(_graph->_frontScreen, _optionsX, _optionsY, _optionsPic, _graph->_shadowTable50); + + _optionEnabled = -1; + int optionsYCord = mousePos.y - (_optionsY + 16); + if (optionsYCord >= 0) { + int selectedOptionNr = optionsYCord / _optionsStep; + if (selectedOptionNr < _optionsNumber) { + _optionEnabled = selectedOptionNr; + } + } + int optionsColor; + int textY = _optionsY + 16; + for (int i = 0; i < _optionsNumber; i++) { + if (i != _optionEnabled) { + optionsColor = _optionsColor1; + } else { + optionsColor = _optionsColor2; + } + Common::String optText; + switch(getLanguage()) { + case Common::PL_POL: + optText = optionsTextPL[i]; + break; + case Common::DE_DEU: + optText = optionsTextDE[i]; + break; + case Common::EN_ANY: + optText = optionsTextEN[i]; + break; + default: + break; + }; + uint16 textW = getTextWidth(optText.c_str()); + uint16 textX = _optionsX + _optionsWidth / 2 - textW / 2; + _font->drawString(_graph->_frontScreen, optText, textX, textY, textW, optionsColor); + textY += _optionsStep; + } + } +} + +void PrinceEngine::checkInvOptions() { + if (_optionsFlag) { + Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _invOptionsWidth, _optionsY + _invOptionsHeight); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + if (!optionsRect.contains(mousePos)) { + _optionsFlag = 0; + _selectedMob = -1; + return; + } + _graph->drawAsShadowSurface(_graph->_screenForInventory, _optionsX, _optionsY, _optionsPicInInventory, _graph->_shadowTable50); + + _optionEnabled = -1; + int optionsYCord = mousePos.y - (_optionsY + 16); + if (optionsYCord >= 0) { + int selectedOptionNr = optionsYCord / _invOptionsStep; + if (selectedOptionNr < _invOptionsNumber) { + _optionEnabled = selectedOptionNr; + } + } + int optionsColor; + int textY = _optionsY + 16; + for (int i = 0; i < _invOptionsNumber; i++) { + if (i != _optionEnabled) { + optionsColor = _optionsColor1; + } else { + optionsColor = _optionsColor2; + } + Common::String invText; + switch(getLanguage()) { + case Common::PL_POL: + invText = invOptionsTextPL[i]; + break; + case Common::DE_DEU: + invText = invOptionsTextDE[i]; + break; + case Common::EN_ANY: + invText = invOptionsTextEN[i]; + break; + default: + error("Unknown game language %d", getLanguage()); + break; + }; + uint16 textW = getTextWidth(invText.c_str()); + uint16 textX = _optionsX + _invOptionsWidth / 2 - textW / 2; + _font->drawString(_graph->_screenForInventory, invText, textX, textY, _graph->_screenForInventory->w, optionsColor); + textY += _invOptionsStep; + } + } +} + +void PrinceEngine::displayInventory() { + + _mainHero->freeOldMove(); + _secondHero->freeOldMove(); + + _interpreter->setFgOpcodePC(0); + + stopAllSamples(); + + prepareInventoryToView(); + + while (!shouldQuit()) { + + if (_textSlots[0]._str != nullptr) { + changeCursor(0); + } else { + changeCursor(_currentPointerNumber); + + Common::Rect inventoryRect(_invX1, _invY1, _invX1 + _invWidth, _invY1 + _invHeight); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + + if (!_invCurInside && inventoryRect.contains(mousePos)) { + _invCurInside = true; + } + + if (_invCurInside && !inventoryRect.contains(mousePos)) { + inventoryFlagChange(false); + _invCurInside = false; + break; + } + } + + rememberScreenInv(); + + Graphics::Surface *suitcase = _suitcaseBmp->getSurface(); + _graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase); + + drawInvItems(); + + showTexts(_graph->_screenForInventory); + + if (!_optionsFlag && _textSlots[0]._str == nullptr) { + _selectedMob = checkMob(_graph->_screenForInventory, _invMobList, false); + } + + checkInvOptions(); + + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + keyHandler(event); + break; + case Common::EVENT_LBUTTONDOWN: + inventoryLeftMouseButton(); + break; + case Common::EVENT_RBUTTONDOWN: + inventoryRightMouseButton(); + break; + default: + break; + } + } + + if (!_showInventoryFlag) { + break; + } + + if (shouldQuit()) + return; + + getDebugger()->onFrame(); + _graph->update(_graph->_screenForInventory); + pausePrinceEngine(); + } + + if (_currentPointerNumber == 2) { + _flags->setFlagValue(Flags::SELITEM, _selectedItem); + } else { + _flags->setFlagValue(Flags::SELITEM, 0); + } +} + +void PrinceEngine::createDialogBox(int dialogBoxNr) { + _dialogLines = 0; + int amountOfDialogOptions = 0; + int dialogDataValue = (int)READ_LE_UINT32(_dialogData); + + byte c; + int sentenceNumber; + _dialogText = _dialogBoxAddr[dialogBoxNr]; + byte *dialogText = _dialogText; + + while ((sentenceNumber = *dialogText) != 0xFF) { + dialogText++; + if (!(dialogDataValue & (1 << sentenceNumber))) { + _dialogLines += calcTextLines((const char *)dialogText); + amountOfDialogOptions++; + } + do { + c = *dialogText; + dialogText++; + } while (c); + } + + _dialogHeight = _font->getFontHeight() * _dialogLines + _dialogLineSpace * (amountOfDialogOptions + 1); + _dialogImage = new Graphics::Surface(); + _dialogImage->create(_dialogWidth, _dialogHeight, Graphics::PixelFormat::createFormatCLUT8()); + Common::Rect dBoxRect(0, 0, _dialogWidth, _dialogHeight); + _dialogImage->fillRect(dBoxRect, _graph->kShadowColor); +} + +void PrinceEngine::dialogRun() { + + _dialogFlag = true; + + while (!shouldQuit()) { + + _interpreter->stepBg(); + drawScreen(); + + int dialogX = (640 - _dialogWidth) / 2; + int dialogY = 460 - _dialogHeight; + _graph->drawAsShadowSurface(_graph->_frontScreen, dialogX, dialogY, _dialogImage, _graph->_shadowTable50); + + int dialogSkipLeft = 14; + int dialogSkipUp = 10; + + int dialogTextX = dialogX + dialogSkipLeft; + int dialogTextY = dialogY + dialogSkipUp; + + Common::Point mousePos = _system->getEventManager()->getMousePos(); + + byte c; + int sentenceNumber; + byte *dialogText = _dialogText; + byte *dialogCurrentText = nullptr; + int dialogSelected = -1; + int dialogDataValue = (int)READ_LE_UINT32(_dialogData); + + while ((sentenceNumber = *dialogText) != 0xFF) { + dialogText++; + int actualColor = _dialogColor1; + + if (!(dialogDataValue & (1 << sentenceNumber))) { + if (getLanguage() == Common::DE_DEU) { + correctStringDEU((char *)dialogText); + } + Common::Array<Common::String> lines; + _font->wordWrapText((const char *)dialogText, _graph->_frontScreen->w, lines); + + Common::Rect dialogOption(dialogTextX, dialogTextY - dialogSkipUp / 2, dialogX + _dialogWidth - dialogSkipLeft, dialogTextY + lines.size() * _font->getFontHeight() + dialogSkipUp / 2 - 1); + if (dialogOption.contains(mousePos)) { + actualColor = _dialogColor2; + dialogSelected = sentenceNumber; + dialogCurrentText = dialogText; + } + + for (uint j = 0; j < lines.size(); j++) { + _font->drawString(_graph->_frontScreen, lines[j], dialogTextX, dialogTextY, _graph->_frontScreen->w, actualColor); + dialogTextY += _font->getFontHeight(); + } + dialogTextY += _dialogLineSpace; + } + do { + c = *dialogText; + dialogText++; + } while (c); + } + + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + keyHandler(event); + break; + case Common::EVENT_LBUTTONDOWN: + if (dialogSelected != -1) { + dialogLeftMouseButton(dialogCurrentText, dialogSelected); + _dialogFlag = false; + } + break; + default: + break; + } + } + + if (shouldQuit()) { + return; + } + + if (!_dialogFlag) { + break; + } + + getDebugger()->onFrame(); + _graph->update(_graph->_frontScreen); + pausePrinceEngine(); + } + _dialogImage->free(); + delete _dialogImage; + _dialogImage = nullptr; + _dialogFlag = false; +} + +void PrinceEngine::dialogLeftMouseButton(byte *string, int dialogSelected) { + _interpreter->setString(string); + talkHero(0); + + int dialogDataValue = (int)READ_LE_UINT32(_dialogData); + dialogDataValue |= (1u << dialogSelected); + WRITE_LE_UINT32(_dialogData, dialogDataValue); + + _flags->setFlagValue(Flags::BOXSEL, dialogSelected + 1); + setVoice(0, 28, dialogSelected + 1); + + _flags->setFlagValue(Flags::VOICE_H_LINE, _dialogOptLines[dialogSelected * 4]); + _flags->setFlagValue(Flags::VOICE_A_LINE, _dialogOptLines[dialogSelected * 4 + 1]); + _flags->setFlagValue(Flags::VOICE_B_LINE, _dialogOptLines[dialogSelected * 4 + 2]); + + _interpreter->setString(_dialogOptAddr[dialogSelected]); +} + +void PrinceEngine::talkHero(int slot) { + // heroSlot = textSlot (slot 0 or 1) + Text &text = _textSlots[slot]; + int lines = calcTextLines((const char *)_interpreter->getString()); + int time = lines * 30; + + if (slot == 0) { + text._color = 220; // TODO - test this + _mainHero->_state = Hero::kHeroStateTalk; + _mainHero->_talkTime = time; + text._x = _mainHero->_middleX; + text._y = _mainHero->_middleY - _mainHero->_scaledFrameYSize; + } else { + text._color = _flags->getFlagValue(Flags::KOLOR); // TODO - test this + _secondHero->_state = Hero::kHeroStateTalk; + _secondHero->_talkTime = time; + text._x = _secondHero->_middleX; + text._y = _secondHero->_middleY - _secondHero->_scaledFrameYSize; + } + text._time = time; + if (getLanguage() == Common::DE_DEU) { + correctStringDEU((char *)_interpreter->getString()); + } + text._str = (const char *)_interpreter->getString(); + _interpreter->increaseString(); +} + +void PrinceEngine::doTalkAnim(int animNumber, int slot, AnimType animType) { + Text &text = _textSlots[slot]; + int lines = calcTextLines((const char *)_interpreter->getString()); + int time = lines * 30; + if (animType == kNormalAnimation) { + Anim &normAnim = _normAnimList[animNumber]; + if (normAnim._animData != nullptr) { + if (!normAnim._state) { + if (normAnim._currW && normAnim._currH) { + text._color = _flags->getFlagValue(Flags::KOLOR); + text._x = normAnim._currX + normAnim._currW / 2; + text._y = normAnim._currY - 10; + } + } + } + } else if (animType == kBackgroundAnimation) { + if (!_backAnimList[animNumber].backAnims.empty()) { + int currAnim = _backAnimList[animNumber]._seq._currRelative; + Anim &backAnim = _backAnimList[animNumber].backAnims[currAnim]; + if (backAnim._animData != nullptr) { + if (!backAnim._state) { + if (backAnim._currW && backAnim._currH) { + text._color = _flags->getFlagValue(Flags::KOLOR); + text._x = backAnim._currX + backAnim._currW / 2; + text._y = backAnim._currY - 10; + } + } + } + } + } else { + error("doTalkAnim() - wrong animType: %d", animType); + } + text._time = time; + if (getLanguage() == Common::DE_DEU) { + correctStringDEU((char *)_interpreter->getString()); + } + text._str = (const char *)_interpreter->getString(); + _interpreter->increaseString(); +} + +void PrinceEngine::freeNormAnim(int slot) { + if (!_normAnimList.empty()) { + _normAnimList[slot]._state = 1; + if (_normAnimList[slot]._animData != nullptr) { + delete _normAnimList[slot]._animData; + _normAnimList[slot]._animData = nullptr; + } + if (_normAnimList[slot]._shadowData != nullptr) { + delete _normAnimList[slot]._shadowData; + _normAnimList[slot]._shadowData = nullptr; + } + } +} + +void PrinceEngine::freeAllNormAnims() { + for (int i = 0; i < kMaxNormAnims; i++) { + freeNormAnim(i); + } +} + +void PrinceEngine::getCurve() { + _flags->setFlagValue(Flags::TORX1, _curveData[_curvPos]); + _flags->setFlagValue(Flags::TORY1, _curveData[_curvPos + 1]); + _curvPos += 2; +} + +void PrinceEngine::makeCurve() { + _curvPos = 0; + int x1 = _flags->getFlagValue(Flags::TORX1); + int y1 = _flags->getFlagValue(Flags::TORY1); + int x2 = _flags->getFlagValue(Flags::TORX2); + int y2 = _flags->getFlagValue(Flags::TORY2); + + for (int i = 0; i < kCurveLen; i++) { + int sum1 = x1 * curveValues[i][0]; + sum1 += (x2 + (x1 - x2) / 2) * curveValues[i][1]; + sum1 += x2 * curveValues[i][2]; + sum1 += x2 * curveValues[i][3]; + + int sum2 = y1 * curveValues[i][0]; + sum2 += (y2 - 20) * curveValues[i][1]; + sum2 += (y2 - 10) * curveValues[i][2]; + sum2 += y2 * curveValues[i][3]; + + _curveData[i * 2] = (sum1 >> 15); + _curveData[i * 2 + 1] = (sum2 >> 15); + } +} + +void PrinceEngine::mouseWeirdo() { + if (_mouseFlag == 3) { + int weirdDir = _randomSource.getRandomNumber(3); + Common::Point mousePos = _system->getEventManager()->getMousePos(); + switch (weirdDir) { + case 0: + mousePos.x += kCelStep; + break; + case 1: + mousePos.x -= kCelStep; + break; + case 2: + mousePos.y += kCelStep; + break; + case 3: + mousePos.y -= kCelStep; + break; + } + mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639); + _flags->setFlagValue(Flags::MXFLAG, mousePos.x); + mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170); + _flags->setFlagValue(Flags::MYFLAG, mousePos.y); + _system->warpMouse(mousePos.x, mousePos.y); + } +} + +void PrinceEngine::showPower() { + if (_flags->getFlagValue(Flags::POWERENABLED)) { + int power = _flags->getFlagValue(Flags::POWER); + + byte *dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarPosY); + for (int y = 0; y < kPowerBarHeight; y++) { + byte *dst2 = dst; + for (int x = 0; x < kPowerBarWidth; x++, dst2++) { + *dst2 = kPowerBarBackgroundColor; + } + dst += _graph->_frontScreen->pitch; + } + + if (power) { + dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarGreenPosY); + for (int y = 0; y < kPowerBarGreenHeight; y++) { + byte *dst2 = dst; + for (int x = 0; x < power + 1; x++, dst2++) { + if (x < 58) { + *dst2 = kPowerBarGreenColor1; + } else { + *dst2 = kPowerBarGreenColor2; + } + } + dst += _graph->_frontScreen->pitch; + } + } + + _graph->change(); + } +} + +void PrinceEngine::scrollCredits() { + byte *scrollAdress = _creditsData; + while (!shouldQuit()) { + for (int scrollPos = 0; scrollPos > -23; scrollPos--) { + const Graphics::Surface *roomSurface = _roomBmp->getSurface(); + if (roomSurface) { + _graph->draw(_graph->_frontScreen, roomSurface); + } + char *s = (char *)scrollAdress; + int drawY = scrollPos; + for (int i = 0; i < 22; i++) { + Common::String line; + char *linePos = s; + while ((*linePos != 13)) { + line += *linePos; + linePos++; + } + if (!line.empty()) { + int drawX = (kNormalWidth - getTextWidth(line.c_str())) / 2; + _font->drawString(_graph->_frontScreen, line, drawX, drawY, _graph->_frontScreen->w, 217); + } + + char letter1; + bool gotIt1 = false; + do { + letter1 = *s; + s++; + if (letter1 == 13) { + if (*s == 10) { + s++; + } + if (*s != 35) { + gotIt1 = true; + } + break; + } + } while (letter1 != 35); + + if (gotIt1) { + drawY += 23; + } else { + break; + } + } + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + if (event.type == Common::EVENT_KEYDOWN) { + if (event.kbd.keycode == Common::KEYCODE_ESCAPE) { + blackPalette(); + return; + } + } + } + if (shouldQuit()) { + return; + } + _graph->change(); + _graph->update(_graph->_frontScreen); + pausePrinceEngine(kFPS * 2); + } + char letter2; + byte *scan2 = scrollAdress; + bool gotIt2 = false; + do { + letter2 = *scan2; + scan2++; + if (letter2 == 13) { + if (*scan2 == 10) { + scan2++; + } + if (*scan2 != 35) { + gotIt2 = true; + } + break; + } + } while (letter2 != 35); + if (gotIt2) { + scrollAdress = scan2; + } else { + break; + } + } + blackPalette(); +} + +// Modified version of Graphics::drawLine() to allow breaking the loop and return value +int PrinceEngine::drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data) { + // Bresenham's line algorithm, as described by Wikipedia + const bool steep = ABS(y1 - y0) > ABS(x1 - x0); + + if (steep) { + SWAP(x0, y0); + SWAP(x1, y1); + } + + const int delta_x = ABS(x1 - x0); + const int delta_y = ABS(y1 - y0); + const int delta_err = delta_y; + int x = x0; + int y = y0; + int err = 0; + + const int x_step = (x0 < x1) ? 1 : -1; + const int y_step = (y0 < y1) ? 1 : -1; + + int stopFlag = 0; + if (steep) + stopFlag = (*plotProc)(y, x, data); + else + stopFlag = (*plotProc)(x, y, data); + + while (x != x1 && !stopFlag) { + x += x_step; + err += delta_err; + if (2 * err > delta_x) { + y += y_step; + err -= delta_x; + } + if (steep) + stopFlag = (*plotProc)(y, x, data); + else + stopFlag = (*plotProc)(x, y, data); + } + return stopFlag; +} + +int PrinceEngine::getPixelAddr(byte *pathBitmap, int x, int y) { + int mask = 128 >> (x & 7); + byte value = pathBitmap[x / 8 + y * 80]; + return (mask & value); +} + +void PrinceEngine::findPoint(int x, int y) { + _fpX = x; + _fpY = y; + + if (getPixelAddr(_roomPathBitmap, x, y)) { + return; + } + + int fpL = x; + int fpU = y; + int fpR = x; + int fpD = y; + + while (1) { + if (fpD != kMaxPicHeight) { + if (getPixelAddr(_roomPathBitmap, x, fpD)) { + _fpX = x; + _fpY = fpD; + break; + } + fpD++; + } + if (fpU) { + if (getPixelAddr(_roomPathBitmap, x, fpU)) { + _fpX = x; + _fpY = fpU; + break; + } + fpU--; + } + if (fpL) { + if (getPixelAddr(_roomPathBitmap, fpL, y)) { + _fpX = fpL; + _fpY = y; + break; + } + fpL--; + } + if (fpR != _sceneWidth) { + if (getPixelAddr(_roomPathBitmap, fpR, y)) { + _fpX = fpR; + _fpY = y; + break; + } + fpR++; + } + if (!fpU && (fpD == kMaxPicHeight)) { + if (!fpL && (fpR == _sceneWidth)) { + break; + } + } + } +} + +Direction PrinceEngine::makeDirection(int x1, int y1, int x2, int y2) { + if (x1 != x2) { + if (y1 != y2) { + if (x1 > x2) { + if (y1 > y2) { + if (x1 - x2 >= y1 - y2) { + return kDirLU; + } else { + return kDirUL; + } + } else { + if (x1 - x2 >= y2 - y1) { + return kDirLD; + } else { + return kDirDL; + } + } + } else { + if (y1 > y2) { + if (x2 - x1 >= y1 - y2) { + return kDirRU; + } else { + return kDirUR; + } + } else { + if (x2 - x1 >= y2 - y1) { + return kDirRD; + } else { + return kDirDR; + } + } + } + } else { + if (x1 >= x2) { + return kDirL; + } else { + return kDirR; + } + } + } else { + if (y1 >= y2) { + return kDirU; + } else { + return kDirD; + } + } +} + +void PrinceEngine::specialPlot(int x, int y) { + if (_coords < _coordsBufEnd) { + WRITE_LE_UINT16(_coords, x); + _coords += 2; + WRITE_LE_UINT16(_coords, y); + _coords += 2; + specialPlot2(x, y); + } +} + +void PrinceEngine::specialPlot2(int x, int y) { + int mask = 128 >> (x & 7); + _roomPathBitmapTemp[x / 8 + y * 80] |= mask; +} + +void PrinceEngine::specialPlotInside(int x, int y) { + if (_coords < _coordsBufEnd) { + WRITE_LE_UINT16(_coords, x); + _coords += 2; + WRITE_LE_UINT16(_coords, y); + _coords += 2; + } +} + +int PrinceEngine::plotTraceLine(int x, int y, void *data) { + PrinceEngine *traceLine = (PrinceEngine *)data; + if (!traceLine->_traceLineFirstPointFlag) { + if (!traceLine->getPixelAddr(traceLine->_roomPathBitmapTemp, x, y)) { + if (traceLine->getPixelAddr(traceLine->_roomPathBitmap, x, y)) { + traceLine->specialPlotInside(x, y); + traceLine->_traceLineLen++; + return 0; + } else { + return -1; + } + } else { + return 1; + } + } else { + traceLine->_traceLineFirstPointFlag = false; + return 0; + } +} + +int PrinceEngine::leftDownDir() { + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::leftDir() { + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::leftUpDir() { + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::rightDownDir() { + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::rightDir() { + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::rightUpDir() { + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::upLeftDir() { + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::upDir() { + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::upRightDir() { + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::downLeftDir() { + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::downDir() { + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::downRightDir() { + if (!checkRightDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDownDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkRightUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + if (!checkLeftUpDir()) { + specialPlot(_checkX, _checkY); + return 0; + } + return -1; +} + +int PrinceEngine::cpe() { + if ((*(_checkBitmap - kPBW) & _checkMask)) { + if ((*(_checkBitmap + kPBW) & _checkMask)) { + int value; + switch (_checkMask) { + case 128: + value = READ_LE_UINT16(_checkBitmap - 1); + value &= 0x4001; + if (value != 0x4001) { + return 0; + } + break; + case 64: + value = *_checkBitmap; + value &= 0xA0; + if (value != 0xA0) { + return 0; + } + break; + case 32: + value = *_checkBitmap; + value &= 0x50; + if (value != 0x50) { + return 0; + } + break; + case 16: + value = *_checkBitmap; + value &= 0x28; + if (value != 0x28) { + return 0; + } + break; + case 8: + value = *_checkBitmap; + value &= 0x14; + if (value != 0x14) { + return 0; + } + break; + case 4: + value = *_checkBitmap; + value &= 0xA; + if (value != 0xA) { + return 0; + } + break; + case 2: + value = *_checkBitmap; + value &= 0x5; + if (value != 0x5) { + return 0; + } + break; + case 1: + value = READ_LE_UINT16(_checkBitmap); + value &= 0x8002; + if (value != 0x8002) { + return 0; + } + break; + default: + error("Wrong _checkMask value - cpe()"); + break; + } + _checkX = _rembX; + _checkY = _rembY; + _checkBitmapTemp = _rembBitmapTemp; + _checkBitmap = _rembBitmap; + _checkMask = _rembMask; + return -1; + } + return 0; + } + return 0; +} + +int PrinceEngine::checkLeftDownDir() { + if (_checkX && _checkY != (kMaxPicHeight / 2 - 1)) { + int tempMask = _checkMask; + if (tempMask != 128) { + tempMask <<= 1; + if ((*(_checkBitmap + kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp + kPBW) & tempMask)) { + _checkBitmap += kPBW; + _checkBitmapTemp += kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap + kPBW - 1) & 1)) { + if (!(*(_checkBitmapTemp + kPBW - 1) & 1)) { + _checkBitmap += (kPBW - 1); + _checkBitmapTemp += (kPBW - 1); + _checkMask = 1; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX--; + _checkY++; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkLeftDir() { + if (_checkX) { + int tempMask = _checkMask; + if (tempMask != 128) { + tempMask <<= 1; + if ((*(_checkBitmap) & tempMask)) { + if (!(*(_checkBitmapTemp) & tempMask)) { + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap - 1) & 1)) { + if (!(*(_checkBitmapTemp - 1) & 1)) { + _checkBitmap--; + _checkBitmapTemp--; + _checkMask = 1; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX--; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkDownDir() { + if (_checkY != (kMaxPicHeight / 2 - 1)) { + if ((*(_checkBitmap + kPBW) & _checkMask)) { + if (!(*(_checkBitmapTemp + kPBW) & _checkMask)) { + _checkBitmap += kPBW; + _checkBitmapTemp += kPBW; + _checkY++; + return cpe(); + } else { + return 1; + } + } else { + return -1; + } + } else { + return -1; + } +} + +int PrinceEngine::checkUpDir() { + if (_checkY) { + if ((*(_checkBitmap - kPBW) & _checkMask)) { + if (!(*(_checkBitmapTemp - kPBW) & _checkMask)) { + _checkBitmap -= kPBW; + _checkBitmapTemp -= kPBW; + _checkY--; + return cpe(); + } else { + return 1; + } + } else { + return -1; + } + } else { + return -1; + } +} + +int PrinceEngine::checkRightDir() { + if (_checkX != (kMaxPicWidth / 2 - 1)) { + int tempMask = _checkMask; + if (tempMask != 1) { + tempMask >>= 1; + if ((*(_checkBitmap) & tempMask)) { + if (!(*(_checkBitmapTemp) & tempMask)) { + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap + 1) & 128)) { + if (!(*(_checkBitmapTemp + 1) & 128)) { + _checkBitmap++; + _checkBitmapTemp++; + _checkMask = 128; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX++; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkLeftUpDir() { + if (_checkX && _checkY) { + int tempMask = _checkMask; + if (tempMask != 128) { + tempMask <<= 1; + if ((*(_checkBitmap - kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp - kPBW) & tempMask)) { + _checkBitmap -= kPBW; + _checkBitmapTemp -= kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap - (kPBW + 1)) & 1)) { + if (!(*(_checkBitmapTemp - (kPBW + 1)) & 1)) { + _checkBitmap -= (kPBW + 1); + _checkBitmapTemp -= (kPBW + 1); + _checkMask = 1; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX--; + _checkY--; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkRightDownDir() { + if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY != (kMaxPicHeight / 2 - 1)) { + int tempMask = _checkMask; + if (tempMask != 1) { + tempMask >>= 1; + if ((*(_checkBitmap + kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp + kPBW) & tempMask)) { + _checkBitmap += kPBW; + _checkBitmapTemp += kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap + kPBW + 1) & 128)) { + if (!(*(_checkBitmapTemp + kPBW + 1) & 128)) { + _checkBitmap += kPBW + 1; + _checkBitmapTemp += kPBW + 1; + _checkMask = 128; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX++; + _checkY++; + return cpe(); + } else { + return -1; + } +} + +int PrinceEngine::checkRightUpDir() { + if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY) { + int tempMask = _checkMask; + if (tempMask != 1) { + tempMask >>= 1; + if ((*(_checkBitmap - kPBW) & tempMask)) { + if (!(*(_checkBitmapTemp - kPBW) & tempMask)) { + _checkBitmap -= kPBW; + _checkBitmapTemp -= kPBW; + _checkMask = tempMask; + } else { + return 1; + } + } else { + return -1; + } + } else { + if ((*(_checkBitmap - kPBW + 1) & 128)) { + if (!(*(_checkBitmapTemp - kPBW + 1) & 128)) { + _checkBitmap -= (kPBW - 1); + _checkBitmapTemp -= (kPBW - 1); + _checkMask = 128; + } else { + return 1; + } + } else { + return -1; + } + } + _checkX++; + _checkY--; + return cpe(); + } else { + return -1; + } +} + +bool PrinceEngine::tracePath(int x1, int y1, int x2, int y2) { + for (uint i = 0; i < kPathBitmapLen; i++) { + _roomPathBitmapTemp[i] = 0; + } + if (x1 != x2 || y1 != y2) { + if (getPixelAddr(_roomPathBitmap, x1, y1)) { + if (getPixelAddr(_roomPathBitmap, x2, y2)) { + _coords = _coordsBuf; + specialPlot(x1, y1); + + int x = x1; + int y = y1; + + while (1) { + int btx = x; + int bty = y; + byte *bcad = _coords; + + _traceLineLen = 0; + _traceLineFirstPointFlag = true; + int drawLineFlag = drawLine(x, y, x2, y2, &this->plotTraceLine, this); + + if (!drawLineFlag) { + return true; + } else if (drawLineFlag == -1 && _traceLineLen >= 2) { + byte *tempCorrds = bcad; + while (tempCorrds != _coords) { + x = READ_LE_UINT16(tempCorrds); + y = READ_LE_UINT16(tempCorrds + 2); + tempCorrds += 4; + specialPlot2(x, y); + } + } else { + _coords = bcad; + x = btx; + y = bty; + } + + Direction dir = makeDirection(x, y, x2, y2); + + _rembBitmapTemp = &_roomPathBitmapTemp[x / 8 + y * 80]; + _rembBitmap = &_roomPathBitmap[x / 8 + y * 80]; + _rembMask = 128 >> (x & 7); + _rembX = x; + _rembY = y; + + _checkBitmapTemp = _rembBitmapTemp; + _checkBitmap = _rembBitmap; + _checkMask = _rembMask; + _checkX = _rembX; + _checkY = _rembY; + + int result; + switch (dir) { + case kDirLD: + result = leftDownDir(); + break; + case kDirL: + result = leftDir(); + break; + case kDirLU: + result = leftUpDir(); + break; + case kDirRD: + result = rightDownDir(); + break; + case kDirR: + result = rightDir(); + break; + case kDirRU: + result = rightUpDir(); + break; + case kDirUL: + result = upLeftDir(); + break; + case kDirU: + result = upDir(); + break; + case kDirUR: + result = upRightDir(); + break; + case kDirDL: + result = downLeftDir(); + break; + case kDirD: + result = downDir(); + break; + case kDirDR: + result = downRightDir(); + break; + default: + result = -1; + error("tracePath: wrong direction %d", dir); + break; + } + + if (result) { + byte *tempCoords = _coords; + tempCoords -= 4; + if (tempCoords > _coordsBuf) { + int tempX = READ_LE_UINT16(tempCoords); + int tempY = READ_LE_UINT16(tempCoords + 2); + if (_checkX == tempX && _checkY == tempY) { + _coords = tempCoords; + } + x = READ_LE_UINT16(tempCoords); + y = READ_LE_UINT16(tempCoords + 2); + } else { + return false; + } + } else { + x = _checkX; + y = _checkY; + } + } + return true; + } else { + error("tracePath: wrong destination point"); + } + } else { + error("tracePath: wrong start point"); + } + } else { + error("tracePath: same point"); + } +} + +void PrinceEngine::specialPlotInside2(int x, int y) { + WRITE_LE_UINT16(_coords2, x); + _coords2 += 2; + WRITE_LE_UINT16(_coords2, y); + _coords2 += 2; +} + +int PrinceEngine::plotTracePoint(int x, int y, void *data) { + PrinceEngine *tracePoint = (PrinceEngine *)data; + if (!tracePoint->_tracePointFirstPointFlag) { + if (tracePoint->getPixelAddr(tracePoint->_roomPathBitmap, x, y)) { + tracePoint->specialPlotInside2(x, y); + return 0; + } else { + return -1; + } + } else { + tracePoint->_tracePointFirstPointFlag = false; + return 0; + } +} + +void PrinceEngine::approxPath() { + byte *oldCoords; + _coords2 = _coordsBuf2; + byte *tempCoordsBuf = _coordsBuf; // first point on path + byte *tempCoords = _coords; + if (tempCoordsBuf != tempCoords) { + tempCoords -= 4; // last point on path + while (tempCoordsBuf != tempCoords) { + int x1 = READ_LE_UINT16(tempCoords); + int y1 = READ_LE_UINT16(tempCoords + 2); + int x2 = READ_LE_UINT16(tempCoordsBuf); + int y2 = READ_LE_UINT16(tempCoordsBuf + 2); + tempCoordsBuf += 4; + //TracePoint + oldCoords = _coords2; + if (_coords2 == _coordsBuf2) { + WRITE_LE_UINT16(_coords2, x1); + WRITE_LE_UINT16(_coords2 + 2, y1); + _coords2 += 4; + } else { + int testX = READ_LE_UINT16(_coords2 - 4); + int testY = READ_LE_UINT16(_coords2 - 2); + if (testX != x1 || testY != y1) { + WRITE_LE_UINT16(_coords2, x1); + WRITE_LE_UINT16(_coords2 + 2, y1); + _coords2 += 4; + } + } + _tracePointFirstPointFlag = true; + bool drawLineFlag = drawLine(x1, y1, x2, y2, &this->plotTracePoint, this); + if (!drawLineFlag) { + tempCoords = tempCoordsBuf - 4; + tempCoordsBuf = _coordsBuf; + } else { + _coords2 = oldCoords; + } + } + } +} + +void PrinceEngine::freeDirectionTable() { + if (_directionTable != nullptr) { + free(_directionTable); + _directionTable = nullptr; + } +} + +int PrinceEngine::scanDirectionsFindNext(byte *tempCoordsBuf, int xDiff, int yDiff) { + + int tempX, tempY, direction; + + tempX = Hero::kHeroDirLeft; + if (xDiff < 0) { + tempX = Hero::kHeroDirRight; + } + + tempY = Hero::kHeroDirUp; + if (yDiff < 0) { + tempY = Hero::kHeroDirDown; + } + + while (1) { + int againPointX1 = READ_LE_UINT16(tempCoordsBuf); + int againPointY1 = READ_LE_UINT16(tempCoordsBuf + 2); + tempCoordsBuf += 4; + + if (tempCoordsBuf == _coords) { + direction = tempX; + break; + } + + int dX = againPointX1 - READ_LE_UINT16(tempCoordsBuf); + int dY = againPointY1 - READ_LE_UINT16(tempCoordsBuf + 2); + + if (dX != xDiff) { + direction = tempY; + break; + } + + if (dY != yDiff) { + direction = tempX; + break; + } + } + return direction; +} + +void PrinceEngine::scanDirections() { + freeDirectionTable(); + byte *tempCoordsBuf = _coordsBuf; + if (tempCoordsBuf != _coords) { + int size = (_coords - tempCoordsBuf) / 4 + 1; // number of coord points plus one for end marker + _directionTable = (byte *)malloc(size); + byte *tempDirTab = _directionTable; + int direction = -1; + int lastDirection = -1; + + while (1) { + int x1 = READ_LE_UINT16(tempCoordsBuf); + int y1 = READ_LE_UINT16(tempCoordsBuf + 2); + tempCoordsBuf += 4; + if (tempCoordsBuf == _coords) { + break; + } + int x2 = READ_LE_UINT16(tempCoordsBuf); + int y2 = READ_LE_UINT16(tempCoordsBuf + 2); + + int xDiff = x1 - x2; + int yDiff = y1 - y2; + + if (xDiff) { + if (yDiff) { + if (lastDirection != -1) { + direction = lastDirection; + if (direction == Hero::kHeroDirLeft) { + if (xDiff < 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else if (direction == Hero::kHeroDirRight) { + if (xDiff >= 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else if (direction == Hero::kHeroDirUp) { + if (yDiff < 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else { + if (yDiff >= 0) { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } + } else { + direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff); + } + } else { + direction = Hero::kHeroDirLeft; + if (xDiff < 0) { + direction = Hero::kHeroDirRight; + } + } + } else { + if (yDiff) { + direction = Hero::kHeroDirUp; + if (yDiff < 0) { + direction = Hero::kHeroDirDown; + } + } else { + direction = lastDirection; + } + } + lastDirection = direction; + *tempDirTab = direction; + tempDirTab++; + } + *tempDirTab = *(tempDirTab - 1); + tempDirTab++; + *tempDirTab = 0; + } +} + +void PrinceEngine::moveShandria() { + int shanLen1 = _shanLen; + if (_flags->getFlagValue(Flags::SHANDOG)) { + _secondHero->freeHeroAnim(); + _secondHero->freeOldMove(); + byte *shanCoords = _mainHero->_currCoords + shanLen1 * 4 - 4; + int shanX = READ_LE_UINT16(shanCoords - 4); + int shanY = READ_LE_UINT16(shanCoords - 2); + int xDiff = shanX - _secondHero->_middleX; + if (xDiff < 0) { + xDiff *= -1; + } + int yDiff = shanY - _secondHero->_middleY; + if (yDiff < 0) { + yDiff *= -1; + } + shanCoords -= 4; + if (shanCoords != _mainHero->_currCoords) { + yDiff *= 1.5; + int shanDis = xDiff * xDiff + yDiff * yDiff; + if (shanDis >= kMinDistance) { + while (1) { + shanCoords -= 4; + if (shanCoords == _mainHero->_currCoords) { + break; + } + int x = READ_LE_UINT16(shanCoords); + int y = READ_LE_UINT16(shanCoords + 2); + int pointDiffX = x - shanX; + if (pointDiffX < 0) { + pointDiffX *= -1; + } + int pointDiffY = y - shanY; + if (pointDiffY < 0) { + pointDiffY *= -1; + } + pointDiffY *= 1.5; + int distance = pointDiffX * pointDiffX + pointDiffY * pointDiffY; + if (distance >= kMinDistance) { + break; + } + } + int pathSizeDiff = (shanCoords - _mainHero->_currCoords) / 4; + int destDir = *(_mainHero->_currDirTab + pathSizeDiff); + _secondHero->_destDirection = destDir; + int destX = READ_LE_UINT16(shanCoords); + int destY = READ_LE_UINT16(shanCoords + 2); + _secondHero->_coords = makePath(kSecondHero, _secondHero->_middleX, _secondHero->_middleY, destX, destY); + if (_secondHero->_coords != nullptr) { + _secondHero->_currCoords = _secondHero->_coords; + int delay = shanLen1 - _shanLen; + if (delay < 6) { + delay = 6; + } + _secondHero->_moveDelay = delay / 2; + _secondHero->_state = Hero::kHeroStateDelayMove; + _secondHero->_dirTab = _directionTable; + _secondHero->_currDirTab = _directionTable; + _directionTable = nullptr; + } + } + } + } +} + +byte *PrinceEngine::makePath(int heroId, int currX, int currY, int destX, int destY) { + int realDestX = destX; + int realDestY = destY; + _flags->setFlagValue(Flags::MOVEDESTX, destX); + _flags->setFlagValue(Flags::MOVEDESTY, destY); + + int x1 = currX / 2; + int y1 = currY / 2; + int x2 = destX / 2; + int y2 = destY / 2; + + if ((x1 != x2) || (y1 != y2)) { + findPoint(x1, y1); + if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) { + return nullptr; + } + if ((x1 != _fpX) || (y1 != _fpY)) { + x1 = _fpX; + y1 = _fpY; + } + findPoint(x2, y2); + if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) { + return nullptr; + } + if ((x2 != _fpX) || (y2 != _fpY)) { + x2 = _fpX; + y2 = _fpY; + if (!_flags->getFlagValue(Flags::EXACTMOVE)) { + realDestX = x2 * 2; + realDestY = y2 * 2; + _flags->setFlagValue(Flags::MOVEDESTX, realDestX); + _flags->setFlagValue(Flags::MOVEDESTY, realDestY); + } else { + return nullptr; + } + } + + if ((x1 == x2) && (y1 == y2)) { + if (!heroId) { + _mainHero->freeOldMove(); + _mainHero->_state = Hero::kHeroStateTurn; + } else if (heroId == 1) { + _secondHero->freeOldMove(); + _secondHero->_state = Hero::kHeroStateTurn; + } + return nullptr; + } + + int pathLen1 = 0; + int pathLen2 = 0; + int stX = x1; + int stY = y1; + int sizeCoords2 = 0; + + if (tracePath(x1, y1, x2, y2)) { + allocCoords2(); + approxPath(); + sizeCoords2 = _coords2 - _coordsBuf2; + for (int i = 0; i < sizeCoords2; i++) { + _coordsBuf[i] = _coordsBuf2[i]; + } + _coords = _coordsBuf + sizeCoords2; + approxPath(); + _coordsBuf3 = _coordsBuf2; + _coordsBuf2 = nullptr; + _coords3 = _coords2; + _coords2 = nullptr; + pathLen1 = _coords3 - _coordsBuf3; + } + if (tracePath(x2, y2, x1, y1)) { + allocCoords2(); + approxPath(); + sizeCoords2 = _coords2 - _coordsBuf2; + for (int i = 0; i < sizeCoords2; i++) { + _coordsBuf[i] = _coordsBuf2[i]; + } + _coords = _coordsBuf + sizeCoords2; + approxPath(); + pathLen2 = _coords2 - _coordsBuf2; + } + + byte *chosenCoordsBuf = _coordsBuf2; + byte *choosenCoords = _coords2; + int choosenLength = pathLen1; + if (pathLen1 < pathLen2) { + chosenCoordsBuf = _coordsBuf3; + choosenCoords = _coords3; + choosenLength = pathLen2; + } + + if (choosenLength) { + if (chosenCoordsBuf != nullptr) { + int tempXBegin = READ_LE_UINT16(chosenCoordsBuf); + int tempYBegin = READ_LE_UINT16(chosenCoordsBuf + 2); + if (stX != tempXBegin || stY != tempYBegin) { + SWAP(chosenCoordsBuf, choosenCoords); + chosenCoordsBuf -= 4; + byte *tempCoordsBuf = _coordsBuf; + while (1) { + int cord = READ_LE_UINT32(chosenCoordsBuf); + WRITE_LE_UINT32(tempCoordsBuf, cord); + tempCoordsBuf += 4; + if (chosenCoordsBuf == choosenCoords) { + break; + } + chosenCoordsBuf -= 4; + } + _coords = tempCoordsBuf; + } else { + int sizeChoosen = choosenCoords - chosenCoordsBuf; + for (int i = 0; i < sizeChoosen; i++) { + _coordsBuf[i] = chosenCoordsBuf[i]; + } + _coords = _coordsBuf + sizeChoosen; + } + WRITE_LE_UINT32(_coords, 0xFFFFFFFF); + freeCoords2(); + freeCoords3(); + scanDirections(); + + byte *tempCoordsBuf = _coordsBuf; + byte *tempCoords = _coords; + byte *newCoords; + if (tempCoordsBuf != tempCoords) { + int normCoordsSize = _coords - _coordsBuf + 4; + newCoords = (byte *)malloc(normCoordsSize); + byte *newCoordsBegin = newCoords; + while (tempCoordsBuf != tempCoords) { + int newValueX = READ_LE_UINT16(tempCoordsBuf); + WRITE_LE_UINT16(newCoords, newValueX * 2); + newCoords += 2; + int newValueY = READ_LE_UINT16(tempCoordsBuf + 2); + WRITE_LE_UINT16(newCoords, newValueY * 2); + newCoords += 2; + tempCoordsBuf += 4; + } + WRITE_LE_UINT16(newCoords - 4, realDestX); + WRITE_LE_UINT16(newCoords - 2, realDestY); + WRITE_LE_UINT32(newCoords, 0xFFFFFFFF); + newCoords += 4; + _shanLen = (newCoords - newCoordsBegin); + _shanLen /= 4; + return newCoordsBegin; + } + } + } + _coords = _coordsBuf; + freeCoords2(); + freeCoords3(); + return nullptr; + } else { + if (!heroId) { + _mainHero->freeOldMove(); + _mainHero->_state = Hero::kHeroStateTurn; + } else if (heroId == 1) { + _secondHero->freeOldMove(); + _secondHero->_state = Hero::kHeroStateTurn; + } + return nullptr; + } +} + +void PrinceEngine::allocCoords2() { + if (_coordsBuf2 == nullptr) { + _coordsBuf2 = (byte *)malloc(kTracePts * 4); + _coords2 = _coordsBuf2; + } +} + +void PrinceEngine::freeCoords2() { + if (_coordsBuf2 != nullptr) { + free(_coordsBuf2); + _coordsBuf2 = nullptr; + _coords2 = nullptr; + } +} + +void PrinceEngine::freeCoords3() { + if (_coordsBuf3 != nullptr) { + free(_coordsBuf3); + _coordsBuf3 = nullptr; + _coords3 = nullptr; + } +} + +void PrinceEngine::openInventoryCheck() { + if (!_optionsFlag) { + if (_mouseFlag == 1 || _mouseFlag == 2) { + if (_mainHero->_visible) { + if (!_flags->getFlagValue(Flags::INVALLOWED)) { + // 29 - Basement, 50 - Map + if (_locationNr != 29 && _locationNr != 50) { + Common::Point mousePos = _system->getEventManager()->getMousePos(); + if (mousePos.y < 4 && !_showInventoryFlag) { + _invCounter++; + } else { + _invCounter = 0; + } + if (_invCounter >= _invMaxCount) { + inventoryFlagChange(true); + } + } + } + } + } + } +} + +void PrinceEngine::mainLoop() { + changeCursor(0); + _currentTime = _system->getMillis(); + + while (!shouldQuit()) { + Common::Event event; + Common::EventManager *eventMan = _system->getEventManager(); + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + keyHandler(event); + break; + case Common::EVENT_LBUTTONDOWN: + leftMouseButton(); + break; + case Common::EVENT_RBUTTONDOWN: + rightMouseButton(); + break; + default: + break; + } + } + + if (shouldQuit()) { + return; + } + + // for "throw a rock" mini-game + mouseWeirdo(); + + _interpreter->stepBg(); + _interpreter->stepFg(); + + drawScreen(); + + _graph->update(_graph->_frontScreen); + + openInventoryCheck(); + + pausePrinceEngine(); + } +} + +} // End of namespace Prince diff --git a/engines/prince/prince.h b/engines/prince/prince.h new file mode 100644 index 0000000000..6dce044a41 --- /dev/null +++ b/engines/prince/prince.h @@ -0,0 +1,674 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_H +#define PRINCE_H + +#include "common/random.h" +#include "common/system.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/textconsole.h" +#include "common/rect.h" +#include "common/events.h" +#include "common/endian.h" +#include "common/savefile.h" +#include "common/serializer.h" + +#include "image/bmp.h" + +#include "gui/debugger.h" + +#include "engines/engine.h" +#include "engines/util.h" + +#include "audio/mixer.h" + +#include "video/flic_decoder.h" + +#include "prince/mob.h" +#include "prince/object.h" +#include "prince/pscr.h" + +namespace Prince { + +struct PrinceGameDescription; +struct SavegameHeader; + +class PrinceEngine; +class GraphicsMan; +class Script; +class Interpreter; +class InterpreterFlags; +class Debugger; +class MusicPlayer; +class VariaTxt; +class Cursor; +class MhwanhDecoder; +class Font; +class Hero; +class Animation; +class Room; +class Pscr; + +struct Text { + const char *_str; + uint16 _x, _y; + uint16 _time; + uint32 _color; + + Text() : _str(nullptr), _x(0), _y(0), _time(0), _color(255){ + } +}; + +struct AnimListItem { + uint16 _type; // type of animation - for background anims RND of frame + uint16 _fileNumber; + uint16 _startPhase; // first phase number + uint16 _endPhase; + uint16 _loopPhase; + int16 _x; + int16 _y; + uint16 _loopType; + uint16 _nextAnim; // number of animation to do for loop = 3 + uint16 _flags; // byte 0 - draw masks, byte 1 - draw in front of mask, byte 2 - load but turn off drawing + bool loadFromStream(Common::SeekableReadStream &stream); +}; + +struct BAS { + int32 _type; // type of sequence + int32 _data; // additional data + int32 _anims; // number of animations + int32 _current; // actual number of animation + int32 _counter; // time counter for animation + int32 _currRelative; //actual relative number for animation + int32 _data2; // additional data for measurements +}; + +const int kStructSizeBAS = 28; + +struct BASA { + int16 _num; // animation number + int16 _start; // initial frame + int16 _end; // final frame + //int16 _pad; // fulfilment to 8 bytes +}; + +const int kStructSizeBASA = 8; + +// background and normal animation +struct Anim { + BASA _basaData; + int32 _addr; //animation adress + int16 _usage; + int16 _state; // state of animation: 0 - turning on, 1 - turning off + int16 _flags; + int16 _frame; // number of phase to show + int16 _lastFrame; // last phase + int16 _loopFrame; // first frame of loop + int16 _showFrame; // actual visible frame of animation + int16 _loopType; // type of loop (0 - last frame; 1 - normal loop (begin from _loopFrame); 2 - no loop; 3 - load new animation) + int16 _nextAnim; // number of next animation to load after actual + int16 _x; + int16 _y; + int32 _currFrame; + int16 _currX; + int16 _currY; + int16 _currW; + int16 _currH; + int16 _packFlag; + int32 _currShadowFrame; + int16 _packShadowFlag; + int32 _shadowBack; + int16 _relX; + int16 _relY; + Animation *_animData; + Animation *_shadowData; + + enum AnimOffsets { + kAnimState = 10, + kAnimFrame = 14, + kAnimLastFrame = 16, + kAnimX = 26 + }; + + int16 getAnimData(Anim::AnimOffsets offset) { + switch (offset) { + case kAnimState: + return _state; + case kAnimFrame: + return _frame + 1; // fix for location 30 - man with a dog animation + case kAnimX: + return _x; + default: + error("getAnimData() - Wrong offset type: %d", (int) offset); + } + } + + void setAnimData(Anim::AnimOffsets offset, int16 value) { + if (offset == kAnimX) { + _x = value; + } else { + error("setAnimData() - Wrong offset: %d, value: %d", (int) offset, value); + } + } +}; + +struct BackgroundAnim { + BAS _seq; + Common::Array<Anim> backAnims; +}; + +enum AnimType { + kBackgroundAnimation, + kNormalAnimation +}; + +// Nak (PL - Nakladka) +struct Mask { + uint16 _state; // visible / invisible + int16 _flags; // turning on / turning off of an mask + int16 _x1; + int16 _y1; + int16 _x2; + int16 _y2; + int16 _z; + int16 _number; // number of mask for background recreating + int16 _width; + int16 _height; + byte *_data; + + int16 getX() const { + return READ_LE_UINT16(_data); + } + + int16 getY() const { + return READ_LE_UINT16(_data + 2); + } + + int16 getWidth() const { + return READ_LE_UINT16(_data + 4); + } + + int16 getHeight() const { + return READ_LE_UINT16(_data + 6); + } + + byte *getMask() const { + return (byte *)(_data + 8); + } +}; + +struct InvItem { + int _x; + int _y; + Graphics::Surface *_surface; + Graphics::Surface *getSurface() const { return _surface; } +}; + +struct DrawNode { + int posX; + int posY; + int posZ; + int32 width; + int32 height; + int32 scaleValue; + Graphics::Surface *s; + Graphics::Surface *originalRoomSurface; + void *data; + void (*drawFunction)(Graphics::Surface *, DrawNode *); +}; + +struct DebugChannel { + +enum Type { + kScript, + kEngine +}; + +}; + +class PrinceEngine : public Engine { +protected: + Common::Error run(); + +public: + PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc); + virtual ~PrinceEngine(); + + virtual bool hasFeature(EngineFeature f) const; + virtual void pauseEngineIntern(bool pause); + virtual bool canSaveGameStateCurrently(); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual Common::Error loadGameState(int slot); + + static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + Common::String generateSaveName(int slot); + void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header); + void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream); + bool loadGame(int slotNumber); + void resetGame(); + + int32 _creditsDataSize; + byte *_creditsData; + void scrollCredits(); + + int getGameType() const; + const char *getGameId() const; + uint32 getFeatures() const; + Common::Language getLanguage() const; + + const PrinceGameDescription *_gameDescription; + Video::FlicDecoder _flicPlayer; + const Graphics::Surface *_flcFrameSurface; + VariaTxt *_variaTxt; + + uint32 _talkTxtSize; + byte *_talkTxt; + + uint32 _mobTranslationSize; + byte *_mobTranslationData; + + bool loadLocation(uint16 locationNr); + bool loadAnim(uint16 animNr, bool loop); + bool loadVoice(uint32 textSlot, uint32 sampleSlot, const Common::String &name); + bool loadSample(uint32 sampleSlot, const Common::String &name); + bool loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName); + bool loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2); + bool loadTrans(byte *transTable, const char *resourceName); + bool loadMobPriority(const char *resourceName); + + void loadMobTranslationTexts(); + void setMobTranslationTexts(); + + bool loadMusic(int musNumber); + void stopMusic(); + + void playSample(uint16 sampleId, uint16 loopType); + void stopSample(uint16 sampleId); + void stopAllSamples(); + void freeSample(uint16 sampleId); + void freeAllSamples(); + + void setVoice(uint16 slot, uint32 sampleSlot, uint16 flag); + + virtual GUI::Debugger *getDebugger(); + + void changeCursor(uint16 curId); + void printAt(uint32 slot, uint8 color, char *s, uint16 x, uint16 y); + int calcTextLines(const char *s); + int calcTextTime(int numberOfLines); + void correctStringDEU(char *s); + + static const uint8 kMaxTexts = 32; + Text _textSlots[kMaxTexts]; + + Hero *_mainHero; + Hero *_secondHero; + + enum HeroId { + kMainHero, + kSecondHero + }; + + int _mouseFlag; + uint32 _currentTime; + uint16 _locationNr; + uint16 _sceneWidth; + int32 _picWindowX; + int32 _picWindowY; + + Image::BitmapDecoder *_roomBmp; + MhwanhDecoder *_suitcaseBmp; + Room *_room; + Script *_script; + InterpreterFlags *_flags; + Interpreter *_interpreter; + GraphicsMan *_graph; + uint8 _currentMidi; + byte *_zoomBitmap; + byte *_shadowBitmap; + byte *_transTable; + + int16 _scaleValue; // scale for hero or special shadow animation + int16 _lightX; // for hero shadow + int16 _lightY; + int32 _shadScaleValue; + int32 _shadLineLen; + byte *_shadowLine; + void setShadowScale(int32 shadowScale); + static void plotShadowLinePoint(int x, int y, int color, void *data); + + static const int16 kFPS = 15; + static const int32 kIntMax = 2147483647; + + static const int16 kMaxPicWidth = 1280; + static const int16 kMaxPicHeight = 480; + static const int16 kZoomStep = 4; + static const int32 kZoomBitmapLen = kMaxPicHeight / kZoomStep * kMaxPicWidth / kZoomStep; + static const int32 kShadowBitmapSize = kMaxPicWidth * kMaxPicHeight / 8; + static const int16 kShadowLineArraySize = 2 * 1280 * 4; + static const int16 kZoomBitmapWidth = kMaxPicWidth / kZoomStep; + static const int16 kZoomBitmapHeight = kMaxPicHeight / kZoomStep; + static const int16 kNormalWidth = 640; + static const int16 kNormalHeight = 480; + static const uint32 kTransTableSize = 256 * 256; + + static const int kMaxNormAnims = 64; + static const int kMaxBackAnims = 64; + static const int kMaxObjects = 64; + static const int kMaxMobs = 64; + + Common::Array<DrawNode> _drawNodeList; + Common::Array<AnimListItem> _animList; + Common::Array<BackgroundAnim> _backAnimList; + Common::Array<Anim> _normAnimList; + Common::Array<Mob> _mobList; + Common::Array<uint32> _mobPriorityList; + Common::Array<Mask> _maskList; + Common::Array<Object *> _objList; + uint16 *_objSlot; + + void freeNormAnim(int slot); + void freeAllNormAnims(); + void removeSingleBackAnim(int slot); + + Common::RandomSource _randomSource; + + void checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z); + void insertMasks(Graphics::Surface *originalRoomSurface); + void showMask(int maskNr, Graphics::Surface *originalRoomSurface); + void clsMasks(); + + void grabMap(); + + int _selectedMob; // number of selected Mob / inventory item + int _selectedItem; // number of item on mouse cursor + int _selectedMode; + int _currentPointerNumber; + + static const int16 kMaxInv = 90; // max amount of inventory items in whole game + static const uint16 kMaxItems = 30; // size of inventory + + uint32 _invTxtSize; + byte *_invTxt; + + Graphics::Surface *_optionsPic; + Graphics::Surface *_optionsPicInInventory; + + bool _optionsFlag; + int _optionEnabled; + int _optionsMob; + int _optionsX; + int _optionsY; + int _optionsWidth; + int _optionsHeight; + int _invOptionsWidth; + int _invOptionsHeight; + int _optionsStep; + int _invOptionsStep; + int _optionsNumber; + int _invOptionsNumber; + int _optionsColor1; // color for non-selected options + int _optionsColor2; // color for selected option + + bool _showInventoryFlag; + int _invExamY; + bool _inventoryBackgroundRemember; + int _invLineX; + int _invLineY; + int _invLine; // number of items in one line + int _invLines; // number of lines with inventory items + int _invLineW; + int _invLineH; + int _maxInvW; + int _maxInvH; + int _invLineSkipX; + int _invLineSkipY; + int _invX1; + int _invY1; + int _invWidth; + int _invHeight; + bool _invCurInside; + int _mst_shadow; + int _mst_shadow2; // blinking after adding new item + int _candleCounter; // special counter for candle inventory item + int _invMaxCount; // time to turn inventory on + int _invCounter; // turning on counter + + void inventoryFlagChange(bool inventoryState); + bool loadAllInv(); + void rememberScreenInv(); + void prepareInventoryToView(); + void drawInvItems(); + void displayInventory(); + void addInv(int heroId, int item, bool addItemQuiet); + void remInv(int heroId, int item); + void clearInv(int heroId); + void swapInv(int heroId); + void addInvObj(); + void makeInvCursor(int itemNr); + void enableOptions(bool checkType); + void checkOptions(); + void checkInvOptions(); + void openInventoryCheck(); + + void leftMouseButton(); + void rightMouseButton(); + void inventoryLeftMouseButton(); + void inventoryRightMouseButton(); + void dialogLeftMouseButton(byte *string, int dialogSelected); + + uint32 _dialogDatSize; + byte *_dialogDat; + byte *_dialogData; // on, off flags for lines of dialog text + + byte *_dialogBoxAddr[32]; // adresses of dialog windows + byte *_dialogOptAddr[32]; // adresses of dialog options + int _dialogOptLines[4 * 32]; // numbers of initial dialog lines + + byte *_dialogText; + int _dialogLines; + bool _dialogFlag; + int _dialogWidth; + int _dialogHeight; + int _dialogLineSpace; + int _dialogColor1; // color for non-selected options + int _dialogColor2; // color for selected option + Graphics::Surface *_dialogImage; + + void createDialogBox(int dialogBoxNr); + void dialogRun(); + void talkHero(int slot); + void doTalkAnim(int animNumber, int slot, AnimType animType); + + static const uint8 zoomInStep = 8; + void initZoomIn(int slot); + void initZoomOut(int slot); + void doZoomIn(int slot); + void doZoomOut(int slot); + void freeZoomObject(int slot); + + static const uint8 kFadeStep = 4; + void blackPalette(); + void setPalette(const byte *palette); + + int getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY); + + // 'Throw a rock' mini-game: + static const int16 kCurveLen = 17; + static const int kCelStep = 4; + int16 *_curveData; + int _curvPos; + void makeCurve(); + void getCurve(); + void mouseWeirdo(); + + static const uint16 kPowerBarPosX = 288; + static const uint16 kPowerBarPosY = 430; + static const uint8 kPowerBarWidth = 64; + static const uint8 kPowerBarHeight = 16; + static const uint8 kPowerBarBackgroundColor = 0; + static const uint16 kPowerBarGreenPosY = 434; + static const uint8 kPowerBarGreenColor1 = 202; + static const uint8 kPowerBarGreenColor2 = 235; + static const uint8 kPowerBarGreenHeight = 8; + void showPower(); + + // Pathfinding + static const int16 kPathGridStep = 2; + static const uint32 kPathBitmapLen = (kMaxPicHeight / kPathGridStep * kMaxPicWidth / kPathGridStep) / 8; + static const int32 kTracePts = 8000; + static const int32 kPBW = kMaxPicWidth / 16; // PathBitmapWidth + static const int kMinDistance = 2500; + + byte *_roomPathBitmap; // PL - Sala + byte *_roomPathBitmapTemp; // PL - SSala + byte *_coordsBufEnd; + byte *_coordsBuf; // optimal path + byte *_coords; // last path point adress from coordsBuf + byte *_coordsBuf2; + byte *_coords2; + byte *_coordsBuf3; + byte *_coords3; + int _traceLineLen; + bool _traceLineFirstPointFlag; // if plotTraceLine after first point + bool _tracePointFirstPointFlag; // if plotTracePoint after first point + byte *_directionTable; + int _shanLen; + + byte *_checkBitmapTemp; + byte *_checkBitmap; + int _checkMask; + int _checkX; + int _checkY; + + byte *_rembBitmapTemp; + byte *_rembBitmap; + int _rembMask; + int _rembX; + int _rembY; + + int _fpX; + int _fpY; + + int drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data); + bool loadPath(const char *resourceName); + byte *makePath(int heroId, int currX, int currY, int destX, int destY); + void findPoint(int x, int y); + int getPixelAddr(byte *pathBitmap, int x, int y); + static int plotTraceLine(int x, int y, void *data); + void specialPlotInside(int x, int y); + bool tracePath(int x1, int y1, int x2, int y2); + Direction makeDirection(int x1, int y1, int x2, int y2); + void specialPlot(int x, int y); + void specialPlot2(int x, int y); + void allocCoords2(); + void freeCoords2(); + void freeCoords3(); + static int plotTracePoint(int x, int y, void *data); + void specialPlotInside2(int x, int y); + void approxPath(); + void freeDirectionTable(); + void scanDirections(); + int scanDirectionsFindNext(byte *coords, int xDiff, int yDiff); + void moveShandria(); + void walkTo(); + void moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag); + + int leftDownDir(); + int leftDir(); + int leftUpDir(); + int rightDownDir(); + int rightDir(); + int rightUpDir(); + int upLeftDir(); + int upDir(); + int upRightDir(); + int downLeftDir(); + int downDir(); + int downRightDir(); + + int cpe(); + int checkLeftDownDir(); + int checkLeftDir(); + int checkDownDir(); + int checkUpDir(); + int checkRightDir(); + int checkLeftUpDir(); + int checkRightDownDir(); + int checkRightUpDir(); + +private: + bool playNextFLCFrame(); + void keyHandler(Common::Event event); + int checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList); + void drawScreen(); + void showTexts(Graphics::Surface *screen); + void init(); + void showLogo(); + void showAnim(Anim &anim); + void showNormAnims(); + void setBackAnim(Anim &backAnim); + void showBackAnims(); + void clearBackAnimList(); + bool spriteCheck(int sprWidth, int sprHeight, int destX, int destY); + void showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ); + void showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ); + void showObjects(); + void showParallax(); + static bool compareDrawNodes(DrawNode d1, DrawNode d2); + void runDrawNodes(); + void makeShadowTable(int brightness); + void pausePrinceEngine(int fps = kFPS); + + uint32 getTextWidth(const char *s); + void debugEngine(const char *s, ...); + + uint8 _cursorNr; + + Common::RandomSource *_rnd; + Cursor *_cursor1; + Graphics::Surface *_cursor2; + Cursor *_cursor3; + Debugger *_debugger; + Font *_font; + MusicPlayer *_midiPlayer; + + static const int kMaxSamples = 60; + Audio::RewindableAudioStream *_audioStream[kMaxSamples]; + Audio::SoundHandle _soundHandle[kMaxSamples]; + + Common::Array<PScr *> _pscrList; + Common::Array<InvItem> _allInvList; + Common::Array<Mob> _invMobList; + + bool _flicLooped; + + void mainLoop(); + +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/pscr.cpp b/engines/prince/pscr.cpp new file mode 100644 index 0000000000..d9d36a3356 --- /dev/null +++ b/engines/prince/pscr.cpp @@ -0,0 +1,75 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/archive.h" +#include "common/stream.h" + +#include "prince/pscr.h" + +namespace Prince { + +PScr::PScr() : _x(0), _y(0), _step(0), _surface(nullptr) +{ +} + +PScr::~PScr() { + if (_surface != nullptr) { + _surface->free(); + delete _surface; + _surface = nullptr; + } +} + +void PScr::loadSurface(Common::SeekableReadStream &stream) { + stream.skip(4); + int width = stream.readUint16LE(); + int height = stream.readUint16LE(); + _surface = new Graphics::Surface(); + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + for (int h = 0; h < _surface->h; h++) { + stream.read(_surface->getBasePtr(0, h), _surface->w); + } +} + +bool PScr::loadFromStream(Common::SeekableReadStream &stream) { + int32 pos = stream.pos(); + uint16 file = stream.readUint16LE(); + if (file == 0xFFFF) { + return false; + } + _x = stream.readUint16LE(); + _y = stream.readUint16LE(); + _step = stream.readUint16LE(); + + const Common::String pscrStreamName = Common::String::format("PS%02d", file); + Common::SeekableReadStream *pscrStream = SearchMan.createReadStreamForMember(pscrStreamName); + if (pscrStream != nullptr) { + loadSurface(*pscrStream); + } + delete pscrStream; + stream.seek(pos + 12); // size of PScrList struct + + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/pscr.h b/engines/prince/pscr.h new file mode 100644 index 0000000000..fdcdb524a9 --- /dev/null +++ b/engines/prince/pscr.h @@ -0,0 +1,48 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_PSCR_H +#define PRINCE_PSCR_H + +#include "graphics/surface.h" + +namespace Prince { + +class PScr { +public: + PScr(); + ~PScr(); + int16 _x; + int16 _y; + int16 _step; + static const int16 kPScrZ = 1000; + + bool loadFromStream(Common::SeekableReadStream &stream); + Graphics::Surface *getSurface() const { return _surface; } +private: + void loadSurface(Common::SeekableReadStream &stream); + Graphics::Surface *_surface; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/resource.h b/engines/prince/resource.h new file mode 100644 index 0000000000..f42eb87842 --- /dev/null +++ b/engines/prince/resource.h @@ -0,0 +1,100 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_RESOURCE_H +#define PRINCE_RESOURCE_H + +#include "common/stream.h" +#include "common/archive.h" +#include "common/debug-channels.h" +#include "common/ptr.h" + +namespace Prince { + +namespace Resource { + + template <typename T> + bool loadFromStream(T &resource, Common::SeekableReadStream &stream) { + return resource.loadStream(stream); + } + + template<typename T> + bool loadResource(T *resource, const char *resourceName, bool required) { + Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName)); + if (!stream) { + if (required) + error("Can't load %s", resourceName); + return false; + } + + return loadFromStream(*resource, *stream); + } + + template <typename T> + bool loadResource(Common::Array<T> &array, Common::SeekableReadStream &stream, bool required = true) { + T t; + while (t.loadFromStream(stream)) + array.push_back(t); + + return true; + } + + + template <typename T> + bool loadResource(Common::Array<T> &array, const char *resourceName, bool required = true) { + Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName)); + if (!stream) { + if (required) + error("Can't load %s", resourceName); + return false; + } + + return loadResource(array, *stream, required); + } + + template <typename T> + bool loadResource(Common::Array<T *> &array, const char *resourceName, bool required = true) { + + Common::ScopedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(resourceName)); + if (!stream) { + if (required) + error("Can't load %s", resourceName); + return false; + } + + // FIXME: This is stupid. Maybe loadFromStream should be helper method that returns initiailzed object + while (true) { + T* t = new T(); + if (!t->loadFromStream(*stream)) { + delete t; + break; + } + array.push_back(t); + } + return true; + } + +} + +} // End of namespace Prince + +#endif diff --git a/engines/prince/saveload.cpp b/engines/prince/saveload.cpp new file mode 100644 index 0000000000..46e598be70 --- /dev/null +++ b/engines/prince/saveload.cpp @@ -0,0 +1,531 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/prince.h" +#include "prince/graphics.h" +#include "prince/detection.h" +#include "prince/flags.h" +#include "prince/script.h" +#include "prince/hero.h" + +#include "common/savefile.h" +#include "common/system.h" +#include "common/config-manager.h" +#include "common/memstream.h" + +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" + +namespace Prince { + +#define kBadSVG 99 +#define kSavegameVersion 1 +#define kSavegameStrSize 14 +#define kSavegameStr "SCUMMVM_PRINCE" + +class InterpreterFlags; +class Interpreter; + +struct SavegameHeader { + uint8 version; + Common::String saveName; + Graphics::Surface *thumbnail; + int saveYear, saveMonth, saveDay; + int saveHour, saveMinutes; +}; + +int PrinceMetaEngine::getMaximumSaveSlot() const { + return 99; +} + +SaveStateList PrinceMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".???"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); filename++) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(filename->c_str() + filename->size() - 3); + + if (slotNum >= 0 && slotNum <= 99) { + + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + Prince::SavegameHeader header; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + file->read(buffer, kSavegameStrSize + 1); + + if (!strncmp(buffer, kSavegameStr, kSavegameStrSize + 1)) { + // Valid savegame + if (Prince::PrinceEngine::readSavegameHeader(file, header)) { + saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); + if (header.thumbnail) { + header.thumbnail->free(); + delete header.thumbnail; + } + } + } else { + // Must be an original format savegame + saveList.push_back(SaveStateDescriptor(slotNum, "Unknown")); + } + + delete file; + } + } + } + + return saveList; +} + +SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); + + if (f) { + Prince::SavegameHeader header; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + f->read(buffer, kSavegameStrSize + 1); + + bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) && + Prince::PrinceEngine::readSavegameHeader(f, header); + delete f; + + if (!hasHeader) { + // Original savegame perhaps? + SaveStateDescriptor desc(slot, "Unknown"); + return desc; + } else { + // Create the return descriptor + SaveStateDescriptor desc(slot, header.saveName); + desc.setThumbnail(header.thumbnail); + desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); + desc.setSaveTime(header.saveHour, header.saveMinutes); + + return desc; + } + } + + return SaveStateDescriptor(); +} + +bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { + header.thumbnail = nullptr; + + // Get the savegame version + header.version = in->readByte(); + if (header.version > kSavegameVersion) + return false; + + // Read in the string + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) + return false; + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +void PrinceMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} + +bool PrinceEngine::canSaveGameStateCurrently() { + if (_mouseFlag && _mouseFlag != 3) { + if (_mainHero->_visible) { + // 29 - Basement + if (_locationNr != 29) { + // No dialog box and not in inventory + if (!_dialogFlag && !_showInventoryFlag) { + return true; + } + } + } + } + return false; +} + +bool PrinceEngine::canLoadGameStateCurrently() { + if (_mouseFlag && _mouseFlag != 3) { + if (_mainHero->_visible) { + // 29 - Basement + if (_locationNr != 29) { + // No dialog box and not in inventory + if (!_dialogFlag && !_showInventoryFlag) { + return true; + } + } + } + } + return false; +} + +Common::Error PrinceEngine::saveGameState(int slot, const Common::String &desc) { + // Set up the serializer + Common::String slotName = generateSaveName(slot); + Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName); + + // Write out the ScummVM savegame header + SavegameHeader header; + header.saveName = desc; + header.version = kSavegameVersion; + writeSavegameHeader(saveFile, header); + + // Write out the data of the savegame + syncGame(nullptr, saveFile); + + // Finish writing out game data + saveFile->finalize(); + delete saveFile; + + return Common::kNoError; +} + +Common::String PrinceEngine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +void PrinceEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) { + // Write out a savegame header + out->write(kSavegameStr, kSavegameStrSize + 1); + + out->writeByte(kSavegameVersion); + + // Write savegame name + out->write(header.saveName.c_str(), header.saveName.size() + 1); + + // Get the active palette + uint8 thumbPalette[256 * 3]; + _system->getPaletteManager()->grabPalette(thumbPalette, 0, 256); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + Graphics::Surface *s = _graph->_frontScreen; // check inventory / map etc.. + ::createThumbnail(thumb, (const byte *)s->getPixels(), s->w, s->h, thumbPalette); + Graphics::saveThumbnail(*out, *thumb); + thumb->free(); + delete thumb; + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); +} + +void PrinceEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) { + int emptyRoom = 0x00; + int normRoom = 0xFF; + byte endInv = 0xFF; + + Common::Serializer s(readStream, writeStream); + + if (s.isSaving()) { + // Flag values + for (int i = 0; i < _flags->kMaxFlags; i++) { + uint32 value = _flags->getFlagValue((Flags::Id)(_flags->kFlagMask + i)); + s.syncAsUint32LE(value); + } + + // Dialog data + for (uint32 i = 0; i < _dialogDatSize; i++) { + byte value = _dialogDat[i]; + s.syncAsByte(value); + } + + // Location number + s.syncAsUint16LE(_locationNr); + + // Rooms + for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) { + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + + if (room->_mobs) { + s.syncAsByte(normRoom); + } else { + s.syncAsByte(emptyRoom); + delete room; + continue; + } + + // Mobs + for (int mobId = 0; mobId < kMaxMobs; mobId++) { + byte value = _script->getMobVisible(room->_mobs, mobId); + s.syncAsByte(value); + } + + // Background animations + for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) { + uint32 value = _script->getBackAnimId(room->_backAnim, backAnimSlot); + s.syncAsUint32LE(value); + } + + // Objects + for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) { + byte value = _script->getObjId(room->_obj, objectSlot); + s.syncAsByte(value); + } + + delete room; + } + + // Main hero + s.syncAsUint16LE(_mainHero->_visible); + s.syncAsUint16LE(_mainHero->_middleX); + s.syncAsUint16LE(_mainHero->_middleY); + s.syncAsUint16LE(_mainHero->_lastDirection); + s.syncAsUint32LE(_mainHero->_color); + s.syncAsUint16LE(_mainHero->_maxBoredom); + s.syncAsUint32LE(_mainHero->_animSetNr); + + for (uint inv1Slot = 0; inv1Slot < _mainHero->_inventory.size(); inv1Slot++) { + s.syncAsByte(_mainHero->_inventory[inv1Slot]); + } + s.syncAsByte(endInv); + + for (uint inv2Slot = 0; inv2Slot < _mainHero->_inventory2.size(); inv2Slot++) { + s.syncAsByte(_mainHero->_inventory2[inv2Slot]); + } + s.syncAsByte(endInv); + + // Second hero + s.syncAsUint16LE(_secondHero->_visible); + s.syncAsUint16LE(_secondHero->_middleX); + s.syncAsUint16LE(_secondHero->_middleY); + s.syncAsUint16LE(_secondHero->_lastDirection); + s.syncAsUint32LE(_secondHero->_color); + s.syncAsUint16LE(_secondHero->_maxBoredom); + s.syncAsUint32LE(_secondHero->_animSetNr); + + for (uint inv1Slot = 0; inv1Slot < _secondHero->_inventory.size(); inv1Slot++) { + s.syncAsByte(_secondHero->_inventory[inv1Slot]); + } + s.syncAsByte(endInv); + + for (uint inv2Slot = 0; inv2Slot < _secondHero->_inventory2.size(); inv2Slot++) { + s.syncAsByte(_secondHero->_inventory2[inv2Slot]); + } + s.syncAsByte(endInv); + + } else { + // Cursor reset + changeCursor(1); + _currentPointerNumber = 1; + + // Flag values + for (int i = 0; i < _flags->kMaxFlags; i++) { + uint32 value = 0; + s.syncAsUint32LE(value); + _flags->setFlagValue((Flags::Id)(_flags->kFlagMask + i), value); + } + + // Dialog data + for (uint32 i = 0; i < _dialogDatSize; i++) { + byte value = 0; + s.syncAsByte(value); + _dialogDat[i] = value; + } + + // Location number + int restoreRoom = 0; + s.syncAsUint16LE(restoreRoom); + _flags->setFlagValue(Flags::RESTOREROOM, restoreRoom); + + // Rooms + for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) { + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + + byte roomType = emptyRoom; + s.syncAsByte(roomType); + if (roomType == emptyRoom) { + delete room; + continue; + } + + // Mobs + for (int mobId = 0; mobId < kMaxMobs; mobId++) { + byte value = 0; + s.syncAsByte(value); + _script->setMobVisible(room->_mobs, mobId, value); + } + + // Background animations + for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) { + uint32 value = 0; + s.syncAsUint32LE(value); + _script->setBackAnimId(room->_backAnim, backAnimSlot, value); + } + + // Objects + for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) { + byte value = 0; + s.syncAsByte(value); + _script->setObjId(room->_obj, objectSlot, value); + } + + delete room; + } + + // Main hero + s.syncAsUint16LE(_mainHero->_visible); + s.syncAsUint16LE(_mainHero->_middleX); + s.syncAsUint16LE(_mainHero->_middleY); + s.syncAsUint16LE(_mainHero->_lastDirection); + s.syncAsUint32LE(_mainHero->_color); + s.syncAsUint16LE(_mainHero->_maxBoredom); + s.syncAsUint32LE(_mainHero->_animSetNr); + _mainHero->loadAnimSet(_mainHero->_animSetNr); + + _mainHero->_inventory.clear(); + byte invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _mainHero->_inventory.push_back(invId); + } + + _mainHero->_inventory2.clear(); + invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _mainHero->_inventory2.push_back(invId); + } + + // Second hero + s.syncAsUint16LE(_secondHero->_visible); + s.syncAsUint16LE(_secondHero->_middleX); + s.syncAsUint16LE(_secondHero->_middleY); + s.syncAsUint16LE(_secondHero->_lastDirection); + s.syncAsUint32LE(_secondHero->_color); + s.syncAsUint16LE(_secondHero->_maxBoredom); + s.syncAsUint32LE(_secondHero->_animSetNr); + _secondHero->loadAnimSet(_secondHero->_animSetNr); + + _secondHero->_inventory.clear(); + invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _secondHero->_inventory.push_back(invId); + } + + _secondHero->_inventory2.clear(); + invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _secondHero->_inventory2.push_back(invId); + } + + // Script + _interpreter->setBgOpcodePC(0); + _interpreter->setFgOpcodePC(_script->_scriptInfo.restoreGame); + + } +} + +Common::Error PrinceEngine::loadGameState(int slot) { + if (!loadGame(slot)) { + return Common::kReadingFailed; + } + return Common::kNoError; +} + +bool PrinceEngine::loadGame(int slotNumber) { + Common::MemoryReadStream *readStream; + + // Open up the savegame file + Common::String slotName = generateSaveName(slotNumber); + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName); + + // Read the data into a data buffer + int size = saveFile->size(); + byte *dataBuffer = (byte *)malloc(size); + saveFile->read(dataBuffer, size); + readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES); + delete saveFile; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + readStream->read(buffer, kSavegameStrSize + 1); + + if (strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) != 0) { + delete readStream; + return false; + } else { + SavegameHeader saveHeader; + + if (!readSavegameHeader(readStream, saveHeader)) { + delete readStream; + return false; + } + + // Delete the thumbnail + saveHeader.thumbnail->free(); + delete saveHeader.thumbnail; + } + + // Get in the savegame + syncGame(readStream, nullptr); + delete readStream; + + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/script.cpp b/engines/prince/script.cpp new file mode 100644 index 0000000000..4ed3cee6f3 --- /dev/null +++ b/engines/prince/script.cpp @@ -0,0 +1,2082 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/script.h" +#include "prince/prince.h" +#include "prince/flags.h" +#include "prince/variatxt.h" +#include "prince/font.h" +#include "prince/hero.h" +#include "prince/resource.h" +#include "prince/animation.h" + +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/archive.h" +#include "common/memstream.h" + +namespace Prince { + +static const uint16 kNumOpcodes = 144; + +Room::Room() {} + +bool Room::loadRoom(byte *roomData) { + int roomSize = 64; + Common::MemoryReadStream roomStream(roomData, roomSize); + + _mobs = roomStream.readSint32LE(); + _backAnim = roomStream.readSint32LE(); + _obj = roomStream.readSint32LE(); + _nak = roomStream.readSint32LE(); + _itemUse = roomStream.readSint32LE(); + _itemGive = roomStream.readSint32LE(); + _walkTo = roomStream.readSint32LE(); + _examine = roomStream.readSint32LE(); + _pickup = roomStream.readSint32LE(); + _use = roomStream.readSint32LE(); + _pushOpen = roomStream.readSint32LE(); + _pullClose = roomStream.readSint32LE(); + _talk = roomStream.readSint32LE(); + _give = roomStream.readSint32LE(); + + return true; +} + +int Room::getOptionOffset(int option) { + switch (option) { + case 0: + return _walkTo; + case 1: + return _examine; + case 2: + return _pickup; + case 3: + return _use; + case 4: + return _pushOpen; + case 5: + return _pullClose; + case 6: + return _talk; + case 7: + return _give; + default: + error("Wrong option - nr %d", option); + } +} + +Script::Script(PrinceEngine *vm) : _vm(vm), _data(nullptr), _dataSize(0) { +} + +Script::~Script() { + if (_data != nullptr) { + free(_data); + _dataSize = 0; + _data = nullptr; + } +} + +bool Script::loadStream(Common::SeekableReadStream &stream) { + _dataSize = stream.size(); + if (!_dataSize) { + return false; + } + + _data = (byte *)malloc(_dataSize); + + if (!_data) { + return false; + } + + stream.read(_data, _dataSize); + + Common::MemoryReadStream scriptDataStream(_data, _dataSize); + _scriptInfo.rooms = scriptDataStream.readSint32LE(); + _scriptInfo.startGame = scriptDataStream.readSint32LE(); + _scriptInfo.restoreGame = scriptDataStream.readSint32LE(); + _scriptInfo.stdExamine = scriptDataStream.readSint32LE(); + _scriptInfo.stdPickup = scriptDataStream.readSint32LE(); + _scriptInfo.stdUse = scriptDataStream.readSint32LE(); + _scriptInfo.stdOpen = scriptDataStream.readSint32LE(); + _scriptInfo.stdClose = scriptDataStream.readSint32LE(); + _scriptInfo.stdTalk = scriptDataStream.readSint32LE(); + _scriptInfo.stdGive = scriptDataStream.readSint32LE(); + _scriptInfo.usdCode = scriptDataStream.readSint32LE(); + _scriptInfo.invObjExam = scriptDataStream.readSint32LE(); + _scriptInfo.invObjUse = scriptDataStream.readSint32LE(); + _scriptInfo.invObjUU = scriptDataStream.readSint32LE(); + _scriptInfo.stdUseItem = scriptDataStream.readSint32LE(); + _scriptInfo.lightSources = scriptDataStream.readSint32LE(); + _scriptInfo.specRout = scriptDataStream.readSint32LE(); + _scriptInfo.invObjGive = scriptDataStream.readSint32LE(); + _scriptInfo.stdGiveItem = scriptDataStream.readSint32LE(); + _scriptInfo.goTester = scriptDataStream.readSint32LE(); + + return true; +} + +uint16 Script::readScript16(uint32 address) { + assert((_data + address + sizeof(uint16)) <= (_data + _dataSize)); + uint16 data = READ_LE_UINT16(&_data[address]); + return data; +} + +uint32 Script::readScript32(uint32 address) { + assert((_data + address + sizeof(uint32)) <= (_data + _dataSize)); + uint32 data = READ_LE_UINT32(&_data[address]); + return data; +} + +int16 Script::getLightX(int locationNr) { + return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8]); +} + +int16 Script::getLightY(int locationNr) { + return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8 + 2]); +} + +int32 Script::getShadowScale(int locationNr) { + return (int)READ_LE_UINT16(&_data[_scriptInfo.lightSources + locationNr * 8 + 4]); +} + +uint32 Script::getStartGameOffset() { + return _scriptInfo.startGame; +} + +uint32 Script::getLocationInitScript(int initRoomTableOffset, int roomNr) { + return (uint32)READ_LE_UINT32(&_data[initRoomTableOffset + roomNr * 4]); +} + +byte Script::getMobVisible(int roomMobOffset, uint16 mob) { + return _data[roomMobOffset + mob]; +} + +void Script::setMobVisible(int roomMobOffset, uint16 mob, byte value) { + _data[roomMobOffset + mob] = value; +} + +uint8 *Script::getRoomOffset(int locationNr) { + return &_data[_scriptInfo.rooms + locationNr * 64]; +} + +int32 Script::getOptionStandardOffset(int option) { + switch (option) { + case 1: + return _scriptInfo.stdExamine; + case 2: + return _scriptInfo.stdPickup; + case 3: + return _scriptInfo.stdUse; + case 4: + return _scriptInfo.stdOpen; + case 5: + return _scriptInfo.stdClose; + case 6: + return _scriptInfo.stdTalk; + case 7: + return _scriptInfo.stdGive; + default: + error("Wrong standard option - nr %d", option); + } +} + +uint8 *Script::getHeroAnimName(int offset) { + return &_data[offset]; +} + +uint32 Script::getBackAnimId(int roomBackAnimOffset, int slot) { + uint32 animId = READ_LE_UINT32(&_data[roomBackAnimOffset + slot * 4]); + return animId; +} + +void Script::setBackAnimId(int roomBackAnimOffset, int slot, int animId) { + WRITE_LE_UINT32(&_data[roomBackAnimOffset + slot * 4], animId); +} + +byte Script::getObjId(int roomObjOffset, int slot) { + return _data[roomObjOffset + slot]; +} + +void Script::setObjId(int roomObjOffset, int slot, byte objectId) { + _data[roomObjOffset + slot] = objectId; +} + +int Script::scanMobEvents(int mobMask, int dataEventOffset) { + debug("mobMask: %d", mobMask); + int i = 0; + int16 mob; + int32 code; + do { + mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 6]); + if (mob == mobMask) { + code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 6 + 2]); + debug("code: %d", code); + return code; + } + i++; + } while (mob != -1); + return -1; +} + +int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask) { + debug("mobMask: %d", mobMask); + int i = 0; + int16 mob; + int16 item; + int32 code; + do { + mob = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8]); + if (mob == mobMask) { + item = (int)READ_LE_UINT16(&_data[dataEventOffset + i * 8 + 2]); + if (item == itemMask) { + code = (int)READ_LE_UINT32(&_data[dataEventOffset + i * 8 + 4]); + debug("itemMask: %d", item); + debug("code: %d", code); + return code; + } + } + i++; + } while (mob != -1); + return -1; +} + +void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset) { + + _vm->removeSingleBackAnim(slot); // free slot before loading + + int offset = roomBackAnimOffset + slot * 4; // BackgroundAnim offset for selected slot number + + BackgroundAnim newBackgroundAnim; // BackgroundAnim seq data and its array of Anim + + int animOffset = READ_LE_UINT32(&_data[offset]); // pos of BackgroundAnim data in script + int anims = READ_LE_UINT32(&_data[animOffset + 8]); // amount of Anim in BackgroundAnim + + if (anims == 0) { + anims = 1; // anims with 0 as amount in game data has just 1 animation + } + + if (animOffset != 0) { + Common::MemoryReadStream stream(_data, _dataSize); // stream from script data + for (int i = 0; i < anims; i++) { + Anim newAnim; + stream.seek(animOffset + kStructSizeBAS + kStructSizeBASA * i); + // Anim BASA data + newAnim._basaData._num = stream.readUint16LE(); + newAnim._basaData._start = stream.readUint16LE(); + newAnim._basaData._end = stream.readUint16LE(); + + // Anim number in game files + int animNumber = newAnim._basaData._num; + const Common::String animName = Common::String::format("AN%02d", animNumber); + const Common::String shadowName = Common::String::format("AN%02dS", animNumber); + newAnim._animData = new Animation(); + newAnim._shadowData = new Animation(); + Resource::loadResource(newAnim._animData, animName.c_str(), true); + if (!Resource::loadResource(newAnim._shadowData, shadowName.c_str(), false)) { + delete newAnim._shadowData; + newAnim._shadowData = nullptr; + } + + newAnim._usage = 0; + newAnim._state = 0; // enabled + if ((_vm->_animList[animNumber]._flags & 4)) { + newAnim._state = 1; + newAnim._frame = _vm->_animList[animNumber]._endPhase; + newAnim._showFrame = _vm->_animList[animNumber]._endPhase; + } else { + newAnim._frame = _vm->_animList[animNumber]._startPhase; + newAnim._showFrame = _vm->_animList[animNumber]._startPhase; + } + newAnim._flags = _vm->_animList[animNumber]._flags; + newAnim._lastFrame = _vm->_animList[animNumber]._endPhase; + newAnim._loopFrame = _vm->_animList[animNumber]._loopPhase; + newAnim._loopType = _vm->_animList[animNumber]._loopType; + newAnim._nextAnim = _vm->_animList[animNumber]._nextAnim; + newAnim._x = _vm->_animList[animNumber]._x; + newAnim._y = _vm->_animList[animNumber]._y; + newAnim._currFrame = 0; + newAnim._currX = _vm->_animList[animNumber]._x; + newAnim._currY = _vm->_animList[animNumber]._y; + newAnim._currW = 0; + newAnim._currH = 0; + newAnim._packFlag = 0; + newAnim._shadowBack = _vm->_animList[animNumber]._type; + newBackgroundAnim.backAnims.push_back(newAnim); + } + + // Anim BAS data + stream.seek(animOffset); + newBackgroundAnim._seq._type = stream.readUint32LE(); + newBackgroundAnim._seq._data = stream.readUint32LE(); + newBackgroundAnim._seq._anims = stream.readUint32LE(); + stream.skip(12); + newBackgroundAnim._seq._current = newBackgroundAnim.backAnims[0]._basaData._num; + newBackgroundAnim._seq._counter = 0; + newBackgroundAnim._seq._currRelative = 0; + newBackgroundAnim._seq._data2 = stream.readUint32LE(); + + int start = newBackgroundAnim.backAnims[0]._basaData._start; // BASA_Start of first frame + int end = newBackgroundAnim.backAnims[0]._basaData._end; // BASA_End of first frame + + if (start != -1) { + newBackgroundAnim.backAnims[0]._frame = start; + newBackgroundAnim.backAnims[0]._showFrame = start; + newBackgroundAnim.backAnims[0]._loopFrame = start; + } + + if (end != -1) { + newBackgroundAnim.backAnims[0]._lastFrame = end; + } + + backAnimList[slot] = newBackgroundAnim; + } +} + +void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset) { + for (int i = 0; i < _vm->kMaxBackAnims; i++) { + installSingleBackAnim(backAnimList, i, roomBackAnimOffset); + } +} + +void Script::installObjects(int offset) { + for (int i = 0; i < _vm->kMaxObjects; i++) { + _vm->_objSlot[i] = _data[offset]; + offset++; + } +} + +bool Script::loadAllMasks(Common::Array<Mask> &maskList, int offset) { + Mask tempMask; + while (1) { + Common::MemoryReadStream maskStream(_data, _dataSize); + maskStream.seek(offset); + tempMask._state = maskStream.readUint16LE(); + if (tempMask._state == 0xffff) { + break; + } + tempMask._flags = maskStream.readUint16LE(); + tempMask._x1 = maskStream.readUint16LE(); + tempMask._y1 = maskStream.readUint16LE(); + tempMask._x2 = maskStream.readUint16LE(); + tempMask._y2 = maskStream.readUint16LE(); + tempMask._z = maskStream.readUint16LE(); + tempMask._number = maskStream.readUint16LE(); + + const Common::String msStreamName = Common::String::format("MS%02d", tempMask._number); + Common::SeekableReadStream *msStream = SearchMan.createReadStreamForMember(msStreamName); + if (!msStream) { + tempMask._width = 0; + tempMask._height = 0; + tempMask._data = nullptr; + debug("Can't load %s", msStreamName.c_str()); + delete msStream; + } else { + int32 dataSize = msStream->size(); + if (dataSize != -1) { + tempMask._data = (byte *)malloc(dataSize); + if (msStream->read(tempMask._data, dataSize) != (uint32)dataSize) { + free(tempMask._data); + delete msStream; + return false; + } + delete msStream; + } + tempMask._width = tempMask.getWidth(); + tempMask._height = tempMask.getHeight(); + } + + maskList.push_back(tempMask); + offset += 16; // size of Mask (Nak) struct + } + return true; +} + +InterpreterFlags::InterpreterFlags() { + resetAllFlags(); +} + +void InterpreterFlags::resetAllFlags() { + memset(_flags, 0, sizeof(_flags)); +} + +void InterpreterFlags::setFlagValue(Flags::Id flagId, int32 value) { + _flags[(uint32)flagId - kFlagMask] = value; +} + +int32 InterpreterFlags::getFlagValue(Flags::Id flagId) { + return _flags[(uint32)flagId - kFlagMask]; +} + +Interpreter::Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags) : + _vm(vm), _script(script), _flags(flags), + _stacktop(0), _opcodeNF(false), _opcodeEnd(false), + _waitFlag(0), _result(true) { + + // Initialize the script + _mode = "fg"; + _fgOpcodePC = _script->getStartGameOffset(); + _bgOpcodePC = 0; +} + +void Interpreter::debugInterpreter(const char *s, ...) { + char buf[STRINGBUFLEN]; + va_list va; + va_start(va, s); + vsnprintf(buf, STRINGBUFLEN, s, va); + va_end(va); + + Common::String str = Common::String::format("@0x%08X: ", _lastInstruction); + str += Common::String::format("op %04d: ", _lastOpcode); + //debugC(10, DebugChannel::kScript, "PrinceEngine::Script %s %s", str.c_str(), buf); + if (!strcmp(_mode, "fg")) { + debug(10, "PrinceEngine::Script %s %s", str.c_str(), buf); + } + //debug("Prince::Script mode %s %s %s", _mode, str.c_str(), buf); +} + +void Interpreter::stepBg() { + if (_bgOpcodePC) { + _mode = "bg"; + _bgOpcodePC = step(_bgOpcodePC); + } +} + +void Interpreter::stepFg() { + if (_fgOpcodePC) { + _mode = "fg"; + _fgOpcodePC = step(_fgOpcodePC); + } +} + +uint32 Interpreter::step(uint32 opcodePC) { + _currentInstruction = opcodePC; + + while (!_opcodeNF) { + _lastInstruction = _currentInstruction; + + // Get the current opcode + _lastOpcode = readScript16(); + + if (_lastOpcode >= kNumOpcodes) + error( + "Trying to execute unknown opcode @0x%04X: %02d", + _currentInstruction, + _lastOpcode); + + // Execute the current opcode + OpcodeFunc op = _opcodes[_lastOpcode]; + (this->*op)(); + if (_opcodeNF) { + _opcodeNF = 0; + break; + } + } + + if (_opcodeEnd) { + _vm->quitGame(); + } + + return _currentInstruction; +} + +void Interpreter::storeNewPC(int opcodePC) { + if (_flags->getFlagValue(Flags::GETACTION) == 1) { + _flags->setFlagValue(Flags::GETACTIONDATA, opcodePC); + opcodePC = _flags->getFlagValue(Flags::GETACTIONBACK); + } + _fgOpcodePC = opcodePC; +} + +int Interpreter::getLastOPCode() { + return _lastOpcode; +} + +int Interpreter::getFgOpcodePC() { + return _fgOpcodePC; +} + +uint32 Interpreter::getCurrentString() { + return _currentString; +} + +void Interpreter::setCurrentString(uint32 value) { + _currentString = value; +} + +byte *Interpreter::getString() { + return _string; +} + +void Interpreter::setString(byte *newString) { + _string = newString; +} + +void Interpreter::increaseString() { + while (*_string) { + _string++; + } + _string++; +} + +void Interpreter::setResult(byte value) { + _result = value; +} + +void Interpreter::setBgOpcodePC(uint32 value) { + _bgOpcodePC = value; +} + +void Interpreter::setFgOpcodePC(uint32 value) { + _fgOpcodePC = value; +} + +uint16 Interpreter::readScript16() { + uint16 data = _script->readScript16(_currentInstruction); + _currentInstruction += sizeof(uint16); + return data; +} + +uint32 Interpreter::readScript32() { + uint32 data = _script->readScript32(_currentInstruction); + _currentInstruction += sizeof(uint32); + return data; +} + +int32 Interpreter::readScriptFlagValue() { + uint16 value = readScript16(); + if (value & InterpreterFlags::kFlagMask) { + return _flags->getFlagValue((Flags::Id)value); + } + return value; +} + +Flags::Id Interpreter::readScriptFlagId() { + return (Flags::Id)readScript16(); +} + +void Interpreter::O_WAITFOREVER() { + _vm->changeCursor(_vm->_currentPointerNumber); + _opcodeNF = 1; + _currentInstruction -= 2; + //debugInterpreter("O_WAITFOREVER"); +} + +void Interpreter::O_BLACKPALETTE() { + _vm->blackPalette(); + debugInterpreter("O_BLACKPALETTE"); +} + +void Interpreter::O_SETUPPALETTE() { + _vm->setPalette(_vm->_roomBmp->getPalette()); + debugInterpreter("O_SETUPPALETTE"); +} + +void Interpreter::O_INITROOM() { + int32 roomId = readScriptFlagValue(); + _vm->loadLocation(roomId); + _opcodeNF = 1; + debugInterpreter("O_INITROOM %d", roomId); +} + +void Interpreter::O_SETSAMPLE() { + int32 sampleId = readScriptFlagValue(); + int32 sampleNameOffset = readScript32(); + const char *sampleName = _script->getString(_currentInstruction + sampleNameOffset - 4); + _vm->loadSample(sampleId, sampleName); + debugInterpreter("O_SETSAMPLE %d %s", sampleId, sampleName); +} + +void Interpreter::O_FREESAMPLE() { + int32 sampleId = readScriptFlagValue(); + _vm->freeSample(sampleId); + debugInterpreter("O_FREESAMPLE sampleId: %d", sampleId); +} + +void Interpreter::O_PLAYSAMPLE() { + int32 sampleId = readScriptFlagValue(); + uint16 loopType = readScript16(); + _vm->playSample(sampleId, loopType); + debugInterpreter("O_PLAYSAMPLE sampleId %d loopType %d", sampleId, loopType); +} + +void Interpreter::O_PUTOBJECT() { + int32 roomId = readScriptFlagValue(); + int32 slot = readScriptFlagValue(); + int32 objectId = readScriptFlagValue(); + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + _vm->_script->setObjId(room->_obj, slot, objectId); + if (_vm->_locationNr == roomId) { + _vm->_objSlot[slot] = objectId; + } + delete room; + debugInterpreter("O_PUTOBJECT roomId %d, slot %d, objectId %d", roomId, slot, objectId); +} + +void Interpreter::O_REMOBJECT() { + int32 roomId = readScriptFlagValue(); + int32 slot = readScriptFlagValue(); + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + _vm->_script->setObjId(room->_obj, slot, 0xFF); + if (_vm->_locationNr == roomId) { + _vm->_objSlot[slot] = 0xFF; + } + delete room; + debugInterpreter("O_REMOBJECT roomId %d slot %d", roomId, slot); +} + +void Interpreter::O_SHOWANIM() { + int32 slot = readScriptFlagValue(); + int32 animId = readScriptFlagValue(); + _vm->freeNormAnim(slot); + Anim &anim = _vm->_normAnimList[slot]; + AnimListItem &animList = _vm->_animList[animId]; + anim._currFrame = 0; + anim._packFlag = 0; + anim._state = 0; + anim._frame = animList._startPhase; + anim._showFrame = animList._startPhase; + anim._lastFrame = animList._endPhase; + anim._loopFrame = animList._loopPhase; + anim._x = animList._x; + anim._y = animList._y; + anim._loopType = animList._loopType; + anim._shadowBack = animList._type; + anim._flags = animList._flags; + anim._nextAnim = animList._nextAnim; + int fileNumber = animList._fileNumber; + const Common::String animName = Common::String::format("AN%02d", fileNumber); + const Common::String shadowName = Common::String::format("AN%02dS", fileNumber); + anim._animData = new Animation(); + anim._shadowData = new Animation(); + Resource::loadResource(anim._animData, animName.c_str(), true); + if (!Resource::loadResource(anim._shadowData, shadowName.c_str(), false)) { + delete anim._shadowData; + anim._shadowData = nullptr; + } + + // WALKAROUND: fix for turning off bard's wife background animation + // in front of bard's house (location 7) after giving her poem (item 33) + // in script: GiveLetter (line 11082) + if (_currentInstruction == kGiveLetterScriptFix) { + _vm->_backAnimList[1].backAnims[0]._state = 1; + } + + debugInterpreter("O_SHOWANIM slot %d, animId %d", slot, animId); +} + +void Interpreter::O_CHECKANIMEND() { + int32 slot = readScriptFlagValue(); + if (_vm->_normAnimList[slot]._frame != _vm->_normAnimList[slot]._lastFrame - 1) { + _currentInstruction -= 4; + _opcodeNF = 1; + } + debugInterpreter("O_CHECKANIMEND slot %d", slot); +} + +void Interpreter::O_FREEANIM() { + int32 slot = readScriptFlagValue(); + _vm->freeNormAnim(slot); + debugInterpreter("O_FREEANIM slot %d", slot); +} + +void Interpreter::O_CHECKANIMFRAME() { + int32 slot = readScriptFlagValue(); + int32 frameNumber = readScriptFlagValue(); + if (_vm->_normAnimList[slot]._frame != frameNumber - 1) { + _currentInstruction -= 6; + _opcodeNF = 1; + } + debugInterpreter("O_CHECKANIMFRAME slot %d, frameNumber %d", slot, frameNumber); +} + +void Interpreter::O_PUTBACKANIM() { + int32 roomId = readScriptFlagValue(); + int32 slot = readScriptFlagValue(); + int32 animId = readScript32(); + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + _vm->_script->setBackAnimId(room->_backAnim, slot, animId); + if (_vm->_locationNr == roomId) { + _vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, room->_backAnim); + } + delete room; + + // WALKAROUND: fix for turning on 'walking bird' background animation too soon, + // after completing 'throw a rock' mini-game in Silmaniona location. + // Second bird shouldn't appear when normal animation is still in use + // in script lines 13814 and 13848 + if (_currentInstruction == kSecondBirdAnimationScriptFix) { + if (_vm->_normAnimList[1]._state == 0) { + _vm->_backAnimList[0].backAnims[0]._state = 1; + } + } + + debugInterpreter("O_PUTBACKANIM roomId %d, slot %d, animId %d", roomId, slot, animId); +} + +void Interpreter::O_REMBACKANIM() { + int32 roomId = readScriptFlagValue(); + int32 slot = readScriptFlagValue(); + if (_vm->_locationNr == roomId) { + _vm->removeSingleBackAnim(slot); + } + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + _vm->_script->setBackAnimId(room->_backAnim, slot, 0); + delete room; + debugInterpreter("O_REMBACKANIM roomId %d, slot %d", roomId, slot); +} + +void Interpreter::O_CHECKBACKANIMFRAME() { + int32 slotId = readScriptFlagValue(); + int32 frameId = readScriptFlagValue(); + int currAnim = _vm->_backAnimList[slotId]._seq._currRelative; + if (_vm->_backAnimList[slotId].backAnims[currAnim]._frame != frameId - 1) { + _currentInstruction -= 6; + _opcodeNF = 1; + } + debugInterpreter("O_CHECKBACKANIMFRAME slotId %d, frameId %d", slotId, frameId); +} + +// Not used in script +void Interpreter::O_FREEALLSAMPLES() { + error("O_FREEALLSAMPLES"); +} + +void Interpreter::O_SETMUSIC() { + uint16 musicId = readScript16(); + _vm->loadMusic(musicId); + debugInterpreter("O_SETMUSIC musicId %d", musicId); +} + +void Interpreter::O_STOPMUSIC() { + _vm->stopMusic(); + debugInterpreter("O_STOPMUSIC"); +} + +void Interpreter::O__WAIT() { + int32 pause = readScriptFlagValue(); + debugInterpreter("O__WAIT pause %d", pause); + if (!_waitFlag) { + // set new wait flag value and continue + _waitFlag = pause; + _opcodeNF = 1; + _currentInstruction -= 4; + return; + } + _waitFlag--; + if (_waitFlag > 0) { + _opcodeNF = 1; + _currentInstruction -= 4; + return; + } +} + +// Not used in script +void Interpreter::O_UPDATEOFF() { + error("O_UPDATEOFF"); +} + +// Not used in script +void Interpreter::O_UPDATEON() { + error("O_UPDATEON"); +} + +// Not used in script +void Interpreter::O_UPDATE () { + error("O_UPDATE"); +} + +// Not used in script +void Interpreter::O_CLS() { + error("O_CLS"); +} + +void Interpreter::O__CALL() { + int32 address = readScript32(); + _stack[_stacktop] = _currentInstruction; + _stacktop++; + _currentInstruction += address - 4; + debugInterpreter("O__CALL 0x%04X", _currentInstruction); +} + +void Interpreter::O_RETURN() { + if (_stacktop > 0) { + _stacktop--; + _currentInstruction = _stack[_stacktop]; + debugInterpreter("O_RETURN 0x%04X", _currentInstruction); + } else { + error("O_RETURN: Stack is empty"); + } +} + +void Interpreter::O_GO() { + int32 opPC = readScript32(); + _currentInstruction += opPC - 4; + debugInterpreter("O_GO 0x%04X", opPC); +} + +void Interpreter::O_BACKANIMUPDATEOFF() { + int32 slotId = readScriptFlagValue(); + int currAnim = _vm->_backAnimList[slotId]._seq._currRelative; + if (!_vm->_backAnimList[slotId].backAnims.empty()) { + _vm->_backAnimList[slotId].backAnims[currAnim]._state = 1; + } + debugInterpreter("O_BACKANIMUPDATEOFF slotId %d", slotId); +} + +void Interpreter::O_BACKANIMUPDATEON() { + int32 slotId = readScriptFlagValue(); + int currAnim = _vm->_backAnimList[slotId]._seq._currRelative; + if (!_vm->_backAnimList[slotId].backAnims.empty()) { + _vm->_backAnimList[slotId].backAnims[currAnim]._state = 0; + } + debugInterpreter("O_BACKANIMUPDATEON slotId %d", slotId); +} + +void Interpreter::O_CHANGECURSOR() { + int32 cursorId = readScriptFlagValue(); + _vm->changeCursor(cursorId); + debugInterpreter("O_CHANGECURSOR %x", cursorId); +} + +// Not used in script +void Interpreter::O_CHANGEANIMTYPE() { + error("O_CHANGEANIMTYPE"); +} + +void Interpreter::O__SETFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _flags->setFlagValue((Flags::Id)(flagId), value); + debugInterpreter("O__SETFLAG 0x%04X (%s) = %d", flagId, Flags::getFlagName(flagId), value); +} + +void Interpreter::O_COMPARE() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _result = _flags->getFlagValue(flagId) != value; + debugInterpreter("O_COMPARE flagId 0x%04X (%s), value %d == %d (%d)", flagId, Flags::getFlagName(flagId), value, _flags->getFlagValue(flagId), _result); +} + +void Interpreter::O_JUMPZ() { + int32 offset = readScript32(); + if (!_result) { + _currentInstruction += offset - 4; + } + debugInterpreter("O_JUMPZ result = %d, next %08x, offset 0x%08X", _result, _currentInstruction, offset); +} + +void Interpreter::O_JUMPNZ() { + int32 offset = readScript32(); + if (_result) { + _currentInstruction += offset - 4; + } + debugInterpreter("O_JUMPNZ result = %d, next %08x, offset 0x%08X", _result, _currentInstruction, offset); +} + +void Interpreter::O_EXIT() { + int32 exitCode = readScriptFlagValue(); + _opcodeEnd = true; + _opcodeNF = 1; + if (exitCode == 0x2EAD) { + _vm->scrollCredits(); + } + debugInterpreter("O_EXIT exitCode %d", exitCode); +} + +void Interpreter::O_ADDFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) + value); + if (_flags->getFlagValue(flagId)) { + _result = 1; + } + else { + _result = 0; + } + debugInterpreter("O_ADDFLAG flagId %04x (%s), value %d", flagId, Flags::getFlagName(flagId), value); +} + +void Interpreter::O_TALKANIM() { + int32 animNumber = readScriptFlagValue(); + int32 slot = readScriptFlagValue(); + _vm->doTalkAnim(animNumber, slot, kNormalAnimation); + debugInterpreter("O_TALKANIM animNumber %d, slot %d", animNumber, slot); +} + +void Interpreter::O_SUBFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) - value); + if (_flags->getFlagValue(flagId)) { + _result = 1; + } + else { + _result = 0; + } + debugInterpreter("O_SUBFLAG flagId %d, value %d", flagId, value); +} + +void Interpreter::O_SETSTRING() { + int32 offset = readScript32(); + _currentString = offset; + if (offset >= 80000) { + _string = _vm->_variaTxt->getString(offset - 80000); + debugInterpreter("GetVaria %s", _string); + } + else if (offset < 2000) { + _vm->_dialogData = &_vm->_dialogDat[offset * 4 - 4]; + uint32 of = READ_LE_UINT32(_vm->_talkTxt + offset * 4); + const char *txt = (const char *)&_vm->_talkTxt[of]; + _string = &_vm->_talkTxt[of]; + debugInterpreter("TalkTxt %d %s", of, txt); + } + debugInterpreter("O_SETSTRING %04d", offset); +} + +void Interpreter::O_ANDFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) & value); + if (_flags->getFlagValue(flagId)) { + _result = 1; + } else { + _result = 0; + } + debugInterpreter("O_ANDFLAG flagId %d, value %d", flagId, value); +} + +void Interpreter::O_GETMOBDATA() { + Flags::Id flagId = readScriptFlagId(); + int32 mobId = readScriptFlagValue(); + int32 mobOffset = readScriptFlagValue(); + int16 value = _vm->_mobList[mobId].getData((Mob::AttrId)mobOffset); + _flags->setFlagValue(flagId, value); + debugInterpreter("O_GETMOBDATA flagId %d, modId %d, mobOffset %d", flagId, mobId, mobOffset); +} + +void Interpreter::O_ORFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) | value); + if (_flags->getFlagValue(flagId)) { + _result = 1; + } else { + _result = 0; + } + debugInterpreter("O_ORFLAG flagId %d, value %d", flagId, value); +} + +void Interpreter::O_SETMOBDATA() { + int32 mobId = readScriptFlagValue(); + int32 mobOffset = readScriptFlagValue(); + int32 value = readScriptFlagValue(); + _vm->_mobList[mobId].setData((Mob::AttrId)mobOffset, value); + debugInterpreter("O_SETMOBDATA mobId %d, mobOffset %d, value %d", mobId, mobOffset, value); +} + +void Interpreter::O_XORFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 value = readScriptFlagValue(); + _flags->setFlagValue(flagId, _flags->getFlagValue(flagId) ^ value); + if (_flags->getFlagValue(flagId)) { + _result = 1; + } else { + _result = 0; + } + debugInterpreter("O_XORFLAG flagId %d, value %d", flagId, value); +} + +void Interpreter::O_GETMOBTEXT() { + int32 mob = readScriptFlagValue(); + _currentString = _vm->_locationNr * 100 + mob + 60001; + _string = (byte *)_vm->_mobList[mob]._examText.c_str(); + debugInterpreter("O_GETMOBTEXT mob %d", mob); +} + +void Interpreter::O_MOVEHERO() { + int32 heroId = readScriptFlagValue(); + int32 x = readScriptFlagValue(); + int32 y = readScriptFlagValue(); + int32 dir = readScriptFlagValue(); + _vm->moveRunHero(heroId, x, y, dir, false); + debugInterpreter("O_MOVEHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir); +} + +void Interpreter::O_WALKHERO() { + int32 heroId = readScriptFlagValue(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else if (heroId == 1) { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + if (hero->_state != Hero::kHeroStateStay) { + _currentInstruction -= 4; + _opcodeNF = 1; + } + } + debugInterpreter("O_WALKHERO %d", heroId); +} + +void Interpreter::O_SETHERO() { + int32 heroId = readScriptFlagValue(); + int32 x = readScriptFlagValue(); + int32 y = readScriptFlagValue(); + int32 dir = readScriptFlagValue(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else if (heroId == 1) { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + hero->setPos(x, y); + hero->_lastDirection = dir; + hero->_visible = 1; + hero->countDrawPosition(); + } + debugInterpreter("O_SETHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir); +} + +void Interpreter::O_HEROOFF() { + int32 heroId = readScriptFlagValue(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else if (heroId == 1) { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + hero->setVisible(false); + } + debugInterpreter("O_HEROOFF %d", heroId); +} + +void Interpreter::O_HEROON() { + int32 heroId = readScriptFlagValue(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else if (heroId == 1) { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + hero->setVisible(true); + } + debugInterpreter("O_HEROON %d", heroId); +} + +void Interpreter::O_CLSTEXT() { + int32 slot = readScriptFlagValue(); + _vm->_textSlots[slot]._str = nullptr; + _vm->_textSlots[slot]._time = 0; + debugInterpreter("O_CLSTEXT slot %d", slot); +} + +void Interpreter::O_CALLTABLE() { + Flags::Id flagId = readScriptFlagId(); + int roomNr = _flags->getFlagValue(flagId); + int32 tableOffset = readScript32(); + int initLocationScript = _script->getLocationInitScript(tableOffset, roomNr); + if (initLocationScript) { + _stack[_stacktop] = _currentInstruction; + _stacktop++; + _currentInstruction = initLocationScript; + } + debugInterpreter("O_CALLTABLE loc %d", roomNr); +} + +void Interpreter::O_CHANGEMOB() { + int32 mob = readScriptFlagValue(); + int32 value = readScriptFlagValue(); + value ^= 1; + _vm->_script->setMobVisible(_vm->_room->_mobs, mob, value); + _vm->_mobList[mob]._visible = value; + debugInterpreter("O_CHANGEMOB mob %d, value %d", mob, value); +} + +void Interpreter::O_ADDINV() { + int32 hero = readScriptFlagValue(); + int32 item = readScriptFlagValue(); + _vm->addInv(hero, item, false); + debugInterpreter("O_ADDINV hero %d, item %d", hero, item); +} + +void Interpreter::O_REMINV() { + int32 hero = readScriptFlagValue(); + int32 item = readScriptFlagValue(); + _vm->remInv(hero, item); + debugInterpreter("O_REMINV hero %d, item %d", hero, item); +} + +// Not used in script +void Interpreter::O_REPINV() { + error("O_REPINV"); +} + +// Not used in script +void Interpreter::O_OBSOLETE_GETACTION() { + error("O_OBSOLETE_GETACTION"); +} + +// Not used in script +void Interpreter::O_ADDWALKAREA() { + error("O_ADDWALKAREA"); +} + +// Not used in script +void Interpreter::O_REMWALKAREA() { + error("O_REMWALKAREA"); +} + + // Not used in script +void Interpreter::O_RESTOREWALKAREA() { + error("O_RESTOREWALKAREA"); +} + +void Interpreter::O_WAITFRAME() { + _opcodeNF = true; + debugInterpreter("O_WAITFRAME"); +} + +void Interpreter::O_SETFRAME() { + int32 anim = readScriptFlagValue(); + int32 frame = readScriptFlagValue(); + _vm->_normAnimList[anim]._frame = frame; + debugInterpreter("O_SETFRAME anim %d, frame %d", anim, frame); +} + +// Not used in script +void Interpreter::O_RUNACTION() { + error("O_RUNACTION"); +} + +void Interpreter::O_COMPAREHI() { + Flags::Id flag = readScriptFlagId(); + int32 value = readScriptFlagValue(); + int32 flagValue = _flags->getFlagValue(flag); + if (flagValue > value) { + _result = 0; + } else { + _result = 1; + } + debugInterpreter("O_COMPAREHI flag %04x - (%s), value %d, flagValue %d, result %d", flag, Flags::getFlagName(flag), value, flagValue, _result); +} + +void Interpreter::O_COMPARELO() { + Flags::Id flag = readScriptFlagId(); + int32 value = readScriptFlagValue(); + int32 flagValue = _flags->getFlagValue(flag); + if (flagValue < value) { + _result = 0; + } else { + _result = 1; + } + debugInterpreter("O_COMPARELO flag %04x - (%s), value %d, flagValue %d, result %d", flag, Flags::getFlagName(flag), value, flagValue, _result); +} + +// Not used in script +void Interpreter::O_PRELOADSET() { + error("O_PRELOADSET"); +} + +// Not used in script +void Interpreter::O_FREEPRELOAD() { + error("O_FREEPRELOAD"); +} + +// Not used in script +void Interpreter::O_CHECKINV() { + error("O_CHECKINV"); +} + +void Interpreter::O_TALKHERO() { + int32 hero = readScriptFlagValue(); + _vm->talkHero(hero); + debugInterpreter("O_TALKHERO hero %d", hero); +} + +void Interpreter::O_WAITTEXT() { + int32 slot = readScriptFlagValue(); + Text &text = _vm->_textSlots[slot]; + if (text._time && text._str) { + if (_flags->getFlagValue(Flags::ESCAPED)) { + text._time = 1; + if (!slot) { + _vm->_mainHero->_talkTime = 1; + } else if (slot == 1) { + _vm->_secondHero->_talkTime = 1; + } + } else { + _opcodeNF = 1; + _currentInstruction -= 4; + } + } + //debugInterpreter("O_WAITTEXT slot %d", slot); +} + +void Interpreter::O_SETHEROANIM() { + int32 heroId = readScriptFlagValue(); + int32 offset = readScript32(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + hero->freeHeroAnim(); + if (hero ->_specAnim == nullptr) { + hero->_specAnim = new Animation(); + if (offset < 100) { + const Common::String animName = Common::String::format("AN%02d", offset); + Resource::loadResource(hero->_specAnim, animName.c_str(), true); + } else { + const Common::String animName = Common::String((const char *)_script->getHeroAnimName(offset)); + Common::String normalizedPath = lastPathComponent(animName, '\\'); + Resource::loadResource(hero->_specAnim, normalizedPath.c_str(), true); + } + hero->_phase = 0; + hero->_state = Hero::kHeroStateSpec; + } + } + debugInterpreter("O_SETHEROANIM hero %d, offset %d", hero, offset); +} + +void Interpreter::O_WAITHEROANIM() { + int32 heroId = readScriptFlagValue(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + if (hero->_state == Hero::kHeroStateSpec) { + _currentInstruction -= 4; + _opcodeNF = 1; + } + } + debugInterpreter("O_WAITHEROANIM heroId %d", heroId); +} + +void Interpreter::O_GETHERODATA() { + Flags::Id flagId = readScriptFlagId(); + int32 heroId = readScriptFlagValue(); + int32 heroOffset = readScriptFlagValue(); + Hero *hero = nullptr; + if (!heroId) { + hero = _vm->_mainHero; + } else { + hero = _vm->_secondHero; + } + if (hero != nullptr) { + _flags->setFlagValue(flagId, hero->getData((Hero::AttrId)heroOffset)); + } + debugInterpreter("O_GETHERODATA flag %04x - (%s), heroId %d, heroOffset %d", flagId, Flags::getFlagName(flagId), heroId, heroOffset); +} + +// No need of implementation here +void Interpreter::O_GETMOUSEBUTTON() { + debugInterpreter("O_GETMOUSEBUTTON"); +} + +void Interpreter::O_CHANGEFRAMES() { + int32 anim = readScriptFlagValue(); + int32 frame = readScriptFlagValue(); + int32 lastFrame = readScriptFlagValue(); + int32 loopFrame = readScriptFlagValue(); + _vm->_normAnimList[anim]._frame = frame; + _vm->_normAnimList[anim]._lastFrame = lastFrame; + _vm->_normAnimList[anim]._loopFrame = loopFrame; + debugInterpreter("O_CHANGFRAMES anim %d, frame %d, lastFrame %d, loopFrame %d", anim, frame, lastFrame, loopFrame); +} + +void Interpreter::O_CHANGEBACKFRAMES() { + int32 anim = readScriptFlagValue(); + int32 frame = readScriptFlagValue(); + int32 lastFrame = readScriptFlagValue(); + int32 loopFrame = readScriptFlagValue(); + int currAnim = _vm->_backAnimList[anim]._seq._currRelative; + Anim &backAnim = _vm->_backAnimList[anim].backAnims[currAnim]; + backAnim._frame = frame; + backAnim._lastFrame = lastFrame; + backAnim._loopFrame = loopFrame; + debugInterpreter("O_CHANGEBACKFRAMES anim %d, frame %d, lastFrame %d, loopFrame %d", anim, frame, lastFrame, loopFrame); +} + +void Interpreter::O_GETBACKANIMDATA() { + Flags::Id flagId = readScriptFlagId(); + int32 animNumber = readScriptFlagValue(); + int32 animDataOffset = readScriptFlagValue(); + int currAnim = _vm->_backAnimList[animNumber]._seq._currRelative; + int16 value = _vm->_backAnimList[animNumber].backAnims[currAnim].getAnimData((Anim::AnimOffsets)(animDataOffset)); + _flags->setFlagValue((Flags::Id)(flagId), value); + debugInterpreter("O_GETBACKANIMDATA flag %04X (%s), animNumber %d, animDataOffset %d, value %d", flagId, Flags::getFlagName(flagId), animNumber, animDataOffset, value); +} + +void Interpreter::O_GETANIMDATA() { + Flags::Id flagId = readScriptFlagId(); + int32 anim = readScriptFlagValue(); + int32 animOffset = readScriptFlagValue(); + if (_vm->_normAnimList[anim]._animData != nullptr) { + _flags->setFlagValue(flagId, _vm->_normAnimList[anim].getAnimData((Anim::AnimOffsets)(animOffset))); + } + debugInterpreter("O_GETANIMDATA flag %04X (%s), anim %d, animOffset %d", flagId, Flags::getFlagName(flagId), anim, animOffset); +} + +void Interpreter::O_SETBGCODE() { + int32 offset = readScript32(); + _bgOpcodePC = _currentInstruction + offset - 4; + debugInterpreter("O_SETBGCODE next %08x, offset %08x", _bgOpcodePC, offset); +} + +void Interpreter::O_SETBACKFRAME() { + int32 anim = readScriptFlagValue(); + int32 frame = readScriptFlagValue(); + int currAnim = _vm->_backAnimList[anim]._seq._currRelative; + if (_vm->_backAnimList[anim].backAnims[currAnim]._animData != nullptr) { + _vm->_backAnimList[anim].backAnims[currAnim]._frame = frame; + } + debugInterpreter("O_SETBACKFRAME anim %d, frame %d", anim, frame); +} + +void Interpreter::O_GETRND() { + Flags::Id flag = readScriptFlagId(); + uint16 rndSeed = readScript16(); + int value = _vm->_randomSource.getRandomNumber(rndSeed - 1); + _flags->setFlagValue(flag, value); + debugInterpreter("O_GETRND flag %d, rndSeed %d, value %d", flag, rndSeed, value); +} + +void Interpreter::O_TALKBACKANIM() { + int32 animNumber = readScriptFlagValue(); + int32 slot = readScriptFlagValue(); + _vm->doTalkAnim(animNumber, slot, kBackgroundAnimation); + debugInterpreter("O_TALKBACKANIM animNumber %d, slot %d", animNumber, slot); +} + +// Simplifying, because used only once in Location 20 +void Interpreter::O_LOADPATH() { + readScript32(); + _vm->loadPath("path2"); + debugInterpreter("O_LOADPATH - path2"); +} + +void Interpreter::O_GETCHAR() { + Flags::Id flagId = readScriptFlagId(); + _flags->setFlagValue(flagId, *_string); + _string++; + debugInterpreter("O_GETCHAR %04X (%s) %02x", flagId, Flags::getFlagName(flagId), _flags->getFlagValue(flagId)); +} + +void Interpreter::O_SETDFLAG() { + Flags::Id flagId = readScriptFlagId(); + int32 address = readScript32(); + _flags->setFlagValue((Flags::Id)(flagId), _currentInstruction + address - 4); + debugInterpreter("O_SETDFLAG 0x%04X (%s) = 0x%04X", flagId, Flags::getFlagName(flagId), _currentInstruction + address - 4); +} + +void Interpreter::O_CALLDFLAG() { + Flags::Id flagId = readScriptFlagId(); + _stack[_stacktop] = _currentInstruction; + _stacktop++; + _currentInstruction = _flags->getFlagValue(flagId); + debugInterpreter("O_CALLDFLAG 0x%04X (%s) = 0x%04X", flagId, Flags::getFlagName(flagId), _currentInstruction); +} + +void Interpreter::O_PRINTAT() { + int32 slot = readScriptFlagValue(); + int32 x = readScriptFlagValue(); + int32 y = readScriptFlagValue(); + int32 color = _flags->getFlagValue(Flags::KOLOR); + _vm->printAt(slot, color, (char *)_string, x, y); + increaseString(); + debugInterpreter("O_PRINTAT slot %d, x %d, y %d", slot, x, y); +} + +void Interpreter::O_ZOOMIN() { + int32 slot = readScriptFlagValue(); + _vm->initZoomIn(slot); + debugInterpreter("O_ZOOMIN slot %04d", slot); +} + +void Interpreter::O_ZOOMOUT() { + int32 slot = readScriptFlagValue(); + _vm->initZoomOut(slot); + debugInterpreter("O_ZOOMOUT slot %d", slot); +} + +// Not used in script +void Interpreter::O_SETSTRINGOFFSET() { + error("O_SETSTRINGOFFSET"); +} + +void Interpreter::O_GETOBJDATA() { + Flags::Id flag = readScriptFlagId(); + int32 slot = readScriptFlagValue(); + int32 objOffset = readScriptFlagValue(); + int nr = _vm->_objSlot[slot]; + if (nr != 0xFF) { + int16 value = _vm->_objList[nr]->getData((Object::AttrId)objOffset); + _flags->setFlagValue(flag, value); + } + debugInterpreter("O_GETOBJDATA flag %d, objSlot %d, objOffset %d", flag, slot, objOffset); +} + +void Interpreter::O_SETOBJDATA() { + int32 slot = readScriptFlagValue(); + int32 objOffset = readScriptFlagValue(); + int32 value = readScriptFlagValue(); + int nr = _vm->_objSlot[slot]; + if (nr != 0xFF) { + _vm->_objList[nr]->setData((Object::AttrId)objOffset, value); + } + debugInterpreter("O_SETOBJDATA objSlot %d, objOffset %d, value %d", slot, objOffset, value); +} + +// Not used in script +void Interpreter::O_SWAPOBJECTS() { + error("O_SWAPOBJECTS"); +} + +void Interpreter::O_CHANGEHEROSET() { + int32 heroId = readScriptFlagValue(); + int32 heroSet = readScriptFlagValue(); + if (!heroId) { + _vm->_mainHero->loadAnimSet(heroSet); + } else if (heroId == 1) { + _vm->_secondHero->loadAnimSet(heroSet); + } + debugInterpreter("O_CHANGEHEROSET hero %d, heroSet %d", heroId, heroSet); +} + +// Not used in script +void Interpreter::O_ADDSTRING() { + error("O_ADDSTRING"); +} + +void Interpreter::O_SUBSTRING() { + int32 value = readScriptFlagValue(); + _string -= value; + debugInterpreter("O_SUBSTRING value %d", value); +} + +int Interpreter::checkSeq(byte *string) { + int freeHSlotIncrease = 0; + byte c; + while ((c = string[0]) != 0xFF) { + string++; + if (c < 0xF0) { + freeHSlotIncrease++; + while ((c = string[0])) { + string++; + } + string++; + } else if (c != 0xFE) { + string++; + } + } + return freeHSlotIncrease; +} + +void Interpreter::O_INITDIALOG() { + if (_string[0] == 255) { + byte *stringCurrOff = _string; + byte *string = _string; + stringCurrOff++; + int32 adressOfFirstSequence = (int)READ_LE_UINT16(stringCurrOff); + stringCurrOff += 2; + _string = string + adressOfFirstSequence; + + for (int i = 0; i < 32; i++) { + _vm->_dialogBoxAddr[i] = 0; + _vm->_dialogOptAddr[i] = 0; + } + + for (int i = 0; i < 4 * 32; i++) { + _vm->_dialogOptLines[i] = 0; + } + + int16 off; + byte *line = nullptr; + + int dialogBox = 0; + while ((off = (int)READ_LE_UINT16(stringCurrOff)) != -1) { + stringCurrOff += 2; + if (off) { + line = string + off; + } + _vm->_dialogBoxAddr[dialogBox] = line; + dialogBox++; + } + stringCurrOff += 2; + + int dialogOpt = 0; + while ((off = (int)READ_LE_UINT16(stringCurrOff)) != -1) { + stringCurrOff += 2; + if (off) { + line = string + off; + } + _vm->_dialogOptAddr[dialogOpt] = line; + dialogOpt++; + } + + _flags->setFlagValue(Flags::VOICE_A_LINE, 0); + _flags->setFlagValue(Flags::VOICE_B_LINE, 0); // bx in original? + + int freeHSlot = 0; + for (int i = 31; i >= 0; i--) { + if (_vm->_dialogOptAddr[i] != 0) { + i++; + freeHSlot = i; + _flags->setFlagValue(Flags::VOICE_H_LINE, i); + break; + } + } + + freeHSlot += checkSeq(_string); + + for (int i = 0; i < 32; i++) { + _vm->_dialogOptLines[i * 4] = freeHSlot; + _vm->_dialogOptLines[i * 4 + 1] = freeHSlot; + _vm->_dialogOptLines[i * 4 + 2] = freeHSlot; + if (_vm->_dialogOptAddr[i]) { + freeHSlot += checkSeq(_vm->_dialogOptAddr[i]); + } + } + } + debugInterpreter("O_INITDIALOG"); +} + +void Interpreter::O_ENABLEDIALOGOPT() { + int32 opt = readScriptFlagValue(); + int dialogDataValue = (int)READ_LE_UINT32(_vm->_dialogData); + dialogDataValue &= ~(1u << opt); + WRITE_LE_UINT32(_vm->_dialogData, dialogDataValue); + debugInterpreter("O_ENABLEDIALOGOPT opt %d", opt); +} + +void Interpreter::O_DISABLEDIALOGOPT() { + int32 opt = readScriptFlagValue(); + int dialogDataValue = (int)READ_LE_UINT32(_vm->_dialogData); + dialogDataValue |= (1u << opt); + WRITE_LE_UINT32(_vm->_dialogData, dialogDataValue); + debugInterpreter("O_DISABLEDIALOGOPT opt %d", opt); +} + +void Interpreter::O_SHOWDIALOGBOX() { + int32 box = readScriptFlagValue(); + uint32 currInstr = _currentInstruction; + _vm->createDialogBox(box); + _flags->setFlagValue(Flags::DIALINES, _vm->_dialogLines); + if (_vm->_dialogLines) { + _vm->changeCursor(1); + _vm->dialogRun(); + _vm->changeCursor(0); + } + _currentInstruction = currInstr; + debugInterpreter("O_SHOWDIALOGBOX box %d", box); +} + +void Interpreter::O_STOPSAMPLE() { + int32 slot = readScriptFlagValue(); + _vm->stopSample(slot); + debugInterpreter("O_STOPSAMPLE slot %d", slot); +} + +void Interpreter::O_BACKANIMRANGE() { + int32 slotId = readScriptFlagValue(); + uint16 animId = readScript16(); + int32 low = readScriptFlagValue(); + int32 high = readScriptFlagValue(); + if (animId != 0xFFFF) { + if (animId & InterpreterFlags::kFlagMask) { + animId = _flags->getFlagValue((Flags::Id)animId); + } + } + _result = 1; + if (!_vm->_backAnimList[slotId].backAnims.empty()) { + int currAnim = _vm->_backAnimList[slotId]._seq._currRelative; + if (_vm->_backAnimList[slotId].backAnims[currAnim]._animData != nullptr) { + if (animId == 0xFFFF || _vm->_backAnimList[slotId]._seq._current == animId) { + Anim &backAnim = _vm->_backAnimList[slotId].backAnims[currAnim]; + if (!backAnim._state) { + if (backAnim._frame >= low) { + if (backAnim._frame <= high) { + _result = 0; + } + } + } + } + } + } + debugInterpreter("O_BACKANIMRANGE slotId %d, animId %d, low %d, high %d, _result %d", slotId, animId, low, high, _result); +} + +void Interpreter::O_CLEARPATH() { + for (uint i = 0; i < _vm->kPathBitmapLen; i++) { + _vm->_roomPathBitmap[i] = 255; + } + debugInterpreter("O_CLEARPATH"); +} + +void Interpreter::O_SETPATH() { + _vm->loadPath("path"); + debugInterpreter("O_SETPATH"); +} + +void Interpreter::O_GETHEROX() { + int32 heroId = readScriptFlagValue(); + Flags::Id flagId = readScriptFlagId(); + if (!heroId) { + _flags->setFlagValue(flagId, _vm->_mainHero->_middleX); + } else if (heroId == 1) { + _flags->setFlagValue(flagId, _vm->_secondHero->_middleX); + } + debugInterpreter("O_GETHEROX heroId %d, flagId %d", heroId, flagId); +} + +void Interpreter::O_GETHEROY() { + int32 heroId = readScriptFlagValue(); + Flags::Id flagId = readScriptFlagId(); + if (!heroId) { + _flags->setFlagValue(flagId, _vm->_mainHero->_middleY); + } else if (heroId == 1) { + _flags->setFlagValue(flagId, _vm->_secondHero->_middleY); + } + debugInterpreter("O_GETHEROY heroId %d, flagId %d", heroId, flagId); +} + +void Interpreter::O_GETHEROD() { + int32 heroId = readScriptFlagValue(); + Flags::Id flagId = readScriptFlagId(); + if (!heroId) { + _flags->setFlagValue(flagId, _vm->_mainHero->_lastDirection); + } else if (heroId == 1) { + _flags->setFlagValue(flagId, _vm->_secondHero->_lastDirection); + } + debugInterpreter("O_GETHEROD heroId %d, flagId %d", heroId, flagId); +} + +void Interpreter::O_PUSHSTRING() { + _stringStack.string = _string; + _stringStack.dialogData = _vm->_dialogData; + _stringStack.currentString = _currentString; + debugInterpreter("O_PUSHSTRING"); +} + +void Interpreter::O_POPSTRING() { + _string = _stringStack.string; + _vm->_dialogData = _stringStack.dialogData; + _currentString = _stringStack.currentString; + debugInterpreter("O_POPSTRING"); +} + +void Interpreter::O_SETFGCODE() { + int32 offset = readScript32(); + _fgOpcodePC = _currentInstruction + offset - 4; + debugInterpreter("O_SETFGCODE next %08x, offset %08x", _fgOpcodePC, offset); +} + +void Interpreter::O_STOPHERO() { + int32 heroId = readScriptFlagValue(); + if (!heroId) { + _vm->_mainHero->freeOldMove(); + } else if (heroId == 1) { + _vm->_secondHero->freeOldMove(); + } + debugInterpreter("O_STOPHERO heroId %d", heroId); +} + +void Interpreter::O_ANIMUPDATEOFF() { + int32 slotId = readScriptFlagValue(); + _vm->_normAnimList[slotId]._state = 1; + debugInterpreter("O_ANIMUPDATEOFF slotId %d", slotId); +} + +void Interpreter::O_ANIMUPDATEON() { + int32 slotId = readScriptFlagValue(); + _vm->_normAnimList[slotId]._state = 0; + debugInterpreter("O_ANIMUPDATEON slotId %d", slotId); +} + +void Interpreter::O_FREECURSOR() { + _vm->changeCursor(0); + _vm->_currentPointerNumber = 1; + // free memory here? + debugInterpreter("O_FREECURSOR"); +} + +void Interpreter::O_ADDINVQUIET() { + int32 hero = readScriptFlagValue(); + int32 item = readScriptFlagValue(); + _vm->addInv(hero, item, true); + debugInterpreter("O_ADDINVQUIET hero %d, item %d", hero, item); +} + +void Interpreter::O_RUNHERO() { + int32 heroId = readScriptFlagValue(); + int32 x = readScriptFlagValue(); + int32 y = readScriptFlagValue(); + int32 dir = readScriptFlagValue(); + _vm->moveRunHero(heroId, x, y, dir, true); + debugInterpreter("O_RUNHERO heroId %d, x %d, y %d, dir %d", heroId, x, y, dir); +} + +void Interpreter::O_SETBACKANIMDATA() { + uint16 animNumber = readScript16(); + uint16 animDataOffset = readScript16(); + Flags::Id flagId = readScriptFlagId(); + uint16 value = _flags->getFlagValue((Flags::Id)(flagId)); + int currAnim = _vm->_backAnimList[animNumber]._seq._currRelative; + _vm->_backAnimList[animNumber].backAnims[currAnim].setAnimData((Anim::AnimOffsets)(animDataOffset), value); + debugInterpreter("O_SETBACKANIMDATA flag %04X (%s), animNumber %d, animDataOffset %d, value %d", flagId, Flags::getFlagName(flagId), animNumber, animDataOffset, value); +} + +void Interpreter::O_VIEWFLC() { + int32 animNr = readScriptFlagValue(); + _vm->_flcFrameSurface = nullptr; + _vm->loadAnim(animNr, false); + debugInterpreter("O_VIEWFLC animNr %d", animNr); +} + +void Interpreter::O_CHECKFLCFRAME() { + int32 frameNr = readScriptFlagValue(); + debugInterpreter("O_CHECKFLCFRAME frame number %d", frameNr); + if (_vm->_flicPlayer.getCurFrame() != frameNr) { + _currentInstruction -= 4; + _opcodeNF = 1; + } +} + +void Interpreter::O_CHECKFLCEND() { + const Video::FlicDecoder &flicPlayer = _vm->_flicPlayer; + debugInterpreter("O_CHECKFLCEND frameCount %d, currentFrame %d", flicPlayer.getFrameCount(), flicPlayer.getCurFrame()); + if (flicPlayer.getFrameCount() - flicPlayer.getCurFrame() > 1) { + _currentInstruction -= 2; + _opcodeNF = 1; + } +} + +void Interpreter::O_FREEFLC() { + _vm->_flcFrameSurface = nullptr; + debugInterpreter("O_FREEFLC"); +} + +void Interpreter::O_TALKHEROSTOP() { + int32 heroId = readScriptFlagValue(); + if (!heroId) { + _vm->_mainHero->_state = Hero::kHeroStateStay; + } else if (heroId == 1) { + _vm->_secondHero->_state = Hero::kHeroStateStay; + } + debugInterpreter("O_TALKHEROSTOP %d", heroId); +} + +void Interpreter::O_HEROCOLOR() { + int32 heroId = readScriptFlagValue(); + int32 color = readScriptFlagValue(); + if (!heroId) { + _vm->_mainHero->_color = color; + } else if (heroId == 1) { + _vm->_secondHero->_color = color; + } + debugInterpreter("O_HEROCOLOR heroId %d, color %d", heroId, color); +} + +void Interpreter::O_GRABMAPA() { + _vm->grabMap(); + debugInterpreter("O_GRABMAPA"); +} + +void Interpreter::O_ENABLENAK() { + int32 nakId = readScriptFlagValue(); + _vm->_maskList[nakId]._flags = 0; + debugInterpreter("O_ENABLENAK nakId %d", nakId); +} + +void Interpreter::O_DISABLENAK() { + int32 nakId = readScriptFlagValue(); + _vm->_maskList[nakId]._flags = 1; + debugInterpreter("O_DISABLENAK nakId %d", nakId); +} + +void Interpreter::O_GETMOBNAME() { + int32 modId = readScriptFlagValue(); + _string = (byte *)_vm->_mobList[modId]._name.c_str(); + debugInterpreter("O_GETMOBNAME modId %d", modId); +} + +void Interpreter::O_SWAPINVENTORY() { + int32 hero = readScriptFlagValue(); + _vm->swapInv(hero); + debugInterpreter("O_SWAPINVENTORY hero %d", hero); +} + +void Interpreter::O_CLEARINVENTORY() { + int32 hero = readScriptFlagValue(); + _vm->clearInv(hero); + debugInterpreter("O_CLEARINVENTORY hero %d", hero); +} + +void Interpreter::O_SKIPTEXT() { + increaseString(); + debugInterpreter("O_SKIPTEXT"); +} + +void Interpreter::O_SETVOICEH() { + int32 slot = readScriptFlagValue(); + static const uint32 VOICE_H_SLOT = 28; + uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE); + _vm->setVoice(slot, VOICE_H_SLOT, voiceLineH); +} + +void Interpreter::O_SETVOICEA() { + int32 slot = readScriptFlagValue(); + static const uint32 VOICE_A_SLOT = 29; + uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE); + _vm->setVoice(slot, VOICE_A_SLOT, voiceLineH); +} + +void Interpreter::O_SETVOICEB() { + int32 slot = readScriptFlagValue(); + static const uint32 VOICE_B_SLOT = 30; + uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE); + _vm->setVoice(slot, VOICE_B_SLOT, voiceLineH); +} + +void Interpreter::O_SETVOICEC() { + int32 slot = readScriptFlagValue(); + static const uint32 VOICE_C_SLOT = 31; + uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE); + _vm->setVoice(slot, VOICE_C_SLOT, voiceLineH); +} + +void Interpreter::O_SETVOICED() { + int32 slot = readScriptFlagValue(); + static const uint32 VOICE_D_SLOT = 32; + uint16 voiceLineH = _flags->getFlagValue(Flags::VOICE_H_LINE); + _vm->setVoice(slot, VOICE_D_SLOT, voiceLineH); +} + +void Interpreter::O_VIEWFLCLOOP() { + int32 animId = readScriptFlagValue(); + _vm->loadAnim(animId, true); + debugInterpreter("O_VIEWFLCLOOP animId %d", animId); +} + +// Not used in script +void Interpreter::O_FLCSPEED() { + int32 speed = readScriptFlagValue(); + error("O_FLCSPEED speed %d", speed); +} + +void Interpreter::O_OPENINVENTORY() { + _vm->_showInventoryFlag = true; + _opcodeNF = 1; + debugInterpreter("O_OPENINVENTORY"); +} + +void Interpreter::O_KRZYWA() { + _vm->makeCurve(); + debugInterpreter("O_KRZYWA"); +} + +void Interpreter::O_GETKRZYWA() { + _vm->getCurve(); + debugInterpreter("O_GETKRZYWA"); +} + +void Interpreter::O_GETMOB() { + Flags::Id flagId = readScriptFlagId(); + int32 posX = readScriptFlagValue(); + int32 posY = readScriptFlagValue(); + int mobNumber = _vm->getMob(_vm->_mobList, true, posX, posY); + _flags->setFlagValue(flagId, mobNumber + 1); + debugInterpreter("O_GETMOB flagId %d, posX %d, posY %d", flagId, posX, posY); +} + +// Not used in game +void Interpreter::O_INPUTLINE() { + error("O_INPUTLINE"); +} + +// Not used in script +void Interpreter::O_BREAK_POINT() { + error("O_BREAK_POINT"); +} + +Interpreter::OpcodeFunc Interpreter::_opcodes[kNumOpcodes] = { + &Interpreter::O_WAITFOREVER, + &Interpreter::O_BLACKPALETTE, + &Interpreter::O_SETUPPALETTE, + &Interpreter::O_INITROOM, + &Interpreter::O_SETSAMPLE, + &Interpreter::O_FREESAMPLE, + &Interpreter::O_PLAYSAMPLE, + &Interpreter::O_PUTOBJECT, + &Interpreter::O_REMOBJECT, + &Interpreter::O_SHOWANIM, + &Interpreter::O_CHECKANIMEND, + &Interpreter::O_FREEANIM, + &Interpreter::O_CHECKANIMFRAME, + &Interpreter::O_PUTBACKANIM, + &Interpreter::O_REMBACKANIM, + &Interpreter::O_CHECKBACKANIMFRAME, + &Interpreter::O_FREEALLSAMPLES, + &Interpreter::O_SETMUSIC, + &Interpreter::O_STOPMUSIC, + &Interpreter::O__WAIT, + &Interpreter::O_UPDATEOFF, + &Interpreter::O_UPDATEON, + &Interpreter::O_UPDATE , + &Interpreter::O_CLS, + &Interpreter::O__CALL, + &Interpreter::O_RETURN, + &Interpreter::O_GO, + &Interpreter::O_BACKANIMUPDATEOFF, + &Interpreter::O_BACKANIMUPDATEON, + &Interpreter::O_CHANGECURSOR, + &Interpreter::O_CHANGEANIMTYPE, + &Interpreter::O__SETFLAG, + &Interpreter::O_COMPARE, + &Interpreter::O_JUMPZ, + &Interpreter::O_JUMPNZ, + &Interpreter::O_EXIT, + &Interpreter::O_ADDFLAG, + &Interpreter::O_TALKANIM, + &Interpreter::O_SUBFLAG, + &Interpreter::O_SETSTRING, + &Interpreter::O_ANDFLAG, + &Interpreter::O_GETMOBDATA, + &Interpreter::O_ORFLAG, + &Interpreter::O_SETMOBDATA, + &Interpreter::O_XORFLAG, + &Interpreter::O_GETMOBTEXT, + &Interpreter::O_MOVEHERO, + &Interpreter::O_WALKHERO, + &Interpreter::O_SETHERO, + &Interpreter::O_HEROOFF, + &Interpreter::O_HEROON, + &Interpreter::O_CLSTEXT, + &Interpreter::O_CALLTABLE, + &Interpreter::O_CHANGEMOB, + &Interpreter::O_ADDINV, + &Interpreter::O_REMINV, + &Interpreter::O_REPINV, + &Interpreter::O_OBSOLETE_GETACTION, + &Interpreter::O_ADDWALKAREA, + &Interpreter::O_REMWALKAREA, + &Interpreter::O_RESTOREWALKAREA, + &Interpreter::O_WAITFRAME, + &Interpreter::O_SETFRAME, + &Interpreter::O_RUNACTION, + &Interpreter::O_COMPAREHI, + &Interpreter::O_COMPARELO, + &Interpreter::O_PRELOADSET, + &Interpreter::O_FREEPRELOAD, + &Interpreter::O_CHECKINV, + &Interpreter::O_TALKHERO, + &Interpreter::O_WAITTEXT, + &Interpreter::O_SETHEROANIM, + &Interpreter::O_WAITHEROANIM, + &Interpreter::O_GETHERODATA, + &Interpreter::O_GETMOUSEBUTTON, + &Interpreter::O_CHANGEFRAMES, + &Interpreter::O_CHANGEBACKFRAMES, + &Interpreter::O_GETBACKANIMDATA, + &Interpreter::O_GETANIMDATA, + &Interpreter::O_SETBGCODE, + &Interpreter::O_SETBACKFRAME, + &Interpreter::O_GETRND, + &Interpreter::O_TALKBACKANIM, + &Interpreter::O_LOADPATH, + &Interpreter::O_GETCHAR, + &Interpreter::O_SETDFLAG, + &Interpreter::O_CALLDFLAG, + &Interpreter::O_PRINTAT, + &Interpreter::O_ZOOMIN, + &Interpreter::O_ZOOMOUT, + &Interpreter::O_SETSTRINGOFFSET, + &Interpreter::O_GETOBJDATA, + &Interpreter::O_SETOBJDATA, + &Interpreter::O_SWAPOBJECTS, + &Interpreter::O_CHANGEHEROSET, + &Interpreter::O_ADDSTRING, + &Interpreter::O_SUBSTRING, + &Interpreter::O_INITDIALOG, + &Interpreter::O_ENABLEDIALOGOPT, + &Interpreter::O_DISABLEDIALOGOPT, + &Interpreter::O_SHOWDIALOGBOX, + &Interpreter::O_STOPSAMPLE, + &Interpreter::O_BACKANIMRANGE, + &Interpreter::O_CLEARPATH, + &Interpreter::O_SETPATH, + &Interpreter::O_GETHEROX, + &Interpreter::O_GETHEROY, + &Interpreter::O_GETHEROD, + &Interpreter::O_PUSHSTRING, + &Interpreter::O_POPSTRING, + &Interpreter::O_SETFGCODE, + &Interpreter::O_STOPHERO, + &Interpreter::O_ANIMUPDATEOFF, + &Interpreter::O_ANIMUPDATEON, + &Interpreter::O_FREECURSOR, + &Interpreter::O_ADDINVQUIET, + &Interpreter::O_RUNHERO, + &Interpreter::O_SETBACKANIMDATA, + &Interpreter::O_VIEWFLC, + &Interpreter::O_CHECKFLCFRAME, + &Interpreter::O_CHECKFLCEND, + &Interpreter::O_FREEFLC, + &Interpreter::O_TALKHEROSTOP, + &Interpreter::O_HEROCOLOR, + &Interpreter::O_GRABMAPA, + &Interpreter::O_ENABLENAK, + &Interpreter::O_DISABLENAK, + &Interpreter::O_GETMOBNAME, + &Interpreter::O_SWAPINVENTORY, + &Interpreter::O_CLEARINVENTORY, + &Interpreter::O_SKIPTEXT, + &Interpreter::O_SETVOICEH, + &Interpreter::O_SETVOICEA, + &Interpreter::O_SETVOICEB, + &Interpreter::O_SETVOICEC, + &Interpreter::O_VIEWFLCLOOP, + &Interpreter::O_FLCSPEED, + &Interpreter::O_OPENINVENTORY, + &Interpreter::O_KRZYWA, + &Interpreter::O_GETKRZYWA, + &Interpreter::O_GETMOB, + &Interpreter::O_INPUTLINE, + &Interpreter::O_SETVOICED, + &Interpreter::O_BREAK_POINT, +}; + +} // End of namespace Prince diff --git a/engines/prince/script.h b/engines/prince/script.h new file mode 100644 index 0000000000..fe79cf5f96 --- /dev/null +++ b/engines/prince/script.h @@ -0,0 +1,398 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_SCRIPT_H +#define PRINCE_SCRIPT_H + +#include "common/random.h" +#include "common/endian.h" +#include "common/array.h" +#include "common/stream.h" + +#include "prince/flags.h" + +namespace Prince { + +class PrinceEngine; +class Animation; +class Object; +struct Anim; +struct BackgroundAnim; +struct Mask; + +class Room { +public: + Room(); + int _mobs; // mob flag offset + int _backAnim; // offset to array of animation numbers + int _obj; // offset to array of object numbers + int _nak; // offset to array of masks + int _itemUse; + int _itemGive; + int _walkTo; // offset to array of WALKTO events or 0 + int _examine; // offset to array of EXAMINE events or 0 + int _pickup; + int _use; + int _pushOpen; + int _pullClose; + int _talk; + int _give; + + bool loadStream(Common::SeekableReadStream &stream); + bool loadRoom(byte *roomData); + int getOptionOffset(int option); + +private: + + typedef void (Room::*LoadingStep)(Common::SeekableReadStream &stream); + + void nextLoadStep(Common::SeekableReadStream &stream, LoadingStep step); + + void loadMobs(Common::SeekableReadStream &stream); + void loadBackAnim(Common::SeekableReadStream &stream); + void loadObj(Common::SeekableReadStream &stream); + void loadNak(Common::SeekableReadStream &stream); + void loadItemUse(Common::SeekableReadStream &stream); + void loadItemGive(Common::SeekableReadStream &stream); + void loadWalkTo(Common::SeekableReadStream &stream); + void loadExamine(Common::SeekableReadStream &stream); + void loadPickup(Common::SeekableReadStream &stream); + void loadUse(Common::SeekableReadStream &stream); + void loadPushOpen(Common::SeekableReadStream &stream); + void loadPullClose(Common::SeekableReadStream &stream); + void loadTalk(Common::SeekableReadStream &stream); + void loadGive(Common::SeekableReadStream &stream); +}; + + +class Script { +public: + static const int16 kMaxRooms = 60; + + Script(PrinceEngine *vm); + ~Script(); + + struct ScriptInfo { + int rooms; + int startGame; + int restoreGame; + int stdExamine; + int stdPickup; + int stdUse; + int stdOpen; + int stdClose; + int stdTalk; + int stdGive; + int usdCode; + int invObjExam; + int invObjUse; + int invObjUU; + int stdUseItem; + int lightSources; + int specRout; + int invObjGive; + int stdGiveItem; + int goTester; + }; + + ScriptInfo _scriptInfo; + + bool loadStream(Common::SeekableReadStream &stream); + + uint16 readScript16(uint32 address); + uint32 readScript32(uint32 address); + + uint32 getStartGameOffset(); + uint32 getLocationInitScript(int initRoomTableOffset, int roomNr); + int16 getLightX(int locationNr); + int16 getLightY(int locationNr); + int32 getShadowScale(int locationNr); + uint8 *getRoomOffset(int locationNr); + int32 getOptionStandardOffset(int option); + uint8 *getHeroAnimName(int offset); + + void installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset); + void installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset); + void installObjects(int offset); + bool loadAllMasks(Common::Array<Mask> &maskList, int offset); + + int scanMobEvents(int mobMask, int dataEventOffset); + int scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask); + + byte getMobVisible(int roomMobOffset, uint16 mob); + void setMobVisible(int roomMobOffset, uint16 mob, byte value); + + uint32 getBackAnimId(int roomBackAnimOffset, int slot); + void setBackAnimId(int roomBackAnimOffset, int slot, int animId); + + byte getObjId(int roomObjOffset, int slot); + void setObjId(int roomObjOffset, int slot, byte objectId); + + const char *getString(uint32 offset) { + return (const char *)(&_data[offset]); + } + +private: + PrinceEngine *_vm; + uint8 *_data; + uint32 _dataSize; + Common::Array<Room> _roomList; +}; + +class InterpreterFlags { +public: + InterpreterFlags(); + + void setFlagValue(Flags::Id flag, int32 value); + int32 getFlagValue(Flags::Id flag); + + void resetAllFlags(); + + static const uint16 kFlagMask = 0x8000; + static const uint16 kMaxFlags = 2000; +private: + int32 _flags[kMaxFlags]; +}; + +class Interpreter { +public: + Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags); + + void stopBg() { _bgOpcodePC = 0; } + + void stepBg(); + void stepFg(); + void storeNewPC(int opcodePC); + int getLastOPCode(); + int getFgOpcodePC(); + + void setBgOpcodePC(uint32 value); + void setFgOpcodePC(uint32 value); + + uint32 getCurrentString(); + void setCurrentString(uint32 value); + + byte *getString(); + void setString(byte *newString); + void increaseString(); + + void setResult(byte value); + +private: + PrinceEngine *_vm; + Script *_script; + InterpreterFlags *_flags; + + uint32 _currentInstruction; + + uint32 _bgOpcodePC; + uint32 _fgOpcodePC; + + uint16 _lastOpcode; + uint32 _lastInstruction; + byte _result; + + bool _opcodeNF; // break interpreter loop + bool _opcodeEnd; // end of a game flag + + static const uint32 _STACK_SIZE = 500; + uint32 _stack[_STACK_SIZE]; + struct stringStack { + byte *string; + byte *dialogData; + uint32 currentString; + } _stringStack; + uint8 _stacktop; + uint32 _waitFlag; + + byte *_string; + uint32 _currentString; + const char *_mode; + + // Helper functions + uint32 step(uint32 opcodePC); + uint16 readScript16(); + uint32 readScript32(); + int32 readScriptFlagValue(); + Flags::Id readScriptFlagId(); + int checkSeq(byte *string); + + void debugInterpreter(const char *s, ...); + + typedef void (Interpreter::*OpcodeFunc)(); + static OpcodeFunc _opcodes[]; + + static const uint kGiveLetterScriptFix = 79002; + static const uint kSecondBirdAnimationScriptFix = 45658; + + // Keep opcode handlers names as they are in original code + // making it easier to switch back and forth + void O_WAITFOREVER(); + void O_BLACKPALETTE(); + void O_SETUPPALETTE(); + void O_INITROOM(); + void O_SETSAMPLE(); + void O_FREESAMPLE(); + void O_PLAYSAMPLE(); + void O_PUTOBJECT(); + void O_REMOBJECT(); + void O_SHOWANIM(); + void O_CHECKANIMEND(); + void O_FREEANIM(); + void O_CHECKANIMFRAME(); + void O_PUTBACKANIM(); + void O_REMBACKANIM(); + void O_CHECKBACKANIMFRAME(); + void O_FREEALLSAMPLES(); + void O_SETMUSIC(); + void O_STOPMUSIC(); + void O__WAIT(); + void O_UPDATEOFF(); + void O_UPDATEON(); + void O_UPDATE (); + void O_CLS(); + void O__CALL(); + void O_RETURN(); + void O_GO(); + void O_BACKANIMUPDATEOFF(); + void O_BACKANIMUPDATEON(); + void O_CHANGECURSOR(); + void O_CHANGEANIMTYPE(); + void O__SETFLAG(); + void O_COMPARE(); + void O_JUMPZ(); + void O_JUMPNZ(); + void O_EXIT(); + void O_ADDFLAG(); + void O_TALKANIM(); + void O_SUBFLAG(); + void O_SETSTRING(); + void O_ANDFLAG(); + void O_GETMOBDATA(); + void O_ORFLAG(); + void O_SETMOBDATA(); + void O_XORFLAG(); + void O_GETMOBTEXT(); + void O_MOVEHERO(); + void O_WALKHERO(); + void O_SETHERO(); + void O_HEROOFF(); + void O_HEROON(); + void O_CLSTEXT(); + void O_CALLTABLE(); + void O_CHANGEMOB(); + void O_ADDINV(); + void O_REMINV(); + void O_REPINV(); + void O_OBSOLETE_GETACTION(); + void O_ADDWALKAREA(); + void O_REMWALKAREA(); + void O_RESTOREWALKAREA(); + void O_WAITFRAME(); + void O_SETFRAME(); + void O_RUNACTION(); + void O_COMPAREHI(); + void O_COMPARELO(); + void O_PRELOADSET(); + void O_FREEPRELOAD(); + void O_CHECKINV(); + void O_TALKHERO(); + void O_WAITTEXT(); + void O_SETHEROANIM(); + void O_WAITHEROANIM(); + void O_GETHERODATA(); + void O_GETMOUSEBUTTON(); + void O_CHANGEFRAMES(); + void O_CHANGEBACKFRAMES(); + void O_GETBACKANIMDATA(); + void O_GETANIMDATA(); + void O_SETBGCODE(); + void O_SETBACKFRAME(); + void O_GETRND(); + void O_TALKBACKANIM(); + void O_LOADPATH(); + void O_GETCHAR(); + void O_SETDFLAG(); + void O_CALLDFLAG(); + void O_PRINTAT(); + void O_ZOOMIN(); + void O_ZOOMOUT(); + void O_SETSTRINGOFFSET(); + void O_GETOBJDATA(); + void O_SETOBJDATA(); + void O_SWAPOBJECTS(); + void O_CHANGEHEROSET(); + void O_ADDSTRING(); + void O_SUBSTRING(); + void O_INITDIALOG(); + void O_ENABLEDIALOGOPT(); + void O_DISABLEDIALOGOPT(); + void O_SHOWDIALOGBOX(); + void O_STOPSAMPLE(); + void O_BACKANIMRANGE(); + void O_CLEARPATH(); + void O_SETPATH(); + void O_GETHEROX(); + void O_GETHEROY(); + void O_GETHEROD(); + void O_PUSHSTRING(); + void O_POPSTRING(); + void O_SETFGCODE(); + void O_STOPHERO(); + void O_ANIMUPDATEOFF(); + void O_ANIMUPDATEON(); + void O_FREECURSOR(); + void O_ADDINVQUIET(); + void O_RUNHERO(); + void O_SETBACKANIMDATA(); + void O_VIEWFLC(); + void O_CHECKFLCFRAME(); + void O_CHECKFLCEND(); + void O_FREEFLC(); + void O_TALKHEROSTOP(); + void O_HEROCOLOR(); + void O_GRABMAPA(); + void O_ENABLENAK(); + void O_DISABLENAK(); + void O_GETMOBNAME(); + void O_SWAPINVENTORY(); + void O_CLEARINVENTORY(); + void O_SKIPTEXT(); + void O_SETVOICEH(); + void O_SETVOICEA(); + void O_SETVOICEB(); + void O_SETVOICEC(); + void O_VIEWFLCLOOP(); + void O_FLCSPEED(); + void O_OPENINVENTORY(); + void O_KRZYWA(); + void O_GETKRZYWA(); + void O_GETMOB(); + void O_INPUTLINE(); + void O_SETVOICED(); + void O_BREAK_POINT(); + +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/sound.cpp b/engines/prince/sound.cpp new file mode 100644 index 0000000000..032297ee43 --- /dev/null +++ b/engines/prince/sound.cpp @@ -0,0 +1,211 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/prince.h" +#include "prince/sound.h" +#include "prince/musNum.h" + +#include "common/config-manager.h" +#include "common/memstream.h" +#include "common/archive.h" +#include "audio/decoders/raw.h" +#include "audio/audiostream.h" + +namespace Prince { + +const char *MusicPlayer::_musTable[] = { + "", + "Battlfld.mid", + "Cave.mid", + "Cemetery.mid", + "Credits.mid", + "Fjord.mid", + "Guitar.mid", + "Hell.mid", + "Jingle.mid", + "Main.mid", + "Night.mid", + "Reality.mid", + "Sunlord.mid", + "Tavern.mid", + "Temple.mid", + "Boruta.mid", + "Intro.mid" +}; + +const uint8 MusicPlayer::_musRoomTable[] = { + 0, + ROOM01MUS, + ROOM02MUS, + ROOM03MUS, + ROOM04MUS, + ROOM05MUS, + ROOM06MUS, + ROOM07MUS, + ROOM08MUS, + ROOM09MUS, + ROOM10MUS, + ROOM11MUS, + ROOM12MUS, + ROOM13MUS, + ROOM14MUS, + ROOM15MUS, + ROOM16MUS, + ROOM17MUS, + ROOM18MUS, + ROOM19MUS, + ROOM20MUS, + ROOM21MUS, + ROOM22MUS, + ROOM23MUS, + ROOM24MUS, + ROOM25MUS, + ROOM26MUS, + ROOM27MUS, + ROOM28MUS, + ROOM29MUS, + ROOM30MUS, + ROOM31MUS, + ROOM32MUS, + ROOM33MUS, + ROOM34MUS, + ROOM35MUS, + ROOM36MUS, + ROOM37MUS, + ROOM38MUS, + ROOM39MUS, + ROOM40MUS, + ROOM41MUS, + ROOM42MUS, + ROOM43MUS, + 0, + 0, + ROOM46MUS, + ROOM47MUS, + ROOM48MUS, + ROOM49MUS, + ROOM50MUS, + ROOM51MUS, + ROOM52MUS, + ROOM53MUS, + ROOM54MUS, + ROOM55MUS, + ROOM56MUS, + ROOM57MUS, + ROOM58MUS, + ROOM59MUS, + ROOM60MUS, + ROOM61MUS +}; + + +MusicPlayer::MusicPlayer(PrinceEngine *vm) : _vm(vm) { + _data = nullptr; + _isGM = false; + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + _driver->setTimerCallback(this, &timerCallback); + } +} + +MusicPlayer::~MusicPlayer() { + killMidi(); +} + +void MusicPlayer::killMidi() { + Audio::MidiPlayer::stop(); + + free(_data); + _data = nullptr; +} + +void MusicPlayer::loadMidi(const char *name) { + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name); + if (!stream) { + debug("Can't load midi stream %s", name); + return; + } + + // Stop any currently playing MIDI file + killMidi(); + + // Read in the data for the file + _dataSize = stream->size(); + _data = (byte *)malloc(_dataSize); + stream->read(_data, _dataSize); + + delete stream; + + // Start playing the music + sndMidiStart(); +} + +void MusicPlayer::sndMidiStart() { + _isGM = true; + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_data, _dataSize)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + // Al the tracks are supposed to loop + _isLooping = true; + _isPlaying = true; + } +} + +void MusicPlayer::send(uint32 b) { + if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + + Audio::MidiPlayer::send(b); +} + +void MusicPlayer::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +} // End of namespace Prince diff --git a/engines/prince/sound.h b/engines/prince/sound.h new file mode 100644 index 0000000000..cc44b0a110 --- /dev/null +++ b/engines/prince/sound.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PRINCE_SOUND_H +#define PRINCE_SOUND_H + +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" +#include "audio/fmopl.h" +#include "audio/mididrv.h" +#include "audio/midiparser.h" +#include "audio/midiplayer.h" +#include "audio/mixer.h" +#include "common/memstream.h" + +namespace Prince { + +class PrinceEngine; + +class MusicPlayer: public Audio::MidiPlayer { +private: + PrinceEngine *_vm; + byte *_data; + int _dataSize; + bool _isGM; + + // Start MIDI File + void sndMidiStart(); + + // Stop MIDI File + void sndMidiStop(); +public: + MusicPlayer(PrinceEngine *vm); + ~MusicPlayer(); + + void loadMidi(const char *); + void killMidi(); + + virtual void send(uint32 b); + virtual void sendToChannel(byte channel, uint32 b); + + static const char *_musTable[]; + static const uint8 _musRoomTable[]; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/variatxt.cpp b/engines/prince/variatxt.cpp new file mode 100644 index 0000000000..c38c65ce5d --- /dev/null +++ b/engines/prince/variatxt.cpp @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "prince/variatxt.h" +#include "common/debug.h" + +namespace Prince { + +VariaTxt::VariaTxt() : _dataSize(0), _data(nullptr) { +} + +VariaTxt::~VariaTxt() { + _dataSize = 0; + if (_data != nullptr) { + free(_data); + _data = nullptr; + } +} + + +bool VariaTxt::loadStream(Common::SeekableReadStream &stream) { + _dataSize = stream.size(); + _data = (byte *)malloc(_dataSize); + stream.read(_data, _dataSize); + return true; +} + +byte *VariaTxt::getString(uint32 stringId) { + uint32 stringOffset = READ_LE_UINT32(_data + stringId * 4); + if (stringOffset > _dataSize) { + assert(false); + } + return _data + stringOffset; +} + +} // End of namespace Prince diff --git a/engines/prince/variatxt.h b/engines/prince/variatxt.h new file mode 100644 index 0000000000..04c34bc0ef --- /dev/null +++ b/engines/prince/variatxt.h @@ -0,0 +1,40 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +namespace Prince { + +class VariaTxt { +public: + VariaTxt(); + ~VariaTxt(); + + bool loadStream(Common::SeekableReadStream &stream); + byte *getString(uint32 stringId); + +private: + uint32 _dataSize; + byte *_data; +}; + +} // End of namespace Prince diff --git a/engines/queen/POTFILES b/engines/queen/POTFILES index 1baf9c24de..28624662ca 100644 --- a/engines/queen/POTFILES +++ b/engines/queen/POTFILES @@ -1 +1 @@ -engines/queen/queen.cpp +engines/queen/detection.cpp diff --git a/engines/queen/logic.cpp b/engines/queen/logic.cpp index d48bb8a498..664a9a15f9 100644 --- a/engines/queen/logic.cpp +++ b/engines/queen/logic.cpp @@ -2115,9 +2115,15 @@ void LogicInterview::setupSpecialMoveTable() { } void LogicGame::useJournal() { + _vm->input()->clearKeyVerb(); + _vm->input()->clearMouseButton(); + _vm->command()->clear(false); _journal->use(); _vm->walk()->stopJoe(); + + _vm->input()->clearKeyVerb(); + _vm->input()->clearMouseButton(); } bool LogicGame::changeToSpecialRoom() { diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp index 1b9d72babc..e86a53d448 100644 --- a/engines/queen/talk.cpp +++ b/engines/queen/talk.cpp @@ -189,7 +189,7 @@ void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) { } } - if (_vm->input()->talkQuit()) + if (_vm->input()->talkQuit() || _vm->shouldQuit()) break; retval = _dialogueTree[level][selectedSentence].dialogueNodeValue1; @@ -1250,15 +1250,12 @@ int16 Talk::selectSentence() { } _vm->input()->clearKeyVerb(); + _vm->input()->clearMouseButton(); if (sentenceCount > 0) { int oldZone = 0; - while (0 == selectedSentence) { - - if (_vm->input()->talkQuit()) - break; - + while (0 == selectedSentence && !_vm->input()->talkQuit() && !_vm->shouldQuit()) { _vm->update(); Common::Point mouse = _vm->input()->getMousePos(); @@ -1328,6 +1325,9 @@ int16 Talk::selectSentence() { } } + _vm->input()->clearKeyVerb(); + _vm->input()->clearMouseButton(); + debug(6, "Selected sentence %i", selectedSentence); arrowBobUp->active = false; diff --git a/engines/saga/actor.h b/engines/saga/actor.h index b8a5436d76..8dc27c74be 100644 --- a/engines/saga/actor.h +++ b/engines/saga/actor.h @@ -468,7 +468,6 @@ public: int actorIdToIndex(uint16 id) { return (id == ID_PROTAG) ? 0 : objectIdToIndex(id); } uint16 actorIndexToId(int index) { return (index == 0) ? ID_PROTAG : objectIndexToId(kGameObjectActor, index); } ActorData *getActor(uint16 actorId); - ActorData *getFirstActor() { return &_actors.front(); } // clarification: Obj - means game object, such Hat, Spoon etc, Object - means Actor,Obj,HitZone,StepZone diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp index 0c1a2ce233..0b129dbcc0 100644 --- a/engines/saga/introproc_ite.cpp +++ b/engines/saga/introproc_ite.cpp @@ -374,7 +374,7 @@ int Scene::ITEIntroCaveCommonProc(int param, int caveScene) { lang = 2; int n_dialogues = 0; - + switch (caveScene) { case 1: n_dialogues = ARRAYSIZE(introDialogueCave1[lang]); diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp index 90ba62070b..e659e09ce8 100644 --- a/engines/saga/saveload.cpp +++ b/engines/saga/saveload.cpp @@ -215,8 +215,7 @@ void SagaEngine::save(const char *fileName, const char *saveName) { #ifdef ENABLE_IHNM if (getGameId() == GID_IHNM) { out->writeSint32LE(_scene->currentChapterNumber()); - // Protagonist - out->writeSint32LE(_scene->currentProtag()); + out->writeSint32LE(0); // obsolete, was used for the protagonist out->writeSint32LE(_scene->getCurrentMusicTrack()); out->writeSint32LE(_scene->getCurrentMusicRepeat()); } @@ -316,7 +315,7 @@ void SagaEngine::load(const char *fileName) { if (getGameId() == GID_IHNM) { int currentChapter = _scene->currentChapterNumber(); _scene->setChapterNumber(in->readSint32LE()); - _scene->setProtag(in->readSint32LE()); + in->skip(4); // obsolete, was used for setting the protagonist if (_scene->currentChapterNumber() != currentChapter) _scene->changeScene(-2, 0, kTransitionFade, _scene->currentChapterNumber()); _scene->setCurrentMusicTrack(in->readSint32LE()); @@ -366,30 +365,6 @@ void SagaEngine::load(const char *fileName) { int volume = _music->getVolume(); _music->setVolume(0); -#ifdef ENABLE_IHNM - // Protagonist swapping - if (getGameId() == GID_IHNM) { - if (_scene->currentProtag() != 0 && _scene->currentChapterNumber() != 6) { - ActorData *actor1 = _actor->getFirstActor(); - ActorData *actor2; - // The original gets actor2 from the current protagonist ID, but this is sometimes wrong - // If the current protagonist ID is not correct, use the stored protagonist - if (!_actor->validActorId(_scene->currentProtag())) { - actor2 = _actor->_protagonist; - } else { - actor2 = _actor->getActor(_scene->currentProtag()); - } - - SWAP(actor1->_location, actor2->_location); - - actor2->_flags &= ~kProtagonist; - actor1->_flags |= kProtagonist; - _actor->_protagonist = _actor->_centerActor = actor1; - _scene->setProtag(actor1->_id); - } - } -#endif - _scene->clearSceneQueue(); _scene->changeScene(sceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade); diff --git a/engines/saga/scene.h b/engines/saga/scene.h index 6a9571d282..410713c5d5 100644 --- a/engines/saga/scene.h +++ b/engines/saga/scene.h @@ -286,8 +286,6 @@ class Scene { #endif return _sceneLUT[sceneNumber]; } - int currentProtag() const { return _currentProtag; } - void setProtag(int pr) { _currentProtag = pr; } int currentSceneNumber() const { return _sceneNumber; } int currentChapterNumber() const { return _chapterNumber; } void setChapterNumber(int ch) { _chapterNumber = ch; } @@ -341,7 +339,6 @@ class Scene { Common::Array<uint16> _sceneLUT; SceneQueueList _sceneQueue; bool _sceneLoaded; - int _currentProtag; int _sceneNumber; int _chapterNumber; int _outsetSceneNumber; diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp index cb963e23ac..2175d8f40a 100644 --- a/engines/saga/sfuncs.cpp +++ b/engines/saga/sfuncs.cpp @@ -748,14 +748,10 @@ void Script::sfSwapActors(SCRIPTFUNC_PARAMS) { actor1->_flags &= ~kProtagonist; actor2->_flags |= kProtagonist; _vm->_actor->_protagonist = _vm->_actor->_centerActor = actor2; - if (_vm->getGameId() == GID_IHNM) - _vm->_scene->setProtag(actorId2); } else if (actor2->_flags & kProtagonist) { actor2->_flags &= ~kProtagonist; actor1->_flags |= kProtagonist; _vm->_actor->_protagonist = _vm->_actor->_centerActor = actor1; - if (_vm->getGameId() == GID_IHNM) - _vm->_scene->setProtag(actorId1); } } diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp index edb12a3dd9..1e1c397212 100644 --- a/engines/saga/shorten.cpp +++ b/engines/saga/shorten.cpp @@ -438,7 +438,7 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by for (i = 0; i < 64; ++i) oldValues[curChannel][i] = 0; - int arrayTerminator = MIN<int>(64, blockSize); + uint arrayTerminator = MIN<int>(64, blockSize); for (i = 0; i < arrayTerminator; ++i) oldValues[curChannel][i] = buffer[curChannel][blockSize - (i + 1)]; diff --git a/engines/savestate.h b/engines/savestate.h index 970e01485d..54eff0f8cb 100644 --- a/engines/savestate.h +++ b/engines/savestate.h @@ -140,7 +140,7 @@ public: * Sets the time the game was played before the save state was created. * * @param hours How many hours the user played the game so far. - * @param min How many minutes the user played the game so far. + * @param minutes How many minutes the user played the game so far. */ void setPlayTime(int hours, int minutes); diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 565e9752c3..e233c4cba4 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -485,6 +485,7 @@ bool Console::cmdGetVersion(int argc, const char **argv) { #endif debugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]); debugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette->isMerging() ? "yes" : "no"); + debugPrintf("Uses 16 bit color matching: %s\n", g_sci->_gfxPalette->isUsing16bitColorMatch() ? "yes" : "no"); debugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc()); debugPrintf("Resource map version: %s\n", g_sci->getResMan()->getMapVersionDesc()); debugPrintf("Contains selector vocabulary (vocab.997): %s\n", hasVocab997 ? "yes" : "no"); @@ -663,7 +664,7 @@ bool Console::cmdDiskDump(int argc, const char **argv) { int resNumFrom = 0; int resNumTo = 0; int resNumCur = 0; - + if (argc != 3) { debugPrintf("Dumps the specified resource to disk as a patch file\n"); debugPrintf("Usage: %s <resource type> <resource number>\n", argv[0]); @@ -671,7 +672,7 @@ bool Console::cmdDiskDump(int argc, const char **argv) { cmdResourceTypes(argc, argv); return true; } - + if (strcmp(argv[2], "*") == 0) { resNumFrom = 0; resNumTo = 65535; diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 4f28738508..85ff1c0062 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -563,31 +563,28 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, // If these files aren't found, it can't be SCI - if (!foundResMap && !foundRes000) { + if (!foundResMap && !foundRes000) return 0; - } ResourceManager resMan; - resMan.addAppropriateSources(fslist); - resMan.init(true); + resMan.addAppropriateSourcesForDetection(fslist); + resMan.initForDetection(); // TODO: Add error handling. #ifndef ENABLE_SCI32 // Is SCI32 compiled in? If not, and this is a SCI32 game, // stop here - if (getSciVersion() >= SCI_VERSION_2) { - return (const ADGameDescription *)&s_fallbackDesc; - } + if (getSciVersionForDetection() >= SCI_VERSION_2) + return 0; #endif ViewType gameViews = resMan.getViewType(); // Have we identified the game views? If not, stop here - // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files - // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI - if (gameViews == kViewUnknown) { + // Can't be SCI (or unsupported SCI views). Pinball Creep by Sierra also uses resource.map/resource.000 files + // but doesn't share SCI format at all + if (gameViews == kViewUnknown) return 0; - } // Set the platform to Amiga if the game is using Amiga views if (gameViews == kViewAmiga) @@ -597,9 +594,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, Common::String sierraGameId = resMan.findSierraGameId(); // If we don't have a game id, the game is not SCI - if (sierraGameId.empty()) { + if (sierraGameId.empty()) return 0; - } Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan); strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1); diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 344298ce9a..0fddaa51b4 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -1832,15 +1832,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, -#if 0 - // The resource.002 file, contained in disk 3, is broken in this version - // (it contains a large chunk of zeroes and several broken resources, - // e.g. pic 250 and views 250 and 251). - // Thus this detection entry isn't accurate. - - // Larry 1 Remake - English Amiga (from www.back2roots.org) + // Larry 1 Remake - English Amiga // Executable scanning reports "1.004.024" // SCI interpreter version 1.000.784 + // NOTE: The resource.002 file, contained in disk 3, is broken in the + // www.back2roots.org version (it contains a large chunk of zeroes and + // several broken resources, e.g. pic 250 and views 250 and 251). {"lsl1sci", "SCI", { {"resource.map", 0, "7d115a9e27dc8ac71e8d5ef33d589bd5", 3366}, {"resource.000", 0, "e67fd129d5810fc7ad8ea509d891cc00", 363073}, @@ -1849,7 +1846,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.003", 0, "4a34c3367c2fe7eb380d741374da1989", 572251}, AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, -#endif // Larry 1 VGA Remake - English DOS (from spookypeanut) // Executable scanning reports "1.000.577", VERSION file reports "2.1" @@ -2607,6 +2603,25 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Phantasmagoria - French DOS + // Supplied by Kervala in bug #6574 + {"phantasmagoria", "", { + {"resmap.001", 0, "4da82dd336d4b9cd8c16f3cc11f0c615", 11524}, + {"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 69963685}, + {"resmap.002", 0, "4f40f43f2b60bf765864433069752bb9", 12064}, + {"ressci.002", 0, "3aae6559aa1df273bc542d5ac6330d75", 78362841}, + {"resmap.003", 0, "6a392a86f14b6ddb4422978ee71e54ac", 12340}, + {"ressci.003", 0, "3aae6559aa1df273bc542d5ac6330d75", 80431189}, + {"resmap.004", 0, "df2e9462c41202de5f3843908c95a715", 12562}, + {"ressci.004", 0, "3aae6559aa1df273bc542d5ac6330d75", 82542844}, + {"resmap.005", 0, "43efd3fe834286c70a2c8b4cd747c1e2", 12616}, + {"ressci.005", 0, "3aae6559aa1df273bc542d5ac6330d75", 83790486}, + {"resmap.006", 0, "b3065e54a00190752a06dacd201b5058", 12538}, + {"ressci.006", 0, "3aae6559aa1df273bc542d5ac6330d75", 85415107}, + {"resmap.007", 0, "5633960bc106c39ca91d2d8fce18fd2d", 7984}, + AD_LISTEND}, + Common::FR_FRA, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Phantasmagoria - English DOS Demo // Executable scanning reports "2.100.002" {"phantasmagoria", "Demo", { @@ -4100,6 +4115,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { FANMADE("Knight's Quest Demo 1.0", "5e816edf993956752ed06fccfeeae6d9", 1260, "959321f88a22905fa1f8c6d897874744", 703836), FANMADE("LockerGnome Quest", "3eeff9130206cad0c4e1551e2b9dd2c5", 420, "ae05ca90806fd90cc43f147c82d3547c", 158906), FANMADE("LockerGnome Quest Redux", "55b0081dbdd77a07807c76cec3606970", 492, "75c9c5e8a475a7b5f1a6cb18edad67f2", 168069), + FANMADE("LockerGnome Quest Redux", "6299578d8ab709cc181baea6b984a0a7", 492, "c0ff4bfcc62fb111337343967e4001fd", 167383), FANMADE("New Year's Mystery", "e4dcab1b1d3cb4a2c070a07a9c9589e0", 708, "e00ca5e44fd4e98d8174b467b31b0f21", 295425), FANMADE("New Year's Mystery (Updated)", "efd1beb5120293725065c95959144f81", 714, "b3bd3c2372ed6efa28adb12403c4c31a", 305027), FANMADE("Ocean Battle", "c2304a0568e0eb84f8e9a0915f01170a", 408, "46c520c1ac9b63528854d0f58c7e1b74", 142234), diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 31e7ca4931..be062dba64 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -344,9 +344,9 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11) // If kDrawPic is called with 6 parameters from the overlay // selector, the game is using old graphics functions. - // Otherwise, if it's called with 8 parameters, it's using new - // graphics functions. - _gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY; + // Otherwise, if it's called with 8 parameters (e.g. SQ3) or 4 parameters + // (e.g. Hoyle 1/2), it's using new graphics functions. + _gfxFunctionsType = (argc == 6) ? SCI_VERSION_0_EARLY : SCI_VERSION_0_LATE; return true; } } diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index dddf845222..a65bcb7df5 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -145,7 +145,7 @@ public: */ Kernel(ResourceManager *resMan, SegManager *segMan); ~Kernel(); - + void init(); uint getSelectorNamesSize() const; @@ -161,7 +161,7 @@ public: * @return The appropriate selector ID, or -1 on error */ int findSelector(const char *selectorName) const; - + bool selectorNamesAvailable(); // Script dissection/dumping functions diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index fc46d16b8d..0c2fd4e3ea 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -468,7 +468,8 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL }, { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL }, { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds }, + { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ir!]", NULL, kUnLoad_workarounds }, + // ^ We allow invalid references here (e.g. bug #6600), since they will be invalidated anyway by the call itself { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL }, diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 61fb717567..c56eb09482 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -37,6 +37,7 @@ #include "sci/engine/state.h" #include "sci/engine/kernel.h" #include "sci/engine/savegame.h" +#include "sci/graphics/menu.h" #include "sci/sound/audio.h" #include "sci/console.h" @@ -913,6 +914,25 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) { // code concerning this via script patch. s->variables[VAR_GLOBAL][0xB3].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId); break; + case GID_JONES: + // HACK: The code that enables certain menu items isn't called when a game is restored from the + // launcher, or the "Restore game" option in the game's main menu - bugs #6537 and #6723. + // These menu entries are disabled when the game is launched, and are enabled when a new game is + // started. The code for enabling these entries is is all in script 1, room1::init, but that code + // path is never followed in these two cases (restoring game from the menu, or restoring a game + // from the ScummVM launcher). Thus, we perform the calls to enable the menus ourselves here. + // These two are needed when restoring from the launcher + // FIXME: The original interpreter saves and restores the menu state, so these attributes + // are automatically reset there. We may want to do the same. + g_sci->_gfxMenu->kernelSetAttribute(257 >> 8, 257 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> About Jones + g_sci->_gfxMenu->kernelSetAttribute(258 >> 8, 258 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> Help + // The rest are normally enabled from room1::init + g_sci->_gfxMenu->kernelSetAttribute(769 >> 8, 769 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Options -> Delete current player + g_sci->_gfxMenu->kernelSetAttribute(513 >> 8, 513 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game + g_sci->_gfxMenu->kernelSetAttribute(515 >> 8, 515 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Restore Game + g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics + g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals + break; default: break; } diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index c2089bcd4d..ee2249bd9d 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -354,13 +354,16 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { } textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16(); + + uint16 languageSplitter = 0; + Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, sep); #ifdef ENABLE_SCI32 if (g_sci->_gfxText32) - g_sci->_gfxText32->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight); + g_sci->_gfxText32->kernelTextSize(splitText.c_str(), font_nr, maxwidth, &textWidth, &textHeight); else #endif - g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight); + g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight); // One of the game texts in LB2 German contains loads of spaces in // its end. We trim the text here, otherwise the graphics code will @@ -376,7 +379,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { // Copy over the trimmed string... s->_segMan->strcpy(argv[1], text.c_str()); // ...and recalculate bounding box dimensions - g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight); + g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight); } } @@ -818,16 +821,29 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { if (!textReference.isNull()) text = s->_segMan->getString(textReference); + uint16 languageSplitter = 0; + Common::String splitText; + + switch (type) { + case SCI_CONTROLS_TYPE_BUTTON: + case SCI_CONTROLS_TYPE_TEXTEDIT: + splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, NULL); + break; + case SCI_CONTROLS_TYPE_TEXT: + splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter); + break; + } + switch (type) { case SCI_CONTROLS_TYPE_BUTTON: debugC(kDebugLevelGraphics, "drawing button %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y); - g_sci->_gfxControls16->kernelDrawButton(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, style, hilite); + g_sci->_gfxControls16->kernelDrawButton(rect, controlObject, splitText.c_str(), languageSplitter, fontId, style, hilite); return; case SCI_CONTROLS_TYPE_TEXT: alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); debugC(kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment); - g_sci->_gfxControls16->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite); + g_sci->_gfxControls16->kernelDrawText(rect, controlObject, splitText.c_str(), languageSplitter, fontId, alignment, style, hilite); s->r_acc = g_sci->_gfxText16->allocAndFillReferenceRectArray(); return; @@ -841,7 +857,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { writeSelectorValue(s->_segMan, controlObject, SELECTOR(cursor), cursorPos); } debugC(kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y); - g_sci->_gfxControls16->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite); + g_sci->_gfxControls16->kernelDrawTextEdit(rect, controlObject, splitText.c_str(), languageSplitter, fontId, mode, style, cursorPos, maxChars, hilite); return; case SCI_CONTROLS_TYPE_ICON: @@ -1165,8 +1181,11 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { argc--; argc--; argv++; argv++; text = g_sci->getKernel()->lookupText(textp, index); } + + uint16 languageSplitter = 0; + Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter); - return g_sci->_gfxPaint16->kernelDisplay(g_sci->strSplit(text.c_str()).c_str(), argc, argv); + return g_sci->_gfxPaint16->kernelDisplay(splitText.c_str(), languageSplitter, argc, argv); } reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) { diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 56dad583e4..eef758a0d9 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -42,10 +42,12 @@ reg_t kStrCat(EngineState *s, int argc, reg_t *argv) { Common::String s1 = s->_segMan->getString(argv[0]); Common::String s2 = s->_segMan->getString(argv[1]); - // The Japanese version of PQ2 splits the two strings here - // (check bug #3396887). - if (g_sci->getGameId() == GID_PQ2 && - g_sci->getLanguage() == Common::JA_JPN) { + // Japanese PC-9801 interpreter splits strings here + // see bug #5834 + // Verified for Police Quest 2 + Quest For Glory 1 + // However Space Quest 4 PC-9801 doesn't + if ((g_sci->getLanguage() == Common::JA_JPN) + && (getSciVersion() <= SCI_VERSION_01)) { s1 = g_sci->strSplit(s1.c_str(), NULL); s2 = g_sci->strSplit(s2.c_str(), NULL); } diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index cdcdcc41e5..9decc4cef6 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -844,6 +844,8 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin if (voc) voc->saveLoadWithSerializer(ser); + // TODO: SSCI (at least JonesCD, presumably more) also stores the Menu state + return true; } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 8bbbd713a6..3d76848a76 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -1917,6 +1917,43 @@ static const SciScriptPatcherEntry pq1vgaSignatures[] = { }; // =========================================================================== +// At the healer's house there is a bird's nest up on the tree. +// The player can throw rocks at it until it falls to the ground. +// The hero will then grab the item, that is in the nest. +// +// When running is active, the hero will not reach the actual destination +// and because of that, the game will get stuck. +// +// We just change the coordinate of the destination slightly, so that walking, +// sneaking and running work. +// +// This bug was fixed by Sierra at least in the Japanese PC-9801 version. +// Applies to at least: English floppy (1.000, 1.012) +// Responsible method: pickItUp::changeState (script 54) +// Fixes bug: #6407 +static const uint16 qfg1egaSignatureThrowRockAtNest[] = { + 0x4a, 0x04, // send 04 (nest::x) + 0x36, // push + SIG_MAGICDWORD, + 0x35, 0x0f, // ldi 0f (15d) + 0x02, // add + 0x36, // push + SIG_END +}; + +static const uint16 qfg1egaPatchThrowRockAtNest[] = { + PATCH_ADDTOOFFSET(+3), + 0x35, 0x12, // ldi 12 (18d) + PATCH_END +}; + +// script, description, signature patch +static const SciScriptPatcherEntry qfg1egaSignatures[] = { + { true, 54, "throw rock at nest while running", 1, qfg1egaSignatureThrowRockAtNest, qfg1egaPatchThrowRockAtNest }, + SCI_SIGNATUREENTRY_TERMINATOR +}; + +// =========================================================================== // script 215 of qfg1vga pointBox::doit actually processes button-presses // during fighting with monsters. It strangely also calls kGetEvent. Because // the main User::doit also calls kGetEvent it's pure luck, where the event @@ -3036,6 +3073,9 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3 case GID_PQ1: signatureTable = pq1vgaSignatures; break; + case GID_QFG1: + signatureTable = qfg1egaSignatures; + break; case GID_QFG1VGA: signatureTable = qfg1vgaSignatures; break; diff --git a/engines/sci/engine/script_patches.h b/engines/sci/engine/script_patches.h index 0b35792949..7023ef327e 100644 --- a/engines/sci/engine/script_patches.h +++ b/engines/sci/engine/script_patches.h @@ -98,7 +98,7 @@ private: void enablePatch(const SciScriptPatcherEntry *patchTable, const char *searchDescription); int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize, bool isMacSci11); void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset, bool isMacSci11); - + Selector *_selectorIdTable; SciScriptPatcherRuntimeEntry *_runtimeTable; }; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 3738fd3dcb..58c2b8d3e3 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -371,7 +371,7 @@ void SegManager::freeHunkEntry(reg_t addr) { HunkTable *ht = (HunkTable *)getSegment(addr.getSegment(), SEG_TYPE_HUNK); if (!ht) { - warning("Attempt to free Hunk from address %04x:%04x: Invalid segment type", PRINT_REG(addr)); + warning("Attempt to free Hunk from address %04x:%04x: Invalid segment type %d", PRINT_REG(addr), getSegmentType(addr.getSegment())); return; } diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index 7701822f6d..c07dc925e0 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -203,59 +203,94 @@ static kLanguage charToLanguage(const char c) { } } -Common::String SciEngine::getSciLanguageString(const Common::String &str, kLanguage lang, kLanguage *lang2) const { - kLanguage secondLang = K_LANG_NONE; - - const char *seeker = str.c_str(); - while (*seeker) { - if ((*seeker == '%') || (*seeker == '#')) { - secondLang = charToLanguage(*(seeker + 1)); - - if (secondLang != K_LANG_NONE) +Common::String SciEngine::getSciLanguageString(const Common::String &str, kLanguage requestedLanguage, kLanguage *secondaryLanguage, uint16 *languageSplitter) const { + kLanguage foundLanguage = K_LANG_NONE; + const byte *textPtr = (byte *)str.c_str(); + byte curChar = 0; + byte curChar2 = 0; + + while (1) { + curChar = *textPtr; + if (!curChar) + break; + + if ((curChar == '%') || (curChar == '#')) { + curChar2 = *(textPtr + 1); + foundLanguage = charToLanguage(curChar2); + + if (foundLanguage != K_LANG_NONE) { + // Return language splitter + if (languageSplitter) + *languageSplitter = curChar | ( curChar2 << 8 ); + // Return the secondary language found in the string + if (secondaryLanguage) + *secondaryLanguage = foundLanguage; break; + } } - - ++seeker; + textPtr++; } - // Return the secondary language found in the string - if (lang2) - *lang2 = secondLang; - - if (secondLang == lang) { - if (*(++seeker) == 'J') { + if (foundLanguage == requestedLanguage) { + if (curChar2 == 'J') { // Japanese including Kanji, displayed with system font // Convert half-width characters to full-width equivalents Common::String fullWidth; - byte c; + uint16 mappedChar; + + textPtr += 2; // skip over language splitter + + while (1) { + curChar = *textPtr; + + switch (curChar) { + case 0: // Terminator NUL + return fullWidth; + case '\\': + // "\n", "\N", "\r" and "\R" were overwritten with SPACE + 0x0D in PC-9801 SSCI + // inside GetLongest() (text16). We do it here, because it's much cleaner and + // we have to process the text here anyway. + // Occurs for example in Police Quest 2 intro + curChar2 = *(textPtr + 1); + switch (curChar2) { + case 'n': + case 'N': + case 'r': + case 'R': + fullWidth += ' '; + fullWidth += 0x0D; // CR + textPtr += 2; + continue; + } + } + + textPtr++; - while ((c = *(++seeker))) { - uint16 mappedChar = s_halfWidthSJISMap[c]; + mappedChar = s_halfWidthSJISMap[curChar]; if (mappedChar) { fullWidth += mappedChar >> 8; fullWidth += mappedChar & 0xFF; } else { // Copy double-byte character - char c2 = *(++seeker); - if (!c2) { - error("SJIS character %02X is missing second byte", c); + curChar2 = *(textPtr++); + if (!curChar) { + error("SJIS character %02X is missing second byte", curChar); break; } - fullWidth += c; - fullWidth += c2; + fullWidth += curChar; + fullWidth += curChar2; } } - return fullWidth; } else { - return Common::String(seeker + 1); + return Common::String((const char *)(textPtr + 2)); } } - if (*seeker) - return Common::String(str.c_str(), seeker - str.c_str()); - else - return str; + if (curChar) + return Common::String(str.c_str(), (const char *)textPtr - str.c_str()); + + return str; } kLanguage SciEngine::getSciLanguage() { @@ -314,25 +349,25 @@ void SciEngine::setSciLanguage() { setSciLanguage(getSciLanguage()); } -Common::String SciEngine::strSplit(const char *str, const char *sep) { - kLanguage lang = getSciLanguage(); - kLanguage subLang = K_LANG_NONE; +Common::String SciEngine::strSplitLanguage(const char *str, uint16 *languageSplitter, const char *sep) { + kLanguage activeLanguage = getSciLanguage(); + kLanguage subtitleLanguage = K_LANG_NONE; if (SELECTOR(subtitleLang) != -1) - subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(subtitleLang)); + subtitleLanguage = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(subtitleLang)); - kLanguage secondLang; - Common::String retval = getSciLanguageString(str, lang, &secondLang); + kLanguage foundLanguage; + Common::String retval = getSciLanguageString(str, activeLanguage, &foundLanguage, languageSplitter); // Don't add subtitle when separator is not set, subtitle language is not set, or // string contains only one language - if ((sep == NULL) || (subLang == K_LANG_NONE) || (secondLang == K_LANG_NONE)) + if ((sep == NULL) || (subtitleLanguage == K_LANG_NONE) || (foundLanguage == K_LANG_NONE)) return retval; // Add subtitle, unless the subtitle language doesn't match the languages in the string - if ((subLang == K_LANG_ENGLISH) || (subLang == secondLang)) { + if ((subtitleLanguage == K_LANG_ENGLISH) || (subtitleLanguage == foundLanguage)) { retval += sep; - retval += getSciLanguageString(str, subLang); + retval += getSciLanguageString(str, subtitleLanguage); } return retval; diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index ea4dc2fe71..c5730b5345 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -91,7 +91,8 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_HOYLE4, 700, -1, 1, "BridgeDefense", "think", -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", -1, 3, { WORKAROUND_FAKE, 0 } }, // rarely while playing bridge { GID_HOYLE4, 700, -1, 1, "Code", "doit", -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", -1, -1, { WORKAROUND_FAKE, 0 } }, // when saving the game (may also occur in other situations) - bug #6601 + { GID_HOYLE4, 700, 921, 0, "Print", "addEdit", -1, 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", -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", 0x1d4d, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts { GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", -1, 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", -1, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662 diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 511d2014bd..b1c002413d 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -262,7 +262,7 @@ SciEvent EventManager::getScummVMEvent() { // Scancodify if appropriate if (modifiers & Common::KBD_ALT) input.character = altify(input.character); - else if ((modifiers & Common::KBD_CTRL) && input.character > 0 && input.character < 27) + if (getSciVersion() <= SCI_VERSION_1_MIDDLE && (modifiers & Common::KBD_CTRL) && input.character > 0 && input.character < 27) input.character += 96; // 0x01 -> 'a' // If no actual key was pressed (e.g. if only a modifier key was pressed), diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp index f2b2ccdfe6..e2e250cf9d 100644 --- a/engines/sci/graphics/controls16.cpp +++ b/engines/sci/graphics/controls16.cpp @@ -280,7 +280,7 @@ int GfxControls16::getPicNotValid() { return _screen->_picNotValid; } -void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 style, bool hilite) { +void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 style, bool hilite) { int16 sci0EarlyPen = 0, sci0EarlyBack = 0; if (!hilite) { if (getSciVersion() == SCI_VERSION_0_EARLY) { @@ -295,7 +295,7 @@ void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *t _paint16->frameRect(rect); rect.grow(-2); _ports->textGreyedOutput(!(style & SCI_CONTROLS_STYLE_ENABLED)); - _text16->Box(text, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId); + _text16->Box(text, languageSplitter, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId); _ports->textGreyedOutput(false); rect.grow(1); if (style & SCI_CONTROLS_STYLE_SELECTED) @@ -318,12 +318,12 @@ void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *t } } -void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *text, int16 fontId, TextAlignment alignment, int16 style, bool hilite) { +void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, TextAlignment alignment, int16 style, bool hilite) { if (!hilite) { rect.grow(1); _paint16->eraseRect(rect); rect.grow(-1); - _text16->Box(text, false, rect, alignment, fontId); + _text16->Box(text, languageSplitter, false, rect, alignment, fontId); if (style & SCI_CONTROLS_STYLE_SELECTED) { _paint16->frameRect(rect); } @@ -335,7 +335,7 @@ void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *tex } } -void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite) { +void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite) { Common::Rect textRect = rect; uint16 oldFontId = _text16->GetFontId(); @@ -343,7 +343,7 @@ void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char _texteditCursorVisible = false; texteditCursorErase(); _paint16->eraseRect(rect); - _text16->Box(text, false, textRect, SCI_TEXT16_ALIGNMENT_LEFT, fontId); + _text16->Box(text, languageSplitter, false, textRect, SCI_TEXT16_ALIGNMENT_LEFT, fontId); _paint16->frameRect(rect); if (style & SCI_CONTROLS_STYLE_SELECTED) { _text16->SetFont(fontId); diff --git a/engines/sci/graphics/controls16.h b/engines/sci/graphics/controls16.h index 6a70c71aae..39ffa243fb 100644 --- a/engines/sci/graphics/controls16.h +++ b/engines/sci/graphics/controls16.h @@ -55,9 +55,9 @@ public: GfxControls16(SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen); ~GfxControls16(); - void kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 style, bool hilite); - void kernelDrawText(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 alignment, int16 style, bool hilite); - void kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite); + void kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 style, bool hilite); + void kernelDrawText(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 alignment, int16 style, bool hilite); + void kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite); void kernelDrawIcon(Common::Rect rect, reg_t obj, GuiResourceId viewId, int16 loopNo, int16 celNo, int16 priority, int16 style, bool hilite); void kernelDrawList(Common::Rect rect, reg_t obj, int16 maxChars, int16 count, const char **entries, GuiResourceId fontId, int16 style, int16 upperPos, int16 cursorPos, bool isAlias, bool hilite); void kernelTexteditChange(reg_t controlObject, reg_t eventObject); diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index 048ec1e9b9..1a58de073c 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -47,7 +47,7 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc _isVisible = true; // center mouse cursor - setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2)); + setPosition(Common::Point(_screen->getScriptWidth() / 2, _screen->getScriptHeight() / 2)); _moveZoneActive = false; _zoomZoneActive = false; @@ -151,14 +151,14 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) { colorMapping[0] = 0; // Black is hardcoded colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR; - colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey + colorMapping[3] = _palette->matchColor(170, 170, 170) & SCI_PALETTE_MATCH_COLORMASK; // Grey // TODO: Figure out if the grey color is hardcoded // HACK for the magnifier cursor in LB1, fixes its color (bug #3487092) if (g_sci->getGameId() == GID_LAURABOW && resourceId == 1) colorMapping[3] = _screen->getColorWhite(); // HACK for Longbow cursors, fixes the shade of grey they're using (bug #3489101) if (g_sci->getGameId() == GID_LONGBOW) - colorMapping[3] = _palette->matchColor(223, 223, 223); // Light Grey + colorMapping[3] = _palette->matchColor(223, 223, 223) & SCI_PALETTE_MATCH_COLORMASK; // Light Grey // Seek to actual data resourceData += 4; @@ -481,7 +481,7 @@ void GfxCursor::kernelSetPos(Common::Point pos) { void GfxCursor::kernelMoveCursor(Common::Point pos) { _coordAdjuster->moveCursor(pos); - if (pos.x > _screen->getWidth() || pos.y > _screen->getHeight()) { + if (pos.x > _screen->getScriptWidth() || pos.y > _screen->getScriptHeight()) { warning("attempt to place cursor at invalid coordinates (%d, %d)", pos.y, pos.x); return; } diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp index e4684ff134..2268ec0459 100644 --- a/engines/sci/graphics/font.cpp +++ b/engines/sci/graphics/font.cpp @@ -48,8 +48,8 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr // filling info for every char for (int16 i = 0; i < _numChars; i++) { _chars[i].offset = READ_SCI32ENDIAN_UINT16(_resourceData + 6 + i * 2); - _chars[i].w = _resourceData[_chars[i].offset]; - _chars[i].h = _resourceData[_chars[i].offset + 1]; + _chars[i].width = _resourceData[_chars[i].offset]; + _chars[i].height = _resourceData[_chars[i].offset + 1]; } } @@ -66,10 +66,10 @@ byte GfxFontFromResource::getHeight() { return _fontHeight; } byte GfxFontFromResource::getCharWidth(uint16 chr) { - return chr < _numChars ? _chars[chr].w : 0; + return chr < _numChars ? _chars[chr].width : 0; } byte GfxFontFromResource::getCharHeight(uint16 chr) { - return chr < _numChars ? _chars[chr].h : 0; + return chr < _numChars ? _chars[chr].height : 0; } byte *GfxFontFromResource::getCharData(uint16 chr) { return chr < _numChars ? _resourceData + _chars[chr].offset + 2 : 0; diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h index 58b2ba4813..451261f315 100644 --- a/engines/sci/graphics/font.h +++ b/engines/sci/graphics/font.h @@ -71,9 +71,11 @@ private: byte *_resourceData; struct Charinfo { - byte w, h; + byte width; + byte height; int16 offset; }; + byte _fontHeight; uint16 _numChars; Charinfo *_chars; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index a322eb8e61..ccc362dc37 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -524,6 +524,10 @@ void GfxFrameout::showVideo() { RobotDecoder *videoDecoder = g_sci->_robotDecoder; uint16 x = videoDecoder->getPos().x; uint16 y = videoDecoder->getPos().y; + uint16 screenWidth = _screen->getWidth(); + uint16 screenHeight = _screen->getHeight(); + uint16 outputWidth; + uint16 outputHeight; if (videoDecoder->hasDirtyPalette()) g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); @@ -532,7 +536,11 @@ void GfxFrameout::showVideo() { if (videoDecoder->needsUpdate()) { const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { - g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, frame->w, frame->h); + // We need to clip here + // At least Phantasmagoria shows a 640x390 video on a 630x450 screen during the intro + outputWidth = frame->w > screenWidth ? screenWidth : frame->w; + outputHeight = frame->h > screenHeight ? screenHeight : frame->h; + g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, outputWidth, outputHeight); if (videoDecoder->hasDirtyPalette()) g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); @@ -782,11 +790,12 @@ void GfxFrameout::kernelFrameout() { // TODO: For some reason, the top left nsRect coordinates get // swapped in the GK1 inventory screen, investigate why. - // HACK: Fix the coordinates by explicitly setting them here. - Common::Rect objNSRect = g_sci->_gfxCompare->getNSRect(itemEntry->object); - if (objNSRect.top == nsRect.left && objNSRect.left == nsRect.top && nsRect.top != 0 && nsRect.left != 0) { + // This is also needed for GK1 rooms 710 and 720 (catacombs, inner and + // outer circle), for handling the tiles and talking to Wolfgang. + // HACK: Fix the coordinates by explicitly setting them here for GK1. + // Also check bug #6729, for another case where this is needed. + if (g_sci->getGameId() == GID_GK1) g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); - } } // Don't attempt to draw sprites that are outside the visible diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index b835eb92ca..f80703e14d 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -476,7 +476,7 @@ void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) { #define SCI_DISPLAY_DUMMY3 117 #define SCI_DISPLAY_DONTSHOWBITS 121 -reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { +reg_t GfxPaint16::kernelDisplay(const char *text, uint16 languageSplitter, int argc, reg_t *argv) { reg_t displayArg; TextAlignment alignment = SCI_TEXT16_ALIGNMENT_LEFT; int16 colorPen = -1, colorBack = -1, width = -1, bRedraw = 1; @@ -572,7 +572,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { } // now drawing the text - _text16->Size(rect, text, -1, width); + _text16->Size(rect, text, languageSplitter, -1, width); rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop); // Note: This code has been found in SCI1 middle and newer games. It was // previously only for SCI1 late and newer, but the LSL1 interpreter contains @@ -588,7 +588,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { result = bitsSave(rect, GFX_SCREEN_MASK_VISUAL); if (colorBack != -1) fillRect(rect, GFX_SCREEN_MASK_VISUAL, colorBack, 0, 0); - _text16->Box(text, false, rect, alignment, -1); + _text16->Box(text, languageSplitter, false, rect, alignment, -1); if (_screen->_picNotValid == 0 && bRedraw) bitsShow(rect); // restoring port and cursor pos diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h index 882f311a5b..955cfdec8f 100644 --- a/engines/sci/graphics/paint16.h +++ b/engines/sci/graphics/paint16.h @@ -80,7 +80,7 @@ public: void kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode); void kernelGraphRedrawBox(Common::Rect rect); - reg_t kernelDisplay(const char *text, int argc, reg_t *argv); + reg_t kernelDisplay(const char *text, uint16 languageSplitter, int argc, reg_t *argv); reg_t kernelPortraitLoad(const Common::String &resourceName); void kernelPortraitShow(const Common::String &resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq); diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp index 7d106b5b02..a210a469f1 100644 --- a/engines/sci/graphics/paint32.cpp +++ b/engines/sci/graphics/paint32.cpp @@ -46,7 +46,7 @@ void GfxPaint32::fillRect(Common::Rect rect, byte color) { Common::Rect clipRect = rect; clipRect.clip(_screen->getWidth(), _screen->getHeight()); - + for (y = clipRect.top; y < clipRect.bottom; y++) { for (x = clipRect.left; x < clipRect.right; x++) { _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, color, 0, 0); diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index a3624c7959..59abef5550 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -65,14 +65,21 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen) // the real merging done in earlier games. If we use the copying over, we // will get issues because some views have marked all colors as being used // and those will overwrite the current palette in that case - if (getSciVersion() < SCI_VERSION_1_1) + if (getSciVersion() < SCI_VERSION_1_1) { _useMerging = true; - else if (getSciVersion() == SCI_VERSION_1_1) + _use16bitColorMatch = true; + } else if (getSciVersion() == SCI_VERSION_1_1) { // there are some games that use inbetween SCI1.1 interpreter, so we have // to detect if the current game is merging or copying _useMerging = _resMan->detectPaletteMergingSci11(); - else // SCI32 + _use16bitColorMatch = _useMerging; + // Note: Laura Bow 2 floppy uses the new palette format and is detected + // as 8 bit color matching because of that. + } else { + // SCI32 _useMerging = false; + _use16bitColorMatch = false; // not verified that SCI32 uses 8-bit color matching + } palVaryInit(); @@ -120,6 +127,10 @@ bool GfxPalette::isMerging() { return _useMerging; } +bool GfxPalette::isUsing16bitColorMatch() { + return _use16bitColorMatch; +} + // meant to get called only once during init of engine void GfxPalette::setDefault() { if (_resMan->getViewType() == kViewEga) @@ -464,8 +475,8 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { // check if exact color could be matched res = matchColor(newPalette->colors[i].r, newPalette->colors[i].g, newPalette->colors[i].b); - if (res & 0x8000) { // exact match was found - newPalette->mapping[i] = res & 0xFF; + if (res & SCI_PALETTE_MATCH_PERFECT) { // exact match was found + newPalette->mapping[i] = res & SCI_PALETTE_MATCH_COLORMASK; continue; } @@ -486,8 +497,8 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { // if still no luck - set an approximate color if (j == 256) { - newPalette->mapping[i] = res & 0xFF; - _sysPalette.colors[res & 0xFF].used |= 0x10; + newPalette->mapping[i] = res & SCI_PALETTE_MATCH_COLORMASK; + _sysPalette.colors[res & SCI_PALETTE_MATCH_COLORMASK].used |= 0x10; } } @@ -509,29 +520,47 @@ void GfxPalette::drewPicture(GuiResourceId pictureId) { } } -uint16 GfxPalette::matchColor(byte r, byte g, byte b) { - byte found = 0xFF; - int diff = 0x2FFFF, cdiff; - int16 dr,dg,db; - - for (int i = 1; i < 255; i++) { - if ((!_sysPalette.colors[i].used)) - continue; - dr = _sysPalette.colors[i].r - r; - dg = _sysPalette.colors[i].g - g; - db = _sysPalette.colors[i].b - b; -// minimum squares match - cdiff = (dr*dr) + (dg*dg) + (db*db); -// minimum sum match (Sierra's) -// cdiff = ABS(dr) + ABS(dg) + ABS(db); - if (cdiff < diff) { - if (cdiff == 0) - return i | 0x8000; // setting this flag to indicate exact match - found = i; - diff = cdiff; +uint16 GfxPalette::matchColor(byte matchRed, byte matchGreen, byte matchBlue) { + int16 colorNr; + int16 differenceRed, differenceGreen, differenceBlue; + int16 differenceTotal = 0; + int16 bestDifference = 0x7FFF; + uint16 bestColorNr = 255; + + if (_use16bitColorMatch) { + // used by SCI0 to SCI1, also by the first few SCI1.1 games + for (colorNr = 0; colorNr < 256; colorNr++) { + if ((!_sysPalette.colors[colorNr].used)) + continue; + differenceRed = ABS(_sysPalette.colors[colorNr].r - matchRed); + differenceGreen = ABS(_sysPalette.colors[colorNr].g - matchGreen); + differenceBlue = ABS(_sysPalette.colors[colorNr].b - matchBlue); + differenceTotal = differenceRed + differenceGreen + differenceBlue; + if (differenceTotal <= bestDifference) { + bestDifference = differenceTotal; + bestColorNr = colorNr; + } + } + } else { + // SCI1.1, starting with QfG3 introduced a bug in the matching code + // we have to implement it as well, otherwise some colors will be "wrong" in comparison to the original interpreter + // See Space Quest 5 bug #6455 + for (colorNr = 0; colorNr < 256; colorNr++) { + if ((!_sysPalette.colors[colorNr].used)) + continue; + differenceRed = (uint8)ABS<int8>(_sysPalette.colors[colorNr].r - matchRed); + differenceGreen = (uint8)ABS<int8>(_sysPalette.colors[colorNr].g - matchGreen); + differenceBlue = (uint8)ABS<int8>(_sysPalette.colors[colorNr].b - matchBlue); + differenceTotal = differenceRed + differenceGreen + differenceBlue; + if (differenceTotal <= bestDifference) { + bestDifference = differenceTotal; + bestColorNr = colorNr; + } } } - return found; + if (differenceTotal == 0) // original interpreter does not do this, instead it does 2 calls for merges in the worst case + return bestColorNr | SCI_PALETTE_MATCH_PERFECT; // we set this flag, so that we can optimize during palette merge + return bestColorNr; } void GfxPalette::getSys(Palette *pal) { @@ -621,7 +650,7 @@ void GfxPalette::kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 int } int16 GfxPalette::kernelFindColor(uint16 r, uint16 g, uint16 b) { - return matchColor(r, g, b) & 0xFF; + return matchColor(r, g, b) & SCI_PALETTE_MATCH_COLORMASK; } // Returns true, if palette got changed diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 347695deb8..500a45eccf 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -31,6 +31,10 @@ namespace Sci { class ResourceManager; class GfxScreen; +// Special flag implemented by us for optimization in palette merge +#define SCI_PALETTE_MATCH_PERFECT 0x8000 +#define SCI_PALETTE_MATCH_COLORMASK 0xFF + enum ColorRemappingType { kRemappingNone = 0, kRemappingByRange = 1, @@ -46,6 +50,7 @@ public: ~GfxPalette(); bool isMerging(); + bool isUsing16bitColorMatch(); void setDefault(); void createFromData(byte *data, int bytesLeft, Palette *paletteOut); @@ -124,6 +129,7 @@ private: bool _sysPaletteChanged; bool _useMerging; + bool _use16bitColorMatch; Common::Array<PalSchedule> _schedules; diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 434a490109..d7ef84dc1e 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -88,10 +88,13 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1 } void GfxPicture::reset() { + int16 startY = _ports->getPort()->top; + int16 startX = 0; int16 x, y; - for (y = _ports->getPort()->top; y < _screen->getHeight(); y++) { - for (x = 0; x < _screen->getWidth(); x++) { - _screen->putPixel(x, y, GFX_SCREEN_MASK_ALL, 255, 0, 0); + _screen->vectorAdjustCoordinate(&startX, &startY); + for (y = startY; y < _screen->getHeight(); y++) { + for (x = startX; x < _screen->getWidth(); x++) { + _screen->vectorPutPixel(x, y, GFX_SCREEN_MASK_ALL, 255, 0, 0); } } } @@ -246,7 +249,7 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos int16 y, lastY, x, leftX, rightX; int pixelCount; uint16 width, height; - + // if the picture is not an overlay and we are also not in EGA mode, use priority 0 if (!isEGA && !_addToFlag) priority = 0; @@ -362,7 +365,7 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos ptr = celBitmap; ptr += skipCelBitmapPixels; ptr += skipCelBitmapLines * width; - + if ((!isEGA) || (priority < 16)) { // VGA + EGA, EGA only checks priority, when given priority is below 16 if (!_mirroredFlag) { @@ -482,6 +485,8 @@ enum { PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT = 4 }; +//#define DEBUG_PICTURE_DRAW 1 + #ifdef DEBUG_PICTURE_DRAW const char *picOpcodeNames[] = { "Set color", @@ -589,6 +594,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { while (curPos < dataSize) { #ifdef DEBUG_PICTURE_DRAW debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos); + _screen->copyToScreen(); + g_system->updateScreen(); + g_system->delayMillis(400); #endif switch (pic_op = data[curPos++]) { case PIC_OP_SET_COLOR: @@ -934,17 +942,17 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by Common::Point p, p1; byte screenMask = _screen->getDrawingMask(color, priority, control); byte matchedMask, matchMask; - int16 w, e, a_set, b_set; bool isEGA = (_resMan->getViewType() == kViewEga); p.x = x + curPort->left; p.y = y + curPort->top; - stack.push(p); - byte searchColor = _screen->getVisual(p.x, p.y); - byte searchPriority = _screen->getPriority(p.x, p.y); - byte searchControl = _screen->getControl(p.x, p.y); + _screen->vectorAdjustCoordinate(&p.x, &p.y); + + byte searchColor = _screen->vectorGetVisual(p.x, p.y); + byte searchPriority = _screen->vectorGetPriority(p.x, p.y); + byte searchControl = _screen->vectorGetControl(p.x, p.y); if (isEGA) { // In EGA games a pixel in the framebuffer is only 4 bits. We store @@ -991,22 +999,31 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by } // hard borders for filling - int l = curPort->rect.left + curPort->left; - int t = curPort->rect.top + curPort->top; - int r = curPort->rect.right + curPort->left - 1; - int b = curPort->rect.bottom + curPort->top - 1; + int16 borderLeft = curPort->rect.left + curPort->left; + int16 borderTop = curPort->rect.top + curPort->top; + int16 borderRight = curPort->rect.right + curPort->left - 1; + int16 borderBottom = curPort->rect.bottom + curPort->top - 1; + int16 curToLeft, curToRight, a_set, b_set; + + // Translate coordinates, if required (needed for Macintosh 480x300) + _screen->vectorAdjustCoordinate(&borderLeft, &borderTop); + _screen->vectorAdjustCoordinate(&borderRight, &borderBottom); + //return; + + stack.push(p); + while (stack.size()) { p = stack.pop(); - if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled + if ((matchedMask = _screen->vectorIsFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled continue; - _screen->putPixel(p.x, p.y, screenMask, color, priority, control); - w = p.x; - e = p.x; + _screen->vectorPutPixel(p.x, p.y, screenMask, color, priority, control); + curToLeft = p.x; + curToRight = p.x; // moving west and east pointers as long as there is a matching color to fill - while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) - _screen->putPixel(--w, p.y, screenMask, color, priority, control); - while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) - _screen->putPixel(++e, p.y, screenMask, color, priority, control); + while (curToLeft > borderLeft && (matchedMask = _screen->vectorIsFillMatch(curToLeft - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) + _screen->vectorPutPixel(--curToLeft, p.y, screenMask, color, priority, control); + while (curToRight < borderRight && (matchedMask = _screen->vectorIsFillMatch(curToRight + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) + _screen->vectorPutPixel(++curToRight, p.y, screenMask, color, priority, control); #if 0 // debug code for floodfill _screen->copyToScreen(); @@ -1015,10 +1032,10 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by #endif // checking lines above and below for possible flood targets a_set = b_set = 0; - while (w <= e) { - if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above + while (curToLeft <= curToRight) { + if (p.y > borderTop && (matchedMask = _screen->vectorIsFillMatch(curToLeft, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above if (a_set == 0) { - p1.x = w; + p1.x = curToLeft; p1.y = p.y - 1; stack.push(p1); a_set = 1; @@ -1026,16 +1043,16 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by } else a_set = 0; - if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below + if (p.y < borderBottom && (matchedMask = _screen->vectorIsFillMatch(curToLeft, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below if (b_set == 0) { - p1.x = w; + p1.x = curToLeft; p1.y = p.y + 1; stack.push(p1); b_set = 1; } } else b_set = 0; - w++; + curToLeft++; } } } @@ -1173,7 +1190,7 @@ void GfxPicture::vectorPatternBox(Common::Rect box, byte color, byte prio, byte for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { - _screen->putPixel(x, y, flag, color, prio, control); + _screen->vectorPutPixel(x, y, flag, color, prio, control); } } } @@ -1186,7 +1203,7 @@ void GfxPicture::vectorPatternTexturedBox(Common::Rect box, byte color, byte pri for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (*textureData) { - _screen->putPixel(x, y, flag, color, prio, control); + _screen->vectorPutPixel(x, y, flag, color, prio, control); } textureData++; } @@ -1203,7 +1220,7 @@ void GfxPicture::vectorPatternCircle(Common::Rect box, byte size, byte color, by for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (bitmap & 1) { - _screen->putPixel(x, y, flag, color, prio, control); + _screen->vectorPutPixel(x, y, flag, color, prio, control); } bitNo++; if (bitNo == 8) { @@ -1222,12 +1239,12 @@ void GfxPicture::vectorPatternTexturedCircle(Common::Rect box, byte size, byte c byte bitNo = 0; const bool *textureData = &vectorPatternTextures[vectorPatternTextureOffset[texture]]; int y, x; - + for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (bitmap & 1) { if (*textureData) { - _screen->putPixel(x, y, flag, color, prio, control); + _screen->vectorPutPixel(x, y, flag, color, prio, control); } textureData++; } @@ -1252,7 +1269,10 @@ void GfxPicture::vectorPattern(int16 x, int16 y, byte color, byte priority, byte rect.top = y; rect.left = x; rect.setHeight((size*2)+1); rect.setWidth((size*2)+2); _ports->offsetRect(rect); - rect.clip(_screen->getWidth(), _screen->getHeight()); + rect.clip(_screen->getScriptWidth(), _screen->getScriptHeight()); + + _screen->vectorAdjustCoordinate(&rect.left, &rect.top); + _screen->vectorAdjustCoordinate(&rect.right, &rect.bottom); if (code & SCI_PATTERN_CODE_RECTANGLE) { // Rectangle diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp index 488450485d..668de616fb 100644 --- a/engines/sci/graphics/portrait.cpp +++ b/engines/sci/graphics/portrait.cpp @@ -134,34 +134,34 @@ void Portrait::init() { // raw lip-sync ID table follows uint32 lipSyncIDTableSize; - + lipSyncIDTableSize = READ_LE_UINT32(data); data += 4; assert( lipSyncIDTableSize == (_lipSyncIDCount * 4) ); _lipSyncIDTable = data; data += lipSyncIDTableSize; - + // raw lip-sync frame table follows uint32 lipSyncDataTableSize; uint32 lipSyncDataTableLastOffset; byte lipSyncData; uint16 lipSyncDataNr; uint16 lipSyncCurOffset; - + lipSyncDataTableSize = READ_LE_UINT32(data); data += 4; assert( lipSyncDataTableSize == 0x220 ); // always this size, just a safety-check - + _lipSyncData = data; lipSyncDataTableLastOffset = lipSyncDataTableSize - 1; _lipSyncDataOffsetTable = new uint16[ _lipSyncIDCount ]; - + lipSyncDataNr = 0; lipSyncCurOffset = 0; while ( (lipSyncCurOffset < lipSyncDataTableSize) && (lipSyncDataNr < _lipSyncIDCount) ) { // We are currently at the start of ID-frame data _lipSyncDataOffsetTable[lipSyncDataNr] = lipSyncCurOffset; - + // Look for end of ID-frame data lipSyncData = *data++; lipSyncCurOffset++; while ( (lipSyncData != 0xFF) && (lipSyncCurOffset < lipSyncDataTableLastOffset) ) { @@ -195,7 +195,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint Resource *syncResource = _resMan->findResource(syncResourceId, true); uint syncOffset = 0; #endif - + #ifdef DEBUG_PORTRAIT // prints out the current lip sync ASCII data char debugPrint[4000]; @@ -246,7 +246,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint warning("kPortrait: no rave resource %d %X", resourceId, audioNumber); return; } - + // Do animation depending on rave resource till audio is done playing int16 raveTicks; uint16 raveID; @@ -264,7 +264,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint raveTicks = raveGetTicks(raveResource, &raveOffset); if (raveTicks < 0) break; - + // get lipSyncID raveID = raveGetID(raveResource, &raveOffset); if (raveID) { @@ -272,7 +272,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint } else { raveLipSyncData = NULL; } - + timerPosition += raveTicks; // Wait till syncTime passed, then show specific animation bitmap @@ -287,7 +287,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint curPosition = _audio->getAudioPosition(); } while ((curPosition != -1) && (curPosition < timerPosition) && (!userAbort)); } - + if (raveLipSyncData) { // lip sync data is // Tick:Byte, Bitmap-Nr:BYTE @@ -308,7 +308,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint } while ((curPosition != -1) && (curPosition < timerPositionWithin) && (!userAbort)); raveLipSyncBitmapNr = *raveLipSyncData++; - + // bitmap nr within sync data is base 1, we need base 0 raveLipSyncBitmapNr--; @@ -319,7 +319,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint } else { warning("kPortrait: rave lip sync data tried to draw non-existent bitmap %d", raveLipSyncBitmapNr); } - + raveLipSyncTicks = *raveLipSyncData++; } } @@ -372,7 +372,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint } } #endif - + // Reset the portrait bitmap to "closed mouth" state (rave.dll seems to do the same) drawBitmap(0); bitsShow(); @@ -393,10 +393,10 @@ int16 Portrait::raveGetTicks(Resource *resource, uint *offset) { byte *curData = resource->data + curOffset; byte curByte; uint16 curValue = 0; - + if (curOffset >= resource->size) return -1; - + while (curOffset < resource->size) { curByte = *curData++; curOffset++; if ( curByte == ' ' ) @@ -418,7 +418,7 @@ uint16 Portrait::raveGetID(Resource *resource, uint *offset) { byte *curData = resource->data + curOffset; byte curByte = 0; uint16 curValue = 0; - + while (curOffset < resource->size) { curByte = *curData++; curOffset++; if ( curByte == ' ' ) @@ -429,7 +429,7 @@ uint16 Portrait::raveGetID(Resource *resource, uint *offset) { curValue |= curByte; } } - + *offset = curOffset; return curValue; } @@ -440,17 +440,17 @@ byte *Portrait::raveGetLipSyncData(uint16 raveID) { byte *lipSyncIDPtr = _lipSyncIDTable; byte lipSyncIDByte1, lipSyncIDByte2; uint16 lipSyncID; - + lipSyncIDPtr++; // skip over first byte while (lipSyncIDNr < _lipSyncIDCount) { lipSyncIDByte1 = *lipSyncIDPtr++; lipSyncIDByte2 = *lipSyncIDPtr++; lipSyncID = ( lipSyncIDByte1 << 8 ) | lipSyncIDByte2; - + if ( lipSyncID == raveID ) { return _lipSyncData + _lipSyncDataOffsetTable[lipSyncIDNr]; } - + lipSyncIDNr++; lipSyncIDPtr += 2; // ID is every 4 bytes } diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h index 877b253bcf..e0888daa86 100644 --- a/engines/sci/graphics/portrait.h +++ b/engines/sci/graphics/portrait.h @@ -72,7 +72,7 @@ private: Common::String _resourceName; byte *_fileData; - + uint32 _lipSyncIDCount; byte *_lipSyncIDTable; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 56c63a7b12..bcc991081e 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -63,10 +63,10 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te openPort(_menuPort); setPort(_menuPort); _text16->SetFont(0); - _menuPort->rect = Common::Rect(0, 0, _screen->getWidth(), _screen->getHeight()); - _menuBarRect = Common::Rect(0, 0, _screen->getWidth(), 9); - _menuRect = Common::Rect(0, 0, _screen->getWidth(), 10); - _menuLine = Common::Rect(0, 9, _screen->getWidth(), 10); + _menuPort->rect = Common::Rect(0, 0, _screen->getScriptWidth(), _screen->getScriptHeight()); + _menuBarRect = Common::Rect(0, 0, _screen->getScriptWidth(), 9); + _menuRect = Common::Rect(0, 0, _screen->getScriptWidth(), 10); + _menuLine = Common::Rect(0, 9, _screen->getScriptWidth(), 10); _wmgrPort = new Port(1); _windowsById.resize(2); @@ -122,13 +122,13 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te } else { _wmgrPort->rect.bottom = _screen->getHeight(); } - _wmgrPort->rect.right = _screen->getWidth(); + _wmgrPort->rect.right = _screen->getScriptWidth(); _wmgrPort->rect.moveTo(0, 0); _wmgrPort->curTop = 0; _wmgrPort->curLeft = 0; _windowList.push_front(_wmgrPort); - _picWind = addWindow(Common::Rect(0, offTop, _screen->getWidth(), _screen->getHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true); + _picWind = addWindow(Common::Rect(0, offTop, _screen->getScriptWidth(), _screen->getScriptHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true); // For SCI0 games till kq4 (.502 - not including) we set _picWind top to offTop instead // Because of the menu/status bar if (_usesOldGfxFunctions) @@ -321,13 +321,13 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor // their interpreter even in the newer VGA games. r.left = r.left & 0xFFFE; - if (r.width() > _screen->getWidth()) { + if (r.width() > _screen->getScriptWidth()) { // We get invalid dimensions at least at the end of sq3 (script bug!). // Same happens very often in lsl5, sierra sci didnt fix it but it looked awful. // Also happens frequently in the demo of GK1. warning("Fixing too large window, left: %d, right: %d", dims.left, dims.right); r.left = 0; - r.right = _screen->getWidth() - 1; + r.right = _screen->getScriptWidth() - 1; if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME)) r.right--; } diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index c5c94d7991..8b0e76332f 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -37,7 +37,15 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { // Scale the screen, if needed _upscaledHires = GFX_SCREEN_UPSCALED_DISABLED; - + + // we default to scripts running at 320x200 + _scriptWidth = 320; + _scriptHeight = 200; + _width = 0; + _height = 0; + _displayWidth = 0; + _displayHeight = 0; + // King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able // to provide that under DOS as well, but as gk1/floppy does support // upscaled hires scriptswise, but doesn't actually have the hires content @@ -50,10 +58,33 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _upscaledHires = GFX_SCREEN_UPSCALED_640x480; #endif } + + // Japanese versions of games use hi-res font on upscaled version of the game. + if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1)) + _upscaledHires = GFX_SCREEN_UPSCALED_640x400; + // Macintosh SCI0 games used 480x300, while the scripts were running at 320x200 if (g_sci->getPlatform() == Common::kPlatformMacintosh) { - if (getSciVersion() <= SCI_VERSION_01) + if (getSciVersion() <= SCI_VERSION_01) { _upscaledHires = GFX_SCREEN_UPSCALED_480x300; + _width = 480; + _height = 300; // regular visual, priority and control map are 480x300 (this is different than other upscaled SCI games) + } + + // Some Mac SCI1/1.1 games only take up 190 rows and do not + // have the menu bar. + // TODO: Verify that LSL1 and LSL5 use height 190 + switch (g_sci->getGameId()) { + case GID_FREDDYPHARKAS: + case GID_KQ5: + case GID_KQ6: + case GID_LSL1: + case GID_LSL5: + case GID_SQ1: + _width = 190; + default: + break; + } } #ifdef ENABLE_SCI32 @@ -65,76 +96,77 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { #endif if (_resMan->detectHires()) { - _width = 640; - _pitch = 640; - _height = 480; - } else { - _width = 320; - _pitch = 320; - _height = getLowResScreenHeight(); + _scriptWidth = 640; + _scriptHeight = 480; } #ifdef ENABLE_SCI32 - // Phantasmagoria 1 sets a window area of 630x450 + // Phantasmagoria 1 effectively outputs 630x450 + // Coordinate translation has to use this resolution as well if (g_sci->getGameId() == GID_PHANTASMAGORIA) { _width = 630; _height = 450; } #endif - // Japanese versions of games use hi-res font on upscaled version of the game. - if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1)) - _upscaledHires = GFX_SCREEN_UPSCALED_640x400; + // if not yet set, set those to script-width/height + if (!_width) + _width = _scriptWidth; + if (!_height) + _height = _scriptHeight; - _pixels = _pitch * _height; + _pixels = _width * _height; switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_480x300: // Space Quest 3, Hoyle 1+2 on MAC use this one - // TODO: Sierra's upscaling worked differently. We need to figure out the exact algo _displayWidth = 480; _displayHeight = 300; - for (int i = 0; i <= _height; i++) + for (int i = 0; i <= _scriptHeight; i++) _upscaledHeightMapping[i] = (i * 3) >> 1; - for (int i = 0; i <= _width; i++) + for (int i = 0; i <= _scriptWidth; i++) _upscaledWidthMapping[i] = (i * 3) >> 1; break; case GFX_SCREEN_UPSCALED_640x400: // Police Quest 2 and Quest For Glory on PC9801 (Japanese) _displayWidth = 640; _displayHeight = 400; - for (int i = 0; i <= _height; i++) + for (int i = 0; i <= _scriptHeight; i++) _upscaledHeightMapping[i] = i * 2; - for (int i = 0; i <= _width; i++) + for (int i = 0; i <= _scriptWidth; i++) _upscaledWidthMapping[i] = i * 2; break; case GFX_SCREEN_UPSCALED_640x440: // used by King's Quest 6 on Windows _displayWidth = 640; _displayHeight = 440; - for (int i = 0; i <= _height; i++) + for (int i = 0; i <= _scriptHeight; i++) _upscaledHeightMapping[i] = (i * 11) / 5; - for (int i = 0; i <= _width; i++) + for (int i = 0; i <= _scriptWidth; i++) _upscaledWidthMapping[i] = i * 2; break; case GFX_SCREEN_UPSCALED_640x480: // Gabriel Knight 1 (VESA, Mac) _displayWidth = 640; _displayHeight = 480; - for (int i = 0; i <= _height; i++) + for (int i = 0; i <= _scriptHeight; i++) _upscaledHeightMapping[i] = (i * 12) / 5; - for (int i = 0; i <= _width; i++) + for (int i = 0; i <= _scriptWidth; i++) _upscaledWidthMapping[i] = i * 2; break; default: - _displayWidth = _pitch; - _displayHeight = _height; + if (!_displayWidth) + _displayWidth = _width; + if (!_displayHeight) + _displayHeight = _height; memset(&_upscaledHeightMapping, 0, sizeof(_upscaledHeightMapping) ); memset(&_upscaledWidthMapping, 0, sizeof(_upscaledWidthMapping) ); break; } _displayPixels = _displayWidth * _displayHeight; + + // Allocate visual, priority, control and display screen _visualScreen = (byte *)calloc(_pixels, 1); _priorityScreen = (byte *)calloc(_pixels, 1); _controlScreen = (byte *)calloc(_pixels, 1); @@ -179,6 +211,34 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { error("Unknown SCI1.1 Mac game"); } else initGraphics(_displayWidth, _displayHeight, _displayWidth > 320); + + // Initialize code pointers + _vectorAdjustCoordinatePtr = &GfxScreen::vectorAdjustCoordinateNOP; + _vectorAdjustLineCoordinatesPtr = &GfxScreen::vectorAdjustLineCoordinatesNOP; + _vectorIsFillMatchPtr = &GfxScreen::vectorIsFillMatchNormal; + _vectorPutPixelPtr = &GfxScreen::putPixelNormal; + _vectorPutLinePixelPtr = &GfxScreen::putPixel; + _vectorGetPixelPtr = &GfxScreen::getPixelNormal; + _putPixelPtr = &GfxScreen::putPixelNormal; + _getPixelPtr = &GfxScreen::getPixelNormal; + + switch (_upscaledHires) { + case GFX_SCREEN_UPSCALED_480x300: + _vectorAdjustCoordinatePtr = &GfxScreen::vectorAdjustCoordinate480x300Mac; + _vectorAdjustLineCoordinatesPtr = &GfxScreen::vectorAdjustLineCoordinates480x300Mac; + // vectorPutPixel -> we already adjust coordinates for vector code, that's why we can set pixels directly + // vectorGetPixel -> see vectorPutPixel + _vectorPutLinePixelPtr = &GfxScreen::vectorPutLinePixel480x300Mac; + _putPixelPtr = &GfxScreen::putPixelAllUpscaled; + _getPixelPtr = &GfxScreen::getPixelUpscaled; + break; + case GFX_SCREEN_UPSCALED_640x400: + case GFX_SCREEN_UPSCALED_640x440: + case GFX_SCREEN_UPSCALED_640x480: + _vectorPutPixelPtr = &GfxScreen::putPixelDisplayUpscaled; + _putPixelPtr = &GfxScreen::putPixelDisplayUpscaled; + break; + } } GfxScreen::~GfxScreen() { @@ -232,7 +292,7 @@ void GfxScreen::copyRectToScreen(const Common::Rect &rect, int16 x, int16 y) { } else { int rectHeight = _upscaledHeightMapping[rect.bottom] - _upscaledHeightMapping[rect.top]; int rectWidth = _upscaledWidthMapping[rect.right] - _upscaledWidthMapping[rect.left]; - + g_system->copyRectToScreen(_activeScreen + _upscaledHeightMapping[rect.top] * _displayWidth + _upscaledWidthMapping[rect.left], _displayWidth, _upscaledWidthMapping[x], _upscaledHeightMapping[y], rectWidth, rectHeight); } } @@ -248,43 +308,162 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) { return flag; } -void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority, byte control) { - int offset = y * _pitch + x; +void GfxScreen::vectorAdjustCoordinateNOP(int16 *x, int16 *y) { +} - if (drawMask & GFX_SCREEN_MASK_VISUAL) { - _visualScreen[offset] = color; - if (!_upscaledHires) { - _displayScreen[offset] = color; +void GfxScreen::vectorAdjustCoordinate480x300Mac(int16 *x, int16 *y) { + *x = _upscaledWidthMapping[*x]; + *y = _upscaledHeightMapping[*y]; +} + +void GfxScreen::vectorAdjustLineCoordinatesNOP(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) { +} + +void GfxScreen::vectorAdjustLineCoordinates480x300Mac(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) { + int16 displayLeft = _upscaledWidthMapping[*left]; + int16 displayRight = _upscaledWidthMapping[*right]; + int16 displayTop = _upscaledHeightMapping[*top]; + int16 displayBottom = _upscaledHeightMapping[*bottom]; + + if (displayLeft < displayRight) { + // one more pixel to the left, one more pixel to the right + if (displayLeft > 0) + vectorPutLinePixel(displayLeft - 1, displayTop, drawMask, color, priority, control); + vectorPutLinePixel(displayRight + 1, displayBottom, drawMask, color, priority, control); + } else if (displayLeft > displayRight) { + if (displayRight > 0) + vectorPutLinePixel(displayRight - 1, displayBottom, drawMask, color, priority, control); + vectorPutLinePixel(displayLeft + 1, displayTop, drawMask, color, priority, control); + } + *left = displayLeft; + *top = displayTop; + *right = displayRight; + *bottom = displayBottom; +} + +byte GfxScreen::vectorIsFillMatchNormal(int16 x, int16 y, byte screenMask, byte checkForColor, byte checkForPriority, byte checkForControl, bool isEGA) { + int offset = y * _width + x; + byte match = 0; + + if (screenMask & GFX_SCREEN_MASK_VISUAL) { + if (!isEGA) { + if (*(_visualScreen + offset) == checkForColor) + match |= GFX_SCREEN_MASK_VISUAL; } else { - putScaledPixelOnDisplay(x, y, color); + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + byte EGAcolor = *(_visualScreen + offset); + if ((x ^ y) & 1) + EGAcolor = (EGAcolor ^ (EGAcolor >> 4)) & 0x0F; + else + EGAcolor = EGAcolor & 0x0F; + if (EGAcolor == checkForColor) + match |= GFX_SCREEN_MASK_VISUAL; } } + if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == checkForPriority) + match |= GFX_SCREEN_MASK_PRIORITY; + if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == checkForControl) + match |= GFX_SCREEN_MASK_CONTROL; + return match; +} + +// Special 480x300 Mac putPixel for vector line drawing, also draws an additional pixel below the actual one +void GfxScreen::vectorPutLinePixel480x300Mac(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + int offset = y * _width + x; + + if (drawMask & GFX_SCREEN_MASK_VISUAL) { + _visualScreen[offset] = color; + _visualScreen[offset + _width] = color; + _displayScreen[offset] = color; + // also set pixel below actual pixel + _displayScreen[offset + _displayWidth] = color; + } + if (drawMask & GFX_SCREEN_MASK_PRIORITY) { + _priorityScreen[offset] = priority; + _priorityScreen[offset + _width] = priority; + } + if (drawMask & GFX_SCREEN_MASK_CONTROL) { + _controlScreen[offset] = control; + _controlScreen[offset + _width] = control; + } +} + +// Directly sets a pixel on various screens, display is not upscaled +void GfxScreen::putPixelNormal(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + int offset = y * _width + x; + + if (drawMask & GFX_SCREEN_MASK_VISUAL) { + _visualScreen[offset] = color; + _displayScreen[offset] = color; + } + if (drawMask & GFX_SCREEN_MASK_PRIORITY) + _priorityScreen[offset] = priority; + if (drawMask & GFX_SCREEN_MASK_CONTROL) + _controlScreen[offset] = control; +} + +// Directly sets a pixel on various screens, display IS upscaled +void GfxScreen::putPixelDisplayUpscaled(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + int offset = y * _width + x; + + if (drawMask & GFX_SCREEN_MASK_VISUAL) { + _visualScreen[offset] = color; + putScaledPixelOnScreen(_displayScreen, x, y, color); + } if (drawMask & GFX_SCREEN_MASK_PRIORITY) _priorityScreen[offset] = priority; if (drawMask & GFX_SCREEN_MASK_CONTROL) _controlScreen[offset] = control; } +// Directly sets a pixel on various screens, ALL screens ARE upscaled +void GfxScreen::putPixelAllUpscaled(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + if (drawMask & GFX_SCREEN_MASK_VISUAL) { + putScaledPixelOnScreen(_visualScreen, x, y, color); + putScaledPixelOnScreen(_displayScreen, x, y, color); + } + if (drawMask & GFX_SCREEN_MASK_PRIORITY) + putScaledPixelOnScreen(_priorityScreen, x, y, priority); + if (drawMask & GFX_SCREEN_MASK_CONTROL) + putScaledPixelOnScreen(_controlScreen, x, y, control); +} + /** * This is used to put font pixels onto the screen - we adjust differently, so that we won't * do triple pixel lines in any case on upscaled hires. That way the font will not get distorted * Sierra SCI didn't do this */ -void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) { - int actualY = startingY + y; +void GfxScreen::putFontPixel(int16 startingY, int16 x, int16 y, byte color) { + int16 actualY = startingY + y; if (_fontIsUpscaled) { // Do not scale ourselves, but put it on the display directly putPixelOnDisplay(x, actualY, color); } else { - int offset = actualY * _pitch + x; + int offset = actualY * _width + x; _visualScreen[offset] = color; switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_DISABLED: _displayScreen[offset] = color; break; + case GFX_SCREEN_UPSCALED_640x400: + case GFX_SCREEN_UPSCALED_640x440: + case GFX_SCREEN_UPSCALED_640x480: { + // to 1-> 4 pixels upscaling for all of those, so that fonts won't look weird + int displayOffset = (_upscaledHeightMapping[startingY] + y * 2) * _displayWidth + x * 2; + _displayScreen[displayOffset] = color; + _displayScreen[displayOffset + 1] = color; + displayOffset += _displayWidth; + _displayScreen[displayOffset] = color; + _displayScreen[displayOffset + 1] = color; + break; + } default: - putScaledPixelOnDisplay(x, actualY, color); + putScaledPixelOnScreen(_displayScreen, x, actualY, color); break; } } @@ -295,12 +474,15 @@ void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) { * only used on upscaled-Hires games where hires content needs to get drawn ONTO * the upscaled display screen (like japanese fonts, hires portraits, etc.). */ -void GfxScreen::putPixelOnDisplay(int x, int y, byte color) { +void GfxScreen::putPixelOnDisplay(int16 x, int16 y, byte color) { int offset = y * _displayWidth + x; _displayScreen[offset] = color; } -void GfxScreen::putScaledPixelOnDisplay(int x, int y, byte color) { +//void GfxScreen::putScaledPixelOnDisplay(int16 x, int16 y, byte color) { +//} + +void GfxScreen::putScaledPixelOnScreen(byte *screen, int16 x, int16 y, byte data) { int displayOffset = _upscaledHeightMapping[y] * _displayWidth + _upscaledWidthMapping[x]; int heightOffsetBreak = (_upscaledHeightMapping[y + 1] - _upscaledHeightMapping[y]) * _displayWidth; int heightOffset = 0; @@ -308,7 +490,7 @@ void GfxScreen::putScaledPixelOnDisplay(int x, int y, byte color) { do { int widthOffset = 0; do { - _displayScreen[displayOffset + heightOffset + widthOffset] = color; + screen[displayOffset + heightOffset + widthOffset] = data; widthOffset++; } while (widthOffset != widthOffsetBreak); heightOffset += _displayWidth; @@ -329,16 +511,18 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte int16 top = CLIP<int16>(startPoint.y, 0, maxHeight); int16 right = CLIP<int16>(endPoint.x, 0, maxWidth); int16 bottom = CLIP<int16>(endPoint.y, 0, maxHeight); - + //set_drawing_flag byte drawMask = getDrawingMask(color, priority, control); + vectorAdjustLineCoordinates(&left, &top, &right, &bottom, drawMask, color, priority, control); + // horizontal line if (top == bottom) { if (right < left) SWAP(right, left); for (int i = left; i <= right; i++) - putPixel(i, top, drawMask, color, priority, control); + vectorPutLinePixel(i, top, drawMask, color, priority, control); return; } // vertical line @@ -346,20 +530,20 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte if (top > bottom) SWAP(top, bottom); for (int i = top; i <= bottom; i++) - putPixel(left, i, drawMask, color, priority, control); + vectorPutLinePixel(left, i, drawMask, color, priority, control); return; } // sloped line - draw with Bresenham algorithm - int dy = bottom - top; - int dx = right - left; - int stepy = dy < 0 ? -1 : 1; - int stepx = dx < 0 ? -1 : 1; + int16 dy = bottom - top; + int16 dx = right - left; + int16 stepy = dy < 0 ? -1 : 1; + int16 stepx = dx < 0 ? -1 : 1; dy = ABS(dy) << 1; dx = ABS(dx) << 1; // setting the 1st and last pixel - putPixel(left, top, drawMask, color, priority, control); - putPixel(right, bottom, drawMask, color, priority, control); + vectorPutLinePixel(left, top, drawMask, color, priority, control); + vectorPutLinePixel(right, bottom, drawMask, color, priority, control); // drawing the line if (dx > dy) { // going horizontal int fraction = dy - (dx >> 1); @@ -370,7 +554,7 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte } left += stepx; fraction += dy; - putPixel(left, top, drawMask, color, priority, control); + vectorPutLinePixel(left, top, drawMask, color, priority, control); } } else { // going vertical int fraction = dx - (dy >> 1); @@ -381,7 +565,7 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte } top += stepy; fraction += dx; - putPixel(left, top, drawMask, color, priority, control); + vectorPutLinePixel(left, top, drawMask, color, priority, control); } } } @@ -394,46 +578,14 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u commonFont->drawChar(displayPtr, chr, _displayWidth, 1, color, 0, -1, -1); } -byte GfxScreen::getVisual(int x, int y) { - return _visualScreen[y * _pitch + x]; -} - -byte GfxScreen::getPriority(int x, int y) { - return _priorityScreen[y * _pitch + x]; -} - -byte GfxScreen::getControl(int x, int y) { - return _controlScreen[y * _pitch + x]; +byte GfxScreen::getPixelNormal(byte *screen, int16 x, int16 y) { + return screen[y * _width + x]; } -byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { - int offset = y * _pitch + x; - byte match = 0; - - if (screenMask & GFX_SCREEN_MASK_VISUAL) { - if (!isEGA) { - if (*(_visualScreen + offset) == t_color) - match |= GFX_SCREEN_MASK_VISUAL; - } else { - // In EGA games a pixel in the framebuffer is only 4 bits. We store - // a full byte per pixel to allow undithering, but when comparing - // pixels for flood-fill purposes, we should only compare the - // visible color of a pixel. - - byte c = *(_visualScreen + offset); - if ((x ^ y) & 1) - c = (c ^ (c >> 4)) & 0x0F; - else - c = c & 0x0F; - if (c == t_color) - match |= GFX_SCREEN_MASK_VISUAL; - } - } - if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri) - match |= GFX_SCREEN_MASK_PRIORITY; - if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con) - match |= GFX_SCREEN_MASK_CONTROL; - return match; +byte GfxScreen::getPixelUpscaled(byte *screen, int16 x, int16 y) { + int16 mappedX = _upscaledWidthMapping[x]; + int16 mappedY = _upscaledHeightMapping[y]; + return screen[mappedY * _width + mappedX]; } int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) { @@ -469,14 +621,14 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) { memcpy(memoryPtr, (void *)&mask, sizeof(mask)); memoryPtr += sizeof(mask); if (mask & GFX_SCREEN_MASK_VISUAL) { - bitsSaveScreen(rect, _visualScreen, _pitch, memoryPtr); + bitsSaveScreen(rect, _visualScreen, _width, memoryPtr); bitsSaveDisplayScreen(rect, memoryPtr); } if (mask & GFX_SCREEN_MASK_PRIORITY) { - bitsSaveScreen(rect, _priorityScreen, _pitch, memoryPtr); + bitsSaveScreen(rect, _priorityScreen, _width, memoryPtr); } if (mask & GFX_SCREEN_MASK_CONTROL) { - bitsSaveScreen(rect, _controlScreen, _pitch, memoryPtr); + bitsSaveScreen(rect, _controlScreen, _width, memoryPtr); } if (mask & GFX_SCREEN_MASK_DISPLAY) { if (!_upscaledHires) @@ -530,14 +682,14 @@ void GfxScreen::bitsRestore(byte *memoryPtr) { memcpy((void *)&mask, memoryPtr, sizeof(mask)); memoryPtr += sizeof(mask); if (mask & GFX_SCREEN_MASK_VISUAL) { - bitsRestoreScreen(rect, memoryPtr, _visualScreen, _pitch); + bitsRestoreScreen(rect, memoryPtr, _visualScreen, _width); bitsRestoreDisplayScreen(rect, memoryPtr); } if (mask & GFX_SCREEN_MASK_PRIORITY) { - bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _pitch); + bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _width); } if (mask & GFX_SCREEN_MASK_CONTROL) { - bitsRestoreScreen(rect, memoryPtr, _controlScreen, _pitch); + bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width); } if (mask & GFX_SCREEN_MASK_DISPLAY) { if (!_upscaledHires) @@ -612,21 +764,22 @@ void GfxScreen::dither(bool addToFlag) { byte color, ditheredColor; byte *visualPtr = _visualScreen; byte *displayPtr = _displayScreen; - + if (!_unditheringEnabled) { // Do dithering on visual and display-screen for (y = 0; y < _height; y++) { - for (x = 0; x < _pitch; x++) { + for (x = 0; x < _width; x++) { color = *visualPtr; if (color & 0xF0) { color ^= color << 4; color = ((x^y) & 1) ? color >> 4 : color & 0x0F; switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_DISABLED: + case GFX_SCREEN_UPSCALED_480x300: *displayPtr = color; break; default: - putScaledPixelOnDisplay(x, y, color); + putScaledPixelOnScreen(_displayScreen, x, y, color); break; } *visualPtr = color; @@ -639,7 +792,7 @@ void GfxScreen::dither(bool addToFlag) { memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors)); // Do dithering on visual screen and put decoded but undithered byte onto display-screen for (y = 0; y < _height; y++) { - for (x = 0; x < _pitch; x++) { + for (x = 0; x < _width; x++) { color = *visualPtr; if (color & 0xF0) { color ^= color << 4; @@ -654,10 +807,11 @@ void GfxScreen::dither(bool addToFlag) { } switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_DISABLED: + case GFX_SCREEN_UPSCALED_480x300: *displayPtr = ditheredColor; break; default: - putScaledPixelOnDisplay(x, y, ditheredColor); + putScaledPixelOnScreen(_displayScreen, x, y, ditheredColor); break; } color = ((x^y) & 1) ? color >> 4 : color & 0x0F; @@ -685,8 +839,8 @@ int16 *GfxScreen::unditherGetDitheredBgColors() { } void GfxScreen::debugShowMap(int mapNo) { - // We cannot really support changing maps when in upscaledHires mode - if (_upscaledHires) + // We cannot really support changing maps when display screen has a different resolution than visual screen + if ((_width != _displayWidth) || (_height != _displayHeight)) return; switch (mapNo) { @@ -779,8 +933,8 @@ void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativ switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_480x300: - x = (x << 1) / 3; - y = (y << 1) / 3; + x = (x * 4) / 6; + y = (y * 4) / 6; break; case GFX_SCREEN_UPSCALED_640x400: x /= 2; @@ -816,26 +970,4 @@ int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) { return oldPicNotValid; } -uint16 GfxScreen::getLowResScreenHeight() { - // Some Mac SCI1/1.1 games only take up 190 rows and do not - // have the menu bar. - // TODO: Verify that LSL1 and LSL5 use height 190 - if (g_sci->getPlatform() == Common::kPlatformMacintosh) { - switch (g_sci->getGameId()) { - case GID_FREDDYPHARKAS: - case GID_KQ5: - case GID_KQ6: - case GID_LSL1: - case GID_LSL5: - case GID_SQ1: - return 190; - default: - break; - } - } - - // Everything else is 200 - return 200; -} - } // End of namespace Sci diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index e266a4ed16..766e32614a 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -69,6 +69,8 @@ public: uint16 getWidth() { return _width; } uint16 getHeight() { return _height; } + uint16 getScriptWidth() { return _scriptWidth; } + uint16 getScriptHeight() { return _scriptHeight; } uint16 getDisplayWidth() { return _displayWidth; } uint16 getDisplayHeight() { return _displayHeight; } byte getColorWhite() { return _colorWhite; } @@ -81,11 +83,51 @@ public: void copyDisplayRectToScreen(const Common::Rect &rect); void copyRectToScreen(const Common::Rect &rect, int16 x, int16 y); + // calls to code pointers + void inline vectorAdjustCoordinate (int16 *x, int16 *y) { + (this->*_vectorAdjustCoordinatePtr)(x, y); + } + void inline vectorAdjustLineCoordinates (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) { + (this->*_vectorAdjustLineCoordinatesPtr)(left, top, right, bottom, drawMask, color, priority, control); + } + byte inline vectorIsFillMatch (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { + return (this->*_vectorIsFillMatchPtr)(x, y, screenMask, t_color, t_pri, t_con, isEGA); + } + void inline vectorPutPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + (this->*_vectorPutPixelPtr)(x, y, drawMask, color, priority, control); + } + void inline vectorPutLinePixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + (this->*_vectorPutLinePixelPtr)(x, y, drawMask, color, priority, control); + } + byte inline vectorGetVisual(int16 x, int16 y) { + return (this->*_vectorGetPixelPtr)(_visualScreen, x, y); + } + byte inline vectorGetPriority(int16 x, int16 y) { + return (this->*_vectorGetPixelPtr)(_priorityScreen, x, y); + } + byte inline vectorGetControl(int16 x, int16 y) { + return (this->*_vectorGetPixelPtr)(_controlScreen, x, y); + } + + + void inline putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { + (this->*_putPixelPtr)(x, y, drawMask, color, priority, control); + } + + byte inline getVisual(int16 x, int16 y) { + return (this->*_getPixelPtr)(_visualScreen, x, y); + } + byte inline getPriority(int16 x, int16 y) { + return (this->*_getPixelPtr)(_priorityScreen, x, y); + } + byte inline getControl(int16 x, int16 y) { + return (this->*_getPixelPtr)(_controlScreen, x, y); + } + byte getDrawingMask(byte color, byte prio, byte control); - void putPixel(int x, int y, byte drawMask, byte color, byte prio, byte control); - void putFontPixel(int startingY, int x, int y, byte color); - void putPixelOnDisplay(int x, int y, byte color); - void putScaledPixelOnDisplay(int x, int y, byte color); + //void putPixel(int16 x, int16 y, byte drawMask, byte color, byte prio, byte control); + void putFontPixel(int16 startingY, int16 x, int16 y, byte color); + void putPixelOnDisplay(int16 x, int16 y, byte color); void drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte prio, byte control); void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) { drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control); @@ -101,10 +143,6 @@ public: void enableUndithering(bool flag); void putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color); - byte getVisual(int x, int y); - byte getPriority(int x, int y); - byte getControl(int x, int y); - byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA); int bitsGetDataSize(Common::Rect rect, byte mask); void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr); @@ -135,9 +173,10 @@ public: private: uint16 _width; - uint16 _pitch; uint16 _height; uint _pixels; + uint16 _scriptWidth; + uint16 _scriptHeight; uint16 _displayWidth; uint16 _displayHeight; uint _displayPixels; @@ -190,8 +229,8 @@ private: * This here holds a translation for vertical+horizontal coordinates between native * (visual) and actual (display) screen. */ - int _upscaledHeightMapping[SCI_SCREEN_UPSCALEDMAXHEIGHT + 1]; - int _upscaledWidthMapping[SCI_SCREEN_UPSCALEDMAXWIDTH + 1]; + int16 _upscaledHeightMapping[SCI_SCREEN_UPSCALEDMAXHEIGHT + 1]; + int16 _upscaledWidthMapping[SCI_SCREEN_UPSCALEDMAXWIDTH + 1]; /** * This defines whether or not the font we're drawing is already scaled @@ -199,7 +238,38 @@ private: */ bool _fontIsUpscaled; - uint16 getLowResScreenHeight(); + // dynamic code + void (GfxScreen::*_vectorAdjustCoordinatePtr) (int16 *x, int16 *y); + void vectorAdjustCoordinateNOP (int16 *x, int16 *y); + void vectorAdjustCoordinate480x300Mac (int16 *x, int16 *y); + + void (GfxScreen::*_vectorAdjustLineCoordinatesPtr) (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control); + void vectorAdjustLineCoordinatesNOP (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control); + void vectorAdjustLineCoordinates480x300Mac (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control); + + byte (GfxScreen::*_vectorIsFillMatchPtr) (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA); + byte vectorIsFillMatchNormal (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA); + byte vectorIsFillMatch480x300Mac (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA); + + void (GfxScreen::*_vectorPutPixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + void vectorPutPixel480x300Mac (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + + void (GfxScreen::*_vectorPutLinePixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + void vectorPutLinePixel480x300Mac (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + + byte (GfxScreen::*_vectorGetPixelPtr) (byte *screen, int16 x, int16 y); + + void (GfxScreen::*_putPixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + void putPixelNormal (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + void putPixelDisplayUpscaled (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + void putPixelAllUpscaled (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control); + + byte (GfxScreen::*_getPixelPtr) (byte *screen, int16 x, int16 y); + byte getPixelNormal (byte *screen, int16 x, int16 y); + byte getPixelUpscaled (byte *screen, int16 x, int16 y); + + // pixel helper + void putScaledPixelOnScreen(byte *screen, int16 x, int16 y, byte color); }; } // End of namespace Sci diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index 245d6996cb..f463dff4b1 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -143,18 +143,30 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1 return textCodeSize; } -static const uint16 text16_punctuationSjis[] = { - 0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82, - 0x4083, 0x4283, 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683, - 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0 +// Has actually punctuation and characters in it, that may not be the first in a line +static const uint16 text16_shiftJIS_punctuation[] = { + 0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82, 0x4083, 0x4283, + 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683, 0x5B81, 0x4181, + 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0 }; // return max # of chars to fit maxwidth with full words, does not include // breaking space -int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId) { +// Also adjusts text pointer to the new position for the caller +// +// Special cases in games: +// Laura Bow 2 - Credits in the game menu - all the text lines start with spaces (bug #5159) +// Act 6 Coroner questionaire - the text of all control buttons has trailing spaces +// "Detective Ryan Hanrahan O'Riley" contains even more spaces (bug #5334) +// Conquests of Camelot - talking with Cobb - one text box of the dialogue contains a longer word, +// that will be broken into 2 lines (bug #5159) +int16 GfxText16::GetLongest(const char *&textPtr, int16 maxWidth, GuiResourceId orgFontId) { uint16 curChar = 0; - int16 maxChars = 0, curCharCount = 0; - uint16 width = 0; + const char *textStartPtr = textPtr; + const char *lastSpacePtr = NULL; + int16 lastSpaceCharCount = 0; + int16 curCharCount = 0, resultCharCount = 0; + uint16 curWidth = 0, tempWidth = 0; GuiResourceId previousFontId = GetFontId(); int16 previousPenColor = _ports->_curPort->penClr; @@ -162,35 +174,38 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF if (!_font) return 0; - while (width <= maxWidth) { - curChar = (*(const byte *)text++); + while (1) { + curChar = (*(const byte *)textPtr); if (_font->isDoubleByte(curChar)) { - curChar |= (*(const byte *)text++) << 8; - curCharCount++; + curChar |= (*(const byte *)(textPtr + 1)) << 8; } switch (curChar) { case 0x7C: if (getSciVersion() >= SCI_VERSION_1_1) { - curCharCount++; - curCharCount += CodeProcessing(text, orgFontId, previousPenColor, false); + curCharCount++; textPtr++; + curCharCount += CodeProcessing(textPtr, orgFontId, previousPenColor, false); continue; } break; // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit - // which means, we split text like - // 'Mature, experienced software analyst available.' 0xD 0xA - // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) - // and 0xA '-------' 0xA (which is the official sierra subtitle separator) + // which means, we split text like for example + // - 'Mature, experienced software analyst available.' 0xD 0xA + // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) + // - 0xA '-------' 0xA (which is the official sierra subtitle separator) (found in multilingual versions) // Sierra did it the same way. case 0xD: // Check, if 0xA is following, if so include it as well - if ((*(const unsigned char *)text) == 0xA) - curCharCount++; + if ((*(const byte *)(textPtr + 1)) == 0xA) { + curCharCount++; textPtr++; + } // it's meant to pass through here case 0xA: case 0x9781: // this one is used by SQ4/japanese as line break as well - curCharCount++; + curCharCount++; textPtr++; + if (curChar > 0xFF) { + curCharCount++; textPtr++; + } // and it's also meant to pass through here case 0: SetFont(previousFontId); @@ -198,55 +213,86 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF return curCharCount; case ' ': - maxChars = curCharCount; // return count up to (but not including) breaking space + lastSpaceCharCount = curCharCount; // return count up to (but not including) breaking space + lastSpacePtr = textPtr + 1; // remember position right after the current space break; } - // Sometimes this can go off the screen, like for example bug #3040161. - // However, we only perform this for non-Japanese games, as these require - // special handling, done after this loop. - if (width + _font->getCharWidth(curChar) > maxWidth && g_sci->getLanguage() != Common::JA_JPN) + tempWidth += _font->getCharWidth(curChar); + + // Width is too large? -> break out + if (tempWidth > maxWidth) break; - width += _font->getCharWidth(curChar); - curCharCount++; + + // still fits, remember width + curWidth = tempWidth; + + // go to next character + curCharCount++; textPtr++; + if (curChar > 0xFF) { + // Double-Byte + curCharCount++; textPtr++; + } } - // Text without spaces, probably Kanji/Japanese - if (maxChars == 0) { - maxChars = curCharCount; + if (lastSpaceCharCount) { + // Break and at least one space was found before that + resultCharCount = lastSpaceCharCount; - uint16 nextChar; + // additionally skip over all spaces, that are following that space, but don't count them for displaying purposes + textPtr = lastSpacePtr; + while (*textPtr == ' ') + textPtr++; - // We remove the last char only, if maxWidth was actually equal width - // before adding the last char. Otherwise we won't get the same cutting - // as in sierra pc98 sci. - if (maxWidth == (width - _font->getCharWidth(curChar))) { - maxChars--; - if (curChar > 0xFF) - maxChars--; - nextChar = curChar; - } else { - nextChar = (*(const byte *)text++); - if (_font->isDoubleByte(nextChar)) - nextChar |= (*(const byte *)text++) << 8; - } - // sierra checked the following character against a punctuation kanji table - if (nextChar > 0xFF) { - // if the character is punctuation, we go back one character - uint nonBreakingNr = 0; - while (text16_punctuationSjis[nonBreakingNr]) { - if (text16_punctuationSjis[nonBreakingNr] == nextChar) { - maxChars--; - if (curChar > 0xFF) - maxChars--; // go back 2 chars, when last char was double byte + } else { + // Break without spaces found, we split the very first word - may also be Kanji/Japanese + if (curChar > 0xFF) { + // current charracter is Japanese + + // PC-9801 SCI actually added the last character, which shouldn't fit anymore, still onto the + // screen in case maxWidth wasn't fully reached with the last character + if (( maxWidth - 1 ) > curWidth) { + curCharCount += 2; textPtr += 2; + + curChar = (*(const byte *)textPtr); + if (_font->isDoubleByte(curChar)) { + curChar |= (*(const byte *)(textPtr + 1)) << 8; + } + } + + // But it also checked, if the current character is not inside a punctuation table and it even + // went backwards in case it found multiple ones inside that table. + uint nonBreakingPos = 0; + + while (1) { + // Look up if character shouldn't be the first on a new line + nonBreakingPos = 0; + while (text16_shiftJIS_punctuation[nonBreakingPos]) { + if (text16_shiftJIS_punctuation[nonBreakingPos] == curChar) + break; + nonBreakingPos++; + } + if (!text16_shiftJIS_punctuation[nonBreakingPos]) { + // character is fine break; } - nonBreakingNr++; + // Character is not acceptable, seek backward in the text + curCharCount -= 2; textPtr -= 2; + if (textPtr < textStartPtr) + error("Seeking back went too far, data corruption?"); + + curChar = (*(const byte *)textPtr); + if (!_font->isDoubleByte(curChar)) + error("Non double byte while seeking back"); + curChar |= (*(const byte *)(textPtr + 1)) << 8; } } + + // We split the word in that case + resultCharCount = curCharCount; } SetFont(previousFontId); _ports->penColor(previousPenColor); - return maxChars; + return resultCharCount; } void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont) { @@ -303,7 +349,7 @@ void GfxText16::DrawString(const char *str, GuiResourceId orgFontId, int16 orgPe Draw(str, 0, (int16)strlen(str), orgFontId, orgPenColor); } -int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) { +int16 GfxText16::Size(Common::Rect &rect, const char *text, uint16 languageSplitter, GuiResourceId fontId, int16 maxWidth) { GuiResourceId previousFontId = GetFontId(); int16 previousPenColor = _ports->_curPort->penClr; int16 charCount; @@ -315,12 +361,12 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId else fontId = previousFontId; - if (g_sci->getLanguage() == Common::JA_JPN) - SwitchToFont900OnSjis(text); - rect.top = rect.left = 0; if (maxWidth < 0) { // force output as single line + if (g_sci->getLanguage() == Common::JA_JPN) + SwitchToFont900OnSjis(text, languageSplitter); + StringWidth(text, fontId, textWidth, textHeight); rect.bottom = textHeight; rect.right = textWidth; @@ -328,17 +374,20 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId // rect.right=found widest line with RTextWidth and GetLongest // rect.bottom=num. lines * GetPointSize rect.right = (maxWidth ? maxWidth : 192); - const char *curPos = text; - while (*curPos) { - charCount = GetLongest(curPos, rect.right, fontId); + const char *curTextPos = text; // in work position for GetLongest() + const char *curTextLine = text; // starting point of current line + while (*curTextPos) { + // We need to check for Shift-JIS every line + if (g_sci->getLanguage() == Common::JA_JPN) + SwitchToFont900OnSjis(curTextPos, languageSplitter); + + charCount = GetLongest(curTextPos, rect.right, fontId); if (charCount == 0) break; - Width(curPos, 0, charCount, fontId, textWidth, textHeight, false); + Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, false); maxTextWidth = MAX(textWidth, maxTextWidth); totalHeight += textHeight; - curPos += charCount; - while (*curPos == ' ') - curPos++; // skip over breaking spaces + curTextLine = curTextPos; } rect.bottom = totalHeight; rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth); @@ -405,34 +454,38 @@ void GfxText16::Show(const char *text, int16 from, int16 len, GuiResourceId orgF } // Draws a text in rect. -void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) { +void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) { int16 textWidth, maxTextWidth, textHeight, charCount; int16 offset = 0; int16 hline = 0; GuiResourceId previousFontId = GetFontId(); int16 previousPenColor = _ports->_curPort->penClr; bool doubleByteMode = false; + const char *curTextPos = text; + const char *curTextLine = text; if (fontId != -1) SetFont(fontId); else fontId = previousFontId; - if (g_sci->getLanguage() == Common::JA_JPN) { - if (SwitchToFont900OnSjis(text)) - doubleByteMode = true; - } - // Reset reference code rects _codeRefRects.clear(); _codeRefTempRect.left = _codeRefTempRect.top = -1; maxTextWidth = 0; - while (*text) { - charCount = GetLongest(text, rect.width(), fontId); + while (*curTextPos) { + // We need to check for Shift-JIS every line + // Police Quest 2 PC-9801 often draws English + Japanese text during the same call + if (g_sci->getLanguage() == Common::JA_JPN) { + if (SwitchToFont900OnSjis(curTextPos, languageSplitter)) + doubleByteMode = true; + } + + charCount = GetLongest(curTextPos, rect.width(), fontId); if (charCount == 0) break; - Width(text, 0, charCount, fontId, textWidth, textHeight, true); + Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, true); maxTextWidth = MAX<int16>(maxTextWidth, textWidth); switch (alignment) { case SCI_TEXT16_ALIGNMENT_RIGHT: @@ -451,15 +504,13 @@ void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextA _ports->moveTo(rect.left + offset, rect.top + hline); if (show) { - Show(text, 0, charCount, fontId, previousPenColor); + Show(curTextLine, 0, charCount, fontId, previousPenColor); } else { - Draw(text, 0, charCount, fontId, previousPenColor); + Draw(curTextLine, 0, charCount, fontId, previousPenColor); } hline += textHeight; - text += charCount; - while (*text == ' ') - text++; // skip over breaking spaces + curTextLine = curTextPos; } SetFont(previousFontId); _ports->penColor(previousPenColor); @@ -521,11 +572,13 @@ void GfxText16::DrawStatus(const char *text) { // Sierra did this in their PC98 interpreter only, they identify a text as being // sjis and then switch to font 900 -bool GfxText16::SwitchToFont900OnSjis(const char *text) { +bool GfxText16::SwitchToFont900OnSjis(const char *text, uint16 languageSplitter) { byte firstChar = (*(const byte *)text++); - if (((firstChar >= 0x81) && (firstChar <= 0x9F)) || ((firstChar >= 0xE0) && (firstChar <= 0xEF))) { - SetFont(900); - return true; + if (languageSplitter != 0x6a23) { // #j prefix as language splitter + if (((firstChar >= 0x81) && (firstChar <= 0x9F)) || ((firstChar >= 0xE0) && (firstChar <= 0xEF))) { + SetFont(900); + return true; + } } return false; } @@ -554,9 +607,9 @@ reg_t GfxText16::allocAndFillReferenceRectArray() { return NULL_REG; } -void GfxText16::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) { +void GfxText16::kernelTextSize(const char *text, uint16 languageSplitter, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) { Common::Rect rect(0, 0, 0, 0); - Size(rect, text, font, maxWidth); + Size(rect, text, languageSplitter, font, maxWidth); *textWidth = rect.width(); *textHeight = rect.height(); } diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h index ab0cb13a64..2724d97347 100644 --- a/engines/sci/graphics/text16.h +++ b/engines/sci/graphics/text16.h @@ -51,15 +51,20 @@ public: void ClearChar(int16 chr); - int16 GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId); + int16 GetLongest(const char *&text, int16 maxWidth, GuiResourceId orgFontId); void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont); void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight); void ShowString(const char *str, GuiResourceId orgFontId, int16 orgPenColor); void DrawString(const char *str, GuiResourceId orgFontId, int16 orgPenColor); - int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth); + int16 Size(Common::Rect &rect, const char *text, uint16 textLanguage, GuiResourceId fontId, int16 maxWidth); void Draw(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor); void Show(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor); - void Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId); + void Box(const char *text, uint16 languageSplitter, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId); + + void Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) { + Box(text, 0, show, rect, alignment, fontId); + } + void DrawString(const char *text); void DrawStatus(const char *text); @@ -67,13 +72,13 @@ public: reg_t allocAndFillReferenceRectArray(); - void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight); + void kernelTextSize(const char *text, uint16 textLanguage, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight); void kernelTextFonts(int argc, reg_t *argv); void kernelTextColors(int argc, reg_t *argv); private: void init(); - bool SwitchToFont900OnSjis(const char *text); + bool SwitchToFont900OnSjis(const char *text, uint16 languageSplitter); GfxCache *_cache; GfxPorts *_ports; diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp index 5e7dbc6c15..ccc7a4389a 100644 --- a/engines/sci/graphics/transitions.cpp +++ b/engines/sci/graphics/transitions.cpp @@ -339,10 +339,10 @@ void GfxTransitions::pixelation(bool blackoutFlag) { do { mask = (mask & 1) ? (mask >> 1) ^ 0xB400 : mask >> 1; - if (mask >= _screen->getWidth() * _screen->getHeight()) + if (mask >= _screen->getScriptWidth() * _screen->getScriptHeight()) continue; - pixelRect.left = mask % _screen->getWidth(); pixelRect.right = pixelRect.left + 1; - pixelRect.top = mask / _screen->getWidth(); pixelRect.bottom = pixelRect.top + 1; + pixelRect.left = mask % _screen->getScriptWidth(); pixelRect.right = pixelRect.left + 1; + pixelRect.top = mask / _screen->getScriptWidth(); pixelRect.bottom = pixelRect.top + 1; pixelRect.clip(_picRect); if (!pixelRect.isEmpty()) copyRectToScreen(pixelRect, blackoutFlag); diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index f3f352e5b8..da61ecf4c3 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -283,6 +283,7 @@ void GfxView::initData(GuiResourceId resourceId) { _isScaleable = false; break; case 0x40: + case 0x4F: // LSL6 Polish, seems to be garbage - bug #6718 case 0: break; // don't do anything, we already have _isScaleable set default: @@ -366,7 +367,7 @@ void GfxView::initData(GuiResourceId resourceId) { default: error("ViewType was not detected, can't continue"); } - + // Inject our own views // Currently only used for Dual mode (speech + text) for games, that do not have a "dual" icon already // Which is Laura Bow 2 + King's Quest 6 diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 6b6058c819..33392e3b42 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -76,8 +76,8 @@ MODULE_OBJS := \ sound/drivers/midi.o \ sound/drivers/pcjr.o \ video/seq_decoder.o - - + + ifdef ENABLE_SCI32 MODULE_OBJS += \ engine/kgraphics32.o \ diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp index 693bbec744..27ebc58704 100644 --- a/engines/sci/parser/said.cpp +++ b/engines/sci/parser/said.cpp @@ -186,8 +186,7 @@ static bool parseList(ParseTreeNode* parentNode); static bool parseListEntry(ParseTreeNode* parentNode); static bool parseWord(ParseTreeNode* parentNode); -static bool parseWord(ParseTreeNode* parentNode) -{ +static bool parseWord(ParseTreeNode* parentNode) { int token = said_tokens[said_token]; if (token & 0x8000) return false; @@ -201,8 +200,7 @@ static bool parseWord(ParseTreeNode* parentNode) return true; } -static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty) -{ +static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -259,8 +257,7 @@ static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty) return false; } -static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty) -{ +static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -318,8 +315,7 @@ static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty) } -static bool parseSlash(ParseTreeNode* parentNode) -{ +static bool parseSlash(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -343,8 +339,7 @@ static bool parseSlash(ParseTreeNode* parentNode) } -static bool parseRef(ParseTreeNode* parentNode) -{ +static bool parseRef(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -411,8 +406,7 @@ static bool parseRef(ParseTreeNode* parentNode) return false; } -static bool parseComma(ParseTreeNode* parentNode) -{ +static bool parseComma(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -435,8 +429,7 @@ static bool parseComma(ParseTreeNode* parentNode) return false; } -static bool parseListEntry(ParseTreeNode* parentNode) -{ +static bool parseListEntry(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -494,8 +487,7 @@ static bool parseListEntry(ParseTreeNode* parentNode) return false; } -static bool parseList(ParseTreeNode* parentNode) -{ +static bool parseList(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -524,8 +516,7 @@ static bool parseList(ParseTreeNode* parentNode) return false; } -static bool parseExpr(ParseTreeNode* parentNode) -{ +static bool parseExpr(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -546,7 +537,6 @@ static bool parseExpr(ParseTreeNode* parentNode) said_attach_subtree(newParent, 0x141, 0x14F, newNode); newParent = newParent->right; - } found = parseRef(newParent); @@ -561,8 +551,7 @@ static bool parseExpr(ParseTreeNode* parentNode) return false; } -static bool parseSpec(ParseTreeNode* parentNode) -{ +static bool parseSpec(ParseTreeNode* parentNode) { // Store current state for rolling back if we fail int curToken = said_token; int curTreePos = said_tree_pos; @@ -748,9 +737,7 @@ static void node_print_desc(ParseTreeNode *) { } - -static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT) -{ +static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT) { outputDepth++; scidprintf("%*smatchTrees on ", outputDepth, ""); node_print_desc(parseT); diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index b4a223dcff..000b037b44 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -530,18 +530,16 @@ bool Vocabulary::tokenizeString(ResultWordListList &retval, const char *sentence *error = NULL; do { - c = sentence[pos_in_sentence++]; + if (Common::isAlnum(c) || (c == '-' && wordLen) || (c >= 0x80)) { currentWord[wordLen] = lowerCaseMap[c]; ++wordLen; - } - // Continue on this word */ - // Words may contain a '-', but may not - // start with one. - else { - if (wordLen) { // Finished a word? + } else if (c == ' ' || c == '\0') { + // Continue on this word. Words may contain a '-', but may not start with + // one. + if (wordLen) { // Finished a word? ResultWordList lookup_result; // Look it up diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 17195c14b8..10740a8b7b 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -55,6 +55,11 @@ SciVersion getSciVersion() { return s_sciVersion; } +SciVersion getSciVersionForDetection() { + assert(!g_sci); + return s_sciVersion; +} + const char *getSciVersionDesc(SciVersion version) { switch (version) { case SCI_VERSION_NONE: @@ -88,9 +93,6 @@ const char *getSciVersionDesc(SciVersion version) { ////////////////////////////////////////////////////////////////////// - -#undef SCI_REQUIRE_RESOURCE_FILES - //#define SCI_VERBOSE_RESMAN 1 static const char *const s_errorDescriptions[] = { @@ -639,7 +641,7 @@ int ResourceManager::addAppropriateSources() { return 1; } -int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { +int ResourceManager::addAppropriateSourcesForDetection(const Common::FSList &fslist) { ResourceSource *map = 0; Common::Array<ResourceSource *> sci21Maps; @@ -858,7 +860,7 @@ void ResourceManager::freeResourceSources() { ResourceManager::ResourceManager() { } -void ResourceManager::init(bool initFromFallbackDetector) { +void ResourceManager::init() { _memoryLocked = 0; _memoryLRU = 0; _LRU.clear(); @@ -890,25 +892,24 @@ void ResourceManager::init(bool initFromFallbackDetector) { debugC(1, kDebugLevelResMan, "resMan: Detected volume version %d: %s", _volVersion, versionDescription(_volVersion)); if ((_mapVersion == kResVersionUnknown) && (_volVersion == kResVersionUnknown)) { - warning("Volume and map version not detected, assuming that this is not a sci game"); + warning("Volume and map version not detected, assuming that this is not a SCI game"); _viewType = kViewUnknown; return; } scanNewSources(); - if (!initFromFallbackDetector) { - if (!addAudioSources()) { - // FIXME: This error message is not always correct. - // OTOH, it is nice to be able to detect missing files/sources - // So we should definitely fix addAudioSources so this error - // only pops up when necessary. Disabling for now. - //error("Somehow I can't seem to find the sound files I need (RESOURCE.AUD/RESOURCE.SFX), aborting"); - } - addScriptChunkSources(); - scanNewSources(); + if (!addAudioSources()) { + // FIXME: This error message is not always correct. + // OTOH, it is nice to be able to detect missing files/sources + // So we should definitely fix addAudioSources so this error + // only pops up when necessary. Disabling for now. + //error("Somehow I can't seem to find the sound files I need (RESOURCE.AUD/RESOURCE.SFX), aborting"); } + addScriptChunkSources(); + scanNewSources(); + detectSciVersion(); debugC(1, kDebugLevelResMan, "resMan: Detected %s", getSciVersionDesc(getSciVersion())); @@ -943,6 +944,22 @@ void ResourceManager::init(bool initFromFallbackDetector) { } } +void ResourceManager::initForDetection() { + assert(!g_sci); + + _memoryLocked = 0; + _memoryLRU = 0; + _LRU.clear(); + _resMap.clear(); + _audioMapSCI1 = NULL; + + _mapVersion = detectMapVersion(); + _volVersion = detectVolVersion(); + + scanNewSources(); + detectSciVersion(); +} + ResourceManager::~ResourceManager() { // freeing resources ResourceMap::iterator itr = _resMap.begin(); @@ -1645,6 +1662,9 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { do { type = fileStream->readByte() & 0x1F; resMap[type].wOffset = fileStream->readUint16LE(); + if (fileStream->eos()) + return SCI_ERROR_RESMAP_NOT_FOUND; + resMap[prevtype].wSize = (resMap[type].wOffset - resMap[prevtype].wOffset) / nEntrySize; prevtype = type; @@ -2330,6 +2350,9 @@ bool ResourceManager::detectPaletteMergingSci11() { // Old palette format used in palette resource? -> it's merging if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0)) return true; + // Hardcoded: Laura Bow 2 floppy uses new palette resource, but still palette merging + 16 bit color matching + if ((g_sci->getGameId() == GID_LAURABOW2) && (!g_sci->isCD()) && (!g_sci->isDemo())) + return true; return false; } return false; diff --git a/engines/sci/resource.h b/engines/sci/resource.h index e90f52a3ce..62f3c584ac 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -312,10 +312,22 @@ public: /** * Initializes the resource manager. */ - void init(bool initFromFallbackDetector = false); + void init(); + /** + * Similar to the function above, only called from the fallback detector + */ + void initForDetection(); + + /** + * Adds all of the resource files for a game + */ int addAppropriateSources(); - int addAppropriateSources(const Common::FSList &fslist); // TODO: Switch from FSList to Common::Archive? + + /** + * Similar to the function above, only called from the fallback detector + */ + int addAppropriateSourcesForDetection(const Common::FSList &fslist); // TODO: Switch from FSList to Common::Archive? /** * Looks up a resource's data. diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 4c7cd9b84a..c775f502c5 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -110,7 +110,7 @@ bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) { unalloc(); return false; } - + _headerSize = file->readByte(); if (type == kResourceTypeAudio) { @@ -710,7 +710,6 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers // 0x20 is set on rhythm channels to prevent remapping // CHECKME: Which SCI versions need that set manually? - channel->flags = (*channel->data) >> 4; if (channel->number == 9) channel->flags |= 2; // Note: flag 1: channel start offset is 0 instead of 10 diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index fc723f18cf..60a1271b89 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -219,7 +219,7 @@ Common::Error SciEngine::run() { // Add the after market GM patches for the specified game, if they exist _resMan->addNewGMPatch(_gameId); _gameObjectAddress = _resMan->findGameObject(); - + _scriptPatcher = new ScriptPatcher(); SegManager *segMan = new SegManager(_resMan, _scriptPatcher); @@ -896,7 +896,7 @@ void SciEngine::syncSoundSettings() { bool SciEngine::speechAndSubtitlesEnabled() { bool subtitlesOn = ConfMan.getBool("subtitles"); bool speechOn = !ConfMan.getBool("speech_mute"); - + if (isCD() && subtitlesOn && speechOn) return true; return false; @@ -936,7 +936,7 @@ void SciEngine::updateScummVMAudioOptions() { // depending on the in-game settings if (isCD() && getSciVersion() == SCI_VERSION_1_1) { uint16 ingameSetting = _gamestate->variables[VAR_GLOBAL][90].getOffset(); - + switch (ingameSetting) { case 1: // subtitles diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 48bc4819d2..4928fd1b4e 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -314,13 +314,16 @@ public: * if NULL is passed no subtitle will be added to the returned string * @return processed string */ - Common::String strSplit(const char *str, const char *sep = "\r----------\r"); + Common::String strSplitLanguage(const char *str, uint16 *splitLanguage, const char *sep = "\r----------\r"); + Common::String strSplit(const char *str, const char *sep = "\r----------\r") { + return strSplitLanguage(str, NULL, sep); + } kLanguage getSciLanguage(); void setSciLanguage(kLanguage lang); void setSciLanguage(); - Common::String getSciLanguageString(const Common::String &str, kLanguage lang, kLanguage *lang2 = NULL) const; + Common::String getSciLanguageString(const Common::String &str, kLanguage lang, kLanguage *lang2 = NULL, uint16 *languageSplitter = NULL) const; // Check if vocabulary needs to get switched (in multilingual parser games) void checkVocabularySwitch(); @@ -429,6 +432,12 @@ extern SciEngine *g_sci; SciVersion getSciVersion(); /** + * Same as above, but this version doesn't assert on unknown SCI versions. + * Only used by the fallback detector + */ +SciVersion getSciVersionForDetection(); + +/** * Convenience function converting an SCI version into a human-readable string. */ const char *getSciVersionDesc(SciVersion version); diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp index 8f535aeddc..baf85de74c 100644 --- a/engines/sci/sound/drivers/midi.cpp +++ b/engines/sci/sound/drivers/midi.cpp @@ -399,24 +399,42 @@ void MidiPlayer_Midi::playSwitch(bool play) { } bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) { - if (size < 1155) + // WORKAROUND: Some Mac games (e.g. LSL5) may have an extra byte at the + // end, so compensate for that here - bug #6725. + if (size == 16890) + size--; + + // Need at least 1153 + 2 bytes for a GM patch. Check readMt32GmPatch() + // below for more info. + if (size < 1153 + 2) return false; + // The maximum number of bytes for an MT-32 patch is 16889. The maximum + // number of timbres is 64, which leads us to: + // 491 + 1 + 64 * 246 + 653 = 16889 if (size > 16889) return true; bool isMt32 = false; bool isMt32Gm = false; + // First, check for a GM patch. The presence of MIDI data after the + // initial 1153 + 2 bytes indicates a GM patch if (READ_LE_UINT16(data + 1153) + 1155 == size) isMt32Gm = true; - int pos = 492 + 246 * data[491]; + // Now check for a regular MT-32 patch. Check readMt32Patch() below for + // more info. + // 491 = 20 + 20 + 20 + 2 + 1 + 11 + 3 * 11 + 256 + 128 + byte timbresNr = data[491]; + int pos = 492 + 246 * timbresNr; + // Patches 49-96 if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd)) - pos += 386; + pos += 386; // 256 + 128 + 2 + // Rhythm key map + partial reserve if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba)) - pos += 267; + pos += 267; // 256 + 9 + 2 if (size == pos) isMt32 = true; @@ -460,10 +478,28 @@ void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, } void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { + // MT-32 patch contents: + // - 20 bytes unkown + // - 20 bytes before-SysEx message + // - 20 bytes goodbye SysEx message + // - 2 bytes volume + // - 1 byte reverb + // - 11 bytes reverb Sysex message + // - 3 * 11 reverb data + // - 256 + 128 bytes patches 1-48 + // --> total: 491 bytes + // - 1 byte number of timbres (64 max) + // - 246 * timbres timbre data + // - 2 bytes flag (0xabcd) + // - 256 + 128 bytes patches 49-96 + // - 2 bytes flag (0xdcba) + // - 256 bytes rhythm key map + // - 9 bytes partial reserve + Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size); // Send before-SysEx text - str->seek(0x14); + str->seek(20); sendMt32SysEx(0x200000, str, 20); // Save goodbye message @@ -527,15 +563,25 @@ void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { } void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { - memcpy(_patchMap, data, 0x80); - memcpy(_keyShift, data + 0x80, 0x80); - memcpy(_volAdjust, data + 0x100, 0x80); - memcpy(_percussionMap, data + 0x180, 0x80); - _channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[0x200]; - memcpy(_velocityMapIdx, data + 0x201, 0x80); - memcpy(_velocityMap, data + 0x281, 0x200); - - uint16 midiSize = READ_LE_UINT16(data + 0x481); + // GM patch contents: + // - 128 bytes patch map + // - 128 bytes key shift + // - 128 bytes volume adjustment + // - 128 bytes percussion map + // - 1 byte volume adjust for the rhythm channel + // - 128 bytes velocity map IDs + // - 512 bytes velocity map + // --> total: 1153 bytes + + memcpy(_patchMap, data, 128); + memcpy(_keyShift, data + 128, 128); + memcpy(_volAdjust, data + 256, 128); + memcpy(_percussionMap, data + 384, 128); + _channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[512]; + memcpy(_velocityMapIdx, data + 513, 128); + memcpy(_velocityMap, data + 641, 512); + + uint16 midiSize = READ_LE_UINT16(data + 1153); if (midiSize > 0) { if (size < midiSize + 1155) @@ -956,7 +1002,7 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) warning("The automatic mapping for General MIDI hasn't been worked on for " "SCI1 games. Music might sound wrong or broken. Please choose another " - "music driver for this game (e.g. Adlib or MT-32) if you are " + "music driver for this game (e.g. AdLib or MT-32) if you are " "experiencing issues with music"); // Modify velocity map to make low velocity notes a little louder diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 362cca699d..7a6eaf62b4 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -142,7 +142,7 @@ void SciMusic::init() { _driverLastChannel = _pMidiDrv->getLastChannel(); if (getSciVersion() <= SCI_VERSION_0_LATE) _globalReverb = _pMidiDrv->getReverb(); // Init global reverb for SCI0 - + _currentlyPlayingSample = NULL; } diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 6149bb799e..4e44074630 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -264,7 +264,7 @@ private: int _driverFirstChannel; int _driverLastChannel; - + MusicEntry *_currentlyPlayingSample; }; diff --git a/engines/scumm/POTFILES b/engines/scumm/POTFILES index 6034320259..6d10537d3c 100644 --- a/engines/scumm/POTFILES +++ b/engines/scumm/POTFILES @@ -1,3 +1,6 @@ engines/scumm/dialogs.cpp engines/scumm/help.cpp engines/scumm/scumm.cpp +engines/scumm/players/player_v3m.cpp +engines/scumm/players/player_v5m.cpp + diff --git a/engines/scumm/cdda.cpp b/engines/scumm/cdda.cpp index adb414ecce..d797712a31 100644 --- a/engines/scumm/cdda.cpp +++ b/engines/scumm/cdda.cpp @@ -46,7 +46,7 @@ private: public: CDDAStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); virtual ~CDDAStream(); - + int readBuffer(int16 *buffer, const int numSamples); bool isStereo() const { return true; } int getRate() const { return 44100; } diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 185ebbce6e..e546c805b5 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -253,7 +253,7 @@ CharsetRenderer::~CharsetRenderer() { CharsetRendererCommon::CharsetRendererCommon(ScummEngine *vm) : CharsetRenderer(vm), _bytesPerPixel(0), _fontHeight(0), _numChars(0) { - _shadowMode = false; + _enableShadow = false; _shadowColor = 0; } @@ -392,6 +392,10 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) { } else if (chr & 0x80) { pos++; width += _vm->_2byteWidth; + // Original keeps glyph width and character dimensions separately + if (_vm->_language == Common::KO_KOR || _vm->_language == Common::ZH_TWN) { + width++; + } continue; } } @@ -478,6 +482,12 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { } else if (chr & 0x80) { pos++; curw += _vm->_2byteWidth; + // Original keeps glyph width and character dimensions separately + if (_vm->_language == Common::KO_KOR || _vm->_language == Common::ZH_TWN) { + curw++; + } + } else { + curw += getCharWidth(chr); } } else { curw += getCharWidth(chr); @@ -507,12 +517,17 @@ int CharsetRendererV3::getCharWidth(uint16 chr) { return spacing; } -void CharsetRendererV3::enableShadow(bool enable) { +void CharsetRendererPC::enableShadow(bool enable) { _shadowColor = 0; - _shadowMode = enable; + _enableShadow = enable; + + if (_vm->_game.version >= 7 && _vm->_language == Common::KO_KOR) + _shadowType = kHorizontalShadowType; + else + _shadowType = kNormalShadowType; } -void CharsetRendererV3::drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height) { +void CharsetRendererPC::drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height) { byte *dst = (byte *)dest.getBasePtr(x, y); byte bits = 0; @@ -525,8 +540,12 @@ void CharsetRendererV3::drawBits1(Graphics::Surface &dest, int x, int y, const b if ((x % 8) == 0) bits = *src++; if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) { - if (_shadowMode) - dst[1] = dst2[0] = dst2[1] = _shadowColor; + if (_enableShadow) { + if (_shadowType == kNormalShadowType) + dst[1] = dst2[0] = dst2[1] = _shadowColor; + else if (_shadowType == kHorizontalShadowType) + dst[1] = _shadowColor; + } dst[0] = col; } dst += dest.format.bytesPerPixel; @@ -615,7 +634,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { if (_left + origWidth > _right + 1) return; - if (_shadowMode) { + if (_enableShadow) { width++; height++; } @@ -658,7 +677,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { if (_str.right < _left) { _str.right = _left; - if (_shadowMode) + if (_enableShadow) _str.right++; } @@ -776,11 +795,15 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { printCharIntern(is2byte, _charPtr, _origWidth, _origHeight, _width, _height, vs, ignoreCharsetMask); + // Original keeps glyph width and character dimensions separately + if ((_vm->_language == Common::ZH_TWN || _vm->_language == Common::KO_KOR) && is2byte) + _origWidth++; + _left += _origWidth; if (_str.right < _left) { _str.right = _left; - if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode) + if (_vm->_game.platform != Common::kPlatformFMTowns && _enableShadow) _str.right++; } @@ -844,7 +867,10 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, drawTop = _top - _vm->_screenTop; } - drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight); + if (is2byte && _vm->_game.platform != Common::kPlatformFMTowns) + drawBits1(dstSurface, _left, drawTop, charPtr, drawTop, origWidth, origHeight); + else + drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight); if (_blitAlso && vs->hasTwoBuffers) { // FIXME: Revisiting this code, I think the _blitAlso mode is likely broken @@ -884,6 +910,24 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, } bool CharsetRendererClassic::prepareDraw(uint16 chr) { + bool is2byte = (chr >= 256 && _vm->_useCJKMode); + if (is2byte) { + if (_vm->_language == Common::KO_KOR) + enableShadow(true); + + _charPtr = _vm->get2byteCharPtr(chr); + _width = _origWidth = _vm->_2byteWidth; + _height = _origHeight = _vm->_2byteHeight; + _offsX = _offsY = 0; + + if (_enableShadow) { + _width++; + _height++; + } + + return true; + } + uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); assert(charOffs < 0x14000); if (!charOffs) @@ -908,8 +952,14 @@ bool CharsetRendererClassic::prepareDraw(uint16 chr) { void CharsetRendererClassic::drawChar(int chr, Graphics::Surface &s, int x, int y) { if (!prepareDraw(chr)) return; + byte *dst = (byte *)s.getBasePtr(x, y); - drawBitsN(s, dst, _charPtr, *_fontPtr, y, _width, _height); + + bool is2byte = (_vm->_useCJKMode && chr >= 256); + if (is2byte) + drawBits1(s, x, y, _charPtr, y, _width, _height); + else + drawBitsN(s, dst, _charPtr, *_fontPtr, y, _width, _height); } void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) { @@ -982,7 +1032,7 @@ int CharsetRendererTownsV3::getFontHeight() { void CharsetRendererTownsV3::enableShadow(bool enable) { _shadowColor = 8; - _shadowMode = enable; + _enableShadow = enable; #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE _shadowColor = 0x88; @@ -1027,13 +1077,13 @@ void CharsetRendererTownsV3::drawBits1(Graphics::Surface &dest, int x, int y, co bits = *src++; if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) { if (dest.format.bytesPerPixel == 2) { - if (_shadowMode) { + if (_enableShadow) { WRITE_UINT16(dst + 2, _vm->_16BitPalette[_shadowColor]); WRITE_UINT16(dst + dest.pitch, _vm->_16BitPalette[_shadowColor]); } WRITE_UINT16(dst, _vm->_16BitPalette[_color]); } else { - if (_shadowMode) { + if (_enableShadow) { #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE if (scale2x) { dst[2] = dst[3] = dst2[2] = dst2[3] = _shadowColor; @@ -1124,11 +1174,11 @@ void CharsetRendererPCE::drawBits1(Graphics::Surface &dest, int x, int y, const bits = *src++; if ((bits & revBitMask(bitCount % 8)) && y + drawTop >= 0) { if (dest.format.bytesPerPixel == 2) { - if (_shadowMode) + if (_enableShadow) WRITE_UINT16(dst + dest.pitch + 2, _vm->_16BitPalette[_shadowColor]); WRITE_UINT16(dst, _vm->_16BitPalette[_color]); } else { - if (_shadowMode) + if (_enableShadow) *(dst + dest.pitch + 1) = _shadowColor; *dst = _color; } @@ -1227,7 +1277,8 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) { int width = _current->getCharWidth(chr); int height = _current->getCharHeight(chr); - if (chr >= 256 && _vm->_useCJKMode) + bool is2byte = chr >= 256 && _vm->_useCJKMode; + if (is2byte) width = _vm->_2byteWidth; shadow.right = _left + width; @@ -1259,8 +1310,8 @@ void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) { _str.left = _left; // Original keeps glyph width and character dimensions separately - if (_vm->_language == Common::ZH_TWN && width == 16) - width = 17; + if ((_vm->_language == Common::ZH_TWN || _vm->_language == Common::KO_KOR) && is2byte) + width++; _left += width; @@ -1327,7 +1378,7 @@ void CharsetRendererNES::printChar(int chr, bool ignoreCharsetMask) { if (_str.right < _left) { _str.right = _left; - if (_shadowMode) + if (_enableShadow) _str.right++; } @@ -1483,7 +1534,7 @@ bool CharsetRendererTownsClassic::prepareDraw(uint16 chr) { _origHeight = _height = _vm->_2byteHeight; _charPtr = _vm->get2byteCharPtr(chr); _offsX = _offsY = 0; - if (_shadowMode) { + if (_enableShadow) { _width++; _height++; } @@ -1495,7 +1546,7 @@ bool CharsetRendererTownsClassic::prepareDraw(uint16 chr) { } void CharsetRendererTownsClassic::setupShadowMode() { - _shadowMode = true; + _enableShadow = true; _shadowColor = _vm->_townsCharsetColorMap[0]; assert(_vm->_cjkFont); diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index 5a9977b7d6..b4b3d88ccd 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -100,7 +100,7 @@ protected: int _numChars; byte _shadowColor; - bool _shadowMode; + bool _enableShadow; public: CharsetRendererCommon(ScummEngine *vm); @@ -110,7 +110,24 @@ public: virtual int getFontHeight(); }; -class CharsetRendererClassic : public CharsetRendererCommon { +class CharsetRendererPC : public CharsetRendererCommon { + enum ShadowType { + kNoShadowType, + kNormalShadowType, + kHorizontalShadowType + }; + + ShadowType _shadowType; + +protected: + virtual void enableShadow(bool enable); + virtual void drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height); + +public: + CharsetRendererPC(ScummEngine *vm) : CharsetRendererCommon(vm), _shadowType(kNoShadowType) { } +}; + +class CharsetRendererClassic : public CharsetRendererPC { protected: virtual void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height); void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); @@ -124,7 +141,7 @@ protected: VirtScreenNumber _drawScreen; public: - CharsetRendererClassic(ScummEngine *vm) : CharsetRendererCommon(vm) {} + CharsetRendererClassic(ScummEngine *vm) : CharsetRendererPC(vm) {} void printChar(int chr, bool ignoreCharsetMask); void drawChar(int chr, Graphics::Surface &s, int x, int y); @@ -170,10 +187,8 @@ public: int getCharWidth(uint16 chr) { return 8; } }; -class CharsetRendererV3 : public CharsetRendererCommon { +class CharsetRendererV3 : public CharsetRendererPC { protected: - virtual void enableShadow(bool enable); - virtual void drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height); virtual int getDrawWidthIntern(uint16 chr); virtual int getDrawHeightIntern(uint16 chr); virtual void setDrawCharIntern(uint16 chr) {} @@ -181,7 +196,7 @@ protected: const byte *_widthTable; public: - CharsetRendererV3(ScummEngine *vm) : CharsetRendererCommon(vm) {} + CharsetRendererV3(ScummEngine *vm) : CharsetRendererPC(vm) {} void printChar(int chr, bool ignoreCharsetMask); void drawChar(int chr, Graphics::Surface &s, int x, int y); diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 7cd50e1f4c..c0db0d6d37 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -244,7 +244,7 @@ static Common::String generateFilenameForDetection(const char *pattern, Filename case kGenRoomNum: result = Common::String::format(pattern, 0); break; - + case kGenDiskNumSteam: case kGenRoomNumSteam: { const SteamIndexFile *indexFile = lookUpSteamIndexFile(pattern, platform); @@ -502,12 +502,7 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF // (since they have identical MD5): if (dr.game.id == GID_MANIAC && !strcmp(gfp->pattern, "%02d.MAN")) { dr.extra = "V1 Demo"; - } - - // HACK: If 'Demo' occurs in the extra string, set the GF_DEMO flag, - // required by some game demos (e.g. Dig, FT and COMI). - if (dr.extra && strstr(dr.extra, "Demo")) { - dr.game.features |= GF_DEMO; + dr.game.features = GF_DEMO; } // HACK: Try to detect languages for translated games diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index ae334c201c..3f08f17aff 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -260,13 +260,16 @@ static const GameSettings gameVariantsTable[] = { {"samnmax", "Floppy", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO1(GUIO_NOSPEECH)}, #ifdef ENABLE_SCUMM_7_8 - {"ft", 0, 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, + {"ft", "", 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, + {"ft", "Demo", 0, GID_FT, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO1(GUIO_NOMIDI)}, - {"dig", "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, + {"dig", "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, + {"dig", "Demo", 0, GID_DIG, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO1(GUIO_NOMIDI)}, {"dig", "Steam", "steam", GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO1(GUIO_NOMIDI)}, - {"comi", 0, 0, GID_CMI, 8, 0, MDT_NONE, 0, Common::kPlatformWindows, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)}, -#endif + {"comi", "", 0, GID_CMI, 8, 0, MDT_NONE, 0, Common::kPlatformWindows, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)}, + {"comi", "Demo", 0, GID_CMI, 8, 0, MDT_NONE, GF_DEMO, Common::kPlatformWindows, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT)}, + #endif // Humongous Entertainment Scumm Version 6 {"activity", "", 0, GID_HEGAME, 6, 62, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO1(GUIO_NOLAUNCHLOAD)}, @@ -677,12 +680,13 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "freddi", "Freddi Demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi", "Freddi Fish", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi", "FreddiD", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "freddi", "FreddiE", kGenHEPC, UNK_LANG, UNK, 0 }, { "freddi", "Freddi Fisk", kGenHEMac, Common::SE_SWE, Common::kPlatformMacintosh, 0 }, { "freddi", "FRITZI", kGenHEPC, Common::DE_DEU, UNK, 0 }, { "freddi", "Marine Malice", kGenHEMac, Common::FR_FRA, Common::kPlatformMacintosh, 0 }, { "freddi", "MM-DEMO", kGenHEPC, UNK_LANG, UNK, 0 }, - { "freddi2", "freddi2", kGenHEPC, UNK_LANG, UNK, 0 }, + { "freddi2", "freddi2", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi2", "FF2-demo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "freddi2", "ff2-demo", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "freddi2", "FFHSDemo", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -744,7 +748,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "lost", "lost", kGenHEPC, UNK_LANG, UNK, 0 }, { "lost", "Lost and Found", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, { "lost", "smaller", kGenHEPC, UNK_LANG, UNK, 0 }, - { "lost", "verloren", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "lost", "verloren", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "lost", "Verloren", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "maze", "maze", kGenHEPC, UNK_LANG, UNK, 0 }, @@ -789,7 +793,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "pajama2", "PJ2 Demo", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "pajama2", "PS2DEMO", kGenHEPC, Common::HE_ISR, UNK, 0 }, - { "pajama3", "pajama3", kGenHEPC, UNK_LANG, UNK, 0 }, + { "pajama3", "pajama3", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, { "pajama3", "FPJ3Demo", kGenHEPC, Common::FR_FRA, UNK, 0 }, { "pajama3", "GPJ3Demo", kGenHEPC, Common::DE_DEU, UNK, 0 }, { "pajama3", "PajamaHTF", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, @@ -888,7 +892,7 @@ static const GameFilenamePattern gameFilenamesTable[] = { { "socks", "socks", kGenHEPC, UNK_LANG, UNK, 0 }, { "socks", "SockWorks", kGenHEMac, UNK_LANG, Common::kPlatformMacintosh, 0 }, - { "socks", "SokkenSoep", kGenHEPC, Common::NL_NLD, UNK, 0 }, + { "socks", "SokkenSoep", kGenHEPC, Common::NL_NLD, Common::kPlatformWindows, 0 }, { "socks", "SokkenSoep", kGenHEMac, Common::NL_NLD, Common::kPlatformMacintosh, 0 }, { "spyfox", "spyfox", kGenHEPC, UNK_LANG, Common::kPlatformWindows, 0 }, diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 8321daa583..c22525b6f2 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -180,9 +180,9 @@ static const ResString string_map_table_v345[] = { // I18N: You may specify 'Yes' symbol at the end of the line, like this: // "Moechten Sie wirklich neu starten? (J/N)J" // Will react to J as 'Yes' - {5, _s("Are you sure you want to restart? (Y/N)")}, + {5, _s("Are you sure you want to restart? (Y/N)Y")}, // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment - {6, _s("Are you sure you want to quit? (Y/N)")}, + {6, _s("Are you sure you want to quit? (Y/N)Y")}, // Added in SCUMM4 {7, _s("Save")}, @@ -428,7 +428,9 @@ const Common::String InfoDialog::queryResString(int stringno) { if (stringno == 0) return String(); - if (_vm->_game.version == 8) + if (_vm->_game.heversion >= 80) + return _(string_map_table_v6[stringno - 1].string); + else if (_vm->_game.version == 8) result = (const byte *)string_map_table_v8[stringno - 1].string; else if (_vm->_game.version == 7) result = _vm->getStringAddressVar(string_map_table_v7[stringno - 1].num); @@ -458,7 +460,7 @@ const Common::String InfoDialog::queryResString(int stringno) { tmp += chr; } } - return tmp; + return _(tmp); } #pragma mark - diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp index 1d5761ef48..d8672c473c 100644 --- a/engines/scumm/nut_renderer.cpp +++ b/engines/scumm/nut_renderer.cpp @@ -395,10 +395,6 @@ void NutRenderer::drawChar(const Graphics::Surface &s, byte c, int x, int y, byt } void NutRenderer::draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color) { - // FIXME: This gets passed a const destination Surface. Intuitively this - // should never get written to. But sadly it does... For now we simply - // cast the const qualifier away. - byte *dst = (byte *)const_cast<void *>(s.getBasePtr(x, y)); const int width = _vm->_2byteWidth; const int height = MIN(_vm->_2byteHeight, s.h - y); const byte *src = _vm->get2byteCharPtr(c); @@ -408,17 +404,50 @@ void NutRenderer::draw2byte(const Graphics::Surface &s, int c, int x, int y, byt return; } - for (int ty = 0; ty < height; ty++) { - for (int tx = 0; tx < width; tx++) { - if ((tx & 7) == 0) - bits = *src++; - if (x + tx < 0 || x + tx >= s.w || y + ty < 0) - continue; - if (bits & revBitMask(tx % 8)) { - dst[tx] = color; + enum ShadowMode { + kNone, + kKoreanV8ShadowMode + }; + + ShadowMode shadowMode = kNone; + + if (_vm->_language == Common::KO_KOR && _vm->_game.version == 8) { + shadowMode = kKoreanV8ShadowMode; + } + + int shadowOffsetXTable[4] = {-1, 0, 1, 0}; + int shadowOffsetYTable[4] = {0, 1, 0, 0}; + int shadowOffsetColorTable[4] = {0, 0, 0, color}; + + int shadowIdx = 3; + if (shadowMode == kKoreanV8ShadowMode) + shadowIdx = 0; + + const byte *origSrc = src; + + for (; shadowIdx < 4; shadowIdx++) { + int offX = x + shadowOffsetXTable[shadowIdx]; + int offY = y + shadowOffsetYTable[shadowIdx]; + byte drawColor = shadowOffsetColorTable[shadowIdx]; + + // FIXME: This gets passed a const destination Surface. Intuitively this + // should never get written to. But sadly it does... For now we simply + // cast the const qualifier away. + byte *dst = (byte *)const_cast<void *>(s.getBasePtr(offX, offY)); + src = origSrc; + + for (int ty = 0; ty < height; ty++) { + for (int tx = 0; tx < width; tx++) { + if ((tx & 7) == 0) + bits = *src++; + if (offX + tx < 0 || offX + tx >= s.w || offY + ty < 0) + continue; + if (bits & revBitMask(tx % 8)) { + dst[tx] = drawColor; + } } + dst += s.pitch; } - dst += s.pitch; } } diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp index 7919075d0b..db836467ef 100644 --- a/engines/scumm/object.cpp +++ b/engines/scumm/object.cpp @@ -1013,7 +1013,6 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte * od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir); } else if (_game.version == 6) { - assert(imhd); od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id)); od->width = READ_LE_UINT16(&cdhd->v6.w); diff --git a/engines/scumm/players/player_mac.cpp b/engines/scumm/players/player_mac.cpp index fe15698494..634fd2de2b 100644 --- a/engines/scumm/players/player_mac.cpp +++ b/engines/scumm/players/player_mac.cpp @@ -21,7 +21,6 @@ */ #include "common/macresman.h" -#include "common/translation.h" #include "engines/engine.h" #include "gui/message.h" #include "scumm/players/player_mac.h" diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 7eadb042fb..0c0f6be73b 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -109,7 +109,12 @@ Common::Error ScummEngine::saveGameState(int slot, const Common::String &desc) { } bool ScummEngine::canSaveGameStateCurrently() { - // FIXME: For now always allow loading in V0-V3 games + // Disallow saving in v0-v3 games when a 'prequel' to a cutscene is shown. + // This is a blank screen with text, and while this is shown, saving should + // be disabled, as no room is set. + if (_game.version <= 3 && _currentScript == 0xFF && _roomResource == 0 && _currentRoom == 0) + return false; + // TODO: Should we disallow saving in some more places, // e.g. when a SAN movie is playing? Not sure whether the // original EXE allowed this. diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index 2c672ccc89..2fe5333bfc 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -972,6 +972,18 @@ void ScummEngine::runEntryScript() { runScript(VAR(VAR_ENTRY_SCRIPT2), 0, 0, 0); } +void ScummEngine::runQuitScript() { + if (VAR_QUIT_SCRIPT != 0xFF && VAR(VAR_QUIT_SCRIPT)) { + int args[NUM_SCRIPT_LOCAL]; + + memset(args, 0, sizeof(args)); + args[0] = 2; + args[1] = 1003; + + runScript(VAR(VAR_QUIT_SCRIPT), 0, 0, args); + } +} + void ScummEngine::killScriptsAndResources() { ScriptSlot *ss; int i; diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp index a7999a2695..af39fdaad8 100644 --- a/engines/scumm/script_v0.cpp +++ b/engines/scumm/script_v0.cpp @@ -589,9 +589,9 @@ void ScummEngine_v0::o_loadRoomWithEgo() { return; } - // The original interpreter seems to set the actors new room X/Y to the last rooms X/Y - // This fixes a problem with MM: script 158 in room 12, the 'Oompf!' script - // This scripts runs before the actor position is set to the correct location + // The original interpreter sets the actors new room X/Y to the last rooms X/Y + // This fixes a problem with MM: script 158 in room 12, the 'Oomph!' script + // This scripts runs before the actor position is set to the correct room entry location a->putActor(a->getPos().x, a->getPos().y, room); _egoPositioned = false; diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index 74d0aa2483..a7ec2e644f 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -1390,7 +1390,14 @@ void ScummEngine_v2::o2_loadRoomWithEgo() { a = derefActor(VAR(VAR_EGO), "o2_loadRoomWithEgo"); - a->putActor(0, 0, room); + // The original interpreter sets the actors new room X/Y to the last rooms X/Y + // This fixes a problem with MM: script 161 in room 12, the 'Oomph!' script + // This scripts runs before the actor position is set to the correct room entry location + if ((_game.id == GID_MANIAC) && (_game.platform != Common::kPlatformNES)) { + a->putActor(a->getPos().x, a->getPos().y, room); + } else { + a->putActor(0, 0, room); + } _egoPositioned = false; x = (int8)fetchScriptByte(); diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index 0eeff57ff7..878b2eeef3 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Wed Jun 25 10:34:07 2014 + This file was generated by the md5table tool on Sat Nov 01 11:52:16 2014 DO NOT EDIT MANUALLY! */ @@ -28,7 +28,7 @@ static const MD5Table md5table[] = { { "055ffe4f47753e47594ac67823220c54", "puttrace", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, { "057c9b456dedcc4d71b991a3072a20b3", "monkey", "SEGA", "", 9465, Common::JA_JPN, Common::kPlatformSegaCD }, { "06b187468113f9ae5a400b148a847fac", "atlantis", "Floppy", "Floppy", 12075, Common::EN_ANY, Common::kPlatformMacintosh }, - { "06c3cf4f31daad8b1cd93153491db9e6", "pajama3", "", "", -1, Common::NL_NLD, Common::kPlatformMacintosh }, + { "06c3cf4f31daad8b1cd93153491db9e6", "pajama3", "", "", 79382, Common::NL_NLD, Common::kPlatformUnknown }, { "07433205acdca3bc553d0e731588b35f", "airport", "", "", -1, Common::EN_ANY, Common::kPlatformWindows }, { "07a1eefd8ca95d77310311446c0f53d0", "brstorm", "", "", 5433, Common::EN_ANY, Common::kPlatformUnknown }, { "07b810e37be7489263f7bc7627d4765d", "freddi4", "unenc", "Unencrypted", -1, Common::RU_RUS, Common::kPlatformWindows }, @@ -112,7 +112,7 @@ static const MD5Table md5table[] = { { "2108d83dcf09f8adb4bc524669c8cf51", "PuttTime", "HE 99", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "21a6592322f92550f144f68a8a4e685e", "dig", "", "", -1, Common::FR_FRA, Common::kPlatformMacintosh }, { "21abe302e1b1e2b66d6f5c12e241ebfd", "freddicove", "unenc", "Unencrypted", -1, Common::RU_RUS, Common::kPlatformWindows }, - { "2232b0b9411575b1f9961713ebc9de61", "balloon", "HE 80", "", -1, Common::UNK_LANG, Common::kPlatformWindows }, + { "2232b0b9411575b1f9961713ebc9de61", "balloon", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformWindows }, { "225e18566e810c634bf7de63e7568e3e", "mustard", "", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "22c9eb04455440131ffc157aeb8d40a8", "fbear", "HE 70", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "22de86b2f7ec6e5db745ed1123310b44", "spyfox2", "", "Demo", 15832, Common::FR_FRA, Common::kPlatformWindows }, @@ -310,6 +310,7 @@ static const MD5Table md5table[] = { { "6bca7a1a96d16e52b8f3c42b50dbdca3", "fbear", "HE 62", "", -1, Common::JA_JPN, Common::kPlatform3DO }, { "6bf70eee5de3d24d2403e0dd3d267e8a", "spyfox", "", "", 49221, Common::UNK_LANG, Common::kPlatformWindows }, { "6c2bff0e327f2962e809c2e1a82d7309", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformAmiga }, + { "6c375c2236d99f56e6c2cf540e74e474", "farm", "", "Demo", 34333, Common::NL_NLD, Common::kPlatformWindows }, { "6d1baa1065ac5f7b210be8ebe4235e49", "freddi", "HE 73", "", -1, Common::NL_NLD, Common::kPlatformMacintosh }, { "6dead580b0ff14d5f7b33b4219f04159", "samnmax", "", "Demo", 16556335, Common::EN_ANY, Common::kPlatformMacintosh }, { "6df20c50c1ab19799de9be7ae7716881", "fbear", "HE 62", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, @@ -393,9 +394,10 @@ static const MD5Table md5table[] = { { "8e3241ddd6c8dadf64305e8740d45e13", "balloon", "HE 100", "Updated", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "8e4ee4db46954bfe2912e259a16fad82", "monkey2", "", "", -1, Common::FR_FRA, Common::kPlatformDOS }, { "8e9417564f33790815445b2136efa667", "atlantis", "", "CD", 11915, Common::JA_JPN, Common::kPlatformMacintosh }, - { "8e9830a6f2702be5b22c8fa0a6aaf977", "freddi2", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformMacintosh }, + { "8e9830a6f2702be5b22c8fa0a6aaf977", "freddi2", "HE 80", "", 65305, Common::NL_NLD, Common::kPlatformUnknown }, { "8eb84cee9b429314c7f0bdcf560723eb", "monkey", "FM-TOWNS", "", 9925, Common::EN_ANY, Common::kPlatformFMTowns }, { "8ee63cafb1fe9d62aa0d5a23117e70e7", "freddi2", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, + { "8f345db2f3f5a25ed6305001957e6f72", "freddicove", "HE 100", "", 41182, Common::NL_NLD, Common::kPlatformUnknown }, { "8f3758ff98c9c5d78e5d635222cad026", "atlantis", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformDOS }, { "8fec68383202d38c0d25e9e3b757c5df", "comi", "Demo", "Demo", 18041, Common::UNK_LANG, Common::kPlatformWindows }, { "8ffd618a776a4c0d8922bb28b09f8ce8", "airport", "", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 73776bad5a..475b146a7b 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -467,6 +467,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) VAR_NUM_SCRIPT_CYCLES = 0xFF; VAR_SCRIPT_CYCLE = 0xFF; + VAR_QUIT_SCRIPT = 0xFF; + VAR_NUM_GLOBAL_OBJS = 0xFF; // Use g_scumm from error() ONLY @@ -2073,6 +2075,7 @@ Common::Error ScummEngine::go() { if (shouldQuit()) { // TODO: Maybe perform an autosave on exit? + runQuitScript(); } } diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 36d05077c6..967909e505 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -642,7 +642,7 @@ protected: byte _opcode; byte _currentScript; int _scummStackPos; - int _vmStack[150]; + int _vmStack[256]; OpcodeEntry _opcodes[256]; @@ -671,6 +671,7 @@ protected: virtual void checkAndRunSentenceScript(); void runExitScript(); void runEntryScript(); + void runQuitScript(); void runAllScripts(); void freezeScripts(int scr); void unfreezeScripts(); @@ -1362,6 +1363,8 @@ public: byte VAR_SCRIPT_CYCLE; // Used in runScript()/runObjectScript() byte VAR_NUM_SCRIPT_CYCLES; // Used in runAllScripts() + byte VAR_QUIT_SCRIPT; // Used in confirmExitDialog() + // Exists both in V7 and in V72HE: byte VAR_NUM_GLOBAL_OBJS; diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp index 4c04901b42..e2f75a6862 100644 --- a/engines/scumm/smush/smush_font.cpp +++ b/engines/scumm/smush/smush_font.cpp @@ -115,9 +115,7 @@ int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) { int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) { int w = _vm->_2byteWidth; int h = _vm->_2byteHeight; - - byte *src = _vm->get2byteCharPtr(idx); - byte *dst = buffer + dst_width * (y + (_vm->_game.id == GID_CMI ? 7 : (_vm->_game.id == GID_DIG ? 2 : 0))) + x; + const byte *src = _vm->get2byteCharPtr(idx); byte bits = 0; char color = (_color != -1) ? _color : 1; @@ -128,18 +126,60 @@ int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) { if (_vm->_game.id == GID_FT) color = 1; - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - if ((i % 8) == 0) - bits = *src++; - if (bits & revBitMask(i % 8)) { - dst[i + 1] = 0; - dst[dst_width + i] = 0; - dst[dst_width + i + 1] = 0; - dst[i] = color; + enum ShadowMode { + kNone, + kNormalShadowMode, + kKoreanV7ShadowMode, + kKoreanV8ShadowMode + }; + + ShadowMode shadowMode = kNone; + + if (_vm->_language == Common::KO_KOR) { + if (_vm->_game.version == 8) + shadowMode = kKoreanV8ShadowMode; + else + shadowMode = kKoreanV7ShadowMode; + } + + int shadowOffsetXTable[4] = {-1, 0, 1, 0}; + int shadowOffsetYTable[4] = {0, 1, 0, 0}; + int shadowOffsetColorTable[4] = {0, 0, 0, color}; + + int shadowIdx = 3; + if (shadowMode == kKoreanV8ShadowMode) + shadowIdx = 0; + else if (shadowMode == kKoreanV7ShadowMode) + shadowIdx = 2; + + const byte *origSrc = src; + + for (; shadowIdx < 4; shadowIdx++) { + int offX = x + shadowOffsetXTable[shadowIdx]; + int offY = y + shadowOffsetYTable[shadowIdx]; + byte drawColor = shadowOffsetColorTable[shadowIdx]; + + src = origSrc; + + byte *dst = buffer + dst_width * (offY + (_vm->_game.id == GID_CMI ? 7 : (_vm->_game.id == GID_DIG ? 2 : 0))) + offX; + + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + if (offX + i < 0) + continue; + if ((i % 8) == 0) + bits = *src++; + if (bits & revBitMask(i % 8)) { + if (shadowMode == kNormalShadowMode) { + dst[i + 1] = 0; + dst[dst_width + i] = 0; + dst[dst_width + i + 1] = 0; + } + dst[i] = drawColor; + } } + dst += dst_width; } - dst += dst_width; } return w + 1; } diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index cb428d1c15..84d2b37f96 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -1066,9 +1066,9 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur Common::File *cddaFile = new Common::File(); if (cddaFile->open("CDDA.SOU")) { Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75); - Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75); + Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75); Audio::SeekableAudioStream *stream = makeCDDAStream(cddaFile, DisposeAfterUse::YES); - + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_loomSteamCDAudioHandle, Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops)); } else { diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index 79d7ed03da..a6be5c3f3a 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -320,6 +320,7 @@ void ScummEngine_v90he::setupScummVars() { ScummEngine_v80he::setupScummVars(); VAR_TIMER = 97; + VAR_QUIT_SCRIPT = 102; VAR_SCRIPT_CYCLE = 103; VAR_NUM_SCRIPT_CYCLES = 104; @@ -714,7 +715,7 @@ void ScummEngine_v99he::resetScummVars() { VAR(140) = 0; #endif } - + if (_game.id == GID_PUTTZOO && _game.heversion == 100 && _game.platform == Common::kPlatformWindows) { // Specific to Nimbus Games version. VAR(156) = 1; diff --git a/engines/sword1/POTFILES b/engines/sword1/POTFILES index 849bef9060..b60d55ff22 100644 --- a/engines/sword1/POTFILES +++ b/engines/sword1/POTFILES @@ -1,4 +1,4 @@ engines/sword1/animation.cpp engines/sword1/control.cpp engines/sword1/logic.cpp -engines/sword1/sword1.cpp + diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index b42b833304..ac358e774b 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -542,7 +542,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * Video::VideoDecoder *dxaDecoder = new Video::DXADecoder(); return new MoviePlayer(vm, textMan, resMan, system, dxaDecoder, kVideoDecoderDXA); #else - GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); + GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib"), _("OK")); dialog.runModal(); return 0; #endif diff --git a/engines/sword1/console.cpp b/engines/sword1/console.cpp index 3eb3b93a19..3a4b51965b 100644 --- a/engines/sword1/console.cpp +++ b/engines/sword1/console.cpp @@ -22,14 +22,37 @@ #include "sword1/console.h" #include "sword1/sword1.h" +#include "sword1/sound.h" +#include "common/config-manager.h" +#include "common/str.h" namespace Sword1 { SwordConsole::SwordConsole(SwordEngine *vm) : GUI::Debugger(), _vm(vm) { assert(_vm); + if (scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1mac") == 0 || scumm_stricmp(ConfMan.get("gameid").c_str(), "sword1macdemo") == 0) + registerCmd("speechEndianness", WRAP_METHOD(SwordConsole, Cmd_SpeechEndianness)); } SwordConsole::~SwordConsole() { } +bool SwordConsole::Cmd_SpeechEndianness(int argc, const char **argv) { + if (argc == 1) { + debugPrintf("Using %s speech\n", _vm->_sound->_bigEndianSpeech ? "be" : "le"); + return true; + } + if (argc == 2) { + if (scumm_stricmp(argv[1], "le") == 0) { + _vm->_sound->_bigEndianSpeech = false; + return false; + } else if (scumm_stricmp(argv[1], "be") == 0) { + _vm->_sound->_bigEndianSpeech = true; + return false; + } + } + debugPrintf("Usage: %s [le | be]\n", argv[0]); + return true; +} + } // End of namespace Sword diff --git a/engines/sword1/console.h b/engines/sword1/console.h index a2bb51f9a4..88ee756151 100644 --- a/engines/sword1/console.h +++ b/engines/sword1/console.h @@ -36,6 +36,7 @@ public: private: SwordEngine *_vm; + bool Cmd_SpeechEndianness(int argc, const char **argv); }; } // End of namespace Sword1 diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp index 0b4d3829d6..4deaf06edc 100644 --- a/engines/sword1/sound.cpp +++ b/engines/sword1/sound.cpp @@ -116,7 +116,7 @@ void Sound::checkSpeechFileEndianness() { return; // I picked the sample to use randomly (I just made sure it is long enough so that there is - // a fair change of the heuristic to have a stable result and work for every language). + // a fair chance of the heuristic to have a stable result and work for every language). int roomNo = _currentCowFile == 1 ? 1 : 129; int localNo = _currentCowFile == 1 ? 2 : 933; // Get the speech data and apply the heuristic @@ -125,32 +125,45 @@ void Sound::checkSpeechFileEndianness() { uint32 index = _cowHeader[locIndex + (localNo * 2) - 1]; if (sampleSize) { uint32 size; - double be_diff_sum = 0., le_diff_sum = 0.; + // Compute average of difference between two consecutive samples for both BE and LE _bigEndianSpeech = false; int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size); - // Compute average of difference between two consecutive samples for both BE and LE - if (data) { - if (size > 4000) - size = 2000; - else - size /= 2; - int16 prev_be_value = (int16)SWAP_BYTES_16(*((uint16 *)(data))); - for (uint32 i = 1; i < size; ++i) { - le_diff_sum += fabs((double)(data[i] - data[i - 1])); - int16 be_value = (int16)SWAP_BYTES_16(*((uint16 *)(data + i))); - be_diff_sum += fabs((double)(be_value - prev_be_value)); - prev_be_value = be_value; - } - delete[] data; - } + uint32 maxSamples = size > 2000 ? 2000 : size; + double le_diff = endiannessHeuristicValue(data, size, maxSamples); + delete[] data; + _bigEndianSpeech = true; + data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size); + double be_diff = endiannessHeuristicValue(data, size, maxSamples); + delete [] data; // Set the big endian flag - _bigEndianSpeech = (be_diff_sum < le_diff_sum); + _bigEndianSpeech = (be_diff < le_diff); if (_bigEndianSpeech) debug(6, "Mac version: using big endian speech file"); else debug(6, "Mac version: using little endian speech file"); - debug(8, "Speech endianness heuristic: average = %f for BE and %f for LE, computed on %d samples)", be_diff_sum / (size - 1), le_diff_sum / (size - 1), size); + debug(8, "Speech endianness heuristic: average = %f for BE and %f for LE (%d samples)", be_diff, le_diff, maxSamples); + } +} + +double Sound::endiannessHeuristicValue(int16* data, uint32 dataSize, uint32 &maxSamples) { + if (!data) + return 50000.; // the heuristic value for the wrong endianess is about 21000 (1/3rd of the 16 bits range) + + double diff_sum = 0.; + uint32 cpt = 0; + int16 prev_value = (int16)FROM_LE_16(*((uint16 *)(data))); + for (uint32 i = 1; i < dataSize && cpt < maxSamples; ++i) { + int16 value = (int16)FROM_LE_16(*((uint16 *)(data + i))); + if (value != prev_value) { + diff_sum += fabs((double)(value - prev_value)); + ++cpt; + prev_value = value; + } } + if (cpt == 0) + return 50000.; + maxSamples = cpt; + return diff_sum / cpt; } diff --git a/engines/sword1/sound.h b/engines/sword1/sound.h index 666598dba0..e65e797934 100644 --- a/engines/sword1/sound.h +++ b/engines/sword1/sound.h @@ -79,6 +79,7 @@ enum CowMode { }; class Sound { + friend class SwordConsole; public: Sound(Audio::Mixer *mixer, ResMan *pResMan); ~Sound(); @@ -101,6 +102,7 @@ public: void engine(); void checkSpeechFileEndianness(); + double endiannessHeuristicValue(int16* data, uint32 dataSize, uint32 &maxSamples); private: uint8 _sfxVolL, _sfxVolR, _speechVolL, _speechVolR; diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp index 08ea02f32d..1e9b7f70f4 100644 --- a/engines/sword1/sword1.cpp +++ b/engines/sword1/sword1.cpp @@ -774,6 +774,8 @@ void SwordEngine::reinitRes() { _logic->newScreen(Logic::_scriptVars[NEW_SCREEN]); _sound->newScreen(Logic::_scriptVars[NEW_SCREEN]); Logic::_scriptVars[SCREEN] = Logic::_scriptVars[NEW_SCREEN]; + _logic->engine(); + _logic->updateScreenParams(); _screen->fullRefresh(); _screen->draw(); } diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h index c58e97e353..eb24ef70fa 100644 --- a/engines/sword1/sword1.h +++ b/engines/sword1/sword1.h @@ -79,6 +79,7 @@ struct SystemVars { }; class SwordEngine : public Engine { + friend class SwordConsole; public: SwordEngine(OSystem *syst); virtual ~SwordEngine(); diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 92fa9d0e44..e93f27f76c 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -442,7 +442,7 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system Video::DXADecoder *dxaDecoder = new Video::DXADecoder(); return new MoviePlayer(vm, system, dxaDecoder, kVideoDecoderDXA); #else - GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); + GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib"), _("OK")); dialog.runModal(); return NULL; #endif diff --git a/engines/sword25/gfx/renderobjectmanager.cpp b/engines/sword25/gfx/renderobjectmanager.cpp index 4927b4c58e..8aeecad6b1 100644 --- a/engines/sword25/gfx/renderobjectmanager.cpp +++ b/engines/sword25/gfx/renderobjectmanager.cpp @@ -107,7 +107,7 @@ bool RenderObjectManager::render() { if (!_currQueue->exists(*it)) _uta->addRect((*it)._bbox); } - + // Add rectangles of objects which are different from the previous frame for (RenderObjectQueue::iterator it = _currQueue->begin(); it != _currQueue->end(); ++it) { if (!_prevQueue->exists(*it)) diff --git a/engines/sword25/kernel/persistenceservice.cpp b/engines/sword25/kernel/persistenceservice.cpp index fb83b7f941..7d68081593 100644 --- a/engines/sword25/kernel/persistenceservice.cpp +++ b/engines/sword25/kernel/persistenceservice.cpp @@ -52,7 +52,7 @@ static const uint SLOT_COUNT = 18; static const uint FILE_COPY_BUFFER_SIZE = 1024 * 10; static const char *VERSIONIDOLD = "SCUMMVM1"; static const char *VERSIONID = "SCUMMVM2"; -static const int VERSIONNUM = 2; +static const int VERSIONNUM = 3; #define MAX_SAVEGAME_SIZE 100 diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp index fb477c1687..cbe16b0d5b 100644 --- a/engines/sword25/util/pluto/pluto.cpp +++ b/engines/sword25/util/pluto/pluto.cpp @@ -325,7 +325,7 @@ static void persisttable(PersistInfo *pi) #ifdef TOTEXT printf("persisttable\n"); #endif - + /* perms reftbl ... tbl */ lua_checkstack(pi->L, 3); if(persistspecialobject(pi, 1)) { @@ -835,7 +835,15 @@ static void persistthread(PersistInfo *pi) #endif write_size(pi, &stackbase); write_size(pi, &stacktop); + + // ptrdiff_t changes sizes based on 32/64 bit + // Hard cast to 64 bit size if SIZE64 is defined +#ifdef SIZES64 + uint64 ptrIndex = static_cast<uint64>(L2->errfunc); + pi_write(pi, &ptrIndex, sizeof(uint64), pi->ud); +#else pi_write(pi, &L2->errfunc, sizeof(ptrdiff_t), pi->ud); +#endif //write_size(pi, (size_t *)&L2->errfunc); } @@ -944,12 +952,6 @@ static void persist(PersistInfo *pi) if(!lua_isnil(pi->L, -1)) { /* perms reftbl ... obj ref */ int zero = 0; - // FIXME: Casting a pointer to an integer data type is a bad idea we - // should really get rid of this by fixing the design of this code. - // For now casting to size_t should silence most (all?) compilers, - // since size_t is supposedly the same size as a pointer on most - // (modern) architectures. - int ref = (int)(size_t)lua_touserdata(pi->L, -1); pi_write(pi, &zero, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_seenobject\n"); @@ -958,7 +960,8 @@ static void persist(PersistInfo *pi) #ifdef TOTEXT printf("persist_seenobject\n"); #endif - pi_write(pi, &ref, sizeof(int), pi->ud); + int *ref = (int *)lua_touserdata(pi->L, -1); + pi_write(pi, ref, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_touserdata_ref %d\n", ref); hrOut(pi); @@ -1011,7 +1014,8 @@ static void persist(PersistInfo *pi) } lua_pushvalue(pi->L, -1); /* perms reftbl ... obj obj */ - lua_pushlightuserdata(pi->L, (void *)(++(pi->counter))); + int *ref = (int *)lua_newuserdata(pi->L, sizeof(int)); + *ref = ++(pi->counter); /* perms reftbl ... obj obj ref */ lua_rawset(pi->L, 2); /* perms reftbl ... obj */ @@ -1188,7 +1192,7 @@ int persist_l(lua_State *L) wi.buf = NULL; wi.buflen = 0; - + lua_settop(L, 2); /* perms? rootobj? */ luaL_checktype(L, 1, LUA_TTABLE); @@ -1737,7 +1741,16 @@ static void unpersistthread(int ref, UnpersistInfo *upi) verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0); read_size(&upi->zio, &stackbase); read_size(&upi->zio, &stacktop); + +#ifdef SIZES64 + uint64 value; + verify(LIF(Z,read)(&upi->zio, &value, sizeof(uint64)) == 0); + + L2->errfunc = static_cast<ptrdiff_t>(value); +#else verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0); +#endif + //read_size(&upi->zio, (size_t *)&L2->errfunc); L2->base = L2->stack + stackbase; L2->top = L2->stack + stacktop; @@ -2032,7 +2045,7 @@ int unpersist_l(lua_State *L) int version_l(lua_State *L) { const char *version = VERSION; - + lua_settop(L, 0); /* (empty) */ lua_pushlstring(L, version, strlen(version)); diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp index 31610a8467..57d8432f0e 100644 --- a/engines/tinsel/tinsel.cpp +++ b/engines/tinsel/tinsel.cpp @@ -821,7 +821,8 @@ const char *const TinselEngine::_textFiles[][3] = { TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) : - Engine(syst), _gameDescription(gameDesc), _random("tinsel") { + Engine(syst), _gameDescription(gameDesc), _random("tinsel"), + _sound(0), _midiMusic(0), _pcmMusic(0), _bmv(0) { _vm = this; _config = new Config(this); @@ -846,13 +847,6 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) if (cd_num >= 0) _system->getAudioCDManager()->openCD(cd_num); - _midiMusic = new MidiMusicPlayer(); - _pcmMusic = new PCMMusicPlayer(); - - _sound = new SoundManager(this); - - _bmv = new BMVPlayer(); - _mousePos.x = 0; _mousePos.y = 0; _keyHandler = NULL; @@ -896,6 +890,11 @@ void TinselEngine::initializePath(const Common::FSNode &gamePath) { } Common::Error TinselEngine::run() { + _midiMusic = new MidiMusicPlayer(); + _pcmMusic = new PCMMusicPlayer(); + _sound = new SoundManager(this); + _bmv = new BMVPlayer(); + // Initialize backend if (getGameID() == GID_DW2) { #ifndef DW2_EXACT_SIZE diff --git a/engines/tony/game.cpp b/engines/tony/game.cpp index c102242dfd..0a2c62330b 100644 --- a/engines/tony/game.cpp +++ b/engines/tony/game.cpp @@ -1557,14 +1557,14 @@ void RMPointer::updateCursor() { for (int i = 0; i < 64; i++) { uint16 *lineP = src; for (int j = 0; j < 64; j++) { - lineP[j] = RMGfxTargetBuffer::_precalcTable[lineP[j] & 0x7FFF]; + lineP[j] = RMGfxTargetBuffer::_precalcTable[lineP[j]]; } src += 64; } } // Get the raw pixel data and set the cursor to it - Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); + Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); CursorMan.replaceCursor(cursorData, 64, 64, _cursorHotspot._x, _cursorHotspot._y, 0, 1, &pixelFormat); } diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp index 9254d59df6..2a32926c53 100644 --- a/engines/tony/gfxcore.cpp +++ b/engines/tony/gfxcore.cpp @@ -422,11 +422,11 @@ uint16 *RMGfxTargetBuffer::_precalcTable = NULL; * called if the user selects the black & white option. */ void RMGfxTargetBuffer::createBWPrecalcTable() { - _precalcTable = new uint16[0x8000]; + _precalcTable = new uint16[0x10000]; - for (int i = 0; i < 0x8000; i++) { - int r = (i >> 10) & 0x1F; - int g = (i >> 5) & 0x1F; + for (int i = 0; i < 0x10000; i++) { + int r = (i >> 11) & 0x1F; + int g = (i >> 6) & 0x1F; int b = i & 0x1F; int min = MIN(r, MIN(g, b)); @@ -434,11 +434,11 @@ void RMGfxTargetBuffer::createBWPrecalcTable() { min = (min + max) / 2; - r = CLIP(min + 8 - 8, 0, 31); - g = CLIP(min + 5 - 8, 0, 31); - b = CLIP(min + 0 - 8, 0, 31); + r = CLIP(min + 8 - 8, 0, 0x1f); + g = CLIP(min + 5 - 8, 0, 0x1f); + b = CLIP(min + 0 - 8, 0, 0x1f); - _precalcTable[i] = (r << 10) | (g << 5) | b; + _precalcTable[i] = (r << 11) | (g << 6) | b; } } @@ -512,8 +512,9 @@ int RMGfxSourceBufferPal::loadPalette(const byte *buf) { void RMGfxSourceBufferPal::preparePalette() { for (int i = 0; i < 256; i++) { - _palFinal[i] = (((int)_pal[i * 3 + 0] >> 3) << 10) | - (((int)_pal[i * 3 + 1] >> 3) << 5) | + // we convert 555 to 565 here. + _palFinal[i] = (((int)_pal[i * 3 + 0] >> 3) << 11) | + (((int)_pal[i * 3 + 1] >> 3) << 6) | (((int)_pal[i * 3 + 2] >> 3) << 0); } } @@ -665,8 +666,8 @@ void RMGfxSourceBuffer8::create(int dimx, int dimy) { RMGfxBuffer::create(dimx, dimy, 8); } -#define GETRED(x) (((x) >> 10) & 0x1F) -#define GETGREEN(x) (((x) >> 5) & 0x1F) +#define GETRED(x) (((x) >> 11) & 0x1F) +#define GETGREEN(x) (((x) >> 5) & 0x3F) #define GETBLUE(x) ((x) & 0x1F) /****************************************************************************\ @@ -684,13 +685,13 @@ int RMGfxSourceBuffer8AB::calcTrasp(int fore, int back) { if (r > 0x1F) r = 0x1F; - if (g > 0x1F) - g = 0x1F; + if (g > 0x3F) + g = 0x3F; if (b > 0x1F) b = 0x1F; - return (r << 10) | (g << 5) | b; + return (r << 11) | (g << 5) | b; } void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { @@ -802,8 +803,8 @@ void RMGfxSourceBuffer8RLE::preparePalette() { // Handle RGB alpha blending if (_alphaBlendColor != -1) { - _alphaR = (_palFinal[_alphaBlendColor] >> 10) & 0x1F; - _alphaG = (_palFinal[_alphaBlendColor] >> 5) & 0x1F; + _alphaR = (_palFinal[_alphaBlendColor] >> 11) & 0x1F; + _alphaG = (_palFinal[_alphaBlendColor] >> 5) & 0x3F; _alphaB = (_palFinal[_alphaBlendColor]) & 0x1F; } } @@ -1054,15 +1055,15 @@ RLEByteDoAlpha2: if (n > nLength) n = nLength; for (int i = 0; i < n; i++) { - int r = (*dst >> 10) & 0x1F; - int g = (*dst >> 5) & 0x1F; + int r = (*dst >> 11) & 0x1F; + int g = (*dst >> 5) & 0x3F; int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); b = (b >> 2) + (_alphaB >> 1); - *dst ++ = (r << 10) | (g << 5) | b; + *dst ++ = (r << 11) | (g << 5) | b; } nLength -= n; @@ -1158,15 +1159,15 @@ RLEByteFlippedDoAlpha2: if (n > nLength) n = nLength; for (int i = 0; i < n; i++) { - int r = (*dst >> 10) & 0x1F; - int g = (*dst >> 5) & 0x1F; + int r = (*dst >> 11) & 0x1F; + int g = (*dst >> 5) & 0x3F; int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); b = (b >> 2) + (_alphaB >> 1); - *dst-- = (r << 10) | (g << 5) | b; + *dst-- = (r << 11) | (g << 5) | b; } nLength -= n; @@ -1302,15 +1303,15 @@ RLEWordDoAlpha2: n = nLength; for (int i = 0; i < n; i++) { - int r = (*dst >> 10) & 0x1F; - int g = (*dst >> 5) & 0x1F; + int r = (*dst >> 11) & 0x1F; + int g = (*dst >> 5) & 0x3F; int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); b = (b >> 2) + (_alphaB >> 1); - *dst++ = (r << 10) | (g << 5) | b; + *dst++ = (r << 11) | (g << 5) | b; } nLength -= n; @@ -1416,15 +1417,15 @@ RLEWordFlippedDoAlpha2: n = nLength; for (int i = 0; i < n; i++) { - int r = (*dst >> 10) & 0x1F; - int g = (*dst >> 5) & 0x1F; + int r = (*dst >> 11) & 0x1F; + int g = (*dst >> 5) & 0x3F; int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); b = (b >> 2) + (_alphaB >> 1); - *dst-- = (r << 10) | (g << 5) | b; + *dst-- = (r << 11) | (g << 5) | b; } nLength -= n; @@ -1543,15 +1544,15 @@ RLEWordDoAlpha2: // @@@ SHOULD NOT BE THERE !!!!! for (int i = 0; i < n; i++) { - int r = (*dst >> 10) & 0x1F; - int g = (*dst >> 5) & 0x1F; + int r = (*dst >> 11) & 0x1F; + int g = (*dst >> 5) & 0x3F; int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); b = (b >> 2) + (_alphaB >> 1); - *dst++ = (r << 10) | (g << 5) | b; + *dst++ = (r << 11) | (g << 5) | b; } nLength -= n; @@ -1570,19 +1571,19 @@ RLEWordDoCopy2: n = nLength; for (int i = 0; i < n; i++) { - int r = (*dst >> 10) & 0x1F; - int g = (*dst >> 5) & 0x1F; + int r = (*dst >> 11) & 0x1F; + int g = (*dst >> 5) & 0x3F; int b = *dst & 0x1F; - int r2 = (_palFinal[*src] >> 10) & 0x1F; - int g2 = (_palFinal[*src] >> 5) & 0x1F; + int r2 = (_palFinal[*src] >> 11) & 0x1F; + int g2 = (_palFinal[*src] >> 5) & 0x3F; int b2 = _palFinal[*src] & 0x1F; r = (r >> 1) + (r2 >> 1); g = (g >> 1) + (g2 >> 1); b = (b >> 1) + (b2 >> 1); - *dst ++ = (r << 10) | (g << 5) | b; + *dst ++ = (r << 11) | (g << 5) | b; src++; } @@ -1732,14 +1733,14 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 5; b /= 5; - if (r > 31) - r = 31; - if (g > 31) - g = 31; - if (b > 31) - b = 31; + if (r > 0x1f) + r = 0x1f; + if (g > 0x3f) + g = 0x3f; + if (b > 0x1f) + b = 0x1f; - mybuf[0] = (r << 10) | (g << 5) | b; + mybuf[0] = (r << 11) | (g << 5) | b; } } @@ -1773,14 +1774,14 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 6; b /= 6; - if (r > 31) - r = 31; - if (g > 31) - g = 31; - if (b > 31) - b = 31; + if (r > 0x1f) + r = 0x1f; + if (g > 0x3f) + g = 0x3f; + if (b > 0x1f) + b = 0x1f; - mybuf[0] = (r << 10) | (g << 5) | b; + mybuf[0] = (r << 11) | (g << 5) | b; } } @@ -1948,8 +1949,17 @@ void RMGfxSourceBuffer16::prepareImage() { // Color space conversion if necessary! uint16 *buf = (uint16 *)_buf; - for (int i = 0; i < _dimx * _dimy; i++) - buf[i] = FROM_LE_16(buf[i]) & 0x7FFF; + // convert 555 to 565 + for (int i = 0; i < _dimx * _dimy; i++) { + uint16 pixel = FROM_LE_16(buf[i]); + int r = (pixel >> 10) & 0x1F; + int g = (pixel >> 5) & 0x1F; + int b = pixel & 0x1F; + + pixel = (r << 11) | (g << 6) | b; + + buf[i] = pixel; + } } RMGfxSourceBuffer16::RMGfxSourceBuffer16(int dimx, int dimy) @@ -1983,7 +1993,8 @@ void RMGfxBox::setColor(byte r, byte g, byte b) { r >>= 3; g >>= 3; b >>= 3; - _wFillColor = (r << 10) | (g << 5) | b; + // These are hard-coded colors, so we convert 555 to 565. + _wFillColor = (r << 11) | (g << 6) | b; } void RMGfxBox::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { diff --git a/engines/tony/mpal/lzo.cpp b/engines/tony/mpal/lzo.cpp index 314d6f3ed5..6cc2333315 100644 --- a/engines/tony/mpal/lzo.cpp +++ b/engines/tony/mpal/lzo.cpp @@ -116,7 +116,7 @@ int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; - do + do *op++ = *ip++; while (--t > 0); diff --git a/engines/tony/window.cpp b/engines/tony/window.cpp index 5c50a50a57..3b3687419b 100644 --- a/engines/tony/window.cpp +++ b/engines/tony/window.cpp @@ -53,9 +53,9 @@ RMWindow::~RMWindow() { * Initializes the graphics window */ void RMWindow::init() { - Graphics::PixelFormat pixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); + Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); initGraphics(RM_SX, RM_SY, true, &pixelFormat); - + reset(); } @@ -83,7 +83,7 @@ void RMWindow::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, for (int i = 0; i < h; i++) { uint16 *dst = (uint16 *)screen->getBasePtr(x, y + i); for (int j = 0; j < w; j++) { - dst[j] = RMGfxTargetBuffer::_precalcTable[src[j] & 0x7FFF]; + dst[j] = RMGfxTargetBuffer::_precalcTable[src[j]]; } src += (pitch / 2); } @@ -291,8 +291,8 @@ void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) { cursrc = &src[RM_SKIPX + x]; *curOut++ = ((*cursrc) & 0x1F) << 3; - *curOut++ = (((*cursrc) >> 5) & 0x1F) << 3; - *curOut++ = (((*cursrc) >> 10) & 0x1F) << 3; + *curOut++ = (((*cursrc) >> 5) & 0x3F) << 3; + *curOut++ = (((*cursrc) >> 11) & 0x1F) << 3; if (lpDestBuf) *lpDestBuf++ = *cursrc; @@ -319,8 +319,8 @@ void RMSnapshot::grabScreenshot(byte *lpBuf, int dezoom, uint16 *lpDestBuf) { curv = v; sommab += cursrc[curv * RM_BBX + u] & 0x1F; - sommag += (cursrc[curv * RM_BBX + u] >> 5) & 0x1F; - sommar += (cursrc[curv * RM_BBX + u] >> 10) & 0x1F; + sommag += (cursrc[curv * RM_BBX + u] >> 6) & 0x1F; + sommar += (cursrc[curv * RM_BBX + u] >> 11) & 0x1F; } } _rgb[k + 0] = (byte)(sommab * 8 / (dezoom * dezoom)); diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp index 51e8dee19f..686fe99beb 100644 --- a/engines/toon/character.cpp +++ b/engines/toon/character.cpp @@ -1059,7 +1059,7 @@ void Character::playAnim(int32 animId, int32 unused, int32 flags) { _specialAnim->loadAnimation(animName); _animSpecialId = animId; - + if (_animationInstance) { _animationInstance->setAnimation(_specialAnim); _animationInstance->setAnimationRange(0, _specialAnim->_numFrames - 1); diff --git a/engines/toon/state.cpp b/engines/toon/state.cpp index 6ac5808219..000f94d659 100644 --- a/engines/toon/state.cpp +++ b/engines/toon/state.cpp @@ -115,7 +115,7 @@ State::State(void) { #endif memset(_conversationState, 0, sizeof(Conversation) * 60); - + _conversationData = nullptr; _currentConversationId = -1; _exitConversation = true; diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index 14e7d104d2..2f5051c157 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -2037,23 +2037,19 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { } } - int32 myId = 0; char *myLine; - if (dialogid < 1000) { + if (dialogid < 1000) myLine = _roomTexts->getText(dialogid); - myId = dialogid; - } else { + else myLine = _genericTexts->getText(dialogid - 1000); - myId = dialogid - 1000; - } if (!myLine) return 0; bool oldMouseHidden = _gameState->_mouseHidden; - if (blocking) { + if (blocking) _gameState->_mouseHidden = true; - } + // get what is before the string int a = READ_LE_UINT16(myLine - 2); @@ -2090,10 +2086,8 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit) doFrame(); } - } else { - if (_audioManager->voiceStillPlaying()) - _audioManager->stopCurrentVoice(); - } + } else if (_audioManager->voiceStillPlaying()) + _audioManager->stopCurrentVoice(); for (int32 i = 0; i < numParticipants - 1; i++) { // listener @@ -2133,10 +2127,10 @@ int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { getTextPosition(talkerId, &_currentTextLineX, &_currentTextLineY); if (dialogid < 1000) { - myId = _roomTexts->getId(dialogid); + int myId = _roomTexts->getId(dialogid); _audioManager->playVoice(myId, false); } else { - myId = _genericTexts->getId(dialogid - 1000); + int myId = _genericTexts->getId(dialogid - 1000); _audioManager->playVoice(myId, true); } diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp index 97a14e5bc1..1d0e136d69 100644 --- a/engines/touche/detection.cpp +++ b/engines/touche/detection.cpp @@ -24,7 +24,6 @@ #include "engines/advancedDetector.h" #include "common/savefile.h" #include "common/system.h" -#include "common/translation.h" #include "base/plugins.h" diff --git a/engines/tsage/POTFILES b/engines/tsage/POTFILES new file mode 100644 index 0000000000..de18cbd072 --- /dev/null +++ b/engines/tsage/POTFILES @@ -0,0 +1,3 @@ +engines/tsage/dialogs.cpp +engines/tsage/scenes.cpp + diff --git a/engines/tsage/blue_force/blueforce_dialogs.cpp b/engines/tsage/blue_force/blueforce_dialogs.cpp index 2f337ac549..5be27c9ae7 100644 --- a/engines/tsage/blue_force/blueforce_dialogs.cpp +++ b/engines/tsage/blue_force/blueforce_dialogs.cpp @@ -20,8 +20,6 @@ * */ -#include "common/translation.h" - #include "gui/dialog.h" #include "gui/widget.h" diff --git a/engines/tsage/debugger.cpp b/engines/tsage/debugger.cpp index b647807f8a..a38796717a 100644 --- a/engines/tsage/debugger.cpp +++ b/engines/tsage/debugger.cpp @@ -42,7 +42,7 @@ Debugger::Debugger() : GUI::Debugger() { registerCmd("moveobject", WRAP_METHOD(Debugger, Cmd_MoveObject)); registerCmd("hotspots", WRAP_METHOD(Debugger, Cmd_Hotspots)); registerCmd("sound", WRAP_METHOD(Debugger, Cmd_Sound)); - registerCmd("setdebug", WRAP_METHOD(Debugger, Cmd_SetDebug)); + registerCmd("setdebug", WRAP_METHOD(Debugger, Cmd_SetOutpostAlphaDebug)); } static int strToInt(const char *s) { @@ -344,7 +344,7 @@ bool Debugger::Cmd_Sound(int argc, const char **argv) { /** * Activate internal debugger, when available */ -bool Debugger::Cmd_SetDebug(int argc, const char **argv) { +bool Debugger::Cmd_SetOutpostAlphaDebug(int argc, const char **argv) { debugPrintf("Not available in this game\n"); return true; } @@ -720,7 +720,7 @@ bool Ringworld2Debugger::Cmd_MoveObject(int argc, const char **argv) { /** * Activate internal debugger, when available */ -bool Ringworld2Debugger::Cmd_SetDebug(int argc, const char **argv) { +bool Ringworld2Debugger::Cmd_SetOutpostAlphaDebug(int argc, const char **argv) { if (argc != 1) { debugPrintf("Usage: %s\n", argv[0]); return true; diff --git a/engines/tsage/debugger.h b/engines/tsage/debugger.h index 610f45de64..b0f4c665dd 100644 --- a/engines/tsage/debugger.h +++ b/engines/tsage/debugger.h @@ -45,7 +45,7 @@ protected: bool Cmd_Sound(int argc, const char **argv); virtual bool Cmd_ListObjects(int argc, const char **argv) = 0; virtual bool Cmd_MoveObject(int argc, const char **argv) = 0; - virtual bool Cmd_SetDebug(int argc, const char **argv); + virtual bool Cmd_SetOutpostAlphaDebug(int argc, const char **argv); }; class DemoDebugger : public Debugger { @@ -70,7 +70,7 @@ class Ringworld2Debugger : public Debugger { protected: virtual bool Cmd_ListObjects(int argc, const char **argv); virtual bool Cmd_MoveObject(int argc, const char **argv); - virtual bool Cmd_SetDebug(int argc, const char **argv); + virtual bool Cmd_SetOutpostAlphaDebug(int argc, const char **argv); }; } // End of namespace TsAGE diff --git a/engines/tsage/module.mk b/engines/tsage/module.mk index 53c03e2e57..d62f398c20 100644 --- a/engines/tsage/module.mk +++ b/engines/tsage/module.mk @@ -35,13 +35,16 @@ MODULE_OBJS := \ ringworld/ringworld_scenes8.o \ ringworld/ringworld_scenes10.o \ ringworld/ringworld_speakers.o \ + ringworld2/ringworld2_airduct.o \ ringworld2/ringworld2_dialogs.o \ ringworld2/ringworld2_logic.o \ + ringworld2/ringworld2_outpost.o \ ringworld2/ringworld2_scenes0.o \ ringworld2/ringworld2_scenes1.o \ ringworld2/ringworld2_scenes2.o \ ringworld2/ringworld2_scenes3.o \ ringworld2/ringworld2_speakers.o \ + ringworld2/ringworld2_vampire.o \ saveload.o \ scenes.o \ sound.o \ diff --git a/engines/tsage/ringworld/ringworld_dialogs.cpp b/engines/tsage/ringworld/ringworld_dialogs.cpp index 226a943f08..1dd3bc158b 100644 --- a/engines/tsage/ringworld/ringworld_dialogs.cpp +++ b/engines/tsage/ringworld/ringworld_dialogs.cpp @@ -20,8 +20,6 @@ * */ -#include "common/translation.h" - #include "gui/dialog.h" #include "gui/widget.h" diff --git a/engines/tsage/ringworld2/ringworld2_airduct.cpp b/engines/tsage/ringworld2/ringworld2_airduct.cpp new file mode 100644 index 0000000000..136e8d5d1b --- /dev/null +++ b/engines/tsage/ringworld2/ringworld2_airduct.cpp @@ -0,0 +1,909 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "tsage/ringworld2/ringworld2_airduct.h" + +namespace TsAGE { + +namespace Ringworld2 { + +/*-------------------------------------------------------------------------- + * Scene 1200 - Air Ducts Maze + * + *--------------------------------------------------------------------------*/ + +Scene1200::Scene1200() { + _nextCrawlDirection = 0; + _field414 = 0; + _field416 = 0; + _field418 = 0; + _field41A = 0; + _fixupMaze = false; +} + +void Scene1200::synchronize(Serializer &s) { + SceneExt::synchronize(s); + + s.syncAsSint16LE(_nextCrawlDirection); + s.syncAsSint16LE(_field414); + s.syncAsSint16LE(_field416); + s.syncAsSint16LE(_field418); + s.syncAsSint16LE(_field41A); + s.syncAsSint16LE(_fixupMaze); +} + +Scene1200::LaserPanel::LaserPanel() { +} + +void Scene1200::LaserPanel::Jumper::init(int state) { + _state = state; + + SceneActor::postInit(); + setup(1003, 1, 1); + fixPriority(255); + + switch (_state) { + case 1: + switch (R2_GLOBALS._ductMazePanel1State) { + case 1: + setFrame2(2); + setPosition(Common::Point(129, 101)); + break; + case 2: + setFrame2(3); + setPosition(Common::Point(135, 95)); + break; + default: + break; + } + break; + case 2: + switch (R2_GLOBALS._ductMazePanel2State) { + case 1: + setFrame2(2); + setPosition(Common::Point(152, 101)); + break; + case 2: + setFrame2(3); + setPosition(Common::Point(158, 122)); + break; + case 3: + setFrame2(3); + setPosition(Common::Point(135, 122)); + break; + default: + break; + } + break; + case 3: + switch (R2_GLOBALS._ductMazePanel3State) { + case 1: + setFrame2(3); + setPosition(Common::Point(158, 95)); + break; + case 2: + setFrame2(2); + setPosition(Common::Point(175, 101)); + break; + default: + break; + } + break; + default: + break; + } + + setDetails(1200, 12, -1, -1, 2, (SceneItem *) NULL); +} + +bool Scene1200::LaserPanel::Jumper::startAction(CursorType action, Event &event) { + if (action != CURSOR_USE) + return SceneActor::startAction(action, event); + + R2_GLOBALS._sound2.play(260); + switch (_state) { + case 1: + if (R2_GLOBALS._ductMazePanel1State == 1) { + R2_GLOBALS._ductMazePanel1State = 2; + setFrame2(3); + setPosition(Common::Point(135, 95)); + } else { + R2_GLOBALS._ductMazePanel1State = 1; + setFrame2(2); + setPosition(Common::Point(129, 101)); + } + break; + case 2: + ++R2_GLOBALS._ductMazePanel2State; + if (R2_GLOBALS._ductMazePanel2State == 4) + R2_GLOBALS._ductMazePanel2State = 1; + + switch (R2_GLOBALS._ductMazePanel2State) { + case 1: + setFrame2(2); + setPosition(Common::Point(152, 101)); + break; + case 2: + setFrame2(3); + setPosition(Common::Point(158, 122)); + break; + case 3: + setFrame2(3); + setPosition(Common::Point(135, 122)); + break; + default: + break; + } + break; + case 3: + if (R2_GLOBALS._ductMazePanel3State == 1) { + R2_GLOBALS._ductMazePanel3State = 2; + setFrame2(2); + setPosition(Common::Point(175, 101)); + } else { + R2_GLOBALS._ductMazePanel3State = 1; + setFrame2(3); + setPosition(Common::Point(158, 95)); + } + break; + default: + break; + } + + Scene1200 *scene = (Scene1200 *)R2_GLOBALS._sceneManager._scene; + scene->_field418 = 0; + + if ((R2_GLOBALS._ductMazePanel1State == 1) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) + scene->_field418 = 1; + else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) + scene->_field418 = 2; + else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 2)) + scene->_field418 = 3; + else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 3) && (R2_GLOBALS._ductMazePanel3State == 1)) + scene->_field418 = 4; + + return true; +} + +void Scene1200::LaserPanel::postInit(SceneObjectList *OwnerList) { + Scene1200 *scene = (Scene1200 *)R2_GLOBALS._sceneManager._scene; + + scene->_field41A = 1; + R2_GLOBALS._events.setCursor(CURSOR_USE); + setup2(1003, 1, 1, 100, 40); + setup3(1200, 11, -1, -1); + R2_GLOBALS._sound2.play(259); + _jumper1.init(1); + _jumper2.init(2); + _jumper3.init(3); + + R2_GLOBALS._player._canWalk = false; +} + +void Scene1200::LaserPanel::remove() { + Scene1200 *scene = (Scene1200 *)R2_GLOBALS._sceneManager._scene; + + scene->_field41A = 0; + scene->_sceneAreas.remove(&_jumper1); + scene->_sceneAreas.remove(&_jumper2); + scene->_sceneAreas.remove(&_jumper3); + _jumper1.remove(); + _jumper2.remove(); + _jumper3.remove(); + + ModalWindow::remove(); + R2_GLOBALS._player._canWalk = true; +} + +void Scene1200::postInit(SceneObjectList *OwnerList) { + loadScene(1200); + SceneExt::postInit(); + + if (R2_GLOBALS._sceneManager._previousScene < 3200) + R2_GLOBALS._sound1.play(257); + + _nextCrawlDirection = CRAWL_EAST; + _field414 = 0; + _field416 = 0; + _field418 = 0; + _field41A = 0; + + if ((R2_GLOBALS._ductMazePanel1State == 1) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) + _field418 = 1; + else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) + _field418 = 2; + else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 2)) + _field418 = 3; + else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 3) && (R2_GLOBALS._ductMazePanel3State == 1)) + _field418 = 4; + + R2_GLOBALS._player.postInit(); + R2_GLOBALS._player.disableControl(); + R2_GLOBALS._player.setup(3156, 1, 6); + R2_GLOBALS._player.setPosition(Common::Point(160, 70)); + R2_GLOBALS._player._numFrames = 10; + R2_GLOBALS._player._oldCharacterScene[R2_MIRANDA] = 1200; + + _actor1.postInit(); + _actor1.hide(); + + _mazeUI.setDisplayBounds(Rect(110, 20, 210, 120)); + + _mazeUI.postInit(); + _mazeUI.load(1); + _mazeUI.setMazePosition(R2_GLOBALS._ventCellPos); + + R2_GLOBALS._player.enableControl(); + _item1.setDetails(Rect(0, 0, 320, 200), 1200, 0, 1, 2, 1, NULL); +} + +void Scene1200::signal() { + switch (_sceneMode++) { + case 1: + // No break on purpose + case 1200: + // No break on purpose + case 1201: + // No break on purpose + case 1202: + // No break on purpose + case 1203: + R2_GLOBALS._player.enableControl(); + // CHECKME: The original is calling _eventManager.waitEvent(); + _sceneMode = 2; + break; + case 10: + _field416 = 1; + _field414 = 6; + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.setStrip(1); + R2_GLOBALS._player.setFrame(5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 11: + // No break on purpose + case 21: + // No break on purpose + case 31: + // No break on purpose + case 41: + _field416 = 0; + break; + case 12: + _field414 = 14; + R2_GLOBALS._player._numFrames = 10; + R2_GLOBALS._player.setup(3155, 1, 4); + R2_GLOBALS._player.setPosition(Common::Point(160, 70)); + R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); + break; + case 13: + // No break on purpose + case 16: + // No break on purpose + case 23: + // No break on purpose + case 26: + // No break on purpose + case 33: + // No break on purpose + case 36: + // No break on purpose + case 43: + // No break on purpose + case 46: + R2_GLOBALS._player.setFrame(4); + _sceneMode = 1; + setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); + break; + case 15: + // No break on purpose + case 25: + // No break on purpose + case 35: + // No break on purpose + case 45: + _field414 = 20; + R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); + break; + case 20: + _field416 = 1; + _field414 = 6; + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.setStrip(2); + R2_GLOBALS._player.setFrame(5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 22: + _field414 = 14; + R2_GLOBALS._player._numFrames = 10; + R2_GLOBALS._player.setup(3155, 2, 4); + R2_GLOBALS._player.setPosition(Common::Point(160, 70)); + R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); + break; + case 30: + _field416 = 1; + _field414 = 6; + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.setStrip(3); + R2_GLOBALS._player.setFrame(5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 32: + _field414 = 14; + R2_GLOBALS._player._numFrames = 10; + R2_GLOBALS._player.setup(3155, 3, 4); + R2_GLOBALS._player.setPosition(Common::Point(160, 70)); + R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); + break; + case 40: + _field416 = 1; + _field414 = 6; + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.setStrip(4); + R2_GLOBALS._player.setFrame(5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 42: + _field414 = 14; + R2_GLOBALS._player._numFrames = 10; + R2_GLOBALS._player.setup(3155, 4, 4); + R2_GLOBALS._player.setPosition(Common::Point(160, 70)); + R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); + break; + case 50: + // No break on purpose + case 55: + // No break on purpose + case 60: + R2_GLOBALS._player.setup(3156, 5, 1); + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 51: + // No break on purpose + case 56: + // No break on purpose + case 117: + R2_GLOBALS._player.setup(3157, 1, 1); + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 52: + // No break on purpose + case 82: + // No break on purpose + case 118: + R2_GLOBALS._player.setup(3156, 3, 6); + _sceneMode = 1; + setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); + break; + case 57: + // No break on purpose + case 91: + // No break on purpose + case 96: + R2_GLOBALS._player.setup(3157, 2, 1); + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 58: + // No break on purpose + case 92: + // No break on purpose + case 122: + R2_GLOBALS._player.setup(3156, 2, 6); + _sceneMode = 1; + setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); + break; + case 61: + R2_GLOBALS._player.setup(3157, 4, 5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 62: + // No break on purpose + case 72: + // No break on purpose + case 98: + R2_GLOBALS._player.setup(3156, 4, 6); + _sceneMode = 1; + setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); + break; + case 70: + // No break on purpose + case 75: + // No break on purpose + case 80: + R2_GLOBALS._player.setup(3156, 6, 1); + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 71: + // No break on purpose + case 76: + // No break on purpose + case 97: + R2_GLOBALS._player.setup(3157, 3, 1); + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 77: + // No break on purpose + case 111: + // No break on purpose + case 116: + R2_GLOBALS._player.setup(3157, 4, 1); + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 78: + // No break on purpose + case 102: + // No break on purpose + case 112: + R2_GLOBALS._player.setup(3156, 1, 6); + _sceneMode = 1; + setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); + break; + case 81: + R2_GLOBALS._player.setup(3157, 2, 5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 90: + // No break on purpose + case 95: + // No break on purpose + case 100: + R2_GLOBALS._player.setup(3156, 7, 1); + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 101: + R2_GLOBALS._player.setup(3157, 1, 5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + case 110: + // No break on purpose + case 115: + // No break on purpose + case 120: + R2_GLOBALS._player.setup(3156, 8, 1); + R2_GLOBALS._player._numFrames = 5; + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + break; + case 121: + R2_GLOBALS._player.setup(3157, 3, 5); + R2_GLOBALS._player.animate(ANIM_MODE_6, this); + break; + default: + // CHECKME: The original is walling _eventManager.waitEvent(); + _sceneMode = 2; + break; + } +} + +void Scene1200::process(Event &event) { + if (_field414 != 0) + return; + + Scene::process(event); + + if (!R2_GLOBALS._player._canWalk) + return; + + if (event.eventType == EVENT_BUTTON_DOWN) { + Common::Point cellPos = R2_GLOBALS._ventCellPos; + _mazeUI.pixelToCellXY(cellPos); + + int cellId = _mazeUI.getCellFromPixelXY(event.mousePos); + switch (R2_GLOBALS._events.getCursor()) { + case CURSOR_WALK: + event.handled = true; + if ((event.mousePos.x > 179) && (event.mousePos.x < 210) && (event.mousePos.y > 50) && (event.mousePos.y < 89)) + startCrawling(CRAWL_EAST); + + if ((event.mousePos.x > 109) && (event.mousePos.x < 140) && (event.mousePos.y > 50) && (event.mousePos.y < 89)) + startCrawling(CRAWL_WEST); + + if ((event.mousePos.x > 140) && (event.mousePos.x < 179) && (event.mousePos.y > 89) && (event.mousePos.y < 120)) + startCrawling(CRAWL_SOUTH); + + if ((event.mousePos.x > 140) && (event.mousePos.x < 179) && (event.mousePos.y > 19) && (event.mousePos.y < 50)) + startCrawling(CRAWL_NORTH); + break; + case CURSOR_USE: + if (cellId > 36) { + if ( ((cellPos.x == 3) && (cellPos.y == 33)) + || ((cellPos.x == 7) && (cellPos.y == 33)) + || ((cellPos.x == 33) && (cellPos.y == 41)) + || ((cellPos.x == 5) && (cellPos.y == 5)) + || ((cellPos.x == 13) && (cellPos.y == 21)) + || ((cellPos.x == 17) && (cellPos.y == 21)) + || ((cellPos.x == 17) && (cellPos.y == 5)) + || ((cellPos.x == 17) && (cellPos.y == 9)) + || ((cellPos.x == 29) && (cellPos.y == 17)) + || ((cellPos.x == 33) && (cellPos.y == 17)) + || ((cellPos.x == 35) && (cellPos.y == 17)) + || ((cellPos.x == 41) && (cellPos.y == 21)) ) { + _laserPanel.postInit(); + event.handled = true; + } + } + + if ((cellId == 1) || (cellId == 4) || (cellId == 11) || (cellId == 14)) { + if ( ((cellPos.x == 3) && (cellPos.y == 9)) + || ((cellPos.x == 11) && (cellPos.y == 27)) + || ((cellPos.x == 17) && (cellPos.y == 7)) + || ((cellPos.x == 17) && (cellPos.y == 27)) + || ((cellPos.x == 17) && (cellPos.y == 33)) + || (cellPos.x == 33) ) { + switch (cellPos.x) { + case 3: + R2_GLOBALS._sceneManager.changeScene(3150); + break; + case 33: + if (R2_GLOBALS._scientistConvIndex >= 4) + R2_GLOBALS._sceneManager.changeScene(3250); + else + SceneItem::display(1200, 6, 0, 280, 1, 160, 9, 1, 2, 20, 7, 154, LIST_END); + break; + default: + SceneItem::display(1200, 5, 0, 280, 1, 160, 9, 1, 2, 20, 7, 154, LIST_END); + break; + } + event.handled = true; + } + } + break; + case CURSOR_LOOK: + if ((cellId == 1) || (cellId == 4) || (cellId == 11) || (cellId == 14)) { + event.handled = true; + switch (cellPos.x) { + case 3: + // It was your cell. + SceneItem::display(1200, 8, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + break; + case 9: + R2_GLOBALS._sceneManager.changeScene(3240); + break; + case 11: + if (cellPos.y == 27) + R2_GLOBALS._sceneManager.changeScene(3210); + else + // A vent grill + SceneItem::display(1200, 10, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + break; + case 17: + switch (cellPos.y) { + case 5: + R2_GLOBALS._sceneManager.changeScene(3230); + break; + case 21: + R2_GLOBALS._sceneManager.changeScene(3220); + break; + case 33: + R2_GLOBALS._sceneManager.changeScene(3200); + break; + default: + // A vent grill + SceneItem::display(1200, 10, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + break; + } + break; + case 33: + R2_GLOBALS._sceneManager.changeScene(3245); + break; + default: + SceneItem::display(1200, 10, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + break; + } + } + if (cellId > 36) { + // "An anti-pest laser" + event.handled = true; + SceneItem::display(1200, 9, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + } + break; + case CURSOR_TALK: + event.handled = true; + break; + default: + return; + } + } else if (event.eventType == EVENT_KEYPRESS) { + if (_field414) { + event.handled = false; + return; + } + + switch (event.kbd.keycode) { + case Common::KEYCODE_KP8: + case Common::KEYCODE_UP: + startCrawling(CRAWL_NORTH); + break; + case Common::KEYCODE_KP4: + case Common::KEYCODE_LEFT: + startCrawling(CRAWL_WEST); + break; + case Common::KEYCODE_KP6: + case Common::KEYCODE_RIGHT: + startCrawling(CRAWL_EAST); + break; + case Common::KEYCODE_KP2: + case Common::KEYCODE_DOWN: + startCrawling(CRAWL_SOUTH); + break; + default: + event.handled = false; + return; + break; + } + } else + return; +} + +void Scene1200::dispatch() { + Rect tmpRect; + Scene::dispatch(); + + if (_fixupMaze) { + _mazeUI.setMazePosition(R2_GLOBALS._ventCellPos); + //_mazeUI.draw(); + _fixupMaze = false; + } + + if (_field414 != 0) { + tmpRect.set(110, 20, 210, 120); + _field414--; + + switch (_nextCrawlDirection) { + case CRAWL_EAST: + R2_GLOBALS._ventCellPos.x += 2; + break; + case CRAWL_WEST: + R2_GLOBALS._ventCellPos.x -= 2; + break; + case CRAWL_SOUTH: + R2_GLOBALS._ventCellPos.y += 2; + break; + case CRAWL_NORTH: + R2_GLOBALS._ventCellPos.y -= 2; + break; + default: + break; + } + + _mazeUI.setMazePosition(R2_GLOBALS._ventCellPos); + //_mazeUI.draw(); + + if (_field416 != 0) { + switch(_nextCrawlDirection) { + case CRAWL_EAST: + R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x - 2, R2_GLOBALS._player._position.y)); + break; + case CRAWL_WEST: + R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x + 2, R2_GLOBALS._player._position.y)); + break; + case CRAWL_SOUTH: + R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x, R2_GLOBALS._player._position.y - 2)); + break; + case CRAWL_NORTH: + R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x, R2_GLOBALS._player._position.y + 2)); + break; + default: + break; + } + } + if (_field414 == 0) { + if (_field416 == 0) + R2_GLOBALS._player.animate(ANIM_MODE_NONE, NULL); + signal(); + } + } +} + +void Scene1200::saveCharacter(int characterIndex) { + R2_GLOBALS._sound1.fadeOut2(NULL); + SceneExt::saveCharacter(characterIndex); +} + +void Scene1200::startCrawling(CrawlDirection dir) { + Common::Point cellPos = R2_GLOBALS._ventCellPos; + _mazeUI.pixelToCellXY(cellPos); + + switch (dir) { + case CRAWL_EAST: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(200, 50)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(200, 88)) > 36)) + && ( ((cellPos.x == 3) && (cellPos.y == 33) && (_field418 != 4)) + || ((cellPos.x == 13) && (cellPos.y == 21) && (_field418 != 2)) + || ((cellPos.x == 29) && (cellPos.y == 17) && (_field418 != 1)) + || ((cellPos.x == 33) && (cellPos.y == 41)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1200; + setAction(&_sequenceManager, this, 1200, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(200, 69)) == 36) { + switch (_nextCrawlDirection) { + case CRAWL_EAST: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 15; + else + _sceneMode = 10; + break; + case CRAWL_WEST: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 76; + else + _sceneMode = 75; + break; + case CRAWL_SOUTH: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 101; + else + _sceneMode = 100; + break; + case CRAWL_NORTH: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 111; + else + _sceneMode = 110; + break; + default: + break; + } + R2_GLOBALS._player.disableControl(); + _nextCrawlDirection = 1; + signal(); + } + break; + case CRAWL_WEST: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(120, 50)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(120, 88)) > 36)) + && ( ((cellPos.x == 7) && (cellPos.y == 33) && (_field418 != 4)) + || ((cellPos.x == 17) && (cellPos.y == 21) && (_field418 != 2)) + || ((cellPos.x == 33) && (cellPos.y == 17) && (_field418 != 1)) + || ((cellPos.x == 5) && (cellPos.y == 5)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1201; + setAction(&_sequenceManager, this, 1201, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(120, 69)) == 36) { + switch (_nextCrawlDirection) { + case CRAWL_EAST: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 56; + else + _sceneMode = 55; + break; + case CRAWL_WEST: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 25; + else + _sceneMode = 20; + break; + case CRAWL_SOUTH: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 91; + else + _sceneMode = 90; + break; + case CRAWL_NORTH: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 121; + else + _sceneMode = 120; + break; + default: + break; + } + R2_GLOBALS._player.disableControl(); + _nextCrawlDirection = 2; + signal(); + } + break; + case CRAWL_SOUTH: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(140, 110)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(178, 110)) > 36)) + && ( ((cellPos.x == 17) && (cellPos.y == 5) && (_field418 != 3)) + || ((cellPos.x == 41) && (cellPos.y == 21)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1203; + setAction(&_sequenceManager, this, 1203, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(160, 110)) == 36) { + switch (_nextCrawlDirection) { + case CRAWL_EAST: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 51; + else + _sceneMode = 50; + break; + case CRAWL_WEST: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 81; + else + _sceneMode = 80; + break; + case CRAWL_SOUTH: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 35; + else + _sceneMode = 30; + break; + case CRAWL_NORTH: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 116; + else + _sceneMode = 115; + break; + default: + break; + } + R2_GLOBALS._player.disableControl(); + _nextCrawlDirection = 3; + signal(); + } + break; + case CRAWL_NORTH: + if ( ((_mazeUI.getCellFromPixelXY(Common::Point(140, 30)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(178, 30)) > 36)) + && ( ((cellPos.x == 17) && (cellPos.y == 9) && (_field418 != 3)) + || ((cellPos.x == 35) && (cellPos.y == 17)) ) + ) { + R2_GLOBALS._player.disableControl(); + _sceneMode = 1202; + setAction(&_sequenceManager, this, 1202, &_actor1, NULL); + } else if (_mazeUI.getCellFromPixelXY(Common::Point(160, 30)) == 36) { + switch (_nextCrawlDirection) { + case CRAWL_EAST: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 61; + else + _sceneMode = 60; + break; + case CRAWL_WEST: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 71; + else + _sceneMode = 70; + break; + case CRAWL_SOUTH: + if (R2_GLOBALS._player._visage == 3156) + _sceneMode = 96; + else + _sceneMode = 95; + break; + case CRAWL_NORTH: + if (R2_GLOBALS._player._visage == 3155) + _sceneMode = 45; + else + _sceneMode = 40; + break; + default: + _sceneMode = 1; + R2_GLOBALS._player.setup(3156, 4, 6); + break; + } + R2_GLOBALS._player.disableControl(); + _nextCrawlDirection = 4; + signal(); + } + break; + default: + break; + } +} + +} // End of namespace Ringworld2 +} // End of namespace TsAGE diff --git a/engines/tsage/ringworld2/ringworld2_airduct.h b/engines/tsage/ringworld2/ringworld2_airduct.h new file mode 100644 index 0000000000..89dfe778d0 --- /dev/null +++ b/engines/tsage/ringworld2/ringworld2_airduct.h @@ -0,0 +1,89 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef TSAGE_RINGWORLD2_AIRDUCT_H +#define TSAGE_RINGWORLD2_AIRDUCT_H + +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/scenes.h" +#include "tsage/globals.h" +#include "tsage/sound.h" +#include "tsage/ringworld2/ringworld2_logic.h" + +namespace TsAGE { + +namespace Ringworld2 { + +using namespace TsAGE; + +class Scene1200 : public SceneExt { + enum CrawlDirection { CRAWL_EAST = 1, CRAWL_WEST = 2, CRAWL_SOUTH = 3, CRAWL_NORTH = 4 }; + + class LaserPanel: public ModalWindow { + public: + class Jumper : public SceneActorExt { + public: + void init(int state); + virtual bool startAction(CursorType action, Event &event); + }; + + Jumper _jumper1; + Jumper _jumper2; + Jumper _jumper3; + + LaserPanel(); + + virtual void postInit(SceneObjectList *OwnerList = NULL); + virtual void remove(); + }; + +public: + NamedHotspot _item1; + SceneActor _actor1; + LaserPanel _laserPanel; + MazeUI _mazeUI; + SequenceManager _sequenceManager; + + int _nextCrawlDirection; + int _field414; + int _field416; + int _field418; + int _field41A; + bool _fixupMaze; + + Scene1200(); + void synchronize(Serializer &s); + + void startCrawling(CrawlDirection dir); + + virtual void postInit(SceneObjectList *OwnerList = NULL); + virtual void signal(); + virtual void process(Event &event); + virtual void dispatch(); + virtual void saveCharacter(int characterIndex); +}; + +} // End of namespace Ringworld2 +} // End of namespace TsAGE + +#endif diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index 99188c1ab6..d24541932f 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -32,6 +32,9 @@ #include "tsage/ringworld2/ringworld2_scenes1.h" #include "tsage/ringworld2/ringworld2_scenes2.h" #include "tsage/ringworld2/ringworld2_scenes3.h" +#include "tsage/ringworld2/ringworld2_airduct.h" +#include "tsage/ringworld2/ringworld2_outpost.h" +#include "tsage/ringworld2/ringworld2_vampire.h" namespace TsAGE { @@ -355,6 +358,11 @@ SceneExt::SceneExt(): Scene() { // to make inter-scene debugging easier, I'm explicitly resetting the _animationCtr // on scene start, since scene objects aren't drawn while it's non-zero R2_GLOBALS._animationCtr = 0; + + // WORKAROUND: We had a case where at some point the number of modal dialogs + // open became incorrect. So reset it on scene changes to fix the problem if + // it ever happens + R2_GLOBALS._insetUp = 0; } void SceneExt::synchronize(Serializer &s) { diff --git a/engines/tsage/ringworld2/ringworld2_outpost.cpp b/engines/tsage/ringworld2/ringworld2_outpost.cpp new file mode 100644 index 0000000000..cad21b4623 --- /dev/null +++ b/engines/tsage/ringworld2/ringworld2_outpost.cpp @@ -0,0 +1,4700 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "graphics/cursorman.h" +#include "tsage/tsage.h" +#include "tsage/staticres.h" +#include "tsage/ringworld2/ringworld2_outpost.h" + +namespace TsAGE { + +namespace Ringworld2 { + +/*-------------------------------------------------------------------------- + * Scene 1337 - Card game + * + *--------------------------------------------------------------------------*/ + +Scene1337::Card::Card() { + _cardId = 0; + _stationPos = Common::Point(0, 0); +} + +void Scene1337::Card::synchronize(Serializer &s) { + _card.synchronize(s); + + s.syncAsSint16LE(_cardId); + s.syncAsSint16LE(_stationPos.x); + s.syncAsSint16LE(_stationPos.y); +} + +bool Scene1337::Card::isIn(Common::Point pt) { + if ((_stationPos.x > pt.x) || (_stationPos.x + 24 < pt.x)) + return false; + + if ((_stationPos.y > pt.y) || (_stationPos.y + 24 < pt.y)) + return false; + + return true; +} + +Scene1337::GameBoardSide::GameBoardSide() { + _card1Pos = Common::Point(0, 0); + _card2Pos = Common::Point(0, 0); + _card3Pos = Common::Point(0, 0); + _card4Pos = Common::Point(0, 0); + _frameNum = 0; +} + +void Scene1337::GameBoardSide::synchronize(Serializer &s) { + SceneHotspot::synchronize(s); + + for (int i = 0; i < 4; i++) + _handCard[i].synchronize(s); + + for (int i = 0; i < 8; i++) + _outpostStation[i].synchronize(s); + + _delayCard.synchronize(s); + _emptyStationPos.synchronize(s); + + s.syncAsSint16LE(_card1Pos.x); + s.syncAsSint16LE(_card1Pos.y); + s.syncAsSint16LE(_card2Pos.x); + s.syncAsSint16LE(_card2Pos.y); + s.syncAsSint16LE(_card3Pos.x); + s.syncAsSint16LE(_card3Pos.y); + s.syncAsSint16LE(_card4Pos.x); + s.syncAsSint16LE(_card4Pos.y); + s.syncAsSint16LE(_frameNum); +} + +Scene1337::Scene1337() { + _autoplay = false; + _cardsAvailableNumb = 0; + _currentDiscardIndex = 0; + + for (int i = 0; i < 100; i++) + _availableCardsPile[i] = 0; + + _shuffleEndedFl = false; + _currentPlayerNumb = 0; + _actionPlayerIdx = 0; + _actionVictimIdx = 0; + _showPlayerTurn = false; + _displayHelpFl = false; + _winnerId = -1; + _instructionsDisplayedFl = false; + _instructionsWaitCount = 0; + + _delayedFunction = nullptr; + _actionCard1 = nullptr; + _actionCard2 = nullptr; + _actionCard3 = nullptr; + + _cursorCurRes = 0; + _cursorCurStrip = 0; + _cursorCurFrame = 0; +} + +void Scene1337::synchronize(Serializer &s) { + _actionCard1->synchronize(s); + _actionCard2->synchronize(s); + _actionCard3->synchronize(s); + _animatedCard.synchronize(s); + _shuffleAnimation.synchronize(s); + _discardedPlatformCard.synchronize(s); + _selectedCard.synchronize(s); + _discardPile.synchronize(s); + _stockCard.synchronize(s); + _aSound1.synchronize(s); + _aSound2.synchronize(s); + _helpIcon.synchronize(s); + _stockPile.synchronize(s); + _actionItem.synchronize(s); + _currentPlayerArrow.synchronize(s); + + for (int i = 0; i < 4; i++) + _gameBoardSide[i].synchronize(s); + + for (int i = 0; i < 8; i++) { + _upperDisplayCard[i].synchronize(s); + _lowerDisplayCard[i].synchronize(s); + } + + // TODO s.syncPointer(_delayedFunction); + s.syncAsByte(_autoplay); + s.syncAsByte(_shuffleEndedFl); + s.syncAsByte(_showPlayerTurn); + s.syncAsByte(_displayHelpFl); + s.syncAsByte(_instructionsDisplayedFl); + s.syncAsSint16LE(_currentDiscardIndex); + s.syncAsSint16LE(_cardsAvailableNumb); + s.syncAsSint16LE(_currentPlayerNumb); + s.syncAsSint16LE(_actionPlayerIdx); + s.syncAsSint16LE(_actionVictimIdx); + s.syncAsSint16LE(_winnerId); + s.syncAsSint16LE(_instructionsWaitCount); + s.syncAsSint16LE(_cursorCurRes); + s.syncAsSint16LE(_cursorCurStrip); + s.syncAsSint16LE(_cursorCurFrame); + + for (int i = 0; i < 100; i++) + s.syncAsSint16LE(_availableCardsPile[i]); + +} + +void Scene1337::Action1337::waitFrames(int32 frameCount) { + uint32 firstFrameNumber = g_globals->_events.getFrameNumber(); + uint32 curFrame = firstFrameNumber; + uint32 destFrame = firstFrameNumber + frameCount; + + while ((curFrame < destFrame) && !g_vm->shouldQuit()) { + TsAGE::Event event; + g_globals->_events.getEvent(event); + curFrame = g_globals->_events.getFrameNumber(); + } + + // CHECKME: The original is calling _eventManager.waitEvent(); +} + +/** + * Display instructions + */ +void Scene1337::Action1::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 1: { + scene->actionDisplay(1331, 6, 159, 10, 1, 200, 0, 7, 0, 154, 154); + R2_GLOBALS._sceneObjects->draw(); + scene->actionDisplay(1331, 7, 159, 10, 1, 200, 0, 7, 0, 154, 154); + scene->actionDisplay(1331, 8, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + scene->_gameBoardSide[1]._outpostStation[0]._cardId = 2; + scene->_gameBoardSide[1]._outpostStation[0]._card.postInit(); + scene->_gameBoardSide[1]._outpostStation[0]._card.setVisage(1332); + scene->_gameBoardSide[1]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[1]._outpostStation[0]._stationPos, 0); + scene->_gameBoardSide[1]._outpostStation[0]._card.setStrip(2); + scene->_gameBoardSide[1]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[1]._outpostStation[0]._cardId); + scene->_gameBoardSide[1]._outpostStation[0]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[1]._outpostStation[0]); + + scene->_gameBoardSide[1]._outpostStation[1]._cardId = 3; + scene->_gameBoardSide[1]._outpostStation[1]._card.postInit(); + scene->_gameBoardSide[1]._outpostStation[1]._card.setVisage(1332); + scene->_gameBoardSide[1]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[1]._outpostStation[1]._stationPos, 0); + scene->_gameBoardSide[1]._outpostStation[1]._card.setStrip(2); + scene->_gameBoardSide[1]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[1]._outpostStation[1]._cardId); + scene->_gameBoardSide[1]._outpostStation[1]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[1]._outpostStation[1]); + + scene->_gameBoardSide[2]._outpostStation[0]._cardId = 4; + scene->_gameBoardSide[2]._outpostStation[0]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[0]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[0]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[0]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[0]._cardId); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[0]); + + scene->_gameBoardSide[3]._outpostStation[0]._cardId = 5; + scene->_gameBoardSide[3]._outpostStation[0]._card.postInit(); + scene->_gameBoardSide[3]._outpostStation[0]._card.setVisage(1332); + scene->_gameBoardSide[3]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[3]._outpostStation[0]._stationPos, 0); + scene->_gameBoardSide[3]._outpostStation[0]._card.setStrip(2); + scene->_gameBoardSide[3]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[3]._outpostStation[0]._cardId); + scene->_gameBoardSide[3]._outpostStation[0]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[3]._outpostStation[0]); + + scene->_gameBoardSide[3]._outpostStation[1]._cardId = 6; + scene->_gameBoardSide[3]._outpostStation[1]._card.postInit(); + scene->_gameBoardSide[3]._outpostStation[1]._card.setVisage(1332); + scene->_gameBoardSide[3]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[3]._outpostStation[1]._stationPos, 0); + scene->_gameBoardSide[3]._outpostStation[1]._card.setStrip(2); + scene->_gameBoardSide[3]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[3]._outpostStation[1]._cardId); + scene->_gameBoardSide[3]._outpostStation[1]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[3]._outpostStation[1]); + + scene->_gameBoardSide[3]._outpostStation[2]._cardId = 7; + scene->_gameBoardSide[3]._outpostStation[2]._card.postInit(); + scene->_gameBoardSide[3]._outpostStation[2]._card.setVisage(1332); + scene->_gameBoardSide[3]._outpostStation[2]._card.setPosition(scene->_gameBoardSide[3]._outpostStation[2]._stationPos, 0); + scene->_gameBoardSide[3]._outpostStation[2]._card.setStrip(2); + scene->_gameBoardSide[3]._outpostStation[2]._card.setFrame(scene->_gameBoardSide[3]._outpostStation[2]._cardId); + scene->_gameBoardSide[3]._outpostStation[2]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[3]._outpostStation[2]); + + scene->_gameBoardSide[0]._outpostStation[0]._cardId = 8; + scene->_gameBoardSide[0]._outpostStation[0]._card.postInit(); + scene->_gameBoardSide[0]._outpostStation[0]._card.setVisage(1332); + scene->_gameBoardSide[0]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[0]._outpostStation[0]._stationPos, 0); + scene->_gameBoardSide[0]._outpostStation[0]._card.setStrip(2); + scene->_gameBoardSide[0]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[0]._outpostStation[0]._cardId); + scene->_gameBoardSide[0]._outpostStation[0]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[0]._outpostStation[0]); + + scene->_gameBoardSide[0]._outpostStation[1]._cardId = 9; + scene->_gameBoardSide[0]._outpostStation[1]._card.postInit(); + scene->_gameBoardSide[0]._outpostStation[1]._card.setVisage(1332); + scene->_gameBoardSide[0]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[0]._outpostStation[1]._stationPos, 0); + scene->_gameBoardSide[0]._outpostStation[1]._card.setStrip(2); + scene->_gameBoardSide[0]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[0]._outpostStation[1]._cardId); + scene->_gameBoardSide[0]._outpostStation[1]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[0]._outpostStation[1]); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(60); + scene->actionDisplay(1331, 9, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + scene->_gameBoardSide[2]._outpostStation[1]._cardId = 2; + scene->_gameBoardSide[2]._outpostStation[1]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[1]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[1]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[1]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[1]._cardId); + scene->_gameBoardSide[2]._outpostStation[1]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[1]); + + scene->_gameBoardSide[2]._outpostStation[2]._cardId = 3; + scene->_gameBoardSide[2]._outpostStation[2]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[2]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[2]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[2]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[2]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[2]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[2]._cardId); + scene->_gameBoardSide[2]._outpostStation[2]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[2]); + + scene->_gameBoardSide[2]._outpostStation[3]._cardId = 5; + scene->_gameBoardSide[2]._outpostStation[3]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[3]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[3]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[3]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[3]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[3]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[3]._cardId); + scene->_gameBoardSide[2]._outpostStation[3]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[3]); + + scene->_gameBoardSide[2]._outpostStation[4]._cardId = 6; + scene->_gameBoardSide[2]._outpostStation[4]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[4]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[4]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[4]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[4]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[4]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[4]._cardId); + scene->_gameBoardSide[2]._outpostStation[4]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[4]); + + scene->_gameBoardSide[2]._outpostStation[5]._cardId = 7; + scene->_gameBoardSide[2]._outpostStation[5]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[5]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[5]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[5]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[5]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[5]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[5]._cardId); + scene->_gameBoardSide[2]._outpostStation[5]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[5]); + + scene->_gameBoardSide[2]._outpostStation[6]._cardId = 8; + scene->_gameBoardSide[2]._outpostStation[6]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[6]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[6]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[6]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[6]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[6]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[6]._cardId); + scene->_gameBoardSide[2]._outpostStation[6]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[6]); + + scene->_gameBoardSide[2]._outpostStation[7]._cardId = 9; + scene->_gameBoardSide[2]._outpostStation[7]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[7]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[7]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[7]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[7]._card.setStrip(2); + scene->_gameBoardSide[2]._outpostStation[7]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[7]._cardId); + scene->_gameBoardSide[2]._outpostStation[7]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[7]); + + scene->_aSound1.play(62); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(120); + scene->_gameBoardSide[2]._outpostStation[0]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[1]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[2]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[3]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[4]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[5]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[6]._card.remove(); + scene->_gameBoardSide[2]._outpostStation[7]._card.remove(); + + scene->_gameBoardSide[1]._outpostStation[0]._card.remove(); + scene->_gameBoardSide[1]._outpostStation[1]._card.remove(); + + scene->_gameBoardSide[3]._outpostStation[0]._card.remove(); + scene->_gameBoardSide[3]._outpostStation[1]._card.remove(); + scene->_gameBoardSide[3]._outpostStation[2]._card.remove(); + + scene->_gameBoardSide[0]._outpostStation[0]._card.remove(); + scene->_gameBoardSide[0]._outpostStation[1]._card.remove(); + + scene->_stockPile.setup(1332, 5, 1); + scene->_stockPile.setPosition(Common::Point(162, 95)); + scene->_stockPile.setPriority(110); + scene->_stockPile._effect = EFFECT_SHADED; + scene->_stockPile.show(); + + scene->_gameBoardSide[1]._handCard[0]._card.postInit(); + scene->_gameBoardSide[1]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[1]._handCard[0]._card.setPosition(scene->_gameBoardSide[1]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[1]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[1]._handCard[0]._card.setFrame(4); + scene->_gameBoardSide[1]._handCard[0]._card.fixPriority(170); + + scene->_gameBoardSide[1]._handCard[1]._card.postInit(); + scene->_gameBoardSide[1]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[1]._handCard[1]._card.setPosition(scene->_gameBoardSide[1]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[1]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[1]._handCard[1]._card.setFrame(4); + scene->_gameBoardSide[1]._handCard[1]._card.fixPriority(170); + + scene->_gameBoardSide[1]._handCard[2]._card.postInit(); + scene->_gameBoardSide[1]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[1]._handCard[2]._card.setPosition(scene->_gameBoardSide[1]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[1]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[1]._handCard[2]._card.setFrame(4); + scene->_gameBoardSide[1]._handCard[2]._card.fixPriority(170); + + scene->_gameBoardSide[2]._handCard[0]._cardId = 30; + scene->_gameBoardSide[2]._handCard[0]._card.postInit(); + scene->_gameBoardSide[2]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[0]._card.setPosition(scene->_gameBoardSide[2]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[2]._handCard[0]._card.setFrame(2); + scene->_gameBoardSide[2]._handCard[0]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[0]); + + scene->_gameBoardSide[2]._handCard[1]._cardId = 16; + scene->_gameBoardSide[2]._handCard[1]._card.postInit(); + scene->_gameBoardSide[2]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[1]._card.setPosition(scene->_gameBoardSide[2]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[2]._handCard[1]._card.setFrame(2); + scene->_gameBoardSide[2]._handCard[1]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[1]); + + scene->_gameBoardSide[2]._handCard[2]._cardId = 1; + scene->_gameBoardSide[2]._handCard[2]._card.postInit(); + scene->_gameBoardSide[2]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[2]._card.setPosition(scene->_gameBoardSide[2]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[2]._handCard[2]._card.setFrame(2); + scene->_gameBoardSide[2]._handCard[2]._card.fixPriority(170); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[2]); + + scene->_gameBoardSide[3]._handCard[0]._card.postInit(); + scene->_gameBoardSide[3]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[3]._handCard[0]._card.setPosition(scene->_gameBoardSide[3]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[3]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[3]._handCard[0]._card.setFrame(3); + scene->_gameBoardSide[3]._handCard[0]._card.fixPriority(170); + + scene->_gameBoardSide[3]._handCard[1]._card.postInit(); + scene->_gameBoardSide[3]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[3]._handCard[1]._card.setPosition(scene->_gameBoardSide[3]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[3]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[3]._handCard[1]._card.setFrame(3); + scene->_gameBoardSide[3]._handCard[1]._card.fixPriority(170); + + scene->_gameBoardSide[3]._handCard[2]._card.postInit(); + scene->_gameBoardSide[3]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[3]._handCard[2]._card.setPosition(scene->_gameBoardSide[3]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[3]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[3]._handCard[2]._card.setFrame(3); + scene->_gameBoardSide[3]._handCard[2]._card.fixPriority(170); + + scene->_gameBoardSide[0]._handCard[0]._card.postInit(); + scene->_gameBoardSide[0]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[0]._handCard[0]._card.setPosition(scene->_gameBoardSide[0]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[0]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[0]._handCard[0]._card.setFrame(2); + scene->_gameBoardSide[0]._handCard[0]._card.fixPriority(170); + + scene->_gameBoardSide[0]._handCard[1]._card.postInit(); + scene->_gameBoardSide[0]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[0]._handCard[1]._card.setPosition(scene->_gameBoardSide[0]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[0]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[0]._handCard[1]._card.setFrame(2); + scene->_gameBoardSide[0]._handCard[1]._card.fixPriority(170); + + scene->_gameBoardSide[0]._handCard[2]._card.postInit(); + scene->_gameBoardSide[0]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[0]._handCard[2]._card.setPosition(scene->_gameBoardSide[0]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[0]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[0]._handCard[2]._card.setFrame(2); + scene->_gameBoardSide[0]._handCard[2]._card.fixPriority(170); + + R2_GLOBALS._sceneObjects->draw(); + + scene->actionDisplay(1331, 10, 159, 10, 1, 200, 0, 7, 0, 154, 154); + scene->_animatedCard._card.setPosition(Common::Point(162, 95), 0); + scene->_animatedCard._card.show(); + scene->_aSound2.play(61); + + Common::Point pt(91, 174); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + } + break; + case 2: { + scene->_gameBoardSide[2]._handCard[3]._cardId = 2; + scene->_gameBoardSide[2]._handCard[3]._card.postInit(); + scene->_gameBoardSide[2]._handCard[3]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[3]._card.setPosition(scene->_gameBoardSide[2]._handCard[3]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[3]._card.setStrip(1); + scene->_gameBoardSide[2]._handCard[3]._card.setFrame(2); + scene->_gameBoardSide[2]._handCard[3]._card.fixPriority(170); + + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[3]); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(60); + scene->actionDisplay(1331, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + scene->actionDisplay(1331, 12, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + scene->_gameBoardSide[2]._outpostStation[1]._cardId = 1; + scene->_gameBoardSide[2]._outpostStation[1]._card.postInit(); + scene->_gameBoardSide[2]._outpostStation[1]._card.setVisage(1332); + scene->_gameBoardSide[2]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[1]._stationPos, 0); + scene->_gameBoardSide[2]._outpostStation[1]._card.hide(); + + scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[2]._card._strip); + scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._card._frame); + scene->_animatedCard._card.animate(ANIM_MODE_NONE, NULL); + + scene->_gameBoardSide[2]._handCard[2]._cardId = 0; + scene->_gameBoardSide[2]._handCard[2]._card.remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[2]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._outpostStation[1]._stationPos, this); + } + break; + case 3: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[1]); + scene->_aSound1.play(59); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(60); + scene->actionDisplay(1331, 13, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + scene->_gameBoardSide[2]._outpostStation[1]._cardId = scene->_gameBoardSide[2]._handCard[3]._cardId; + + scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[3]._card._strip); + scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[3]._card._frame); + + scene->_gameBoardSide[2]._handCard[3]._cardId = 0; + scene->_gameBoardSide[2]._handCard[3]._card.remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[3]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._outpostStation[1]._stationPos, this); + } + break; + case 4: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[1]); + scene->_aSound1.play(59); + + scene->_discardPile._cardId = 1; + scene->_discardPile._card.hide(); + + scene->_animatedCard._card.setStrip(5); + scene->_animatedCard._card.setFrame(1); + scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._outpostStation[1]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); + } + break; + case 5: { + scene->_animatedCard._card.hide(); + + scene->_discardPile._card.postInit(); + scene->_discardPile._card.setVisage(1332); + scene->_discardPile._card.setPosition(scene->_discardPile._stationPos, 0); + scene->setAnimationInfo(&scene->_discardPile); + scene->_aSound2.play(61); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(60); + scene->actionDisplay(1331, 14, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + scene->_gameBoardSide[2]._delayCard._card.postInit(); + scene->_gameBoardSide[2]._delayCard._card.setVisage(1332); + scene->_gameBoardSide[2]._delayCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); + scene->_gameBoardSide[2]._delayCard._card.hide(); + + scene->_gameBoardSide[3]._handCard[2]._cardId = 0; + scene->_gameBoardSide[3]._handCard[2].remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[3]._handCard[2]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._delayCard._stationPos, this); + } + break; + case 6: { + scene->_animatedCard._card.hide(); + scene->_gameBoardSide[2]._delayCard._cardId = 21; + scene->setAnimationInfo(&scene->_gameBoardSide[2]._delayCard); + scene->_aSound1.play(57); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(60); + scene->actionDisplay(1331, 15, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + int tmpVal = 15; + int i = -1; + + for (i = 0; i <= 7; i++) { + tmpVal += 29; + + scene->_upperDisplayCard[i].postInit(); + scene->_upperDisplayCard[i].setVisage(1332); + scene->_upperDisplayCard[i].setPosition(Common::Point(tmpVal, 90), 0); + scene->_upperDisplayCard[i].setStrip(3); + scene->_upperDisplayCard[i].fixPriority(190); + + scene->_lowerDisplayCard[i].postInit(); + scene->_lowerDisplayCard[i].setVisage(1332); + scene->_lowerDisplayCard[i].setPosition(Common::Point(tmpVal, 90), 0); + scene->_lowerDisplayCard[i].setStrip(7); + scene->_lowerDisplayCard[i].setFrame(1); + scene->_lowerDisplayCard[i].fixPriority(180); + } + + scene->_upperDisplayCard[0].setFrame(1); + scene->_upperDisplayCard[1].setFrame(3); + scene->_upperDisplayCard[2].setFrame(6); + scene->_upperDisplayCard[3].setFrame(8); + scene->_upperDisplayCard[4].setFrame(9); + scene->_upperDisplayCard[5].setFrame(10); + scene->_upperDisplayCard[6].setFrame(11); + scene->_upperDisplayCard[7].setFrame(12); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(240); + + scene->_upperDisplayCard[0].remove(); + scene->_upperDisplayCard[1].remove(); + scene->_upperDisplayCard[2].remove(); + scene->_upperDisplayCard[3].remove(); + scene->_upperDisplayCard[4].remove(); + scene->_upperDisplayCard[5].remove(); + scene->_upperDisplayCard[6].remove(); + scene->_upperDisplayCard[7].remove(); + + scene->_lowerDisplayCard[0].remove(); + scene->_lowerDisplayCard[1].remove(); + scene->_lowerDisplayCard[2].remove(); + scene->_lowerDisplayCard[3].remove(); + scene->_lowerDisplayCard[4].remove(); + scene->_lowerDisplayCard[5].remove(); + scene->_lowerDisplayCard[6].remove(); + scene->_lowerDisplayCard[7].remove(); + + scene->_discardPile._cardId = scene->_gameBoardSide[2]._delayCard._cardId; + + scene->_gameBoardSide[2]._delayCard._cardId = 0; + scene->_gameBoardSide[2]._delayCard._card.remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); + } + break; + case 7: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_discardPile); + scene->_aSound2.play(61); + + R2_GLOBALS._sceneObjects->draw(); + + scene->_gameBoardSide[2]._delayCard._card.postInit(); + scene->_gameBoardSide[2]._delayCard._card.setVisage(1332); + scene->_gameBoardSide[2]._delayCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); + scene->_gameBoardSide[2]._delayCard._card.hide(); + + scene->_gameBoardSide[3]._handCard[1]._cardId = 0; + scene->_gameBoardSide[3]._handCard[1].remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[3]._handCard[1]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._delayCard._stationPos, this); + } + break; + case 8: { + scene->_animatedCard._card.hide(); + scene->_gameBoardSide[2]._delayCard._cardId = 14; + scene->setAnimationInfo(&scene->_gameBoardSide[2]._delayCard); + scene->_aSound1.play(57); + + R2_GLOBALS._sceneObjects->draw(); + + scene->actionDisplay(1331, 16, 159, 10, 1, 200, 0, 7, 0, 154, 154); + int tmpVal = 72; + int i = -1; + + for (i = 0; i <= 3; i++) { + tmpVal += 29; + scene->_upperDisplayCard[i].postInit(); + scene->_upperDisplayCard[i].setVisage(1332); + scene->_upperDisplayCard[i].setPosition(Common::Point(tmpVal, 71), 0); + scene->_upperDisplayCard[i].setStrip(3); + scene->_upperDisplayCard[i].fixPriority(190); + + scene->_lowerDisplayCard[i].postInit(); + scene->_lowerDisplayCard[i].setVisage(1332); + scene->_lowerDisplayCard[i].setPosition(Common::Point(tmpVal, 71), 0); + scene->_lowerDisplayCard[i].setStrip(7); + scene->_lowerDisplayCard[i].setFrame(1); + scene->_lowerDisplayCard[i].fixPriority(180); + } + + scene->_upperDisplayCard[0].setFrame(2); + scene->_upperDisplayCard[1].setFrame(5); + scene->_upperDisplayCard[2].setFrame(7); + scene->_upperDisplayCard[3].setFrame(15); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(240); + scene->actionDisplay(1331, 17, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + tmpVal = 72; + for (i = 4; i <= 7; i++) { + tmpVal += 29; + + scene->_upperDisplayCard[i].postInit(); + scene->_upperDisplayCard[i].setVisage(1332); + scene->_upperDisplayCard[i].setPosition(Common::Point(tmpVal, 100), 0); + scene->_upperDisplayCard[i].setStrip(4); + scene->_upperDisplayCard[i].fixPriority(190); + + scene->_lowerDisplayCard[i].postInit(); + scene->_lowerDisplayCard[i].setVisage(1332); + scene->_lowerDisplayCard[i].setPosition(Common::Point(tmpVal, 100), 0); + scene->_lowerDisplayCard[i].setStrip(7); + scene->_lowerDisplayCard[i].setFrame(1); + scene->_lowerDisplayCard[i].fixPriority(180); + } + + scene->_upperDisplayCard[4].setFrame(1); + scene->_upperDisplayCard[5].setFrame(5); + scene->_upperDisplayCard[6].setFrame(7); + scene->_upperDisplayCard[7].setFrame(3); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(240); + + scene->_upperDisplayCard[0].remove(); + scene->_upperDisplayCard[1].remove(); + scene->_upperDisplayCard[2].remove(); + scene->_upperDisplayCard[3].remove(); + scene->_upperDisplayCard[4].remove(); + scene->_upperDisplayCard[5].remove(); + scene->_upperDisplayCard[6].remove(); + scene->_upperDisplayCard[7].remove(); + + scene->_lowerDisplayCard[0].remove(); + scene->_lowerDisplayCard[1].remove(); + scene->_lowerDisplayCard[2].remove(); + scene->_lowerDisplayCard[3].remove(); + scene->_lowerDisplayCard[4].remove(); + scene->_lowerDisplayCard[5].remove(); + scene->_lowerDisplayCard[6].remove(); + scene->_lowerDisplayCard[7].remove(); + + scene->_discardPile._cardId = scene->_gameBoardSide[2]._handCard[0]._cardId; + + scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[0]._card._strip); + scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._card._frame); + scene->_animatedCard._card.animate(ANIM_MODE_NONE, NULL); + + scene->_gameBoardSide[2]._handCard[0]._cardId = 0; + scene->_gameBoardSide[2]._handCard[0]._card.remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[0]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._delayCard._stationPos, this); + } + break; + case 9: { + scene->_aSound1.play(58); + scene->_gameBoardSide[2]._delayCard._cardId = 0; + scene->_gameBoardSide[2]._delayCard.remove(); + scene->_animatedCard._card.setStrip(5); + scene->_animatedCard._card.setFrame(1); + scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); + } + break; + case 10: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_discardPile); + scene->_aSound2.play(61); + + R2_GLOBALS._sceneObjects->draw(); + scene->actionDisplay(1331, 18, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + scene->_upperDisplayCard[0].postInit(); + scene->_upperDisplayCard[0].setVisage(1332); + scene->_upperDisplayCard[0].setPosition(Common::Point(131, 71), 0); + scene->_upperDisplayCard[0].fixPriority(190); + scene->_upperDisplayCard[0].setStrip(3); + scene->_upperDisplayCard[0].setFrame(4); + + scene->_lowerDisplayCard[0].postInit(); + scene->_lowerDisplayCard[0].setVisage(1332); + scene->_lowerDisplayCard[0].setPosition(Common::Point(131, 71), 0); + scene->_lowerDisplayCard[0].setStrip(7); + scene->_lowerDisplayCard[0].setFrame(1); + scene->_lowerDisplayCard[0].fixPriority(180); + + scene->_upperDisplayCard[1].postInit(); + scene->_upperDisplayCard[1].setVisage(1332); + scene->_upperDisplayCard[1].setPosition(Common::Point(160, 71), 0); + scene->_upperDisplayCard[1].fixPriority(190); + scene->_upperDisplayCard[1].setStrip(3); + scene->_upperDisplayCard[1].setFrame(16); + + scene->_lowerDisplayCard[1].postInit(); + scene->_lowerDisplayCard[1].setVisage(1332); + scene->_lowerDisplayCard[1].setPosition(Common::Point(160, 71), 0); + scene->_lowerDisplayCard[1].setStrip(7); + scene->_lowerDisplayCard[1].setFrame(1); + scene->_lowerDisplayCard[1].fixPriority(180); + + scene->_upperDisplayCard[2].postInit(); + scene->_upperDisplayCard[2].setVisage(1332); + scene->_upperDisplayCard[2].setPosition(Common::Point(131, 100), 0); + scene->_upperDisplayCard[2].fixPriority(190); + scene->_upperDisplayCard[2].setStrip(4); + scene->_upperDisplayCard[2].setFrame(4); + + scene->_lowerDisplayCard[2].postInit(); + scene->_lowerDisplayCard[2].setVisage(1332); + scene->_lowerDisplayCard[2].setPosition(Common::Point(131, 100), 0); + scene->_lowerDisplayCard[2].setStrip(7); + scene->_lowerDisplayCard[2].setFrame(1); + scene->_lowerDisplayCard[2].fixPriority(180); + + scene->_upperDisplayCard[3].postInit(); + scene->_upperDisplayCard[3].setVisage(1332); + scene->_upperDisplayCard[3].setPosition(Common::Point(160, 100), 0); + scene->_upperDisplayCard[3].fixPriority(190); + scene->_upperDisplayCard[3].setStrip(4); + scene->_upperDisplayCard[3].setFrame(2); + + scene->_lowerDisplayCard[3].postInit(); + scene->_lowerDisplayCard[3].setVisage(1332); + scene->_lowerDisplayCard[3].setPosition(Common::Point(160, 100), 0); + scene->_lowerDisplayCard[3].setStrip(7); + scene->_lowerDisplayCard[3].setFrame(1); + scene->_lowerDisplayCard[3].fixPriority(180); + + R2_GLOBALS._sceneObjects->draw(); + + waitFrames(240); + + scene->_upperDisplayCard[0].remove(); + scene->_upperDisplayCard[1].remove(); + scene->_upperDisplayCard[2].remove(); + scene->_upperDisplayCard[3].remove(); + + scene->_lowerDisplayCard[0].remove(); + scene->_lowerDisplayCard[1].remove(); + scene->_lowerDisplayCard[2].remove(); + scene->_lowerDisplayCard[3].remove(); + + scene->_currentPlayerArrow.setFrame(1); + scene->_currentPlayerArrow.show(); + scene->_currentPlayerArrow.animate(ANIM_MODE_2, NULL); + + R2_GLOBALS._sceneObjects->draw(); + + scene->actionDisplay(1331, 19, 159, 10, 1, 220, 0, 7, 0, 154, 154); + + scene->_currentPlayerArrow.hide(); + + scene->actionDisplay(1331, 20, 159, 10, 1, 220, 0, 7, 0, 154, 154); + scene->actionDisplay(1331, 21, 159, 10, 1, 220, 0, 7, 0, 154, 154); + + scene->_discardPile._cardId = scene->_gameBoardSide[2]._handCard[1]._cardId; + + scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[1]._card._strip); + scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._card._frame); + scene->_animatedCard._card.animate(ANIM_MODE_NONE, NULL); + + scene->_gameBoardSide[2]._handCard[1]._cardId = 0; + scene->_gameBoardSide[2]._handCard[1]._card.remove(); + + scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[1]._stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); + } + break; + case 11: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_discardPile); + scene->_aSound2.play(61); + scene->_animatedCard._card.setStrip(5); + scene->_animatedCard._card.setFrame(1); + scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); + + R2_GLOBALS._sceneObjects->draw(); + + scene->actionDisplay(1331, 22, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + int i = -1; + for (i = 0; i <= 3; i ++) { + scene->_gameBoardSide[3]._handCard[i]._cardId = 0; + scene->_gameBoardSide[3]._handCard[i]._card.remove(); + + scene->_gameBoardSide[2]._handCard[i]._cardId = 0; + scene->_gameBoardSide[2]._handCard[i]._card.remove(); + + scene->_gameBoardSide[0]._handCard[i]._cardId = 0; + scene->_gameBoardSide[0]._handCard[i]._card.remove(); + + scene->_gameBoardSide[1]._handCard[i]._cardId = 0; + scene->_gameBoardSide[1]._handCard[i]._card.remove(); + } + + for (i = 0; i <= 7; i++) { + scene->_gameBoardSide[3]._outpostStation[i]._cardId = 0; + scene->_gameBoardSide[3]._outpostStation[i]._card.remove(); + + scene->_gameBoardSide[2]._outpostStation[i]._cardId = 0; + scene->_gameBoardSide[2]._outpostStation[i]._card.remove(); + + scene->_gameBoardSide[0]._outpostStation[i]._cardId = 0; + scene->_gameBoardSide[0]._outpostStation[i]._card.remove(); + + scene->_gameBoardSide[1]._outpostStation[i]._cardId = 0; + scene->_gameBoardSide[1]._outpostStation[i]._card.remove(); + } + + scene->_gameBoardSide[2]._delayCard._cardId = 0; + scene->_gameBoardSide[2]._delayCard._card.remove(); + + scene->_discardPile._cardId = 0; + scene->_discardPile._card.remove(); + + scene->_stockPile.remove(); + } + // No break on purpose + case 0: + R2_GLOBALS._sceneObjects->draw(); + signal(); + break; + case 12: + scene->suggestInstructions(); + remove(); + break; + default: + break; + } +} + +/** + * Shuffle cards animation + */ +void Scene1337::Action2::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: + scene->_shuffleAnimation._card.postInit(); + scene->_shuffleAnimation._card.setVisage(1332); + scene->_shuffleAnimation._card.setStrip(8); + scene->_shuffleAnimation._card.setFrame(1); + scene->_shuffleAnimation._card.fixPriority(300); + scene->_shuffleAnimation._card.setPosition(Common::Point(156, 108)); + + scene->_discardPile._card.remove(); + scene->_discardPile._cardId = 0; + + scene->_aSound1.play(60); + scene->_shuffleAnimation._card.animate(ANIM_MODE_5, this); + break; + case 1: + scene->_shuffleAnimation._card.setFrame(1); + + scene->_aSound1.play(60); + scene->_shuffleAnimation._card.animate(ANIM_MODE_5, this); + break; + case 2: { + Common::Point pt(156, 108); + NpcMover *mover = new NpcMover(); + scene->_shuffleAnimation._card.addMover(mover, &pt, this); + } + break; + case 3: + scene->_shuffleAnimation._card.remove(); + scene->_stockPile.setup(1332, 5, 1); + scene->_stockPile.setPosition(Common::Point(162, 95)); + scene->_stockPile.setPriority(110); + scene->_stockPile._effect = EFFECT_SHADED; + scene->_stockPile.show(); + scene->_shuffleEndedFl = true; + break; + default: + break; + } +} + +/** + * Deal cards + */ +void Scene1337::Action3::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + scene->_animatedCard._card.setPosition(Common::Point(162, 95), 0); + + switch (_actionIndex++) { + case 0: { + scene->_animatedCard._card._moveDiff = Common::Point(30, 30); + scene->_animatedCard._card.setVisage(1332); + scene->_animatedCard._card.setStrip(5); + scene->_animatedCard._card.setFrame(1); + scene->_animatedCard._card.fixPriority(400); + scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); + scene->_aSound2.play(61); + + Common::Point pt(283, 146); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_animatedCard._card.show(); + scene->_gameBoardSide[1]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 1: { + scene->_gameBoardSide[1]._handCard[0]._card.postInit(); + scene->_gameBoardSide[1]._handCard[0]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[1]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[1]._handCard[0]._card.setPosition(scene->_gameBoardSide[1]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[1]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[1]._handCard[0]._card.setFrame(4); + scene->_gameBoardSide[1]._handCard[0]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(10, 174); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[2]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 2: { + scene->_gameBoardSide[2]._handCard[0]._card.postInit(); + scene->_gameBoardSide[2]._handCard[0]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[2]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[0]._card.setPosition(scene->_gameBoardSide[2]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[0]._card.fixPriority(170); + if (scene->_gameBoardSide[2]._handCard[0]._cardId > 25) { + scene->_gameBoardSide[2]._handCard[0]._card.setStrip(4); + scene->_gameBoardSide[2]._handCard[0]._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._cardId - 25); + } else if (scene->_gameBoardSide[2]._handCard[0]._cardId > 9) { + scene->_gameBoardSide[2]._handCard[0]._card.setStrip(3); + scene->_gameBoardSide[2]._handCard[0]._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._cardId - 9); + } else { + scene->_gameBoardSide[2]._handCard[0]._card.setStrip(2); + scene->_gameBoardSide[2]._handCard[0]._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._cardId); + } + scene->_aSound2.play(61); + + Common::Point pt(14, 14); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[3]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 3: { + scene->_gameBoardSide[3]._handCard[0]._card.postInit(); + scene->_gameBoardSide[3]._handCard[0]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[3]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[3]._handCard[0]._card.setPosition(scene->_gameBoardSide[3]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[3]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[3]._handCard[0]._card.setFrame(3); + scene->_gameBoardSide[3]._handCard[0]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(280, 5); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[0]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 4: { + scene->_gameBoardSide[0]._handCard[0]._card.postInit(); + scene->_gameBoardSide[0]._handCard[0]._card._moveDiff = Common::Point(30,30); + scene->_gameBoardSide[0]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[0]._handCard[0]._card.setPosition(scene->_gameBoardSide[0]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[0]._handCard[0]._card.setStrip(5); + scene->_gameBoardSide[0]._handCard[0]._card.setFrame(1); + scene->_gameBoardSide[0]._handCard[0]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(283, 124); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[1]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 5: { + scene->_gameBoardSide[1]._handCard[1]._card.postInit(); + scene->_gameBoardSide[1]._handCard[1]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[1]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[1]._handCard[1]._card.setPosition(scene->_gameBoardSide[1]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[1]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[1]._handCard[1]._card.setFrame(4); + scene->_gameBoardSide[1]._handCard[1]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(37, 174); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[2]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 6: { + scene->_gameBoardSide[2]._handCard[1]._card.postInit(); + scene->_gameBoardSide[2]._handCard[1]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[2]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[1]._card.setPosition(scene->_gameBoardSide[2]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[1]._card.fixPriority(170); + + if (scene->_gameBoardSide[2]._handCard[1]._cardId > 25) { + scene->_gameBoardSide[2]._handCard[1]._card.setStrip(4); + scene->_gameBoardSide[2]._handCard[1]._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._cardId - 25); + } else if (scene->_gameBoardSide[2]._handCard[1]._cardId > 9) { + scene->_gameBoardSide[2]._handCard[1]._card.setStrip(3); + scene->_gameBoardSide[2]._handCard[1]._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._cardId - 9); + } else { + scene->_gameBoardSide[2]._handCard[1]._card.setStrip(2); + scene->_gameBoardSide[2]._handCard[1]._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._cardId); + } + + scene->_aSound2.play(61); + + Common::Point pt(14, 36); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[3]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 7: { + scene->_gameBoardSide[3]._handCard[1]._card.postInit(); + scene->_gameBoardSide[3]._handCard[1]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[3]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[3]._handCard[1]._card.setPosition(scene->_gameBoardSide[3]._handCard[1]._stationPos); + scene->_gameBoardSide[3]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[3]._handCard[1]._card.setFrame(3); + scene->_gameBoardSide[3]._handCard[1]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(253, 5); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[0]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 8: { + scene->_gameBoardSide[0]._handCard[1]._card.postInit(); + scene->_gameBoardSide[0]._handCard[1]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[0]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[0]._handCard[1]._card.setPosition(scene->_gameBoardSide[0]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[0]._handCard[1]._card.setStrip(5); + scene->_gameBoardSide[0]._handCard[1]._card.setFrame(1); + scene->_gameBoardSide[0]._handCard[1]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(283, 102); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[1]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 9: { + scene->_gameBoardSide[1]._handCard[2]._card.postInit(); + scene->_gameBoardSide[1]._handCard[2]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[1]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[1]._handCard[2]._card.setPosition(scene->_gameBoardSide[1]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[1]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[1]._handCard[2]._card.setFrame(4); + scene->_gameBoardSide[1]._handCard[2]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(64, 174); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[2]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 10: { + scene->_gameBoardSide[2]._handCard[2]._card.postInit(); + scene->_gameBoardSide[2]._handCard[2]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[2]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[2]._handCard[2]._card.setPosition(scene->_gameBoardSide[2]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[2]._handCard[2]._card.fixPriority(170); + + if (scene->_gameBoardSide[2]._handCard[2]._cardId > 25) { + scene->_gameBoardSide[2]._handCard[2]._card.setStrip(4); + scene->_gameBoardSide[2]._handCard[2]._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._cardId - 25); + } else if (scene->_gameBoardSide[2]._handCard[2]._cardId > 9) { + scene->_gameBoardSide[2]._handCard[2]._card.setStrip(3); + scene->_gameBoardSide[2]._handCard[2]._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._cardId - 9); + } else { + scene->_gameBoardSide[2]._handCard[2]._card.setStrip(2); + scene->_gameBoardSide[2]._handCard[2]._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._cardId); + } + + scene->_aSound2.play(61); + + Common::Point pt(14, 58); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[3]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 11: { + scene->_gameBoardSide[3]._handCard[2]._card.postInit(); + scene->_gameBoardSide[3]._handCard[2]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[3]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[3]._handCard[2]._card.setPosition(scene->_gameBoardSide[3]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[3]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[3]._handCard[2]._card.setFrame(3); + scene->_gameBoardSide[3]._handCard[2]._card.fixPriority(170); + scene->_aSound2.play(61); + + Common::Point pt(226, 5); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + + scene->_gameBoardSide[0]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + } + break; + case 12: + scene->_gameBoardSide[0]._handCard[2]._card.postInit(); + scene->_gameBoardSide[0]._handCard[2]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[0]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[0]._handCard[2]._card.setPosition(scene->_gameBoardSide[0]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[0]._handCard[2]._card.setStrip(5); + scene->_gameBoardSide[0]._handCard[2]._card.setFrame(1); + scene->_gameBoardSide[0]._handCard[2]._card.fixPriority(170); + scene->_animatedCard._card.hide(); + default: + break; + } + + if (_actionIndex > 12) { + scene->_currentPlayerNumb = 0; + R2_GLOBALS._sceneObjects->draw(); + scene->actionDisplay(1330, 0, 159, 10, 1, 200, 0, 7, 0, 154, 154); + scene->handleNextTurn(); + } else if (_actionIndex >= 1) { + scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; + scene->_cardsAvailableNumb--; + } +} + +/** + * Action used to handle the other players' turn + */ +void Scene1337::Action4::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: + if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._cardId == 0) + && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { + if (scene->_cardsAvailableNumb < 0) + scene->shuffleCards(); + scene->_animatedCard._card.setPosition(Common::Point(162, 95), 0); + scene->_animatedCard._card.show(); + scene->_aSound2.play(61); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card1Pos, this); + + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; + scene->_cardsAvailableNumb--; + + if (scene->_cardsAvailableNumb < 0) + scene->_stockPile.remove(); + } else { + // Self call, forcing next actionIndex + signal(); + } + break; + case 1: + if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card1Pos.x) + && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card1Pos.y) ) { + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.postInit(); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setVisage(1332); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._stationPos, 0); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setStrip(1); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.fixPriority(170); + } + + if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) + scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]); + + scene->_animatedCard._card.hide(); + if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._cardId == 0) + && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { + if (scene->_cardsAvailableNumb < 0) + scene->shuffleCards(); + scene->_animatedCard._card.setPosition(Common::Point(162, 95)); + scene->_animatedCard._card.show(); + + scene->_aSound2.play(61); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card2Pos, this); + + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; + scene->_cardsAvailableNumb--; + if (scene->_cardsAvailableNumb < 0) + scene->_stockPile.remove(); + } else + signal(); + break; + case 2: + if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card2Pos.x) + && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card2Pos.y) ) { + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.postInit(); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setVisage(1332); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._stationPos, 0); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setStrip(1); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.fixPriority(170); + } + + if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) + scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]); + + scene->_animatedCard._card.hide(); + if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._cardId == 0) + && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { + if (scene->_cardsAvailableNumb < 0) + scene->shuffleCards(); + scene->_animatedCard._card.setPosition(Common::Point(162, 95)); + scene->_animatedCard._card.show(); + + scene->_aSound2.play(61); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card3Pos, this); + + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; + scene->_cardsAvailableNumb--; + if (scene->_cardsAvailableNumb < 0) + scene->_stockPile.remove(); + } else + signal(); + break; + case 3: + if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card3Pos.x) + && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card3Pos.y) ) { + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.postInit(); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setVisage(1332); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._stationPos, 0); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setStrip(1); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.fixPriority(170); + } + + if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) + scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]); + + scene->_animatedCard._card.hide(); + if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._cardId == 0) + && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { + if (scene->_cardsAvailableNumb < 0) + scene->shuffleCards(); + scene->_animatedCard._card.setPosition(Common::Point(162, 95)); + scene->_animatedCard._card.show(); + + scene->_aSound2.play(61); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card4Pos, this); + + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; + scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; + scene->_cardsAvailableNumb--; + if (scene->_cardsAvailableNumb < 0) + scene->_stockPile.remove(); + } else + signal(); + break; + case 4: + if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card4Pos.x) + && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card4Pos.y) ) { + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.postInit(); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card._moveDiff = Common::Point(30, 30); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setVisage(1332); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._stationPos, 0); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setStrip(1); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); + scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.fixPriority(170); + } + + if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) + scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]); + + scene->_animatedCard._card.hide(); + switch (scene->_currentPlayerNumb) { + case 0: + scene->handlePlayer0(); + break; + case 1: + scene->handlePlayer1(); + break; + case 2: + scene->handleAutoplayPlayer2(); + break; + case 3: + scene->handlePlayer3(); + break; + default: + break; + } + break; + default: + break; + } +} + +/** + * Animations for discarding a card + */ +void Scene1337::Action5::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard1->_cardId; + scene->_currentDiscardIndex--; + if (!g_globals->_sceneObjects->contains(&scene->_discardPile._card)) { + // The first discarded card makes the pile appear + scene->_discardPile._card.postInit(); + scene->_discardPile._card.hide(); + scene->_discardPile._card.setVisage(1332); + scene->_discardPile._card.setPosition(scene->_discardPile._stationPos, 0); + scene->_discardPile._card.fixPriority(170); + } + + scene->_discardPile._cardId = scene->_actionCard1->_cardId; + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + + if (scene->_actionCard1 == &scene->_selectedCard) { + scene->setCursorData(5, 1, 4); + scene->subC4CEC(); + } + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + Common::Point pt(128, 95); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &pt, this); + } + break; + case 1: + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(&scene->_discardPile); + scene->_aSound2.play(61); + scene->handleNextTurn(); + break; + default: + break; + } +} + +/** + * Animations for playing a platform card + */ +void Scene1337::Action6::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_actionCard2->_cardId = 1; + scene->_actionCard2->_card.postInit(); + scene->_actionCard2->_card.hide(); + scene->_actionCard2->_card.setVisage(1332); + scene->_actionCard2->_card.setPosition(scene->_actionCard2->_stationPos); + scene->_actionCard2->_card.fixPriority(170); + + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 1: + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(59); + if (scene->_actionCard1 == &scene->_selectedCard) { + scene->setCursorData(5, 1, 4); + scene->subC4CEC(); + } + scene->handleNextTurn(); + break; + default: + break; + } +} + +/** + * Upgrade platform to station by playing a station card on top of it + */ +void Scene1337::Action7::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; + + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 1: + if (scene->_actionCard1 == &scene->_selectedCard) { + scene->setCursorData(5, 1, 4); + scene->subC4CEC(); + } + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(59); + scene->_discardedPlatformCard._cardId = 1; + scene->_discardedPlatformCard._stationPos = scene->_actionCard2->_stationPos; + scene->_discardedPlatformCard._card.postInit(); + scene->_discardedPlatformCard._card.hide(); + scene->_discardedPlatformCard._card._flags = OBJFLAG_HIDING; + + scene->discardCard(&scene->_discardedPlatformCard); + break; + default: + break; + } +} + +// Remove a delay card +void Scene1337::Action8::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; + scene->_currentDiscardIndex--; + + scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; + scene->_actionCard1->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 1: + scene->_animatedCard._card.hide(); + + if (scene->_actionCard1 == &scene->_selectedCard) { + scene->setCursorData(5, 1, 4); + scene->subC4CEC(); + } + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(58); + scene->discardCard(scene->_actionCard2); + break; + default: + break; + } +} + +// Play delay card +void Scene1337::Action9::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; + scene->_actionCard2->_card.postInit(); + scene->_actionCard2->_card.hide(); + scene->_actionCard2->_card.setVisage(1332); + scene->_actionCard2->_card.setPosition(scene->_actionCard2->_stationPos, 0); + scene->_actionCard2->_card.fixPriority(170); + + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 1: + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(57); + + if (scene->_actionCard1 == &scene->_selectedCard) { + scene->setCursorData(5, 1, 4); + scene->subC4CEC(); + } + + scene->handleNextTurn(); + break; + default: + break; + } +} + +// Play a card on the central outpost. +// This card is either a counter-trick card or a meteor card +void Scene1337::Action10::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_actionCard3->_card.postInit(); + scene->_actionCard3->_card.hide(); + scene->_actionCard3->_card.setVisage(1332); + scene->_actionCard3->_card.setPosition(scene->_actionCard3->_stationPos, 0); + scene->_actionCard3->_card.fixPriority(170); + scene->_actionCard3->_cardId = scene->_actionCard1->_cardId; + + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + + if (scene->_actionCard1 == &scene->_selectedCard) { + scene->setCursorData(5, 1, 4); + scene->subC4CEC(); + } + + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard3->_stationPos, this); + } + break; + case 1: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(scene->_actionCard3); + scene->_aSound1.play(57); + + bool found = false; + int indexFound = -1; + + switch (scene->_actionPlayerIdx) { + case 0: + for (indexFound = 0; indexFound < 3; indexFound++) { + // Check for the presence of an interceptor card + if (scene->_gameBoardSide[0]._handCard[indexFound]._cardId == 29) { + found = true; + break; + } + } + break; + case 1: + for (indexFound = 0; indexFound < 3; indexFound++) { + // Check for the presence of an interceptor card + if (scene->_gameBoardSide[1]._handCard[indexFound]._cardId == 29) { + found = true; + break; + } + } + break; + case 2: + for (indexFound = 0; indexFound < 3; indexFound++) { + // Check for the presence of an interceptor card + if (scene->_gameBoardSide[2]._handCard[indexFound]._cardId == 29) { + found = true; + break; + } + } + break; + case 3: + for (indexFound = 0; indexFound < 3; indexFound++) { + // Check for the presence of an interceptor card + if (scene->_gameBoardSide[3]._handCard[indexFound]._cardId == 29) { + found = true; + break; + } + } + break; + default: + break; + } + + bool found2 = false; + + if (found) { + switch (scene->_actionPlayerIdx) { + case 0: + scene->playInterceptorCard(&scene->_gameBoardSide[0]._handCard[indexFound], scene->_actionCard3); + found2 = true; + break; + case 1: + scene->playInterceptorCard(&scene->_gameBoardSide[1]._handCard[indexFound], scene->_actionCard3); + found2 = true; + break; + case 2: + scene->subC4CD2(); + if (MessageDialog::show(USE_INTERCEPTOR, NO_MSG, YES_MSG) == 0) + scene->subC4CEC(); + else { + scene->playInterceptorCard(&scene->_gameBoardSide[2]._handCard[indexFound], scene->_actionCard3); + found2 = true; + } + break; + case 3: + scene->playInterceptorCard(&scene->_gameBoardSide[3]._handCard[indexFound], scene->_actionCard3); + found2 = true; + break; + default: + break; + } + } + + if (found2) + break; + + if (scene->_actionPlayerIdx == 2) { + int stationCount = 0; + for (int i = 0; i <= 7; i++) { + if (scene->_gameBoardSide[2]._outpostStation[i]._cardId != 0) + ++stationCount; + } + + if (stationCount <= 1) { + for (int i = 0; i <= 7; i++) { + if (scene->_gameBoardSide[2]._outpostStation[i]._cardId != 0) { + scene->_actionCard2 = &scene->_gameBoardSide[2]._outpostStation[i]; + break; + } + } + } else { + scene->subC4CD2(); + + found2 = false; + while (!found2) { + scene->actionDisplay(1330, 130, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + // Wait for a mouse or keypress + Event event; + while (!g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !g_vm->shouldQuit()) { + g_globals->_scenePalette.signalListeners(); + R2_GLOBALS._sceneObjects->draw(); + g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); + } + + scene->_selectedCard._stationPos = event.mousePos; + + for (int i = 0; i <= 7; i++) { + if (scene->_gameBoardSide[2]._outpostStation[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[2]._outpostStation[i]._cardId != 0)) { + scene->_actionCard2 = &scene->_gameBoardSide[2]._outpostStation[i]; + found2 = true; + break; + } + } + } + scene->subC4CEC(); + } + } + + scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; + scene->_currentDiscardIndex--; + scene->_actionCard2->_cardId = 0; + scene->_actionCard2->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard2->_stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard3->_stationPos, this); + } + break; + case 2: + scene->_animatedCard._card.hide(); + scene->discardCard(scene->_actionCard3); + break; + default: + break; + } +} + +// Use Thief card (#25) and pick a card from the opponent +void Scene1337::Action11::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_actionCard2->_card.postInit(); + scene->_actionCard2->_card.hide(); + scene->_actionCard2->_card.setVisage(1332); + scene->_actionCard2->_card.setPosition(scene->_actionCard2->_stationPos, 0); + scene->_actionCard2->_card.fixPriority(170); + scene->_actionCard2->_cardId = 25; + + if (scene->_actionPlayerIdx == 2) { + scene->_animatedCard._card.setPosition(scene->_actionCard2->_stationPos, 0); + scene->setCursorData(5, 1, 4); + } else { + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + } + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 1: { + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(57); + + bool found = false; + bool noAction = true; + + int i = -1; + + switch (scene->_actionVictimIdx) { + case 0: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[0]._handCard[i]._cardId == 27) { + found = true; + break; + } + } + + if ((found) && (scene->getRandomCardFromHand(scene->_actionPlayerIdx) != -1)) { + scene->_actionCard1 = &scene->_gameBoardSide[0]._handCard[i]; + scene->_actionCard2 = &scene->_gameBoardSide[0]._emptyStationPos; + if (scene->_actionPlayerIdx != 0) { + int tmpVal = scene->getRandomCardFromHand(scene->_actionPlayerIdx); + scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionPlayerIdx]._handCard[tmpVal]; + } + scene->_actionItem.setAction(&scene->_action12); + noAction = false; + } + break; + case 1: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[1]._handCard[i]._cardId == 27) { + found = true; + break; + } + } + + if ((found) && (scene->getRandomCardFromHand(scene->_actionPlayerIdx) != -1)) { + scene->_actionCard1 = &scene->_gameBoardSide[1]._handCard[i]; + scene->_actionCard2 = &scene->_gameBoardSide[1]._emptyStationPos; + if (scene->_actionPlayerIdx != 1) { + int tmpVal = scene->getRandomCardFromHand(scene->_actionPlayerIdx); + scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionPlayerIdx]._handCard[tmpVal]; + } + scene->_actionItem.setAction(&scene->_action12); + noAction = false; + } + break; + case 2: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[2]._handCard[i]._cardId == 27) { + found = true; + break; + } + } + + if ((found) && (scene->getRandomCardFromHand(scene->_actionPlayerIdx) != -1)) { + scene->subC4CD2(); + if (MessageDialog::show(USE_DOUBLE_AGENT, NO_MSG, YES_MSG) == 0) + scene->subC4CEC(); + else { + scene->subC4CEC(); + scene->_actionCard1 = &scene->_gameBoardSide[2]._handCard[i]; + scene->_actionCard2 = &scene->_gameBoardSide[2]._emptyStationPos; + if (scene->_actionPlayerIdx != 2) { + int tmpVal = scene->getRandomCardFromHand(scene->_actionPlayerIdx); + scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionPlayerIdx]._handCard[tmpVal]; + } + scene->_actionItem.setAction(&scene->_action12); + noAction = false; + } + } + break; + case 3: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[3]._handCard[i]._cardId == 27) { + found = true; + break; + } + } + + if ((found) && (scene->getRandomCardFromHand(scene->_actionPlayerIdx) != -1)) { + scene->_actionCard1 = &scene->_gameBoardSide[3]._handCard[i]; + scene->_actionCard2 = &scene->_gameBoardSide[3]._emptyStationPos; + if (scene->_actionPlayerIdx != 3) { + int tmpVal = scene->getRandomCardFromHand(scene->_actionPlayerIdx); + scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionPlayerIdx]._handCard[tmpVal]; + } + scene->_actionItem.setAction(&scene->_action12); + noAction = false; + } + break; + default: + break; + } + + if (!noAction) + break; + + if (scene->_actionPlayerIdx == 2) { + int count = 0; + if (scene->_actionVictimIdx != 2) { + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[scene->_actionVictimIdx]._handCard[i]._cardId != 0) + ++count; + } + } + + if (count > 1) { + scene->subC4CD2(); + + found = false; + while (!found) { + switch (scene->_actionVictimIdx) { + case 0: + scene->actionDisplay(1330, 131, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 1: + scene->actionDisplay(1330, 132, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 3: + scene->actionDisplay(1330, 133, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + + Event event; + while (!g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !g_vm->shouldQuit()) { + g_globals->_scenePalette.signalListeners(); + R2_GLOBALS._sceneObjects->draw(); + g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); + } + + scene->_selectedCard._stationPos = event.mousePos; + + found = false; + + if (scene->_actionVictimIdx != 2) { + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[scene->_actionVictimIdx]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[scene->_actionVictimIdx]._handCard[i]._cardId != 0)) { + scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionVictimIdx]._handCard[i]; + found = true; + break; + } + } + } + } // while + scene->_displayHelpFl = true; + scene->subC4CEC(); + } else if (scene->_actionVictimIdx != 2) { + int tmpVal = scene->getRandomCardFromHand(scene->_actionVictimIdx); + scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionVictimIdx]._handCard[tmpVal]; + } + } + + scene->_actionCard1->_card.postInit(); + scene->_actionCard1->_card.hide(); + scene->_actionCard1->_card.setVisage(1332); + scene->_actionCard1->_card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_actionCard1->_card.fixPriority(170); + scene->_actionCard1->_card.setStrip2(1); + scene->_actionCard1->_cardId = scene->_actionCard3->_cardId; + + scene->_actionCard3->_cardId = 0; + scene->_actionCard3->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard3->_stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard1->_stationPos, this); + } + break; + case 2: + scene->_animatedCard._card.hide(); + switch (scene->_actionPlayerIdx) { + case 0: + scene->_actionCard1->_card.setFrame2(2); + scene->_actionCard1->_card.show(); + break; + case 1: + scene->_actionCard1->_card.setFrame2(4); + scene->_actionCard1->_card.show(); + break; + case 3: + scene->_actionCard1->_card.setFrame2(3); + scene->_actionCard1->_card.show(); + break; + default: + scene->setAnimationInfo(scene->_actionCard1); + break; + } + + scene->_currentPlayerNumb--; + scene->_showPlayerTurn = false; + scene->discardCard(scene->_actionCard2); + break; + default: + break; + } +} + +// Pick a card in opponent hand +void Scene1337::Action12::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: + signal(); + break; + case 1: { + scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; + scene->_currentDiscardIndex++; + scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 2: + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(58); + if (scene->_actionVictimIdx == 2) { + int count = 0; + int i = -1; + switch (scene->_actionPlayerIdx) { + case 0: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[0]._handCard[i]._cardId != 0) + ++count; + } + break; + case 1: + for (i = 0; i <= 3; i++) { + // The original game was counting in the hand of player 3, which is obviously wrong + if (scene->_gameBoardSide[1]._handCard[i]._cardId != 0) + ++count; + } + break; + case 3: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[3]._handCard[i]._cardId != 0) + ++count; + } + break; + default: + break; + } + + if (count > 1) { + scene->subC4CD2(); + + bool found = false; + + while (!found) { + switch (scene->_actionPlayerIdx) { + case 0: + scene->actionDisplay(1330, 131, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 1: + scene->actionDisplay(1330, 132, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 3: + scene->actionDisplay(1330, 133, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + + Event event; + while (!g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !g_vm->shouldQuit()) { + g_globals->_scenePalette.signalListeners(); + R2_GLOBALS._sceneObjects->draw(); + g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); + } + + scene->_selectedCard._stationPos = event.mousePos; + + switch (scene->_actionPlayerIdx) { + case 0: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[0]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[0]._handCard[i]._cardId != 0)) { + found = true; + scene->_actionCard3 = &scene->_gameBoardSide[0]._handCard[i]; + break; + } + } + break; + case 1: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[1]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[1]._handCard[i]._cardId != 0)) { + found = true; + scene->_actionCard3 = &scene->_gameBoardSide[1]._handCard[i]; + break; + } + } + break; + case 3: + for (i = 0; i <= 3; i++) { + if (scene->_gameBoardSide[3]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[3]._handCard[i]._cardId != 0)) { + found = true; + scene->_actionCard3 = &scene->_gameBoardSide[3]._handCard[i]; + break; + } + } + break; + default: + break; + } + } + scene->subC4CEC(); + } else { + switch (scene->_actionPlayerIdx) { + case 0: + scene->_actionCard3 = &scene->_gameBoardSide[0]._handCard[scene->getRandomCardFromHand(0)]; + break; + case 1: + scene->_actionCard3 = &scene->_gameBoardSide[1]._handCard[scene->getRandomCardFromHand(1)]; + break; + case 3: + scene->_actionCard3 = &scene->_gameBoardSide[3]._handCard[scene->getRandomCardFromHand(3)]; + break; + default: + break; + } + } + + scene->_actionCard1->_card.postInit(); + scene->_actionCard1->_card.hide(); + scene->_actionCard1->_card.setVisage(1332); + scene->_actionCard1->_card.setPosition(scene->_actionCard1->_stationPos); + scene->_actionCard1->_card.fixPriority(170); + scene->_actionCard1->_card.setStrip2(1); + scene->_actionCard1->_cardId = scene->_actionCard3->_cardId; + + scene->_actionCard3->_cardId = 0; + scene->_actionCard3->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard3->_stationPos); + scene->_animatedCard._card.show(); + scene->_aSound1.play(57); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard1->_stationPos, this); + } + break; + case 3: + scene->_animatedCard._card.hide(); + switch (scene->_actionVictimIdx) { + case 0: + scene->_actionCard1->_card.setFrame2(2); + scene->_actionCard1->_card.show(); + break; + case 1: + scene->_actionCard1->_card.setFrame2(4); + scene->_actionCard1->_card.show(); + break; + case 3: + scene->_actionCard1->_card.setFrame2(3); + scene->_actionCard1->_card.show(); + break; + default: + scene->setAnimationInfo(scene->_actionCard1); + break; + } + scene->discardCard(scene->_actionCard2); + break; + default: + break; + } +} + +// Handle the animations of the interceptor card +void Scene1337::Action13::signal() { + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + + switch (_actionIndex++) { + case 0: { + scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; + scene->_currentDiscardIndex--; + + scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; + + scene->_actionCard1->_cardId = 0; + scene->_actionCard1->_card.remove(); + + scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); + scene->_animatedCard._card.show(); + + NpcMover *mover = new NpcMover(); + scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); + } + break; + case 1: + scene->_animatedCard._card.hide(); + scene->setAnimationInfo(scene->_actionCard2); + scene->_aSound1.play(58); + signal(); + break; + case 2: + scene->discardCard(scene->_actionCard2); + break; + default: + break; + } +} + +void Scene1337::postInit(SceneObjectList *OwnerList) { +// In the original, may be found in subPostInit. +// Without it, enableControl asserts + loadScene(1330); + R2_GLOBALS._uiElements._active = false; + SceneExt::postInit(); +// + + // Hide the user interface + BF_GLOBALS._interfaceY = SCREEN_HEIGHT; + R2_GLOBALS._uiElements._visible = false; + + R2_GLOBALS._player.enableControl(); + R2_GLOBALS._player._canWalk = false; + R2_GLOBALS._player._uiEnabled = false; + + _delayedFunction = nullptr; + + _actionCard1 = nullptr; + _actionCard2 = nullptr; + _actionCard3 = nullptr; + + _gameBoardSide[2]._handCard[0]._stationPos = Common::Point(10, 174); + _gameBoardSide[2]._handCard[1]._stationPos = Common::Point(37, 174); + _gameBoardSide[2]._handCard[2]._stationPos = Common::Point(64, 174); + _gameBoardSide[2]._handCard[3]._stationPos = Common::Point(91, 174); + + _gameBoardSide[2]._outpostStation[0]._stationPos = Common::Point(119, 174); + _gameBoardSide[2]._outpostStation[1]._stationPos = Common::Point(119, 148); + _gameBoardSide[2]._outpostStation[2]._stationPos = Common::Point(119, 122); + _gameBoardSide[2]._outpostStation[3]._stationPos = Common::Point(145, 122); + _gameBoardSide[2]._outpostStation[4]._stationPos = Common::Point(171, 122); + _gameBoardSide[2]._outpostStation[5]._stationPos = Common::Point(171, 148); + _gameBoardSide[2]._outpostStation[6]._stationPos = Common::Point(171, 174); + _gameBoardSide[2]._outpostStation[7]._stationPos = Common::Point(145, 174); + + _gameBoardSide[2]._delayCard._stationPos = Common::Point(199, 174); + + _gameBoardSide[2]._emptyStationPos._stationPos = Common::Point(145, 148); + + _gameBoardSide[2]._card1Pos = Common::Point(10, 174); + _gameBoardSide[2]._card2Pos = Common::Point(37, 174); + _gameBoardSide[2]._card3Pos = Common::Point(64, 174); + _gameBoardSide[2]._card4Pos = Common::Point(91, 174); + _gameBoardSide[2]._frameNum = 2; + + _gameBoardSide[3]._handCard[0]._stationPos = Common::Point(14, 14); + _gameBoardSide[3]._handCard[1]._stationPos = Common::Point(14, 36); + _gameBoardSide[3]._handCard[2]._stationPos = Common::Point(14, 58); + _gameBoardSide[3]._handCard[3]._stationPos = Common::Point(14, 80); + + _gameBoardSide[3]._outpostStation[0]._stationPos = Common::Point(37, 66); + _gameBoardSide[3]._outpostStation[1]._stationPos = Common::Point(63, 66); + _gameBoardSide[3]._outpostStation[2]._stationPos = Common::Point(89, 66); + _gameBoardSide[3]._outpostStation[3]._stationPos = Common::Point(89, 92); + _gameBoardSide[3]._outpostStation[4]._stationPos = Common::Point(89, 118); + _gameBoardSide[3]._outpostStation[5]._stationPos = Common::Point(63, 118); + _gameBoardSide[3]._outpostStation[6]._stationPos = Common::Point(37, 118); + _gameBoardSide[3]._outpostStation[7]._stationPos = Common::Point(37, 92); + + _gameBoardSide[3]._delayCard._stationPos = Common::Point(37, 145); + + _gameBoardSide[3]._emptyStationPos._stationPos = Common::Point(63, 92); + + _gameBoardSide[3]._card1Pos = Common::Point(14, 14); + _gameBoardSide[3]._card2Pos = Common::Point(14, 36); + _gameBoardSide[3]._card3Pos = Common::Point(14, 58); + _gameBoardSide[3]._card4Pos = Common::Point(14, 80); + _gameBoardSide[3]._frameNum = 3; + + _gameBoardSide[0]._handCard[0]._stationPos = Common::Point(280, 5); + _gameBoardSide[0]._handCard[1]._stationPos = Common::Point(253, 5); + _gameBoardSide[0]._handCard[2]._stationPos = Common::Point(226, 5); + _gameBoardSide[0]._handCard[3]._stationPos = Common::Point(199, 5); + + _gameBoardSide[0]._outpostStation[0]._stationPos = Common::Point(171, 16); + _gameBoardSide[0]._outpostStation[1]._stationPos = Common::Point(171, 42); + _gameBoardSide[0]._outpostStation[2]._stationPos = Common::Point(171, 68); + _gameBoardSide[0]._outpostStation[3]._stationPos = Common::Point(145, 68); + _gameBoardSide[0]._outpostStation[4]._stationPos = Common::Point(119, 68); + _gameBoardSide[0]._outpostStation[5]._stationPos = Common::Point(119, 42); + _gameBoardSide[0]._outpostStation[6]._stationPos = Common::Point(119, 16); + _gameBoardSide[0]._outpostStation[7]._stationPos = Common::Point(145, 16); + + _gameBoardSide[0]._delayCard._stationPos = Common::Point(91, 16); + + _gameBoardSide[0]._emptyStationPos._stationPos = Common::Point(145, 42); + + _gameBoardSide[0]._card1Pos = Common::Point(280, 5); + _gameBoardSide[0]._card2Pos = Common::Point(253, 5); + _gameBoardSide[0]._card3Pos = Common::Point(226, 5); + _gameBoardSide[0]._card4Pos = Common::Point(199, 5); + _gameBoardSide[0]._frameNum = 2; + + _gameBoardSide[1]._handCard[0]._stationPos = Common::Point(283, 146); + _gameBoardSide[1]._handCard[1]._stationPos = Common::Point(283, 124); + _gameBoardSide[1]._handCard[2]._stationPos = Common::Point(283, 102); + _gameBoardSide[1]._handCard[3]._stationPos = Common::Point(283, 80); + + _gameBoardSide[1]._outpostStation[0]._stationPos = Common::Point(253, 122); + _gameBoardSide[1]._outpostStation[1]._stationPos = Common::Point(227, 122); + _gameBoardSide[1]._outpostStation[2]._stationPos = Common::Point(201, 122); + _gameBoardSide[1]._outpostStation[3]._stationPos = Common::Point(201, 96); + _gameBoardSide[1]._outpostStation[4]._stationPos = Common::Point(201, 70); + _gameBoardSide[1]._outpostStation[5]._stationPos = Common::Point(227, 70); + _gameBoardSide[1]._outpostStation[6]._stationPos = Common::Point(253, 70); + _gameBoardSide[1]._outpostStation[7]._stationPos = Common::Point(253, 96); + + _gameBoardSide[1]._delayCard._stationPos = Common::Point(253, 43); + + _gameBoardSide[1]._emptyStationPos._stationPos = Common::Point(227, 96); + + _gameBoardSide[1]._card1Pos = Common::Point(283, 146); + _gameBoardSide[1]._card2Pos = Common::Point(283, 124); + _gameBoardSide[1]._card3Pos = Common::Point(283, 102); + _gameBoardSide[1]._card4Pos = Common::Point(283, 80); + _gameBoardSide[1]._frameNum = 4; + + subPostInit(); + + _stockPile.postInit(); +} + +void Scene1337::remove() { + if (R2_GLOBALS._v57709 > 1) { + subD1917(); + subD1940(false); + } + + R2_GLOBALS._uiElements._active = true; + R2_GLOBALS._uiElements._visible = true; + SceneExt::remove(); +} + +void Scene1337::process(Event &event) { + if (event.eventType == EVENT_BUTTON_DOWN) { + if (event.btnState == BTNSHIFT_RIGHT) { + updateCursorId(R2_GLOBALS._mouseCursorId, true); + event.handled = true; + } else if (_delayedFunction) { + FunctionPtrType tmpFctPtr = _delayedFunction; + _delayedFunction = nullptr; + (this->*tmpFctPtr)(); + event.handled = true; + } + } else if (event.eventType == EVENT_KEYPRESS) { + if (event.kbd.keycode == Common::KEYCODE_SPACE) { + if (_delayedFunction) { + FunctionPtrType tmpFctPtr = _delayedFunction; + _delayedFunction = nullptr; + (this->*tmpFctPtr)(); + event.handled = true; + } + } else + warning("Fixme: Find proper keycode value"); + } + + if (!event.handled) + Scene::process(event); +} + +void Scene1337::dispatch() { + if (!_instructionsDisplayedFl) { + ++_instructionsWaitCount; + if (_instructionsWaitCount == 4) { + _instructionsDisplayedFl = true; + suggestInstructions(); + } + } + + // The following code is in the original in sceneHandler::process(), + // which is terrible as it's checked in every scene of the game. + setCursorData(5, _cursorCurStrip, _cursorCurFrame); + // + + Scene::dispatch(); +} + +void Scene1337::actionDisplay(int resNum, int lineNum, int x, int y, int keepOnScreen, int width, int textMode, int fontNum, int colFG, int colBGExt, int colFGExt) { + // TODO: Check if it's normal that arg5 is unused and replaced by an hardcoded 0 value + // May hide an original bug + + SceneItem::display(resNum, lineNum, SET_X, x, SET_Y, y, SET_KEEP_ONSCREEN, 0, + SET_WIDTH, width, SET_POS_MODE, -1, SET_TEXT_MODE, textMode, + SET_FONT, fontNum, SET_FG_COLOR, colFG, SET_EXT_BGCOLOR, colBGExt, + SET_EXT_FGCOLOR, colFGExt, LIST_END); +} + +void Scene1337::setAnimationInfo(Card *card) { + if (!card) + return; + + if (card->_cardId > 25) { + card->_card.setStrip2(4); + card->_card.setFrame(card->_cardId - 25); + } else if (card->_cardId > 9) { + card->_card.setStrip2(3); + card->_card.setFrame(card->_cardId - 9); + } else { + card->_card.setStrip2(2); + card->_card.setFrame(card->_cardId); + } + + card->_card.show(); + R2_GLOBALS._sceneObjects->draw(); +} + +void Scene1337::handleNextTurn() { + switch (_winnerId) { + case -1: + ++_currentPlayerNumb; + if (_currentPlayerNumb > 3) + _currentPlayerNumb = 0; + + if (_showPlayerTurn) { + _currentPlayerArrow.show(); + switch (_currentPlayerNumb) { + case 0: + _currentPlayerArrow.setStrip(3); + break; + case 1: + _currentPlayerArrow.setStrip(4); + break; + case 2: + subD1975(174, 107); + _currentPlayerArrow.setStrip(1); + break; + case 3: + subC4CEC(); + _currentPlayerArrow.setStrip(2); + break; + default: + break; + } + + if (!_autoplay) + _delayedFunction = &Scene1337::handlePlayerTurn; + else + handlePlayerTurn(); + } else { + handlePlayerTurn(); + } + break; + case 0: + _aSound2.play(62); + actionDisplay(1330, 135, 159, 10, 1, 200, 0, 7, 0, 154, 154); + actionDisplay(1330, 121, 20, 99, 1, 136, 0, 7, 0, 172, 172); + actionDisplay(1330, 122, 300, 99, 1, 136, 0, 7, 0, 117, 117); + R2_GLOBALS._sceneObjects->draw(); + actionDisplay(1330, 123, 159, 134, 1, 200, 0, 7, 0, 105, 105); + break; + case 1: + _aSound2.play(62); + actionDisplay(1330, 151, 300, 99, 1, 136, 0, 7, 0, 117, 117); + actionDisplay(1330, 118, 20, 99, 1, 136, 0, 7, 0, 172, 172); + actionDisplay(1330, 119, 159, 10, 1, 200, 0, 7, 0, 154, 154); + R2_GLOBALS._sceneObjects->draw(); + actionDisplay(1330, 120, 159, 134, 1, 200, 0, 7, 0, 105, 105); + break; + case 2: + _aSound2.play(62); + actionDisplay(1330, 134, 159, 134, 1, 200, 0, 7, 0, 105, 105); + actionDisplay(1330, 124, 20, 99, 1, 136, 0, 7, 0, 172, 172); + actionDisplay(1330, 126, 159, 10, 1, 200, 0, 7, 0, 154, 154); + R2_GLOBALS._sceneObjects->draw(); + actionDisplay(1330, 125, 300, 99, 1, 136, 0, 7, 0, 117, 117); + break; + case 3: + _aSound2.play(62); + actionDisplay(1330, 150, 20, 99, 1, 136, 0, 7, 0, 172, 172); + actionDisplay(1330, 115, 300, 99, 1, 136, 0, 7, 0, 117, 117); + actionDisplay(1330, 116, 159, 10, 1, 200, 0, 7, 0, 154, 154); + R2_GLOBALS._sceneObjects->draw(); + actionDisplay(1330, 117, 159, 134, 1, 200, 0, 7, 0, 105, 105); + break; + default: + break; + } + + if (_winnerId != -1) + R2_GLOBALS._sceneManager.changeScene(125); + +} + +void Scene1337::handlePlayerTurn() { + if (_showPlayerTurn) + _currentPlayerArrow.hide(); + + switch (_currentPlayerNumb) { + case 2: + subC4CD2(); + if (_displayHelpFl) + actionDisplay(1330, 114, 159, 10, 1, 200, 0, 7, 0, 154, 154); + _displayHelpFl = false; + // No break on purpose + case 0: + // No break on purpose + case 1: + // No break on purpose + case 3: + _actionItem.setAction(&_action4); + default: + break; + } + + _showPlayerTurn = true; + +} + +bool Scene1337::isStationCard(int cardId) { + switch (cardId) { + case 10: + // No break on purpose + case 12: + // No break on purpose + case 15: + // No break on purpose + case 17: + // No break on purpose + case 18: + // No break on purpose + case 19: + // No break on purpose + case 20: + // No break on purpose + case 21: + return true; + default: + return false; + } +} + +bool Scene1337::isStopConstructionCard(int cardId) { + switch (cardId) { + case 11: + // No break on purpose + case 14: + // No break on purpose + case 16: + // No break on purpose + case 24: + return true; + default: + return false; + } +} + +int Scene1337::getStationId(int playerId, int handCardId) { + if ((_gameBoardSide[playerId]._handCard[handCardId]._cardId > 1) && (_gameBoardSide[playerId]._handCard[handCardId]._cardId <= 9)) + return handCardId; + + return -1; +} + +int Scene1337::findPlatformCardInHand(int playerId) { + for (int i = 0; i <= 3; i++) { + if (_gameBoardSide[playerId]._handCard[i]._cardId == 1) + return i; + } + + return -1; +} + +int Scene1337::findMeteorCardInHand(int playerId) { + for (int i = 0; i <= 3; i++) { + if (_gameBoardSide[playerId]._handCard[i]._cardId == 13) + return i; + } + + return -1; +} + +int Scene1337::findThieftCardInHand(int playerId) { + for (int i = 0; i <= 3; i++) { + if (_gameBoardSide[playerId]._handCard[i]._cardId == 25) + return i; + } + + return -1; +} + +int Scene1337::isDelayCard(int cardId) { + switch (cardId) { + case 11: + // No break on purpose + case 14: + // No break on purpose + case 16: + // No break on purpose + case 24: + return cardId; + break; + default: + return -1; + break; + } +} + +int Scene1337::getStationCardId(int cardId) { + switch (cardId) { + case 10: + // No break on purpose + case 12: + // No break on purpose + case 15: + // No break on purpose + case 17: + // No break on purpose + case 18: + // No break on purpose + case 19: + // No break on purpose + case 20: + // No break on purpose + case 21: + return cardId; + default: + return -1; + } +} + +void Scene1337::handlePlayer01Discard(int playerId) { + switch (playerId) { + case 0: + for (int i = 0; i <= 3; i++) { + if (getStationCardId(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if (isDelayCard(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Outpost Card + if ((_gameBoardSide[playerId]._handCard[i]._cardId > 1) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 9)) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if ((_gameBoardSide[playerId]._handCard[i]._cardId >= 26) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 33)) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Station Card + if (_gameBoardSide[playerId]._handCard[i]._cardId == 1) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Thief card + if (_gameBoardSide[playerId]._handCard[i]._cardId == 25) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Meteor Card + if (_gameBoardSide[playerId]._handCard[i]._cardId == 13) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + break; + case 1: + for (int i = 0; i <= 3; i++) { + if ((_gameBoardSide[playerId]._handCard[i]._cardId >= 26) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 33)) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Station Card + if (_gameBoardSide[playerId]._handCard[i]._cardId == 1) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Outpost Card + if ((_gameBoardSide[playerId]._handCard[i]._cardId > 1) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 9)) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if (getStationCardId(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if (isDelayCard(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Thief card + if (_gameBoardSide[playerId]._handCard[i]._cardId == 25) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + for (int i = 0; i <= 3; i++) { + // Meteor Card + if (_gameBoardSide[playerId]._handCard[i]._cardId == 13) { + discardCard(&_gameBoardSide[playerId]._handCard[i]); + return; + } + } + + break; + default: + break; + } +} + +void Scene1337::playThieftCard(int playerId, Card *card, int victimId) { + _actionPlayerIdx = playerId; + _actionVictimIdx = victimId; + + int randIndx; + + for (;;) { + randIndx = R2_GLOBALS._randomSource.getRandomNumber(3); + if (_gameBoardSide[victimId]._handCard[randIndx]._cardId != 0) + break; + } + + _actionCard1 = card; + _actionCard2 = &_gameBoardSide[victimId]._emptyStationPos; + _actionCard3 = &_gameBoardSide[victimId]._handCard[randIndx]; + + _actionItem.setAction(&_action11); +} + +int Scene1337::getPreventionCardId(int cardId) { + int retVal; + + switch (cardId) { + case 10: + retVal = 2; + break; + case 12: + retVal = 3; + break; + case 15: + retVal = 5; + break; + case 17: + retVal = 9; + break; + case 18: + retVal = 6; + break; + case 19: + retVal = 4; + break; + case 20: + retVal = 8; + break; + case 21: + retVal = 7; + break; + default: + retVal = -1; + } + + return retVal; +} + +bool Scene1337::isAttackPossible(int victimId, int cardId) { + if (victimId < 0 || victimId >= ARRAYSIZE(_gameBoardSide)) + error("Scene1337::isAttackPossible() victimId:%d out of range 0 to %d", victimId, ARRAYSIZE(_gameBoardSide)-1); + + for (int i = 0; i <= 7; i++) { + if (_gameBoardSide[victimId]._outpostStation[i]._cardId != 0) { + if (getPreventionCardId(cardId) == _gameBoardSide[victimId]._outpostStation[i]._cardId) + return false; + } + } + return true; +} + +int Scene1337::getPlayerWithOutpost(int playerId) { + int randPlayerId = R2_GLOBALS._randomSource.getRandomNumber(3); + + for (int i = 0; i <= 3; i++) { + if (randPlayerId != playerId) { + for (int j = 0; j <= 7; j++) { + if (_gameBoardSide[randPlayerId]._outpostStation[j]._cardId != 0) + return randPlayerId; + } + } + + if (playerId == 1) { + randPlayerId--; + if (randPlayerId < 0) + randPlayerId = 3; + } else { + ++randPlayerId; + if (randPlayerId > 3) + randPlayerId = 0; + } + } + + return -1; +} + +bool Scene1337::checkAntiDelayCard(int delayCardId, int cardId) { + if ((delayCardId == 11) && (cardId == 26)) // Diplomacy + return true; + + if ((delayCardId == 14) && (cardId == 30)) // Cure + return true; + + if ((delayCardId == 16) && (cardId == 32)) // Agreement + return true; + + if ((delayCardId == 24) && (cardId == 28)) // Innovation + return true; + + return false; +} + +void Scene1337::playStationCard(Card *station, Card *platform) { + _actionCard1 = station; + _actionCard2 = platform; + _actionItem.setAction(&_action7); +} + +int Scene1337::getRandomCardFromHand(int playerId) { + if ( (_gameBoardSide[playerId]._handCard[0]._cardId == 0) + && (_gameBoardSide[playerId]._handCard[1]._cardId == 0) + && (_gameBoardSide[playerId]._handCard[2]._cardId == 0) + && (_gameBoardSide[playerId]._handCard[3]._cardId == 0)) + return -1; + + int randIndx; + for (;;) { + randIndx = R2_GLOBALS._randomSource.getRandomNumber(3); + if (_gameBoardSide[playerId]._handCard[randIndx]._cardId != 0) + break; + } + + return randIndx; +} + +void Scene1337::playPlatformCard(Card *card, Card *dest) { + _actionCard1 = card; + _actionCard2 = dest; + + _actionItem.setAction(&_action6); +} + +void Scene1337::playDelayCard(Card *card, Card *dest) { + _actionCard1 = card; + _actionCard2 = dest; + + _actionItem.setAction(&_action9); +} + +void Scene1337::playAntiDelayCard(Card *card, Card *dest) { + _actionCard1 = card; + _actionCard2 = dest; + + _actionItem.setAction(&_action8); + + // WORKAROUND: Restore the default cursor and for a call to signal. + // This works around the cursor caching we got rid of, and avoid + // the game ends in an eternal loop when a player reacts to another player + // attack. + setCursorData(5, 1, 4); + signal(); +} + + +Scene1337::Card *Scene1337::getStationCard(int playerId) { + for (int i = 0; i <= 7; i++) { + if ((_gameBoardSide[playerId]._outpostStation[i]._cardId >= 1) && (_gameBoardSide[playerId]._outpostStation[i]._cardId <= 9)) + return &_gameBoardSide[playerId]._outpostStation[i]; + } + + return nullptr; +} + +void Scene1337::playCentralOutpostCard(Card *card, int playerId) { + _actionCard1 = card; + _actionCard2 = getStationCard(playerId); + _actionCard3 = &_gameBoardSide[playerId]._emptyStationPos; + _actionPlayerIdx = playerId; + _actionItem.setAction(&_action10); +} + +void Scene1337::discardCard(Card *card) { + _actionCard1 = card; + + _actionItem.setAction(&_action5); +} + +void Scene1337::subC4CD2() { + if (R2_GLOBALS._v57709 > 0) { + subD1917(); + subD1940(false); // _v5780C-- + } +} + +void Scene1337::subC4CEC() { + if (R2_GLOBALS._v57709 == 0) { + subD18F5(); + subD1940(true); // _v5780C++ + } +} + +// Play Interceptor card +void Scene1337::playInterceptorCard(Card *subObj1, Card *subObj2) { + _actionCard1 = subObj1; + _actionCard2 = subObj2; + + _actionItem.setAction(&_action13); +} + +void Scene1337::displayDialog(int dialogNumb) { + switch (dialogNumb - 1) { + case 0: + actionDisplay(1330, 53, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 1: + actionDisplay(1330, 57, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 2: + actionDisplay(1330, 58, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 3: + actionDisplay(1330, 59, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 4: + actionDisplay(1330, 60, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 5: + actionDisplay(1330, 61, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 6: + actionDisplay(1330, 62, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 7: + actionDisplay(1330, 63, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 8: + actionDisplay(1330, 64, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 9: + actionDisplay(1330, 65, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 10: + actionDisplay(1330, 67, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 11: + actionDisplay(1330, 69, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 12: + actionDisplay(1330, 71, 159, 10, 1, 200, 0, 7, 0, 154, 154); + actionDisplay(1330, 72, 159, 10, 1, 200, 0, 7, 0, 154, 154); + actionDisplay(1330, 73, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 13: + actionDisplay(1330, 79, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 14: + actionDisplay(1330, 81, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + actionDisplay(1330, 83, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 16: + actionDisplay(1330, 85, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 17: + actionDisplay(1330, 87, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 18: + actionDisplay(1330, 89, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 19: + actionDisplay(1330, 91, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + actionDisplay(1330, 93, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 23: + actionDisplay(1330, 95, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 24: + actionDisplay(1330, 97, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 25: + actionDisplay(1330, 104, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 26: + actionDisplay(1330, 105, 159, 10, 1, 200, 0, 7, 0, 154, 154); + actionDisplay(1330, 106, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 27: + actionDisplay(1330, 110, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 28: + actionDisplay(1330, 108, 159, 10, 1, 200, 0, 7, 0, 154, 154); + actionDisplay(1330, 109, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 29: + actionDisplay(1330, 111, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 31: + actionDisplay(1330, 112, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } +} + +void Scene1337::subPostInit() { + R2_GLOBALS._v57709 = 0; + R2_GLOBALS._v5780C = 0; + updateCursorId(1, false); + subD1940(true); // _v5780C++ + subD18F5(); + +// loadScene(1330); +// SceneExt::postInit(); + + R2_GLOBALS._scenePalette.addRotation(224, 235, 1); + + _availableCardsPile[0] = 1; + _availableCardsPile[1] = 1; + _availableCardsPile[2] = 1; + _availableCardsPile[3] = 1; + _availableCardsPile[4] = 1; + _availableCardsPile[5] = 1; + _availableCardsPile[6] = 1; + _availableCardsPile[7] = 1; + _availableCardsPile[8] = 26; + _availableCardsPile[9] = 2; + _availableCardsPile[10] = 2; + _availableCardsPile[11] = 2; + _availableCardsPile[12] = 2; + _availableCardsPile[13] = 2; + _availableCardsPile[14] = 26; + _availableCardsPile[15] = 3; + _availableCardsPile[16] = 3; + _availableCardsPile[17] = 3; + _availableCardsPile[18] = 3; + _availableCardsPile[19] = 3; + _availableCardsPile[20] = 28; + _availableCardsPile[21] = 4; + _availableCardsPile[22] = 4; + _availableCardsPile[23] = 4; + _availableCardsPile[24] = 4; + _availableCardsPile[25] = 4; + _availableCardsPile[26] = 28; + _availableCardsPile[27] = 5; + _availableCardsPile[28] = 5; + _availableCardsPile[29] = 5; + _availableCardsPile[30] = 5; + _availableCardsPile[31] = 5; + _availableCardsPile[32] = 30; + _availableCardsPile[33] = 6; + _availableCardsPile[34] = 6; + _availableCardsPile[35] = 6; + _availableCardsPile[36] = 6; + _availableCardsPile[37] = 6; + _availableCardsPile[38] = 30; + _availableCardsPile[39] = 7; + _availableCardsPile[40] = 7; + _availableCardsPile[41] = 7; + _availableCardsPile[42] = 7; + _availableCardsPile[43] = 7; + _availableCardsPile[44] = 32; + _availableCardsPile[45] = 8; + _availableCardsPile[46] = 8; + _availableCardsPile[47] = 8; + _availableCardsPile[48] = 8; + _availableCardsPile[49] = 8; + _availableCardsPile[50] = 32; + _availableCardsPile[51] = 9; + _availableCardsPile[52] = 9; + _availableCardsPile[53] = 9; + _availableCardsPile[54] = 9; + _availableCardsPile[55] = 9; + _availableCardsPile[56] = 10; + _availableCardsPile[57] = 11; + _availableCardsPile[58] = 12; + _availableCardsPile[59] = 13; + _availableCardsPile[60] = 13; + _availableCardsPile[61] = 14; + _availableCardsPile[62] = 15; + _availableCardsPile[63] = 16; + _availableCardsPile[64] = 17; + _availableCardsPile[65] = 18; + _availableCardsPile[66] = 19; + _availableCardsPile[67] = 20; + _availableCardsPile[68] = 21; + _availableCardsPile[69] = 26; + _availableCardsPile[70] = 28; + _availableCardsPile[71] = 24; + _availableCardsPile[72] = 25; + _availableCardsPile[73] = 25; + _availableCardsPile[74] = 25; + _availableCardsPile[75] = 25; + _availableCardsPile[76] = 26; + _availableCardsPile[77] = 26; + _availableCardsPile[78] = 26; + _availableCardsPile[79] = 27; + _availableCardsPile[80] = 27; + _availableCardsPile[81] = 28; + _availableCardsPile[82] = 28; + _availableCardsPile[83] = 28; + _availableCardsPile[84] = 29; + _availableCardsPile[85] = 29; + _availableCardsPile[86] = 29; + _availableCardsPile[87] = 30; + _availableCardsPile[88] = 30; + _availableCardsPile[89] = 30; + _availableCardsPile[90] = 30; + _availableCardsPile[91] = 32; + _availableCardsPile[92] = 1; + _availableCardsPile[93] = 32; + _availableCardsPile[94] = 32; + _availableCardsPile[95] = 32; + _availableCardsPile[96] = 1; + _availableCardsPile[97] = 1; + _availableCardsPile[98] = 1; + _availableCardsPile[99] = 0; + + _cardsAvailableNumb = 98; + _currentDiscardIndex = 98; // CHECKME: Would make more sense at pos 99 + + _discardPile._cardId = 0; + _discardPile._stationPos = Common::Point(128, 95); + + _stockCard._cardId = 0; + _stockCard._stationPos = Common::Point(162, 95); + + _selectedCard._cardId = 0; + + _animatedCard._card.postInit(); + _animatedCard._card.setVisage(1332); + _animatedCard._card.setStrip(5); + _animatedCard._card.setFrame(1); + _animatedCard._card._moveDiff = Common::Point(10, 10); + _animatedCard._card.fixPriority(400); + _animatedCard._card.setPosition(Common::Point(128, 95), 0); + _animatedCard._card.animate(ANIM_MODE_2, NULL); + _animatedCard._card.hide(); + + _currentPlayerArrow.postInit(); + _currentPlayerArrow.setVisage(1334); + _currentPlayerArrow.setStrip(1); + _currentPlayerArrow.setFrame(1); + _currentPlayerArrow._numFrames = 12; + _currentPlayerArrow.fixPriority(500); + _currentPlayerArrow.setPosition(Common::Point(174, 107), 0); + _currentPlayerArrow.animate(ANIM_MODE_2, NULL); + _currentPlayerArrow.hide(); + + _showPlayerTurn = true; + _displayHelpFl = false; + _winnerId = -1; + + _helpIcon.postInit(); + _helpIcon.setup(9531, 1, 1); + _helpIcon.setPosition(Common::Point(249, 168)); + _helpIcon.setPriority(155); + _helpIcon._effect = EFFECT_NONE; + _helpIcon.show(); + + _autoplay = false; + _instructionsDisplayedFl = false; + _instructionsWaitCount = 0; +} + +void Scene1337::suggestInstructions() { + if (R2_GLOBALS._v57709 > 0) + subD1917(); + + if (MessageDialog::show(NEED_INSTRUCTIONS, NO_MSG, YES_MSG) == 0) { + if (R2_GLOBALS._v57709 == 0) + subD18F5(); + dealCards(); + } else { + if (R2_GLOBALS._v57709 == 0) + subD18F5(); + displayInstructions(); + } +} + +void Scene1337::displayInstructions() { + _actionItem.setAction(&_action1); +} + +void Scene1337::shuffleCards() { + R2_GLOBALS._sceneObjects->draw(); + + // Remove holes in card pile + for (int i = 0; i <= 98; i++) { + if (_availableCardsPile[i] == 0) { + for (int j = i + 1; j <= 98; j ++) { + if (_availableCardsPile[j] != 0) { + _availableCardsPile[i] = _availableCardsPile[j]; + _availableCardsPile[j] = 0; + break; + } + } + } + } + + // Compute the number of available cards + for (int i = 0; i <= 99; i ++) { + if (_availableCardsPile[i] == 0) { + // CHECKME: This will fail if i == 0, which shouldn't happen + // as we don't shuffle cards when no card is available. + _cardsAvailableNumb = i - 1; + _currentDiscardIndex = 98; // CHECKME: Would make more sense at pos 99 + break; + } + } + + for (int i = 0; i < 2000; i ++) { + int randIndx = R2_GLOBALS._randomSource.getRandomNumber(_cardsAvailableNumb); + int swap = _availableCardsPile[0]; + _availableCardsPile[0] = _availableCardsPile[randIndx]; + _availableCardsPile[randIndx] = swap; + } + + _shuffleEndedFl = false; + + // Shuffle cards + _animatedCard._card.setAction(&_action2); + + while(!_shuffleEndedFl && !g_vm->shouldQuit()) { + g_globals->_sceneObjects->recurse(SceneHandler::dispatchObject); + g_globals->_scenePalette.signalListeners(); + R2_GLOBALS._sceneObjects->draw(); + g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); + } +} + +void Scene1337::dealCards() { + _animatedCard._card._moveDiff = Common::Point(30, 30); + shuffleCards(); + + // Deal cards + _actionItem.setAction(&_action3); +} + +void Scene1337::showOptionsDialog() { + // Display menu with "Auto Play", "New Game", "Quit" and "Continue" + OptionsDialog::show(); +} + +void Scene1337::handleClick(int arg1, Common::Point pt) { + int curReg = R2_GLOBALS._sceneRegions.indexOf(g_globals->_events._mousePos); + + if (arg1 == 3) { + bool found = false; + int i; + for (i = 0; i <= 7; i++) { + if ( _gameBoardSide[2]._outpostStation[i].isIn(pt) + || _gameBoardSide[0]._outpostStation[i].isIn(pt) + || _gameBoardSide[1]._outpostStation[i].isIn(pt) + || _gameBoardSide[3]._outpostStation[i].isIn(pt) ) { + found = true; + break; + } + } + + if (found) { + switch (curReg) { + case 5: + if (_gameBoardSide[2]._outpostStation[i]._cardId != 0) + displayDialog(_gameBoardSide[2]._outpostStation[i]._cardId); + else + actionDisplay(1330, 20, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 10: + if (_gameBoardSide[3]._outpostStation[i]._cardId != 0) + displayDialog(_gameBoardSide[3]._outpostStation[i]._cardId); + else + actionDisplay(1330, 22, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + if (_gameBoardSide[0]._outpostStation[i]._cardId != 0) + displayDialog(_gameBoardSide[0]._outpostStation[i]._cardId); + else + actionDisplay(1330, 21, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + if (_gameBoardSide[1]._outpostStation[i]._cardId != 0) + displayDialog(_gameBoardSide[1]._outpostStation[i]._cardId); + else + actionDisplay(1330, 23, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } else if ( _gameBoardSide[2]._delayCard.isIn(pt) + || _gameBoardSide[0]._delayCard.isIn(pt) + || _gameBoardSide[1]._delayCard.isIn(pt) + || _gameBoardSide[3]._delayCard.isIn(pt) ) { + switch (curReg) { + case 5: + if (_gameBoardSide[2]._delayCard._cardId != 0) + displayDialog(_gameBoardSide[2]._delayCard._cardId); + else + actionDisplay(1330, 10, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 10: + if (_gameBoardSide[3]._delayCard._cardId != 0) + displayDialog(_gameBoardSide[3]._delayCard._cardId); + else + actionDisplay(1330, 16, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + if (_gameBoardSide[0]._delayCard._cardId != 0) + displayDialog(_gameBoardSide[0]._delayCard._cardId); + else + actionDisplay(1330, 13, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + if (_gameBoardSide[1]._delayCard._cardId != 0) + displayDialog(_gameBoardSide[1]._delayCard._cardId); + else + actionDisplay(1330, 18, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } else if (_discardPile.isIn(pt)) { + if (_discardPile._cardId != 0) + displayDialog(_discardPile._cardId); + else + actionDisplay(1330, 7, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (_helpIcon._bounds.contains(pt)) + actionDisplay(1330, 43, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else if (_stockCard.isIn(pt)) + actionDisplay(1330, 4, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else if ( (_gameBoardSide[2]._emptyStationPos.isIn(pt)) + || (_gameBoardSide[3]._emptyStationPos.isIn(pt)) + || (_gameBoardSide[0]._emptyStationPos.isIn(pt)) + || (_gameBoardSide[1]._emptyStationPos.isIn(pt)) ) + actionDisplay(1330, 32, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else if (_gameBoardSide[2]._handCard[0].isIn(pt)) + displayDialog(_gameBoardSide[2]._handCard[0]._cardId); + else if (_gameBoardSide[2]._handCard[1].isIn(pt)) + displayDialog(_gameBoardSide[2]._handCard[1]._cardId); + else if (_gameBoardSide[2]._handCard[2].isIn(pt)) + displayDialog(_gameBoardSide[2]._handCard[2]._cardId); + else if (_gameBoardSide[2]._handCard[3].isIn(pt)) + displayDialog(_gameBoardSide[2]._handCard[3]._cardId); + else if ((curReg >= 6) && (curReg <= 9)) + actionDisplay(1330, 29, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else if ((curReg >= 11) && (curReg <= 14)) + actionDisplay(1330, 31, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else if ((curReg >= 16) && (curReg <= 19)) + actionDisplay(1330, 30, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else { + switch (curReg) { + case 0: + actionDisplay(1330, 2, 159, 134, 1, 200, 0, 7, 0, 105, 105); + break; + case 5: + actionDisplay(1330, 25, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 10: + actionDisplay(1330, 27, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + actionDisplay(1330, 26, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + actionDisplay(1330, 28, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 21: + actionDisplay(1330, 24, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } + } + + if (arg1 != 1) + return; + + for (int i = 0; i <= 7; i++) { + if (_gameBoardSide[2]._outpostStation[i].isIn(pt)) { + switch (_gameBoardSide[2]._outpostStation[i]._cardId) { + case 0: + actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 1: + actionDisplay(1330, 54, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + actionDisplay(1330, 34, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + } + return; + } + if (_gameBoardSide[0]._outpostStation[i].isIn(pt)) { + switch (_gameBoardSide[0]._outpostStation[i]._cardId) { + case 0: + actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + actionDisplay(1330, 1, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + } + return; + } + if (_gameBoardSide[1]._outpostStation[i].isIn(pt)) { + switch (_gameBoardSide[1]._outpostStation[i]._cardId) { + case 0: + actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); + break; + default: + actionDisplay(1330, 144, 300, 99, 1, 136, 0, 7, 0, 117, 117); + break; + } + return; + } + if (_gameBoardSide[3]._outpostStation[i].isIn(pt)) { + switch (_gameBoardSide[3]._outpostStation[i]._cardId) { + case 0: + actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); + break; + default: + actionDisplay(1330, 145, 20, 99, 1, 136, 0, 7, 0, 172, 172); + break; + } + return; + } + } + + if (_gameBoardSide[2]._delayCard.isIn(pt)) { + // The original uses _gameBoardSide[0], which is obviously a bug. + if (_gameBoardSide[2]._delayCard._cardId != 0) + actionDisplay(1330, 39, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else + actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + return; + } + if (_gameBoardSide[3]._delayCard.isIn(pt)) { + if (_gameBoardSide[3]._delayCard._cardId != 0) + actionDisplay(1330, 145, 20, 99, 1, 136, 0, 7, 0, 172, 172); + else + actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); + + return; + } + if (_gameBoardSide[1]._delayCard.isIn(pt)) { + if (_gameBoardSide[1]._delayCard._cardId != 0) + actionDisplay(1330, 144, 300, 99, 1, 136, 0, 7, 0, 117, 117); + else + actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); + + return; + } + if (_gameBoardSide[0]._delayCard.isIn(pt)) { + if (_gameBoardSide[0]._delayCard._cardId != 0) + actionDisplay(1330, 1, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else + actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + + return; + } + if (_gameBoardSide[3]._emptyStationPos.isIn(pt)) { + actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); + return; + } + if (_gameBoardSide[1]._emptyStationPos.isIn(pt)) { + actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); + return; + } + if (_gameBoardSide[0]._emptyStationPos.isIn(pt)) { + actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + return; + } + + if (_helpIcon._bounds.contains(pt)) { + showOptionsDialog(); + return; + } + + if (_discardPile.isIn(pt)) + actionDisplay(1330, 9, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else if (_stockCard.isIn(pt)) + actionDisplay(1330, 5, 159, 10, 1, 200, 0, 7, 0, 154, 154); + else { + switch (curReg) { + case 0: + actionDisplay(1330, 3, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 6: + // no break on purpose + case 7: + // no break on purpose + case 8: + // no break on purpose + case 9: + actionDisplay(1330, 145, 20, 99, 1, 136, 0, 7, 0, 172, 172); + break; + case 10: + actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); + break; + case 11: + // no break on purpose + case 12: + // no break on purpose + case 13: + // no break on purpose + case 14: + actionDisplay(1330, 1, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 16: + // no break on purpose + case 17: + // no break on purpose + case 18: + // no break on purpose + case 19: + actionDisplay(1330, 144, 300, 99, 1, 136, 0, 7, 0, 117, 117); + break; + case 20: + actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); + break; + default: + actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + } + } +} + +void Scene1337::handlePlayer0() { + if (_gameBoardSide[0]._delayCard._cardId != 0) { + switch (_gameBoardSide[0]._delayCard._cardId) { + case 10: + //No break on purpose + case 12: + //No break on purpose + case 15: + //No break on purpose + case 17: + //No break on purpose + case 18: + //No break on purpose + case 19: + //No break on purpose + case 20: + //No break on purpose + case 21: + discardCard(&_gameBoardSide[0]._delayCard); + return; + default: + for (int i = 0; i <= 3; i++) { + if (checkAntiDelayCard(_gameBoardSide[0]._delayCard._cardId, _gameBoardSide[0]._handCard[i]._cardId)) { + playAntiDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[0]._delayCard); + return; + } + } + + break; + } + } + + for (int i = 0; i <= 3; i++) { + int tmpVal = getStationId(0, i); + + if (tmpVal != -1) { + bool stationAlreadyPresentFl = false; + for (int j = 0; j <= 7; j++) { + if (_gameBoardSide[0]._outpostStation[j]._cardId == _gameBoardSide[0]._handCard[tmpVal]._cardId) { + stationAlreadyPresentFl = true; + break; + } + } + + if (!stationAlreadyPresentFl) { + for (int j = 0; j <= 7; j++) { + if ((_gameBoardSide[0]._outpostStation[j]._cardId == 1) && !isStopConstructionCard(_gameBoardSide[0]._delayCard._cardId)) { + int stationCount = 0; + for (int k = 0; k <= 7; k++) { + if ((_gameBoardSide[0]._outpostStation[k]._cardId > 1) && (_gameBoardSide[0]._outpostStation[k]._cardId <= 9)) { + ++stationCount; + } + } + + if (stationCount == 7) + _winnerId = 0; + + playStationCard(&_gameBoardSide[0]._handCard[tmpVal], &_gameBoardSide[0]._outpostStation[j]); + return; + } + } + } + } + } + + int tmpVal = findPlatformCardInHand(0); + + if (tmpVal != -1) { + for (int i = 0; i <= 7; i++) { + if ((_gameBoardSide[0]._outpostStation[i]._cardId == 0) && !isStopConstructionCard(_gameBoardSide[0]._delayCard._cardId)) { + playPlatformCard(&_gameBoardSide[0]._handCard[tmpVal], &_gameBoardSide[0]._outpostStation[i]); + return; + } + } + } + + int meteorCardId = findMeteorCardInHand(0); + if (meteorCardId != -1) { + for (int i = 0; i <= 7; i++) { + if (_gameBoardSide[2]._outpostStation[i]._cardId != 0) { + playCentralOutpostCard(&_gameBoardSide[0]._handCard[meteorCardId], 2); + return; + } + } + } + + int thieftId = findThieftCardInHand(0); + if (thieftId != -1) { + if ( (_gameBoardSide[2]._handCard[0]._cardId != 0) + || (_gameBoardSide[2]._handCard[1]._cardId != 0) + || (_gameBoardSide[2]._handCard[2]._cardId != 0) + || (_gameBoardSide[2]._handCard[3]._cardId != 0) ) { + playThieftCard(0, &_gameBoardSide[0]._handCard[thieftId], 2); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if ((isDelayCard(_gameBoardSide[0]._handCard[i]._cardId) != -1) + && (_gameBoardSide[2]._delayCard._cardId == 0) + && isAttackPossible(2, _gameBoardSide[0]._handCard[i]._cardId)) { + playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[2]._delayCard); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if ((getStationCardId(_gameBoardSide[0]._handCard[i]._cardId) != -1) + && (_gameBoardSide[2]._delayCard._cardId == 0) + && isAttackPossible(2, _gameBoardSide[0]._handCard[i]._cardId)) { + playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[2]._delayCard); + return; + } + } + + meteorCardId = findMeteorCardInHand(0); + int victimId = getPlayerWithOutpost(0); + + if ((meteorCardId != -1) && (victimId != -1)) { + playCentralOutpostCard(&_gameBoardSide[0]._handCard[meteorCardId], victimId); + return; + } + + thieftId = findThieftCardInHand(0); + if (thieftId != -1) { + if ( (_gameBoardSide[1]._handCard[0]._cardId != 0) + || (_gameBoardSide[1]._handCard[1]._cardId != 0) + || (_gameBoardSide[1]._handCard[2]._cardId != 0) + || (_gameBoardSide[1]._handCard[3]._cardId != 0) ) { + playThieftCard(0, &_gameBoardSide[0]._handCard[thieftId], 1); + return; + } + } + + for (int i = 0; i <= 3; i++) { + if (getStationCardId(_gameBoardSide[0]._handCard[i]._cardId) != -1) { + if ((_gameBoardSide[1]._delayCard._cardId == 0) && isAttackPossible(1, _gameBoardSide[0]._handCard[i]._cardId)) { + playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[1]._delayCard); + return; + } + + if ((_gameBoardSide[3]._delayCard._cardId == 0) && isAttackPossible(3, _gameBoardSide[0]._handCard[i]._cardId)) { + playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[3]._delayCard); + return; + } + } + } + + for (int i = 0; i <= 3; i++) { + tmpVal = isDelayCard(_gameBoardSide[0]._handCard[i]._cardId); + if (tmpVal != -1) { + if ((_gameBoardSide[1]._delayCard._cardId == 0) && isAttackPossible(1, _gameBoardSide[0]._handCard[i]._cardId)) { + playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[1]._delayCard); + return; + } + + if ((_gameBoardSide[3]._delayCard._cardId == 0) && isAttackPossible(3, _gameBoardSide[0]._handCard[i]._cardId)) { + playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[3]._delayCard); + return; + } + } + } + + handlePlayer01Discard(0); +} + +void Scene1337::handlePlayer1() { + if (this->_gameBoardSide[1]._delayCard._cardId != 0) { + switch (_gameBoardSide[1]._delayCard._cardId) { + case 10: + // No break on purpose + case 12: + // No break on purpose + case 15: + // No break on purpose + case 17: + // No break on purpose + case 18: + // No break on purpose + case 19: + // No break on purpose + case 20: + // No break on purpose + case 21: + discardCard(&_gameBoardSide[1]._delayCard); + return; + default: + for (int i = 0; i <= 3; i++) { + if (checkAntiDelayCard(_gameBoardSide[1]._delayCard._cardId, _gameBoardSide[1]._handCard[i]._cardId)) { + playAntiDelayCard(&_gameBoardSide[1]._handCard[i], &_gameBoardSide[1]._delayCard); + return; + } + } + break; + } + } + + for (int i = 0; i <= 3; i++) { + int tmpIndx = getStationId(1, i); + if (tmpIndx == -1) + break; + + int tmpVal = 0; + for (int j = 0; j <= 7; j++) { + if (_gameBoardSide[1]._outpostStation[j]._cardId == _gameBoardSide[1]._handCard[tmpIndx]._cardId) { + tmpVal = 1; + break; + } + } + + if (tmpVal == 0) + break; + + for (int j = 0; j <= 7; j++) { + if ((_gameBoardSide[1]._outpostStation[j]._cardId == 1) && !isStopConstructionCard(_gameBoardSide[1]._delayCard._cardId)) { + int stationCount = 0; + for (int k = 0; k <= 7; k++) { + if ((_gameBoardSide[1]._outpostStation[k]._cardId > 1) && (_gameBoardSide[1]._outpostStation[k]._cardId <= 9)) + ++stationCount; + } + + if (stationCount == 7) + _winnerId = 1; + + playStationCard(&_gameBoardSide[1]._handCard[tmpIndx], &_gameBoardSide[1]._outpostStation[j]); + return; + } + } + } + + int normalCardId = findPlatformCardInHand(1); + if (normalCardId != -1) { + for (int i = 0; i <= 7; i++) { + if ((_gameBoardSide[1]._outpostStation[i]._cardId == 0) && !isStopConstructionCard(_gameBoardSide[1]._delayCard._cardId)) { + playPlatformCard(&_gameBoardSide[1]._handCard[normalCardId], &_gameBoardSide[1]._outpostStation[i]); + return; + } + } + } + + int meterorCardId = findMeteorCardInHand(1); + int victimId = getPlayerWithOutpost(1); + + if ((meterorCardId != -1) && (victimId != -1)) { + playCentralOutpostCard(&_gameBoardSide[1]._handCard[meterorCardId], victimId); + return; + } + + int thieftId = findThieftCardInHand(1); + if (thieftId != -1) { + int playerIdFound = -1; + int rndVal = R2_GLOBALS._randomSource.getRandomNumber(3); + for (int i = 0; i <= 3; i++) { + if (rndVal != 1) { + if ( (_gameBoardSide[rndVal]._handCard[0]._cardId != 0) + || (_gameBoardSide[rndVal]._handCard[1]._cardId != 0) + || (_gameBoardSide[rndVal]._handCard[2]._cardId != 0) + || (_gameBoardSide[rndVal]._handCard[3]._cardId != 0)) { + playerIdFound = rndVal; + break; + } + } + // The original was only updating in the rndVal block, + // which was a bug as the checks were stopping at this point + rndVal--; + if (rndVal < 0) + rndVal = 3; + } + + if (playerIdFound != -1) { + playThieftCard(1, &_gameBoardSide[1]._handCard[thieftId], playerIdFound); + return; + } + } + + for (int i = 0; i <= 3; i++) { + int tmpVal = isDelayCard(_gameBoardSide[1]._handCard[i]._cardId); + if (tmpVal != -1) { + victimId = -1; + int rndVal = R2_GLOBALS._randomSource.getRandomNumber(3); + + for (int j = 0; j <= 3; j++) { + if (rndVal != 1) { + if ((_gameBoardSide[rndVal]._delayCard._cardId == 0) && isAttackPossible(rndVal, _gameBoardSide[1]._handCard[i]._cardId)) + victimId = rndVal; + } + + if (victimId != -1) { + playDelayCard(&_gameBoardSide[1]._handCard[i], &_gameBoardSide[victimId]._delayCard); + return; + } else { + rndVal--; + if (rndVal < 0) + rndVal = 3; + } + } + } + } + + for (int j = 0; j <= 3; j++) { + if (getStationCardId(_gameBoardSide[1]._handCard[j]._cardId) != -1) { + victimId = -1; + int rndVal = R2_GLOBALS._randomSource.getRandomNumber(3); + for (int l = 0; l <= 3; l++) { + if (rndVal != 1) { + if ((_gameBoardSide[rndVal]._delayCard._cardId == 0) && (_gameBoardSide[1]._handCard[j]._cardId == 1)) + victimId = rndVal; + } + if (victimId != -1) { + playDelayCard(&_gameBoardSide[1]._handCard[j], &_gameBoardSide[victimId]._delayCard); + return; + } else { + rndVal--; + if (rndVal < 0) + rndVal = 3; + } + } + } + } + + handlePlayer01Discard(1); +} + +void Scene1337::handlePlayer3() { + if (_gameBoardSide[3]._delayCard._cardId != 0) { + switch (_gameBoardSide[3]._delayCard._cardId) { + case 10: + // No break on purpose + case 12: + // No break on purpose + case 15: + // No break on purpose + case 17: + // No break on purpose + case 18: + // No break on purpose + case 19: + // No break on purpose + case 20: + // No break on purpose + case 21: + discardCard(&_gameBoardSide[3]._delayCard); + return; + default: + for (int i = 0; i <= 3; i++) { + if (checkAntiDelayCard(_gameBoardSide[3]._delayCard._cardId, _gameBoardSide[3]._handCard[i]._cardId)) { + playAntiDelayCard(&_gameBoardSide[3]._handCard[i], &_gameBoardSide[3]._delayCard); + return; + } + } + break; + } + } + + int randIndx = R2_GLOBALS._randomSource.getRandomNumber(3); + if (_gameBoardSide[3]._handCard[randIndx]._cardId == 1) { + // Station Card + for (int i = 0; i <= 7; i++) { + if ((_gameBoardSide[3]._outpostStation[i]._cardId == 0) && !isStopConstructionCard(_gameBoardSide[3]._delayCard._cardId)) { + playPlatformCard(&_gameBoardSide[3]._handCard[randIndx], &_gameBoardSide[3]._outpostStation[i]); + return; + } + } + } else if (_gameBoardSide[3]._handCard[randIndx]._cardId <= 9) { + // Outpost Card + for (int i = 0; i <= 7; i++) { + if (_gameBoardSide[3]._outpostStation[i]._cardId == _gameBoardSide[3]._handCard[randIndx]._cardId) { + discardCard(&_gameBoardSide[3]._handCard[randIndx]); + return; + } + } + + for (int i = 0; i <= 7; i++) { + if ((_gameBoardSide[3]._outpostStation[i]._cardId == 1) && !isStopConstructionCard(_gameBoardSide[3]._delayCard._cardId)) { + int stationCount = 0; + for (int j = 0; j <= 7; j++) { + if ((_gameBoardSide[3]._outpostStation[j]._cardId > 1) && (_gameBoardSide[3]._outpostStation[j]._cardId <= 9)) + ++stationCount; + } + + if (stationCount == 7) + _winnerId = 3; + + playStationCard(&_gameBoardSide[3]._handCard[randIndx], &_gameBoardSide[3]._outpostStation[i]); + return; + } + } + } else if (_gameBoardSide[3]._handCard[randIndx]._cardId == 13) { + // Meteor Card + int victimId = getPlayerWithOutpost(3); + + if (victimId != -1) { + playCentralOutpostCard(&_gameBoardSide[3]._handCard[randIndx], victimId); + return; + } + } else if (_gameBoardSide[3]._handCard[randIndx]._cardId == 25) { + // Thief card + int victimId = -1; + int tmpRandIndx = R2_GLOBALS._randomSource.getRandomNumber(3); + + for (int i = 0; i <= 3; i++) { + if ( (tmpRandIndx != 3) + && ( (_gameBoardSide[tmpRandIndx]._handCard[0]._cardId != 0) + || (_gameBoardSide[tmpRandIndx]._handCard[1]._cardId != 0) + || (_gameBoardSide[tmpRandIndx]._handCard[2]._cardId != 0) + || (_gameBoardSide[tmpRandIndx]._handCard[3]._cardId != 0) )) { + victimId = tmpRandIndx; + break; + } + + ++tmpRandIndx; + if (tmpRandIndx > 3) + tmpRandIndx = 0; + } + + if (victimId != -1) { + playThieftCard(3, &_gameBoardSide[3]._handCard[randIndx], victimId); + return; + } + } else { + switch (_gameBoardSide[3]._handCard[randIndx]._cardId) { + case 10: + // No break on purpose + case 11: + // No break on purpose + case 12: + // No break on purpose + case 14: + // No break on purpose + case 15: + // No break on purpose + case 16: + // No break on purpose + case 17: + // No break on purpose + case 18: + // No break on purpose + case 19: + // No break on purpose + case 20: + // No break on purpose + case 21: + // No break on purpose + case 24: { + int victimId = -1; + int tmpRandIndx = R2_GLOBALS._randomSource.getRandomNumber(3); + + for (int i = 0; i <= 3; i++) { + if (tmpRandIndx != 3) { + if ((_gameBoardSide[tmpRandIndx]._delayCard._cardId == 0) + && isAttackPossible(tmpRandIndx, _gameBoardSide[3]._handCard[randIndx]._cardId)) + victimId = tmpRandIndx; + } + + ++tmpRandIndx; + if (tmpRandIndx > 3) + tmpRandIndx = 0; + + if (victimId != -1) + break; + } + + if (victimId != -1) { + // Useless second identical check skipped + playDelayCard(&_gameBoardSide[3]._handCard[randIndx], &_gameBoardSide[victimId]._delayCard); + return; + } + } + default: + break; + } + } + + discardCard(&_gameBoardSide[3]._handCard[randIndx]); +} + +void Scene1337::handleAutoplayPlayer2() { + if (getStationCardId(this->_gameBoardSide[2]._delayCard._cardId) == -1) + _delayedFunction = &Scene1337::handlePlayer2; + else + discardCard(&_gameBoardSide[2]._delayCard); +} + +void Scene1337::handlePlayer2() { + _selectedCard._stationPos = g_globals->_events._mousePos; + + if (R2_GLOBALS._v57810 == 200) { + // Hand + int i; + for (i = 0; i < 4; i++) { + if ((_gameBoardSide[2]._handCard[i].isIn(_selectedCard._stationPos)) && (_gameBoardSide[2]._handCard[i]._cardId != 0)) { + Card *handcard = &_gameBoardSide[2]._handCard[i]; + _selectedCard._cardId = handcard->_cardId; + _selectedCard._stationPos = handcard->_stationPos; + //warning("_selectedCard._actorName = handcard->_actorName;"); + //warning("_selectedCard._fieldE = handcard->_fieldE;"); + //warning("_selectedCard._field10 = handcard->_field10;"); + //warning("_selectedCard._field12 = handcard->_field12;"); + //warning("_selectedCard._field14 = handcard->_field14;"); + //warning("_selectedCard._field16 = handcard->_field16;"); + _selectedCard._sceneRegionId = handcard->_sceneRegionId; + _selectedCard._position = handcard->_position; + _selectedCard._yDiff = handcard->_yDiff; + _selectedCard._bounds = handcard->_bounds; + _selectedCard._resNum = handcard->_resNum; + _selectedCard._lookLineNum = handcard->_lookLineNum; + _selectedCard._talkLineNum = handcard->_talkLineNum; + _selectedCard._useLineNum = handcard->_useLineNum; + _selectedCard._action = handcard->_action; + //warning("_selectedCard._field0 = handcard->_field0;"); + _selectedCard._card._updateStartFrame = handcard->_card._updateStartFrame; + _selectedCard._card._walkStartFrame = handcard->_card._walkStartFrame; + _selectedCard._card._oldPosition = handcard->_card._oldPosition; + _selectedCard._card._percent = handcard->_card._percent; + _selectedCard._card._priority = handcard->_card._priority; + _selectedCard._card._angle = handcard->_card._angle; + _selectedCard._card._flags = handcard->_card._flags; + _selectedCard._card._xe = handcard->_card._xe; + _selectedCard._card._xs = handcard->_card._xs; + _selectedCard._card._paneRects[0] = handcard->_card._paneRects[0]; + _selectedCard._card._paneRects[1] = handcard->_card._paneRects[1]; + _selectedCard._card._visage = handcard->_card._visage; + _selectedCard._card._objectWrapper = handcard->_card._objectWrapper; + _selectedCard._card._strip = handcard->_card._strip; + _selectedCard._card._animateMode = handcard->_card._animateMode; + _selectedCard._card._frame = handcard->_card._frame; + _selectedCard._card._endFrame = handcard->_card._endFrame; + _selectedCard._card._loopCount = handcard->_card._loopCount; + _selectedCard._card._frameChange = handcard->_card._frameChange; + _selectedCard._card._numFrames = handcard->_card._numFrames; + _selectedCard._card._regionIndex = handcard->_card._regionIndex; + _selectedCard._card._mover = handcard->_card._mover; + _selectedCard._card._moveDiff = handcard->_card._moveDiff; + _selectedCard._card._moveRate = handcard->_card._moveRate; + _selectedCard._card._actorDestPos = handcard->_card._actorDestPos; + _selectedCard._card._endAction = handcard->_card._endAction; + _selectedCard._card._regionBitList = handcard->_card._regionBitList; + // _selectedCard._object1._actorName = handcard->_object1._actorName; + //warning("_selectedCard._card._fieldE = handcard->_card._fieldE;"); + //warning("_selectedCard._card._field10 = handcard->_card._field10;"); + //warning("_selectedCard._card._field12 = handcard->_card._field12;"); + //warning("_selectedCard._card._field14 = handcard->_card._field14;"); + //warning("_selectedCard._card._field16 = handcard->_card._field16;"); + + _gameBoardSide[2]._handCard[i]._cardId = 0; + _gameBoardSide[2]._handCard[i]._card.remove(); + break; + } + } + + if (i == 4) { + handleClick(1, _selectedCard._stationPos); + handleAutoplayPlayer2(); + return; + } else { + setCursorData(1332, _selectedCard._card._strip, _selectedCard._card._frame); + R2_GLOBALS._sceneObjects->draw(); + } + } else if (R2_GLOBALS._v57810 == 300) { + // Eye + handleClick(3, _selectedCard._stationPos); + handleAutoplayPlayer2(); + return; + } else { + // The original code is calling a function full of dead code. + // Only this message remains after a cleanup. + MessageDialog::show(WRONG_ANSWER_MSG, OK_BTN_STRING); + // + handleAutoplayPlayer2(); + return; + } + + Event event; + bool found; + for (;;) { + if ( ((g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN)) && (event.btnState == BTNSHIFT_RIGHT)) + || (g_globals->_events.getEvent(event, EVENT_KEYPRESS)) ){ + _selectedCard._stationPos = g_globals->_events._mousePos; + found = false; + + for (int i = 0; i <= 3; i ++) { + if (_gameBoardSide[2]._handCard[i].isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if (_gameBoardSide[2]._handCard[i]._cardId == 0) { + _gameBoardSide[2]._handCard[i]._cardId = _selectedCard._cardId; + _gameBoardSide[2]._handCard[i]._card.postInit(); + _gameBoardSide[2]._handCard[i]._card.hide(); + _gameBoardSide[2]._handCard[i]._card.setVisage(1332); + _gameBoardSide[2]._handCard[i]._card.setPosition(_gameBoardSide[2]._handCard[i]._stationPos, 0); + _gameBoardSide[2]._handCard[i]._card.fixPriority(170); + setAnimationInfo(&_gameBoardSide[2]._handCard[i]); + setCursorData(5, 1, 4); + _currentPlayerNumb--; + _showPlayerTurn = false; + handleNextTurn(); + return; + } else { + actionDisplay(1330, 127, 159, 10, 1, 200, 0, 7, 0, 154, 154); + found = true; + } + break; + } + } + + if (!found) { + if (_discardPile.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + discardCard(&_selectedCard); + return; + } else if (_selectedCard._cardId == 1) { + bool isInCardFl = false; + int i; + for (i = 0; i <= 7; i++) { + if (_gameBoardSide[2]._outpostStation[i].isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + isInCardFl = true; + break; + } + } + + if ((isInCardFl) && (_gameBoardSide[2]._outpostStation[i]._cardId == 0)) { + if (isDelayCard(_gameBoardSide[2]._delayCard._cardId) != -1) { + actionDisplay(1330, 55, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else { + playPlatformCard(&_selectedCard, &_gameBoardSide[2]._outpostStation[i]); + return; + } + } else { + actionDisplay(1330, 56, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else if (_selectedCard._cardId <= 9) { + bool isInCardFl = false; + int i; + for (i = 0; i <= 7; i++) { + if (_gameBoardSide[2]._outpostStation[i].isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + isInCardFl = true; + break; + } + } + if ((isInCardFl) && (_gameBoardSide[2]._outpostStation[i]._cardId == 1)) { + isInCardFl = false; + for (int j = 0; j <= 7; j++) { + if (_selectedCard._cardId == _gameBoardSide[2]._outpostStation[j]._cardId) { + isInCardFl = true; + break; + } + } + if (isInCardFl) { + // This station is already in place + actionDisplay(1330, 34, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (isDelayCard(_gameBoardSide[2]._delayCard._cardId) != -1) { + // You must eliminate your delay before you can play a station + actionDisplay(1330, 35, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else { + int stationCount = 0; + for (int k = 0; k <= 7; k++) { + if ((_gameBoardSide[2]._outpostStation[k]._cardId > 1) && (_gameBoardSide[2]._outpostStation[k]._cardId <= 9)) + ++stationCount; + } + + if (stationCount == 7) + _winnerId = 2; + + playStationCard(&_selectedCard, &_gameBoardSide[2]._outpostStation[i]); + return; + } + } else { + actionDisplay(1330, 37, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else if ((_selectedCard._cardId == 26) || (_selectedCard._cardId == 30) ||(_selectedCard._cardId == 32) || (_selectedCard._cardId == 28)) { + // Check anti-delay card (26 = Diplomacy, 28 = Innovation, 30 = Cure, 32 = Agreement) + if (_gameBoardSide[2]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + actionDisplay(1330, 42, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (checkAntiDelayCard(_gameBoardSide[2]._delayCard._cardId, _selectedCard._cardId)) { + playAntiDelayCard(&_selectedCard, &_gameBoardSide[2]._delayCard); + return; + } else { + if (_gameBoardSide[2]._delayCard._cardId != 0) { + switch (_gameBoardSide[2]._delayCard._cardId) { + case 11: + actionDisplay(1330, 68, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 14: + actionDisplay(1330, 80, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 16: + actionDisplay(1330, 84, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 24: + actionDisplay(1330, 96, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } else { + actionDisplay(1330, 41, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } + } else if ((getStationCardId(_selectedCard._cardId) == -1) && (isDelayCard(_selectedCard._cardId) == -1)) { + if (_selectedCard._cardId == 13) { + // Meteor Card + if (_gameBoardSide[0]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + for (int k = 0; k <= 7; k++) { + if (_gameBoardSide[0]._outpostStation[k]._cardId != 0) { + playCentralOutpostCard(&_selectedCard, 0); + return; + } + } + actionDisplay(1330, 74, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (_gameBoardSide[3]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + for (int k = 0; k <= 7; k++) { + if (_gameBoardSide[3]._outpostStation[k]._cardId != 0) { + playCentralOutpostCard(&_selectedCard, 3); + return; + } + } + actionDisplay(1330, 74, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (_gameBoardSide[1]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + for (int k = 0; k <= 7; k++) { + if (_gameBoardSide[1]._outpostStation[k]._cardId == 0) { + playCentralOutpostCard(&_selectedCard, 1); + return; + } + } + actionDisplay(1330, 74, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else { + actionDisplay(1330, 128, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else if (_selectedCard._cardId == 25) { + // Thief card + if (_gameBoardSide[0]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if ( (_gameBoardSide[0]._handCard[0]._cardId != 0) + || (_gameBoardSide[0]._handCard[1]._cardId != 0) + || (_gameBoardSide[0]._handCard[2]._cardId != 0) + || (_gameBoardSide[0]._handCard[3]._cardId != 0) ) { + int k; + for (k = 0; k <= 3; k++){ + if (_gameBoardSide[2]._handCard[k]._cardId == 0) + break; + } + playThieftCard(2, &_gameBoardSide[2]._handCard[k], 0); + return; + } else { + actionDisplay(1330, 99, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else if (_gameBoardSide[1]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if ( (_gameBoardSide[1]._handCard[0]._cardId != 0) + || (_gameBoardSide[1]._handCard[1]._cardId != 0) + || (_gameBoardSide[1]._handCard[2]._cardId != 0) + || (_gameBoardSide[1]._handCard[3]._cardId != 0) ) { + int k; + for (k = 0; k <= 3; k++){ + if (_gameBoardSide[2]._handCard[k]._cardId == 0) + break; + } + playThieftCard(2, &_gameBoardSide[2]._handCard[k], 1); + return; + } else { + actionDisplay(1330, 99, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } + + if (_gameBoardSide[3]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if ( (_gameBoardSide[3]._handCard[0]._cardId != 0) + || (_gameBoardSide[3]._handCard[1]._cardId != 0) + || (_gameBoardSide[3]._handCard[2]._cardId != 0) + || (_gameBoardSide[3]._handCard[3]._cardId != 0) ) { + int k; + for (k = 0; k <= 3; k++){ + if (_gameBoardSide[2]._handCard[k]._cardId == 0) + break; + } + playThieftCard(2, &_gameBoardSide[2]._handCard[k], 3); + return; + } else { + actionDisplay(1330, 99, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else { + actionDisplay(1330, 129, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else if (_selectedCard._cardId == 29) { + // Interceptor cards are used to prevent collision + actionDisplay(1330, 136, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (_selectedCard._cardId == 27) { + actionDisplay(1330, 137, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } else if (_gameBoardSide[0]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if (_gameBoardSide[0]._delayCard._cardId != 0) { + actionDisplay(1330, 15, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (!isAttackPossible(0, _selectedCard._cardId)) { + switch (_selectedCard._cardId) { + case 10: + actionDisplay(1330, 66, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 12: + actionDisplay(1330, 70, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + actionDisplay(1330, 82, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 17: + actionDisplay(1330, 86, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 18: + actionDisplay(1330, 88, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 19: + actionDisplay(1330, 90, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + actionDisplay(1330, 92, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 21: + actionDisplay(1330, 94, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } else { + playDelayCard(&_selectedCard, &_gameBoardSide[0]._delayCard); + return; + } + } else if (_gameBoardSide[3]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if (_gameBoardSide[3]._delayCard._cardId != 0) { + actionDisplay(1330, 17, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (!isAttackPossible(3, _selectedCard._cardId)) { + switch (_selectedCard._cardId) { + case 10: + actionDisplay(1330, 66, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 12: + actionDisplay(1330, 70, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + actionDisplay(1330, 82, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 17: + actionDisplay(1330, 86, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 18: + actionDisplay(1330, 88, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 19: + actionDisplay(1330, 90, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + actionDisplay(1330, 92, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 21: + actionDisplay(1330, 94, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } else { + playDelayCard(&_selectedCard, &_gameBoardSide[3]._delayCard); + return; + } + } else if (_gameBoardSide[1]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { + if (_gameBoardSide[1]._delayCard._cardId != 0) { + actionDisplay(1330, 19, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } else if (!isAttackPossible(1, _selectedCard._cardId)) { + switch (_selectedCard._cardId) { + case 10: + actionDisplay(1330, 66, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 12: + actionDisplay(1330, 70, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 15: + actionDisplay(1330, 82, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 17: + actionDisplay(1330, 86, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 18: + actionDisplay(1330, 88, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 19: + actionDisplay(1330, 90, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 20: + actionDisplay(1330, 92, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + case 21: + actionDisplay(1330, 94, 159, 10, 1, 200, 0, 7, 0, 154, 154); + break; + default: + break; + } + } else { + playDelayCard(&_selectedCard, &_gameBoardSide[1]._delayCard); + return; + } + } else { + actionDisplay(1330, 38, 159, 10, 1, 200, 0, 7, 0, 154, 154); + } + } + } else { + g_globals->_scenePalette.signalListeners(); + R2_GLOBALS._sceneObjects->draw(); + g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); + } + + g_globals->_sceneObjects->recurse(SceneHandler::dispatchObject); + } +} + +void Scene1337::updateCursorId(int cursorId, bool updateFl) { + if ((R2_GLOBALS._v57709 != 0) || (R2_GLOBALS._v5780C != 0)) + return; + + R2_GLOBALS._mouseCursorId = cursorId; + + if (updateFl) { + R2_GLOBALS._mouseCursorId++; + + if (R2_GLOBALS._mouseCursorId < 1) + R2_GLOBALS._mouseCursorId = 2; + + if (R2_GLOBALS._mouseCursorId > 2) + R2_GLOBALS._mouseCursorId = 1; + } + + // The original was using an intermediate function to call setCursorData. + // It has been removed to improve readability + if (R2_GLOBALS._mouseCursorId == 1) { + R2_GLOBALS._v57810 = 200; + setCursorData(5, 1, 4); + } else if (R2_GLOBALS._mouseCursorId == 2) { + R2_GLOBALS._v57810 = 300; + setCursorData(5, 1, 5); + } else { + R2_GLOBALS._v57810 = 0; + setCursorData(5, 0, 0); + } +} + +void Scene1337::setCursorData(int resNum, int rlbNum, int frameNum) { + _cursorCurRes = resNum; + _cursorCurStrip = rlbNum; + _cursorCurFrame = frameNum; + + if (!frameNum) { + // Should be a hardcoded cursor displaying only a dot. + // FIXME: Use another cursor when possible + R2_GLOBALS._events.setCursor(CURSOR_CROSSHAIRS); + } else { + // TODO: The original was using some resource caching, which was useless and complex + // and which has been removed. This cursor behavior clearly made intensive use of this caching... + // We now have to find a way to cache these cursor pointers and avoid loading them multiple times per seconds + uint size; + byte *cursor = g_resourceManager->getSubResource(resNum, rlbNum, frameNum, &size); + // Decode the cursor + GfxSurface s = surfaceFromRes(cursor); + + Graphics::Surface surface = s.lockSurface(); + const byte *cursorData = (const byte *)surface.getPixels(); + CursorMan.replaceCursor(cursorData, surface.w, surface.h, s._centroid.x, s._centroid.y, s._transColor); + s.unlockSurface(); + + DEALLOCATE(cursor); + } +} + +void Scene1337::subD18F5() { + if (R2_GLOBALS._v57709 == 0) + // The original restores a copy of the default cursor (the hand), which isn't possible with our implementation + // We reload that cursor instead. + setCursorData(5, 1, 4); + + ++R2_GLOBALS._v57709; +} + +void Scene1337::subD1917() { + if (R2_GLOBALS._v57709 != 0) { + R2_GLOBALS._v57709--; + if (R2_GLOBALS._v57709 == 0) { + // The original was using an intermediate function to call setCursorData. + // It has been removed to improve readability + setCursorData(5, _cursorCurStrip, _cursorCurFrame); + } + } +} + +void Scene1337::subD1940(bool flag) { + if (flag) + ++R2_GLOBALS._v5780C; + else if (R2_GLOBALS._v5780C != 0) + --R2_GLOBALS._v5780C; +} + +void Scene1337::subD1975(int arg1, int arg2) { + // No implementation required in ScummVM: Mouse handling with tons of caching +} + +void Scene1337::OptionsDialog::show() { + OptionsDialog *dlg = new OptionsDialog(); + dlg->draw(); + + // Show the dialog + GfxButton *btn = dlg->execute(NULL); + + // Figure out the new selected character + if (btn == &dlg->_quitGame) + R2_GLOBALS._sceneManager.changeScene(125); + else if (btn == &dlg->_restartGame) + R2_GLOBALS._sceneManager.changeScene(1330); + + // Remove the dialog + dlg->remove(); + delete dlg; +} + +Scene1337::OptionsDialog::OptionsDialog() { + // Set the elements text + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + _autoplay.setText(scene->_autoplay ? AUTO_PLAY_ON : AUTO_PLAY_OFF); + _restartGame.setText(START_NEW_CARD_GAME); + _quitGame.setText(QUIT_CARD_GAME); + _continueGame.setText(CONTINUE_CARD_GAME); + + // Set position of the elements + _autoplay._bounds.moveTo(5, 2); + _restartGame._bounds.moveTo(5, _autoplay._bounds.bottom + 2); + _quitGame._bounds.moveTo(5, _restartGame._bounds.bottom + 2); + _continueGame._bounds.moveTo(5, _quitGame._bounds.bottom + 2); + + // Add the items to the dialog + addElements(&_autoplay, &_restartGame, &_quitGame, &_continueGame, NULL); + + // Set the dialog size and position + frame(); + _bounds.collapse(-6, -6); + setCenter(160, 100); +} + +GfxButton *Scene1337::OptionsDialog::execute(GfxButton *defaultButton) { + _gfxManager.activate(); + + // Event loop + GfxButton *selectedButton = NULL; + + bool breakFlag = false; + while (!g_vm->shouldQuit() && !breakFlag) { + Event event; + while (g_globals->_events.getEvent(event) && !breakFlag) { + // Adjust mouse positions to be relative within the dialog + event.mousePos.x -= _gfxManager._bounds.left; + event.mousePos.y -= _gfxManager._bounds.top; + + for (GfxElementList::iterator i = _elements.begin(); i != _elements.end(); ++i) { + if ((*i)->process(event)) + selectedButton = static_cast<GfxButton *>(*i); + } + + if (selectedButton == &_autoplay) { + // Toggle Autoplay + selectedButton = NULL; + Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; + scene->_autoplay = !scene->_autoplay; + + _autoplay.setText(scene->_autoplay ? AUTO_PLAY_ON : AUTO_PLAY_OFF); + _autoplay.draw(); + } else if (selectedButton) { + breakFlag = true; + break; + } else if (!event.handled) { + if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) { + selectedButton = NULL; + breakFlag = true; + break; + } + } + } + + g_system->delayMillis(10); + GLOBALS._screenSurface.updateScreen(); + } + + _gfxManager.deactivate(); + return selectedButton; +} + +} // End of namespace Ringworld2 +} // End of namespace TsAGE diff --git a/engines/tsage/ringworld2/ringworld2_outpost.h b/engines/tsage/ringworld2/ringworld2_outpost.h new file mode 100644 index 0000000000..6c6952c196 --- /dev/null +++ b/engines/tsage/ringworld2/ringworld2_outpost.h @@ -0,0 +1,259 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef TSAGE_RINGWORLD2_OUTPOST_H +#define TSAGE_RINGWORLD2_OUTPOST_H + +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/scenes.h" +#include "tsage/globals.h" +#include "tsage/sound.h" +#include "tsage/ringworld2/ringworld2_logic.h" + +namespace TsAGE { + +namespace Ringworld2 { + +using namespace TsAGE; + +class Scene1337 : public SceneExt { + class OptionsDialog: public GfxDialog { + private: + GfxButton _autoplay; + GfxButton _restartGame; + GfxButton _quitGame; + GfxButton _continueGame; + + OptionsDialog(); + virtual ~OptionsDialog() {} + virtual GfxButton *execute(GfxButton *defaultButton); + public: + static void show(); + }; + + class Card: public SceneHotspot { + public: + SceneObject _card; + + int _cardId; + Common::Point _stationPos; + + Card(); + void synchronize(Serializer &s); + bool isIn(Common::Point pt); + }; + + class GameBoardSide: public SceneHotspot { + public: + Card _handCard[4]; + Card _outpostStation[8]; + Card _delayCard; + Card _emptyStationPos; + + Common::Point _card1Pos; + Common::Point _card2Pos; + Common::Point _card3Pos; + Common::Point _card4Pos; + int _frameNum; + + GameBoardSide(); + void synchronize(Serializer &s); + }; + + class Action1337: public Action { + public: + void waitFrames(int32 frameCount); + }; + + class Action1: public Action1337 { + public: + void signal(); + }; + class Action2: public Action1337 { + public: + void signal(); + }; + class Action3: public Action1337 { + public: + void signal(); + }; + class Action4: public Action1337 { + public: + void signal(); + }; + class Action5: public Action1337 { + public: + void signal(); + }; + class Action6: public Action1337 { + public: + void signal(); + }; + class Action7: public Action1337 { + public: + void signal(); + }; + class Action8: public Action1337 { + public: + void signal(); + }; + class Action9: public Action1337 { + public: + void signal(); + }; + class Action10: public Action1337 { + public: + void signal(); + }; + class Action11: public Action1337 { + public: + void signal(); + }; + class Action12: public Action1337 { + public: + void signal(); + }; + class Action13: public Action1337 { + public: + void signal(); + }; +public: + Action1 _action1; + Action2 _action2; + Action3 _action3; + Action4 _action4; + Action5 _action5; + Action6 _action6; + Action7 _action7; + Action8 _action8; + Action9 _action9; + Action10 _action10; + Action11 _action11; + Action12 _action12; + Action13 _action13; + + typedef void (Scene1337::*FunctionPtrType)(); + FunctionPtrType _delayedFunction; + + bool _autoplay; + bool _shuffleEndedFl; + bool _showPlayerTurn; + bool _displayHelpFl; + bool _instructionsDisplayedFl; + + // Discarded cards are put in the available cards pile, with an higher index so there no conflict + int _currentDiscardIndex; + int _availableCardsPile[100]; + int _cardsAvailableNumb; + int _currentPlayerNumb; + int _actionPlayerIdx; + int _actionVictimIdx; + int _winnerId; + int _instructionsWaitCount; + int _cursorCurRes; + int _cursorCurStrip; + int _cursorCurFrame; + + ASound _aSound1; + ASound _aSound2; + GameBoardSide _gameBoardSide[4]; + SceneActor _helpIcon; + SceneActor _stockPile; + SceneItem _actionItem; + SceneObject _currentPlayerArrow; + + Card *_actionCard1; + Card *_actionCard2; + Card *_actionCard3; + Card _animatedCard; + Card _shuffleAnimation; + Card _discardedPlatformCard; + Card _selectedCard; + Card _discardPile; + Card _stockCard; + + SceneObject _upperDisplayCard[8]; + SceneObject _lowerDisplayCard[8]; + + Scene1337(); + virtual void synchronize(Serializer &s); + + void actionDisplay(int resNum, int lineNum, int x, int y, int keepOnScreen, int width, int textMode, int fontNum, int colFG, int colBGExt, int colFGExt); + void setAnimationInfo(Card *card); + void handleNextTurn(); + void handlePlayerTurn(); + bool isStationCard(int cardId); + bool isStopConstructionCard(int cardId); + int getStationId(int playerId, int handCardId); + int findPlatformCardInHand(int playerId); + int findMeteorCardInHand(int playerId); + int findThieftCardInHand(int playerId); + int isDelayCard(int cardId); + int getStationCardId(int cardId); + void handlePlayer01Discard(int playerId); + void playThieftCard(int playerId, Card *card, int victimId); + int getPreventionCardId(int cardId); + bool isAttackPossible(int victimId, int cardId); + int getPlayerWithOutpost(int playerId); + bool checkAntiDelayCard(int delayCardId, int cardId); + void playStationCard(Card *station, Card *platform); + void playDelayCard(Card *card, Card *dest); + void playPlatformCard(Card *card, Card *dest); + void playAntiDelayCard(Card *card, Card *dest); + Card *getStationCard(int arg1); + void playCentralOutpostCard(Card *card, int playerId); + int getRandomCardFromHand(int playerId); + void discardCard(Card *card); + void subC4CD2(); + void subC4CEC(); + void playInterceptorCard(Card *subObj1, Card *subObj2); + void displayDialog(int dialogNumb); + void subPostInit(); + void displayInstructions(); + void suggestInstructions(); + void shuffleCards(); + void dealCards(); + void showOptionsDialog(); + void handleClick(int arg1, Common::Point pt); + void handlePlayer0(); + void handlePlayer1(); + void handlePlayer2(); + void handlePlayer3(); + void handleAutoplayPlayer2(); + void updateCursorId(int arg1, bool arg2); + void setCursorData(int resNum, int rlbNum, int frameNum); + void subD18F5(); + void subD1917(); + void subD1940(bool flag); + void subD1975(int arg1, int arg2); + + virtual void postInit(SceneObjectList *OwnerList = NULL); + virtual void remove(); + virtual void process(Event &event); + virtual void dispatch(); +}; + +} // End of namespace Ringworld2 +} // End of namespace TsAGE + +#endif diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp index b82565332a..573cbbb29a 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp @@ -627,7 +627,7 @@ void Scene125::postInit(SceneObjectList *OwnerList) { SceneExt::postInit(); _palette.loadPalette(0); - if (R2_GLOBALS._sceneManager._previousScene != 125) + if ((R2_GLOBALS._sceneManager._previousScene != 125) && (R2_GLOBALS._sceneManager._previousScene != 1337) && (R2_GLOBALS._sceneManager._previousScene != 1330)) // Save the prior scene to return to when the console is turned off R2_GLOBALS._player._oldCharacterScene[R2_QUINN] = R2_GLOBALS._sceneManager._previousScene; diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.cpp b/engines/tsage/ringworld2/ringworld2_scenes1.cpp index e2c22bd0b4..81dc05e2a4 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes1.cpp @@ -1321,5469 +1321,6 @@ void Scene1100::saveCharacter(int characterIndex) { } /*-------------------------------------------------------------------------- - * Scene 1200 - Air Ducts Maze - * - *--------------------------------------------------------------------------*/ - -Scene1200::Scene1200() { - _nextCrawlDirection = 0; - _field414 = 0; - _field416 = 0; - _field418 = 0; - _field41A = 0; - _fixupMaze = false; -} - -void Scene1200::synchronize(Serializer &s) { - SceneExt::synchronize(s); - - s.syncAsSint16LE(_nextCrawlDirection); - s.syncAsSint16LE(_field414); - s.syncAsSint16LE(_field416); - s.syncAsSint16LE(_field418); - s.syncAsSint16LE(_field41A); - s.syncAsSint16LE(_fixupMaze); -} - -Scene1200::LaserPanel::LaserPanel() { -} - -void Scene1200::LaserPanel::Jumper::init(int state) { - _state = state; - - SceneActor::postInit(); - setup(1003, 1, 1); - fixPriority(255); - - switch (_state) { - case 1: - switch (R2_GLOBALS._ductMazePanel1State) { - case 1: - setFrame2(2); - setPosition(Common::Point(129, 101)); - break; - case 2: - setFrame2(3); - setPosition(Common::Point(135, 95)); - break; - default: - break; - } - break; - case 2: - switch (R2_GLOBALS._ductMazePanel2State) { - case 1: - setFrame2(2); - setPosition(Common::Point(152, 101)); - break; - case 2: - setFrame2(3); - setPosition(Common::Point(158, 122)); - break; - case 3: - setFrame2(3); - setPosition(Common::Point(135, 122)); - break; - default: - break; - } - break; - case 3: - switch (R2_GLOBALS._ductMazePanel3State) { - case 1: - setFrame2(3); - setPosition(Common::Point(158, 95)); - break; - case 2: - setFrame2(2); - setPosition(Common::Point(175, 101)); - break; - default: - break; - } - break; - default: - break; - } - - setDetails(1200, 12, -1, -1, 2, (SceneItem *) NULL); -} - -bool Scene1200::LaserPanel::Jumper::startAction(CursorType action, Event &event) { - if (action != CURSOR_USE) - return SceneActor::startAction(action, event); - - R2_GLOBALS._sound2.play(260); - switch (_state) { - case 1: - if (R2_GLOBALS._ductMazePanel1State == 1) { - R2_GLOBALS._ductMazePanel1State = 2; - setFrame2(3); - setPosition(Common::Point(135, 95)); - } else { - R2_GLOBALS._ductMazePanel1State = 1; - setFrame2(2); - setPosition(Common::Point(129, 101)); - } - break; - case 2: - ++R2_GLOBALS._ductMazePanel2State; - if (R2_GLOBALS._ductMazePanel2State == 4) - R2_GLOBALS._ductMazePanel2State = 1; - - switch (R2_GLOBALS._ductMazePanel2State) { - case 1: - setFrame2(2); - setPosition(Common::Point(152, 101)); - break; - case 2: - setFrame2(3); - setPosition(Common::Point(158, 122)); - break; - case 3: - setFrame2(3); - setPosition(Common::Point(135, 122)); - break; - default: - break; - } - break; - case 3: - if (R2_GLOBALS._ductMazePanel3State == 1) { - R2_GLOBALS._ductMazePanel3State = 2; - setFrame2(2); - setPosition(Common::Point(175, 101)); - } else { - R2_GLOBALS._ductMazePanel3State = 1; - setFrame2(3); - setPosition(Common::Point(158, 95)); - } - break; - default: - break; - } - - Scene1200 *scene = (Scene1200 *)R2_GLOBALS._sceneManager._scene; - scene->_field418 = 0; - - if ((R2_GLOBALS._ductMazePanel1State == 1) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) - scene->_field418 = 1; - else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) - scene->_field418 = 2; - else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 2)) - scene->_field418 = 3; - else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 3) && (R2_GLOBALS._ductMazePanel3State == 1)) - scene->_field418 = 4; - - return true; -} - -void Scene1200::LaserPanel::postInit(SceneObjectList *OwnerList) { - Scene1200 *scene = (Scene1200 *)R2_GLOBALS._sceneManager._scene; - - scene->_field41A = 1; - R2_GLOBALS._events.setCursor(CURSOR_USE); - setup2(1003, 1, 1, 100, 40); - setup3(1200, 11, -1, -1); - R2_GLOBALS._sound2.play(259); - _jumper1.init(1); - _jumper2.init(2); - _jumper3.init(3); - - R2_GLOBALS._player._canWalk = false; -} - -void Scene1200::LaserPanel::remove() { - Scene1200 *scene = (Scene1200 *)R2_GLOBALS._sceneManager._scene; - - scene->_field41A = 0; - scene->_sceneAreas.remove(&_jumper1); - scene->_sceneAreas.remove(&_jumper2); - scene->_sceneAreas.remove(&_jumper3); - _jumper1.remove(); - _jumper2.remove(); - _jumper3.remove(); - - ModalWindow::remove(); - R2_GLOBALS._player._canWalk = true; -} - -void Scene1200::postInit(SceneObjectList *OwnerList) { - loadScene(1200); - SceneExt::postInit(); - - if (R2_GLOBALS._sceneManager._previousScene < 3200) - R2_GLOBALS._sound1.play(257); - - _nextCrawlDirection = CRAWL_EAST; - _field414 = 0; - _field416 = 0; - _field418 = 0; - _field41A = 0; - - if ((R2_GLOBALS._ductMazePanel1State == 1) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) - _field418 = 1; - else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 1)) - _field418 = 2; - else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 1) && (R2_GLOBALS._ductMazePanel3State == 2)) - _field418 = 3; - else if ((R2_GLOBALS._ductMazePanel1State == 2) && (R2_GLOBALS._ductMazePanel2State == 3) && (R2_GLOBALS._ductMazePanel3State == 1)) - _field418 = 4; - - R2_GLOBALS._player.postInit(); - R2_GLOBALS._player.disableControl(); - R2_GLOBALS._player.setup(3156, 1, 6); - R2_GLOBALS._player.setPosition(Common::Point(160, 70)); - R2_GLOBALS._player._numFrames = 10; - R2_GLOBALS._player._oldCharacterScene[R2_MIRANDA] = 1200; - - _actor1.postInit(); - _actor1.hide(); - - _mazeUI.setDisplayBounds(Rect(110, 20, 210, 120)); - - _mazeUI.postInit(); - _mazeUI.load(1); - _mazeUI.setMazePosition(R2_GLOBALS._ventCellPos); - - R2_GLOBALS._player.enableControl(); - _item1.setDetails(Rect(0, 0, 320, 200), 1200, 0, 1, 2, 1, NULL); -} - -void Scene1200::signal() { - switch (_sceneMode++) { - case 1: - // No break on purpose - case 1200: - // No break on purpose - case 1201: - // No break on purpose - case 1202: - // No break on purpose - case 1203: - R2_GLOBALS._player.enableControl(); - // CHECKME: The original is calling _eventManager.waitEvent(); - _sceneMode = 2; - break; - case 10: - _field416 = 1; - _field414 = 6; - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.setStrip(1); - R2_GLOBALS._player.setFrame(5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 11: - // No break on purpose - case 21: - // No break on purpose - case 31: - // No break on purpose - case 41: - _field416 = 0; - break; - case 12: - _field414 = 14; - R2_GLOBALS._player._numFrames = 10; - R2_GLOBALS._player.setup(3155, 1, 4); - R2_GLOBALS._player.setPosition(Common::Point(160, 70)); - R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); - break; - case 13: - // No break on purpose - case 16: - // No break on purpose - case 23: - // No break on purpose - case 26: - // No break on purpose - case 33: - // No break on purpose - case 36: - // No break on purpose - case 43: - // No break on purpose - case 46: - R2_GLOBALS._player.setFrame(4); - _sceneMode = 1; - setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); - break; - case 15: - // No break on purpose - case 25: - // No break on purpose - case 35: - // No break on purpose - case 45: - _field414 = 20; - R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); - break; - case 20: - _field416 = 1; - _field414 = 6; - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.setStrip(2); - R2_GLOBALS._player.setFrame(5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 22: - _field414 = 14; - R2_GLOBALS._player._numFrames = 10; - R2_GLOBALS._player.setup(3155, 2, 4); - R2_GLOBALS._player.setPosition(Common::Point(160, 70)); - R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); - break; - case 30: - _field416 = 1; - _field414 = 6; - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.setStrip(3); - R2_GLOBALS._player.setFrame(5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 32: - _field414 = 14; - R2_GLOBALS._player._numFrames = 10; - R2_GLOBALS._player.setup(3155, 3, 4); - R2_GLOBALS._player.setPosition(Common::Point(160, 70)); - R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); - break; - case 40: - _field416 = 1; - _field414 = 6; - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.setStrip(4); - R2_GLOBALS._player.setFrame(5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 42: - _field414 = 14; - R2_GLOBALS._player._numFrames = 10; - R2_GLOBALS._player.setup(3155, 4, 4); - R2_GLOBALS._player.setPosition(Common::Point(160, 70)); - R2_GLOBALS._player.animate(ANIM_MODE_2, NULL); - break; - case 50: - // No break on purpose - case 55: - // No break on purpose - case 60: - R2_GLOBALS._player.setup(3156, 5, 1); - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 51: - // No break on purpose - case 56: - // No break on purpose - case 117: - R2_GLOBALS._player.setup(3157, 1, 1); - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 52: - // No break on purpose - case 82: - // No break on purpose - case 118: - R2_GLOBALS._player.setup(3156, 3, 6); - _sceneMode = 1; - setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); - break; - case 57: - // No break on purpose - case 91: - // No break on purpose - case 96: - R2_GLOBALS._player.setup(3157, 2, 1); - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 58: - // No break on purpose - case 92: - // No break on purpose - case 122: - R2_GLOBALS._player.setup(3156, 2, 6); - _sceneMode = 1; - setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); - break; - case 61: - R2_GLOBALS._player.setup(3157, 4, 5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 62: - // No break on purpose - case 72: - // No break on purpose - case 98: - R2_GLOBALS._player.setup(3156, 4, 6); - _sceneMode = 1; - setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); - break; - case 70: - // No break on purpose - case 75: - // No break on purpose - case 80: - R2_GLOBALS._player.setup(3156, 6, 1); - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 71: - // No break on purpose - case 76: - // No break on purpose - case 97: - R2_GLOBALS._player.setup(3157, 3, 1); - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 77: - // No break on purpose - case 111: - // No break on purpose - case 116: - R2_GLOBALS._player.setup(3157, 4, 1); - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 78: - // No break on purpose - case 102: - // No break on purpose - case 112: - R2_GLOBALS._player.setup(3156, 1, 6); - _sceneMode = 1; - setAction(&_sequenceManager, this, 1, &R2_GLOBALS._player, NULL); - break; - case 81: - R2_GLOBALS._player.setup(3157, 2, 5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 90: - // No break on purpose - case 95: - // No break on purpose - case 100: - R2_GLOBALS._player.setup(3156, 7, 1); - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 101: - R2_GLOBALS._player.setup(3157, 1, 5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - case 110: - // No break on purpose - case 115: - // No break on purpose - case 120: - R2_GLOBALS._player.setup(3156, 8, 1); - R2_GLOBALS._player._numFrames = 5; - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - break; - case 121: - R2_GLOBALS._player.setup(3157, 3, 5); - R2_GLOBALS._player.animate(ANIM_MODE_6, this); - break; - default: - // CHECKME: The original is walling _eventManager.waitEvent(); - _sceneMode = 2; - break; - } -} - -void Scene1200::process(Event &event) { - if (_field414 != 0) - return; - - Scene::process(event); - - if (!R2_GLOBALS._player._canWalk) - return; - - if (event.eventType == EVENT_BUTTON_DOWN) { - Common::Point cellPos = R2_GLOBALS._ventCellPos; - _mazeUI.pixelToCellXY(cellPos); - - int cellId = _mazeUI.getCellFromPixelXY(event.mousePos); - switch (R2_GLOBALS._events.getCursor()) { - case CURSOR_WALK: - event.handled = true; - if ((event.mousePos.x > 179) && (event.mousePos.x < 210) && (event.mousePos.y > 50) && (event.mousePos.y < 89)) - startCrawling(CRAWL_EAST); - - if ((event.mousePos.x > 109) && (event.mousePos.x < 140) && (event.mousePos.y > 50) && (event.mousePos.y < 89)) - startCrawling(CRAWL_WEST); - - if ((event.mousePos.x > 140) && (event.mousePos.x < 179) && (event.mousePos.y > 89) && (event.mousePos.y < 120)) - startCrawling(CRAWL_SOUTH); - - if ((event.mousePos.x > 140) && (event.mousePos.x < 179) && (event.mousePos.y > 19) && (event.mousePos.y < 50)) - startCrawling(CRAWL_NORTH); - break; - case CURSOR_USE: - if (cellId > 36) { - if ( ((cellPos.x == 3) && (cellPos.y == 33)) - || ((cellPos.x == 7) && (cellPos.y == 33)) - || ((cellPos.x == 33) && (cellPos.y == 41)) - || ((cellPos.x == 5) && (cellPos.y == 5)) - || ((cellPos.x == 13) && (cellPos.y == 21)) - || ((cellPos.x == 17) && (cellPos.y == 21)) - || ((cellPos.x == 17) && (cellPos.y == 5)) - || ((cellPos.x == 17) && (cellPos.y == 9)) - || ((cellPos.x == 29) && (cellPos.y == 17)) - || ((cellPos.x == 33) && (cellPos.y == 17)) - || ((cellPos.x == 35) && (cellPos.y == 17)) - || ((cellPos.x == 41) && (cellPos.y == 21)) ) { - _laserPanel.postInit(); - event.handled = true; - } - } - - if ((cellId == 1) || (cellId == 4) || (cellId == 11) || (cellId == 14)) { - if ( ((cellPos.x == 3) && (cellPos.y == 9)) - || ((cellPos.x == 11) && (cellPos.y == 27)) - || ((cellPos.x == 17) && (cellPos.y == 7)) - || ((cellPos.x == 17) && (cellPos.y == 27)) - || ((cellPos.x == 17) && (cellPos.y == 33)) - || (cellPos.x == 33) ) { - switch (cellPos.x) { - case 3: - R2_GLOBALS._sceneManager.changeScene(3150); - break; - case 33: - if (R2_GLOBALS._scientistConvIndex >= 4) - R2_GLOBALS._sceneManager.changeScene(3250); - else - SceneItem::display(1200, 6, 0, 280, 1, 160, 9, 1, 2, 20, 7, 154, LIST_END); - break; - default: - SceneItem::display(1200, 5, 0, 280, 1, 160, 9, 1, 2, 20, 7, 154, LIST_END); - break; - } - event.handled = true; - } - } - break; - case CURSOR_LOOK: - if ((cellId == 1) || (cellId == 4) || (cellId == 11) || (cellId == 14)) { - event.handled = true; - switch (cellPos.x) { - case 3: - // It was your cell. - SceneItem::display(1200, 8, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - break; - case 9: - R2_GLOBALS._sceneManager.changeScene(3240); - break; - case 11: - if (cellPos.y == 27) - R2_GLOBALS._sceneManager.changeScene(3210); - else - // A vent grill - SceneItem::display(1200, 10, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - break; - case 17: - switch (cellPos.y) { - case 5: - R2_GLOBALS._sceneManager.changeScene(3230); - break; - case 21: - R2_GLOBALS._sceneManager.changeScene(3220); - break; - case 33: - R2_GLOBALS._sceneManager.changeScene(3200); - break; - default: - // A vent grill - SceneItem::display(1200, 10, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - break; - } - break; - case 33: - R2_GLOBALS._sceneManager.changeScene(3245); - break; - default: - SceneItem::display(1200, 10, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - break; - } - } - if (cellId > 36) { - // "An anti-pest laser" - event.handled = true; - SceneItem::display(1200, 9, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - } - break; - case CURSOR_TALK: - event.handled = true; - break; - default: - return; - } - } else if (event.eventType == EVENT_KEYPRESS) { - if (_field414) { - event.handled = false; - return; - } - - switch (event.kbd.keycode) { - case Common::KEYCODE_KP8: - case Common::KEYCODE_UP: - startCrawling(CRAWL_NORTH); - break; - case Common::KEYCODE_KP4: - case Common::KEYCODE_LEFT: - startCrawling(CRAWL_WEST); - break; - case Common::KEYCODE_KP6: - case Common::KEYCODE_RIGHT: - startCrawling(CRAWL_EAST); - break; - case Common::KEYCODE_KP2: - case Common::KEYCODE_DOWN: - startCrawling(CRAWL_SOUTH); - break; - default: - event.handled = false; - return; - break; - } - } else - return; -} - -void Scene1200::dispatch() { - Rect tmpRect; - Scene::dispatch(); - - if (_fixupMaze) { - _mazeUI.setMazePosition(R2_GLOBALS._ventCellPos); - //_mazeUI.draw(); - _fixupMaze = false; - } - - if (_field414 != 0) { - tmpRect.set(110, 20, 210, 120); - _field414--; - - switch (_nextCrawlDirection) { - case CRAWL_EAST: - R2_GLOBALS._ventCellPos.x += 2; - break; - case CRAWL_WEST: - R2_GLOBALS._ventCellPos.x -= 2; - break; - case CRAWL_SOUTH: - R2_GLOBALS._ventCellPos.y += 2; - break; - case CRAWL_NORTH: - R2_GLOBALS._ventCellPos.y -= 2; - break; - default: - break; - } - - _mazeUI.setMazePosition(R2_GLOBALS._ventCellPos); - //_mazeUI.draw(); - - if (_field416 != 0) { - switch(_nextCrawlDirection) { - case CRAWL_EAST: - R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x - 2, R2_GLOBALS._player._position.y)); - break; - case CRAWL_WEST: - R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x + 2, R2_GLOBALS._player._position.y)); - break; - case CRAWL_SOUTH: - R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x, R2_GLOBALS._player._position.y - 2)); - break; - case CRAWL_NORTH: - R2_GLOBALS._player.setPosition(Common::Point(R2_GLOBALS._player._position.x, R2_GLOBALS._player._position.y + 2)); - break; - default: - break; - } - } - if (_field414 == 0) { - if (_field416 == 0) - R2_GLOBALS._player.animate(ANIM_MODE_NONE, NULL); - signal(); - } - } -} - -void Scene1200::saveCharacter(int characterIndex) { - R2_GLOBALS._sound1.fadeOut2(NULL); - SceneExt::saveCharacter(characterIndex); -} - -void Scene1200::startCrawling(CrawlDirection dir) { - Common::Point cellPos = R2_GLOBALS._ventCellPos; - _mazeUI.pixelToCellXY(cellPos); - - switch (dir) { - case CRAWL_EAST: - if ( ((_mazeUI.getCellFromPixelXY(Common::Point(200, 50)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(200, 88)) > 36)) - && ( ((cellPos.x == 3) && (cellPos.y == 33) && (_field418 != 4)) - || ((cellPos.x == 13) && (cellPos.y == 21) && (_field418 != 2)) - || ((cellPos.x == 29) && (cellPos.y == 17) && (_field418 != 1)) - || ((cellPos.x == 33) && (cellPos.y == 41)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1200; - setAction(&_sequenceManager, this, 1200, &_actor1, NULL); - } else if (_mazeUI.getCellFromPixelXY(Common::Point(200, 69)) == 36) { - switch (_nextCrawlDirection) { - case CRAWL_EAST: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 15; - else - _sceneMode = 10; - break; - case CRAWL_WEST: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 76; - else - _sceneMode = 75; - break; - case CRAWL_SOUTH: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 101; - else - _sceneMode = 100; - break; - case CRAWL_NORTH: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 111; - else - _sceneMode = 110; - break; - default: - break; - } - R2_GLOBALS._player.disableControl(); - _nextCrawlDirection = 1; - signal(); - } - break; - case CRAWL_WEST: - if ( ((_mazeUI.getCellFromPixelXY(Common::Point(120, 50)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(120, 88)) > 36)) - && ( ((cellPos.x == 7) && (cellPos.y == 33) && (_field418 != 4)) - || ((cellPos.x == 17) && (cellPos.y == 21) && (_field418 != 2)) - || ((cellPos.x == 33) && (cellPos.y == 17) && (_field418 != 1)) - || ((cellPos.x == 5) && (cellPos.y == 5)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1201; - setAction(&_sequenceManager, this, 1201, &_actor1, NULL); - } else if (_mazeUI.getCellFromPixelXY(Common::Point(120, 69)) == 36) { - switch (_nextCrawlDirection) { - case CRAWL_EAST: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 56; - else - _sceneMode = 55; - break; - case CRAWL_WEST: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 25; - else - _sceneMode = 20; - break; - case CRAWL_SOUTH: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 91; - else - _sceneMode = 90; - break; - case CRAWL_NORTH: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 121; - else - _sceneMode = 120; - break; - default: - break; - } - R2_GLOBALS._player.disableControl(); - _nextCrawlDirection = 2; - signal(); - } - break; - case CRAWL_SOUTH: - if ( ((_mazeUI.getCellFromPixelXY(Common::Point(140, 110)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(178, 110)) > 36)) - && ( ((cellPos.x == 17) && (cellPos.y == 5) && (_field418 != 3)) - || ((cellPos.x == 41) && (cellPos.y == 21)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1203; - setAction(&_sequenceManager, this, 1203, &_actor1, NULL); - } else if (_mazeUI.getCellFromPixelXY(Common::Point(160, 110)) == 36) { - switch (_nextCrawlDirection) { - case CRAWL_EAST: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 51; - else - _sceneMode = 50; - break; - case CRAWL_WEST: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 81; - else - _sceneMode = 80; - break; - case CRAWL_SOUTH: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 35; - else - _sceneMode = 30; - break; - case CRAWL_NORTH: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 116; - else - _sceneMode = 115; - break; - default: - break; - } - R2_GLOBALS._player.disableControl(); - _nextCrawlDirection = 3; - signal(); - } - break; - case CRAWL_NORTH: - if ( ((_mazeUI.getCellFromPixelXY(Common::Point(140, 30)) > 36) || (_mazeUI.getCellFromPixelXY(Common::Point(178, 30)) > 36)) - && ( ((cellPos.x == 17) && (cellPos.y == 9) && (_field418 != 3)) - || ((cellPos.x == 35) && (cellPos.y == 17)) ) - ) { - R2_GLOBALS._player.disableControl(); - _sceneMode = 1202; - setAction(&_sequenceManager, this, 1202, &_actor1, NULL); - } else if (_mazeUI.getCellFromPixelXY(Common::Point(160, 30)) == 36) { - switch (_nextCrawlDirection) { - case CRAWL_EAST: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 61; - else - _sceneMode = 60; - break; - case CRAWL_WEST: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 71; - else - _sceneMode = 70; - break; - case CRAWL_SOUTH: - if (R2_GLOBALS._player._visage == 3156) - _sceneMode = 96; - else - _sceneMode = 95; - break; - case CRAWL_NORTH: - if (R2_GLOBALS._player._visage == 3155) - _sceneMode = 45; - else - _sceneMode = 40; - break; - default: - _sceneMode = 1; - R2_GLOBALS._player.setup(3156, 4, 6); - break; - } - R2_GLOBALS._player.disableControl(); - _nextCrawlDirection = 4; - signal(); - } - break; - default: - break; - } -} - -/*-------------------------------------------------------------------------- - * Scene 1337 - Card game - * - *--------------------------------------------------------------------------*/ - -Scene1337::Card::Card() { - _cardId = 0; - _stationPos = Common::Point(0, 0); -} - -void Scene1337::Card::synchronize(Serializer &s) { - warning("STUBBED: Card::synchronize()"); -} - -bool Scene1337::Card::isIn(Common::Point pt) { - if ((_stationPos.x > pt.x) || (_stationPos.x + 24 < pt.x)) - return false; - - if ((_stationPos.y > pt.y) || (_stationPos.y + 24 < pt.y)) - return false; - - return true; -} - -Scene1337::GameBoardSide::GameBoardSide() { - _card1Pos = Common::Point(0, 0); - _card2Pos = Common::Point(0, 0); - _card3Pos = Common::Point(0, 0); - _card4Pos = Common::Point(0, 0); - _frameNum = 0; -} - -void Scene1337::GameBoardSide::synchronize(Serializer &s) { - warning("STUBBED: GameBoardSide::synchronize()"); -} - -Scene1337::Scene1337() { - _autoplay = false; - _cardsAvailableNumb = 0; - _currentDiscardIndex = 0; - - for (int i = 0; i < 100; i++) - _availableCardsPile[i] = 0; - - _shuffleEndedFl = false; - _currentPlayerNumb = 0; - _actionIdx1 = 0; - _actionIdx2 = 0; - _showPlayerTurn = false; - _displayHelpFl = false; - _winnerId = -1; - _instructionsDisplayedFl = false; - _instructionsWaitCount = 0; - - _delayedFunction = nullptr; - _actionCard1 = nullptr; - _actionCard2 = nullptr; - _actionCard3 = nullptr; - - _cursorCurRes = 0; - _cursorCurStrip = 0; - _cursorCurFrame = 0; -} - -void Scene1337::synchronize(Serializer &s) { - warning("STUBBED: Scene1337::synchronize()"); -} - -void Scene1337::Action1337::waitFrames(int32 frameCount) { - uint32 firstFrameNumber = g_globals->_events.getFrameNumber(); - uint32 curFrame = firstFrameNumber; - uint32 destFrame = firstFrameNumber + frameCount; - - while ((curFrame < destFrame) && !g_vm->shouldQuit()) { - TsAGE::Event event; - g_globals->_events.getEvent(event); - curFrame = g_globals->_events.getFrameNumber(); - } - - // CHECKME: The original is calling _eventManager.waitEvent(); -} - -/** - * Display instructions - */ -void Scene1337::Action1::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 1: { - scene->actionDisplay(1331, 6, 159, 10, 1, 200, 0, 7, 0, 154, 154); - R2_GLOBALS._sceneObjects->draw(); - scene->actionDisplay(1331, 7, 159, 10, 1, 200, 0, 7, 0, 154, 154); - scene->actionDisplay(1331, 8, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - scene->_gameBoardSide[1]._outpostStation[0]._cardId = 2; - scene->_gameBoardSide[1]._outpostStation[0]._card.postInit(); - scene->_gameBoardSide[1]._outpostStation[0]._card.setVisage(1332); - scene->_gameBoardSide[1]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[1]._outpostStation[0]._stationPos, 0); - scene->_gameBoardSide[1]._outpostStation[0]._card.setStrip(2); - scene->_gameBoardSide[1]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[1]._outpostStation[0]._cardId); - scene->_gameBoardSide[1]._outpostStation[0]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[1]._outpostStation[0]); - - scene->_gameBoardSide[1]._outpostStation[1]._cardId = 3; - scene->_gameBoardSide[1]._outpostStation[1]._card.postInit(); - scene->_gameBoardSide[1]._outpostStation[1]._card.setVisage(1332); - scene->_gameBoardSide[1]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[1]._outpostStation[1]._stationPos, 0); - scene->_gameBoardSide[1]._outpostStation[1]._card.setStrip(2); - scene->_gameBoardSide[1]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[1]._outpostStation[1]._cardId); - scene->_gameBoardSide[1]._outpostStation[1]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[1]._outpostStation[1]); - - scene->_gameBoardSide[2]._outpostStation[0]._cardId = 4; - scene->_gameBoardSide[2]._outpostStation[0]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[0]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[0]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[0]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[0]._cardId); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[0]); - - scene->_gameBoardSide[3]._outpostStation[0]._cardId = 5; - scene->_gameBoardSide[3]._outpostStation[0]._card.postInit(); - scene->_gameBoardSide[3]._outpostStation[0]._card.setVisage(1332); - scene->_gameBoardSide[3]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[3]._outpostStation[0]._stationPos, 0); - scene->_gameBoardSide[3]._outpostStation[0]._card.setStrip(2); - scene->_gameBoardSide[3]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[3]._outpostStation[0]._cardId); - scene->_gameBoardSide[3]._outpostStation[0]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[3]._outpostStation[0]); - - scene->_gameBoardSide[3]._outpostStation[1]._cardId = 6; - scene->_gameBoardSide[3]._outpostStation[1]._card.postInit(); - scene->_gameBoardSide[3]._outpostStation[1]._card.setVisage(1332); - scene->_gameBoardSide[3]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[3]._outpostStation[1]._stationPos, 0); - scene->_gameBoardSide[3]._outpostStation[1]._card.setStrip(2); - scene->_gameBoardSide[3]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[3]._outpostStation[1]._cardId); - scene->_gameBoardSide[3]._outpostStation[1]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[3]._outpostStation[1]); - - scene->_gameBoardSide[3]._outpostStation[2]._cardId = 7; - scene->_gameBoardSide[3]._outpostStation[2]._card.postInit(); - scene->_gameBoardSide[3]._outpostStation[2]._card.setVisage(1332); - scene->_gameBoardSide[3]._outpostStation[2]._card.setPosition(scene->_gameBoardSide[3]._outpostStation[2]._stationPos, 0); - scene->_gameBoardSide[3]._outpostStation[2]._card.setStrip(2); - scene->_gameBoardSide[3]._outpostStation[2]._card.setFrame(scene->_gameBoardSide[3]._outpostStation[2]._cardId); - scene->_gameBoardSide[3]._outpostStation[2]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[3]._outpostStation[2]); - - scene->_gameBoardSide[0]._outpostStation[0]._cardId = 8; - scene->_gameBoardSide[0]._outpostStation[0]._card.postInit(); - scene->_gameBoardSide[0]._outpostStation[0]._card.setVisage(1332); - scene->_gameBoardSide[0]._outpostStation[0]._card.setPosition(scene->_gameBoardSide[0]._outpostStation[0]._stationPos, 0); - scene->_gameBoardSide[0]._outpostStation[0]._card.setStrip(2); - scene->_gameBoardSide[0]._outpostStation[0]._card.setFrame(scene->_gameBoardSide[0]._outpostStation[0]._cardId); - scene->_gameBoardSide[0]._outpostStation[0]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[0]._outpostStation[0]); - - scene->_gameBoardSide[0]._outpostStation[1]._cardId = 9; - scene->_gameBoardSide[0]._outpostStation[1]._card.postInit(); - scene->_gameBoardSide[0]._outpostStation[1]._card.setVisage(1332); - scene->_gameBoardSide[0]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[0]._outpostStation[1]._stationPos, 0); - scene->_gameBoardSide[0]._outpostStation[1]._card.setStrip(2); - scene->_gameBoardSide[0]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[0]._outpostStation[1]._cardId); - scene->_gameBoardSide[0]._outpostStation[1]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[0]._outpostStation[1]); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(60); - scene->actionDisplay(1331, 9, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - scene->_gameBoardSide[2]._outpostStation[1]._cardId = 2; - scene->_gameBoardSide[2]._outpostStation[1]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[1]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[1]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[1]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[1]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[1]._cardId); - scene->_gameBoardSide[2]._outpostStation[1]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[1]); - - scene->_gameBoardSide[2]._outpostStation[2]._cardId = 3; - scene->_gameBoardSide[2]._outpostStation[2]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[2]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[2]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[2]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[2]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[2]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[2]._cardId); - scene->_gameBoardSide[2]._outpostStation[2]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[2]); - - scene->_gameBoardSide[2]._outpostStation[3]._cardId = 5; - scene->_gameBoardSide[2]._outpostStation[3]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[3]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[3]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[3]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[3]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[3]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[3]._cardId); - scene->_gameBoardSide[2]._outpostStation[3]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[3]); - - scene->_gameBoardSide[2]._outpostStation[4]._cardId = 6; - scene->_gameBoardSide[2]._outpostStation[4]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[4]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[4]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[4]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[4]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[4]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[4]._cardId); - scene->_gameBoardSide[2]._outpostStation[4]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[4]); - - scene->_gameBoardSide[2]._outpostStation[5]._cardId = 7; - scene->_gameBoardSide[2]._outpostStation[5]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[5]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[5]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[5]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[5]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[5]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[5]._cardId); - scene->_gameBoardSide[2]._outpostStation[5]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[5]); - - scene->_gameBoardSide[2]._outpostStation[6]._cardId = 8; - scene->_gameBoardSide[2]._outpostStation[6]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[6]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[6]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[6]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[6]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[6]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[6]._cardId); - scene->_gameBoardSide[2]._outpostStation[6]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[6]); - - scene->_gameBoardSide[2]._outpostStation[7]._cardId = 9; - scene->_gameBoardSide[2]._outpostStation[7]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[7]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[7]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[7]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[7]._card.setStrip(2); - scene->_gameBoardSide[2]._outpostStation[7]._card.setFrame(scene->_gameBoardSide[2]._outpostStation[7]._cardId); - scene->_gameBoardSide[2]._outpostStation[7]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[7]); - - scene->_aSound1.play(62); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(120); - scene->_gameBoardSide[2]._outpostStation[0]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[1]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[2]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[3]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[4]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[5]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[6]._card.remove(); - scene->_gameBoardSide[2]._outpostStation[7]._card.remove(); - - scene->_gameBoardSide[1]._outpostStation[0]._card.remove(); - scene->_gameBoardSide[1]._outpostStation[1]._card.remove(); - - scene->_gameBoardSide[3]._outpostStation[0]._card.remove(); - scene->_gameBoardSide[3]._outpostStation[1]._card.remove(); - scene->_gameBoardSide[3]._outpostStation[2]._card.remove(); - - scene->_gameBoardSide[0]._outpostStation[0]._card.remove(); - scene->_gameBoardSide[0]._outpostStation[1]._card.remove(); - - scene->_stockPile.setup(1332, 5, 1); - scene->_stockPile.setPosition(Common::Point(165, 95)); - scene->_stockPile.setPriority(110); - scene->_stockPile._effect = EFFECT_SHADED; - scene->_stockPile.show(); - - scene->_gameBoardSide[1]._handCard[0]._card.postInit(); - scene->_gameBoardSide[1]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[1]._handCard[0]._card.setPosition(scene->_gameBoardSide[1]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[1]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[1]._handCard[0]._card.setFrame(4); - scene->_gameBoardSide[1]._handCard[0]._card.fixPriority(170); - - scene->_gameBoardSide[1]._handCard[1]._card.postInit(); - scene->_gameBoardSide[1]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[1]._handCard[1]._card.setPosition(scene->_gameBoardSide[1]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[1]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[1]._handCard[1]._card.setFrame(4); - scene->_gameBoardSide[1]._handCard[1]._card.fixPriority(170); - - scene->_gameBoardSide[1]._handCard[2]._card.postInit(); - scene->_gameBoardSide[1]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[1]._handCard[2]._card.setPosition(scene->_gameBoardSide[1]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[1]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[1]._handCard[2]._card.setFrame(4); - scene->_gameBoardSide[1]._handCard[2]._card.fixPriority(170); - - scene->_gameBoardSide[2]._handCard[0]._cardId = 30; - scene->_gameBoardSide[2]._handCard[0]._card.postInit(); - scene->_gameBoardSide[2]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[0]._card.setPosition(scene->_gameBoardSide[2]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[2]._handCard[0]._card.setFrame(2); - scene->_gameBoardSide[2]._handCard[0]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[0]); - - scene->_gameBoardSide[2]._handCard[1]._cardId = 16; - scene->_gameBoardSide[2]._handCard[1]._card.postInit(); - scene->_gameBoardSide[2]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[1]._card.setPosition(scene->_gameBoardSide[2]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[2]._handCard[1]._card.setFrame(2); - scene->_gameBoardSide[2]._handCard[1]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[1]); - - scene->_gameBoardSide[2]._handCard[2]._cardId = 1; - scene->_gameBoardSide[2]._handCard[2]._card.postInit(); - scene->_gameBoardSide[2]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[2]._card.setPosition(scene->_gameBoardSide[2]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[2]._handCard[2]._card.setFrame(2); - scene->_gameBoardSide[2]._handCard[2]._card.fixPriority(170); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[2]); - - scene->_gameBoardSide[3]._handCard[0]._card.postInit(); - scene->_gameBoardSide[3]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[3]._handCard[0]._card.setPosition(scene->_gameBoardSide[3]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[3]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[3]._handCard[0]._card.setFrame(3); - scene->_gameBoardSide[3]._handCard[0]._card.fixPriority(170); - - scene->_gameBoardSide[3]._handCard[1]._card.postInit(); - scene->_gameBoardSide[3]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[3]._handCard[1]._card.setPosition(scene->_gameBoardSide[3]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[3]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[3]._handCard[1]._card.setFrame(3); - scene->_gameBoardSide[3]._handCard[1]._card.fixPriority(170); - - scene->_gameBoardSide[3]._handCard[2]._card.postInit(); - scene->_gameBoardSide[3]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[3]._handCard[2]._card.setPosition(scene->_gameBoardSide[3]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[3]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[3]._handCard[2]._card.setFrame(3); - scene->_gameBoardSide[3]._handCard[2]._card.fixPriority(170); - - scene->_gameBoardSide[0]._handCard[0]._card.postInit(); - scene->_gameBoardSide[0]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[0]._handCard[0]._card.setPosition(scene->_gameBoardSide[0]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[0]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[0]._handCard[0]._card.setFrame(2); - scene->_gameBoardSide[0]._handCard[0]._card.fixPriority(170); - - scene->_gameBoardSide[0]._handCard[1]._card.postInit(); - scene->_gameBoardSide[0]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[0]._handCard[1]._card.setPosition(scene->_gameBoardSide[0]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[0]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[0]._handCard[1]._card.setFrame(2); - scene->_gameBoardSide[0]._handCard[1]._card.fixPriority(170); - - scene->_gameBoardSide[0]._handCard[2]._card.postInit(); - scene->_gameBoardSide[0]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[0]._handCard[2]._card.setPosition(scene->_gameBoardSide[0]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[0]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[0]._handCard[2]._card.setFrame(2); - scene->_gameBoardSide[0]._handCard[2]._card.fixPriority(170); - - R2_GLOBALS._sceneObjects->draw(); - - scene->actionDisplay(1331, 10, 159, 10, 1, 200, 0, 7, 0, 154, 154); - scene->_animatedCard._card.setPosition(Common::Point(162, 95), 0); - scene->_animatedCard._card.show(); - scene->_aSound2.play(61); - - Common::Point pt(91, 174); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - } - break; - case 2: { - scene->_gameBoardSide[2]._handCard[3]._cardId = 2; - scene->_gameBoardSide[2]._handCard[3]._card.postInit(); - scene->_gameBoardSide[2]._handCard[3]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[3]._card.setPosition(scene->_gameBoardSide[2]._handCard[3]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[3]._card.setStrip(1); - scene->_gameBoardSide[2]._handCard[3]._card.setFrame(2); - scene->_gameBoardSide[2]._handCard[3]._card.fixPriority(170); - - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._handCard[3]); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(60); - scene->actionDisplay(1331, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - scene->actionDisplay(1331, 12, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - scene->_gameBoardSide[2]._outpostStation[1]._cardId = 1; - scene->_gameBoardSide[2]._outpostStation[1]._card.postInit(); - scene->_gameBoardSide[2]._outpostStation[1]._card.setVisage(1332); - scene->_gameBoardSide[2]._outpostStation[1]._card.setPosition(scene->_gameBoardSide[2]._outpostStation[1]._stationPos, 0); - scene->_gameBoardSide[2]._outpostStation[1]._card.hide(); - - scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[2]._card._strip); - scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._card._frame); - scene->_animatedCard._card.animate(ANIM_MODE_NONE, NULL); - - scene->_gameBoardSide[2]._handCard[2]._cardId = 0; - scene->_gameBoardSide[2]._handCard[2]._card.remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[2]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._outpostStation[1]._stationPos, this); - } - break; - case 3: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[1]); - scene->_aSound1.play(59); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(60); - scene->actionDisplay(1331, 13, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - scene->_gameBoardSide[2]._outpostStation[1]._cardId = scene->_gameBoardSide[2]._handCard[3]._cardId; - - scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[3]._card._strip); - scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[3]._card._frame); - - scene->_gameBoardSide[2]._handCard[3]._cardId = 0; - scene->_gameBoardSide[2]._handCard[3]._card.remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[3]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._outpostStation[1]._stationPos, this); - } - break; - case 4: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_gameBoardSide[2]._outpostStation[1]); - scene->_aSound1.play(59); - - scene->_discardPile._cardId = 1; - scene->_discardPile._card.hide(); - - scene->_animatedCard._card.setStrip(5); - scene->_animatedCard._card.setFrame(1); - scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._outpostStation[1]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); - } - break; - case 5: { - scene->_animatedCard._card.hide(); - - scene->_discardPile._card.postInit(); - scene->_discardPile._card.setVisage(1332); - scene->_discardPile._card.setPosition(scene->_discardPile._stationPos, 0); - scene->setAnimationInfo(&scene->_discardPile); - scene->_aSound2.play(61); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(60); - scene->actionDisplay(1331, 14, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - scene->_gameBoardSide[2]._delayCard._card.postInit(); - scene->_gameBoardSide[2]._delayCard._card.setVisage(1332); - scene->_gameBoardSide[2]._delayCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); - scene->_gameBoardSide[2]._delayCard._card.hide(); - - scene->_gameBoardSide[3]._handCard[2]._cardId = 0; - scene->_gameBoardSide[3]._handCard[2].remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[3]._handCard[2]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._delayCard._stationPos, this); - } - break; - case 6: { - scene->_animatedCard._card.hide(); - scene->_gameBoardSide[2]._delayCard._cardId = 21; - scene->setAnimationInfo(&scene->_gameBoardSide[2]._delayCard); - scene->_aSound1.play(57); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(60); - scene->actionDisplay(1331, 15, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - int tmpVal = 15; - int i = -1; - - for (i = 0; i <= 7; i++) { - tmpVal += 29; - - scene->_upperDisplayCard[i].postInit(); - scene->_upperDisplayCard[i].setVisage(1332); - scene->_upperDisplayCard[i].setPosition(Common::Point(tmpVal, 90), 0); - scene->_upperDisplayCard[i].setStrip(3); - scene->_upperDisplayCard[i].fixPriority(190); - - scene->_lowerDisplayCard[i].postInit(); - scene->_lowerDisplayCard[i].setVisage(1332); - scene->_lowerDisplayCard[i].setPosition(Common::Point(tmpVal, 90), 0); - scene->_lowerDisplayCard[i].setStrip(7); - scene->_lowerDisplayCard[i].setFrame(1); - scene->_lowerDisplayCard[i].fixPriority(180); - } - - scene->_upperDisplayCard[0].setFrame(1); - scene->_upperDisplayCard[1].setFrame(3); - scene->_upperDisplayCard[2].setFrame(6); - scene->_upperDisplayCard[3].setFrame(8); - scene->_upperDisplayCard[4].setFrame(9); - scene->_upperDisplayCard[5].setFrame(10); - scene->_upperDisplayCard[6].setFrame(11); - scene->_upperDisplayCard[7].setFrame(12); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(240); - - scene->_upperDisplayCard[0].remove(); - scene->_upperDisplayCard[1].remove(); - scene->_upperDisplayCard[2].remove(); - scene->_upperDisplayCard[3].remove(); - scene->_upperDisplayCard[4].remove(); - scene->_upperDisplayCard[5].remove(); - scene->_upperDisplayCard[6].remove(); - scene->_upperDisplayCard[7].remove(); - - scene->_lowerDisplayCard[0].remove(); - scene->_lowerDisplayCard[1].remove(); - scene->_lowerDisplayCard[2].remove(); - scene->_lowerDisplayCard[3].remove(); - scene->_lowerDisplayCard[4].remove(); - scene->_lowerDisplayCard[5].remove(); - scene->_lowerDisplayCard[6].remove(); - scene->_lowerDisplayCard[7].remove(); - - scene->_discardPile._cardId = scene->_gameBoardSide[2]._delayCard._cardId; - - scene->_gameBoardSide[2]._delayCard._cardId = 0; - scene->_gameBoardSide[2]._delayCard._card.remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); - } - break; - case 7: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_discardPile); - scene->_aSound2.play(61); - - R2_GLOBALS._sceneObjects->draw(); - - scene->_gameBoardSide[2]._delayCard._card.postInit(); - scene->_gameBoardSide[2]._delayCard._card.setVisage(1332); - scene->_gameBoardSide[2]._delayCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); - scene->_gameBoardSide[2]._delayCard._card.hide(); - - scene->_gameBoardSide[3]._handCard[1]._cardId = 0; - scene->_gameBoardSide[3]._handCard[1].remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[3]._handCard[1]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._delayCard._stationPos, this); - } - break; - case 8: { - scene->_animatedCard._card.hide(); - scene->_gameBoardSide[2]._delayCard._cardId = 14; - scene->setAnimationInfo(&scene->_gameBoardSide[2]._delayCard); - scene->_aSound1.play(57); - - R2_GLOBALS._sceneObjects->draw(); - - scene->actionDisplay(1331, 16, 159, 10, 1, 200, 0, 7, 0, 154, 154); - int tmpVal = 72; - int i = -1; - - for (i = 0; i <= 3; i++) { - tmpVal += 29; - scene->_upperDisplayCard[i].postInit(); - scene->_upperDisplayCard[i].setVisage(1332); - scene->_upperDisplayCard[i].setPosition(Common::Point(tmpVal, 71), 0); - scene->_upperDisplayCard[i].setStrip(3); - scene->_upperDisplayCard[i].fixPriority(190); - - scene->_lowerDisplayCard[i].postInit(); - scene->_lowerDisplayCard[i].setVisage(1332); - scene->_lowerDisplayCard[i].setPosition(Common::Point(tmpVal, 71), 0); - scene->_lowerDisplayCard[i].setStrip(7); - scene->_lowerDisplayCard[i].setFrame(1); - scene->_lowerDisplayCard[i].fixPriority(180); - } - - scene->_upperDisplayCard[0].setFrame(2); - scene->_upperDisplayCard[1].setFrame(5); - scene->_upperDisplayCard[2].setFrame(7); - scene->_upperDisplayCard[3].setFrame(15); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(240); - scene->actionDisplay(1331, 17, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - tmpVal = 72; - for (i = 4; i <= 7; i++) { - tmpVal += 29; - - scene->_upperDisplayCard[i].postInit(); - scene->_upperDisplayCard[i].setVisage(1332); - scene->_upperDisplayCard[i].setPosition(Common::Point(tmpVal, 100), 0); - scene->_upperDisplayCard[i].setStrip(4); - scene->_upperDisplayCard[i].fixPriority(190); - - scene->_lowerDisplayCard[i].postInit(); - scene->_lowerDisplayCard[i].setVisage(1332); - scene->_lowerDisplayCard[i].setPosition(Common::Point(tmpVal, 100), 0); - scene->_lowerDisplayCard[i].setStrip(7); - scene->_lowerDisplayCard[i].setFrame(1); - scene->_lowerDisplayCard[i].fixPriority(180); - } - - scene->_upperDisplayCard[4].setFrame(1); - scene->_upperDisplayCard[5].setFrame(5); - scene->_upperDisplayCard[6].setFrame(7); - scene->_upperDisplayCard[7].setFrame(3); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(240); - - scene->_upperDisplayCard[0].remove(); - scene->_upperDisplayCard[1].remove(); - scene->_upperDisplayCard[2].remove(); - scene->_upperDisplayCard[3].remove(); - scene->_upperDisplayCard[4].remove(); - scene->_upperDisplayCard[5].remove(); - scene->_upperDisplayCard[6].remove(); - scene->_upperDisplayCard[7].remove(); - - scene->_lowerDisplayCard[0].remove(); - scene->_lowerDisplayCard[1].remove(); - scene->_lowerDisplayCard[2].remove(); - scene->_lowerDisplayCard[3].remove(); - scene->_lowerDisplayCard[4].remove(); - scene->_lowerDisplayCard[5].remove(); - scene->_lowerDisplayCard[6].remove(); - scene->_lowerDisplayCard[7].remove(); - - scene->_discardPile._cardId = scene->_gameBoardSide[2]._handCard[0]._cardId; - - scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[0]._card._strip); - scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._card._frame); - scene->_animatedCard._card.animate(ANIM_MODE_NONE, NULL); - - scene->_gameBoardSide[2]._handCard[0]._cardId = 0; - scene->_gameBoardSide[2]._handCard[0]._card.remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[0]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[2]._delayCard._stationPos, this); - } - break; - case 9: { - scene->_aSound1.play(58); - scene->_gameBoardSide[2]._delayCard._cardId = 0; - scene->_gameBoardSide[2]._delayCard.remove(); - scene->_animatedCard._card.setStrip(5); - scene->_animatedCard._card.setFrame(1); - scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._delayCard._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); - } - break; - case 10: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_discardPile); - scene->_aSound2.play(61); - - R2_GLOBALS._sceneObjects->draw(); - scene->actionDisplay(1331, 18, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - scene->_upperDisplayCard[0].postInit(); - scene->_upperDisplayCard[0].setVisage(1332); - scene->_upperDisplayCard[0].setPosition(Common::Point(131, 71), 0); - scene->_upperDisplayCard[0].fixPriority(190); - scene->_upperDisplayCard[0].setStrip(3); - scene->_upperDisplayCard[0].setFrame(4); - - scene->_lowerDisplayCard[0].postInit(); - scene->_lowerDisplayCard[0].setVisage(1332); - scene->_lowerDisplayCard[0].setPosition(Common::Point(131, 71), 0); - scene->_lowerDisplayCard[0].setStrip(7); - scene->_lowerDisplayCard[0].setFrame(1); - scene->_lowerDisplayCard[0].fixPriority(180); - - scene->_upperDisplayCard[1].postInit(); - scene->_upperDisplayCard[1].setVisage(1332); - scene->_upperDisplayCard[1].setPosition(Common::Point(160, 71), 0); - scene->_upperDisplayCard[1].fixPriority(190); - scene->_upperDisplayCard[1].setStrip(3); - scene->_upperDisplayCard[1].setFrame(16); - - scene->_lowerDisplayCard[1].postInit(); - scene->_lowerDisplayCard[1].setVisage(1332); - scene->_lowerDisplayCard[1].setPosition(Common::Point(160, 71), 0); - scene->_lowerDisplayCard[1].setStrip(7); - scene->_lowerDisplayCard[1].setFrame(1); - scene->_lowerDisplayCard[1].fixPriority(180); - - scene->_upperDisplayCard[2].postInit(); - scene->_upperDisplayCard[2].setVisage(1332); - scene->_upperDisplayCard[2].setPosition(Common::Point(131, 100), 0); - scene->_upperDisplayCard[2].fixPriority(190); - scene->_upperDisplayCard[2].setStrip(4); - scene->_upperDisplayCard[2].setFrame(4); - - scene->_lowerDisplayCard[2].postInit(); - scene->_lowerDisplayCard[2].setVisage(1332); - scene->_lowerDisplayCard[2].setPosition(Common::Point(131, 100), 0); - scene->_lowerDisplayCard[2].setStrip(7); - scene->_lowerDisplayCard[2].setFrame(1); - scene->_lowerDisplayCard[2].fixPriority(180); - - scene->_upperDisplayCard[3].postInit(); - scene->_upperDisplayCard[3].setVisage(1332); - scene->_upperDisplayCard[3].setPosition(Common::Point(160, 100), 0); - scene->_upperDisplayCard[3].fixPriority(190); - scene->_upperDisplayCard[3].setStrip(4); - scene->_upperDisplayCard[3].setFrame(2); - - scene->_lowerDisplayCard[3].postInit(); - scene->_lowerDisplayCard[3].setVisage(1332); - scene->_lowerDisplayCard[3].setPosition(Common::Point(160, 100), 0); - scene->_lowerDisplayCard[3].setStrip(7); - scene->_lowerDisplayCard[3].setFrame(1); - scene->_lowerDisplayCard[3].fixPriority(180); - - R2_GLOBALS._sceneObjects->draw(); - - waitFrames(240); - - scene->_upperDisplayCard[0].remove(); - scene->_upperDisplayCard[1].remove(); - scene->_upperDisplayCard[2].remove(); - scene->_upperDisplayCard[3].remove(); - - scene->_lowerDisplayCard[0].remove(); - scene->_lowerDisplayCard[1].remove(); - scene->_lowerDisplayCard[2].remove(); - scene->_lowerDisplayCard[3].remove(); - - scene->_currentPlayerArrow.setFrame(1); - scene->_currentPlayerArrow.show(); - scene->_currentPlayerArrow.animate(ANIM_MODE_2, NULL); - - R2_GLOBALS._sceneObjects->draw(); - - scene->actionDisplay(1331, 19, 159, 10, 1, 220, 0, 7, 0, 154, 154); - - scene->_currentPlayerArrow.hide(); - - scene->actionDisplay(1331, 20, 159, 10, 1, 220, 0, 7, 0, 154, 154); - scene->actionDisplay(1331, 21, 159, 10, 1, 220, 0, 7, 0, 154, 154); - - scene->_discardPile._cardId = scene->_gameBoardSide[2]._handCard[1]._cardId; - - scene->_animatedCard._card.setStrip(scene->_gameBoardSide[2]._handCard[1]._card._strip); - scene->_animatedCard._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._card._frame); - scene->_animatedCard._card.animate(ANIM_MODE_NONE, NULL); - - scene->_gameBoardSide[2]._handCard[1]._cardId = 0; - scene->_gameBoardSide[2]._handCard[1]._card.remove(); - - scene->_animatedCard._card.setPosition(scene->_gameBoardSide[2]._handCard[1]._stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_discardPile._stationPos, this); - } - break; - case 11: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_discardPile); - scene->_aSound2.play(61); - scene->_animatedCard._card.setStrip(5); - scene->_animatedCard._card.setFrame(1); - scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); - - R2_GLOBALS._sceneObjects->draw(); - - scene->actionDisplay(1331, 22, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - int i = -1; - for (i = 0; i <= 3; i ++) { - scene->_gameBoardSide[3]._handCard[i]._cardId = 0; - scene->_gameBoardSide[3]._handCard[i]._card.remove(); - - scene->_gameBoardSide[2]._handCard[i]._cardId = 0; - scene->_gameBoardSide[2]._handCard[i]._card.remove(); - - scene->_gameBoardSide[0]._handCard[i]._cardId = 0; - scene->_gameBoardSide[0]._handCard[i]._card.remove(); - - scene->_gameBoardSide[1]._handCard[i]._cardId = 0; - scene->_gameBoardSide[1]._handCard[i]._card.remove(); - } - - for (i = 0; i <= 7; i++) { - scene->_gameBoardSide[3]._outpostStation[i]._cardId = 0; - scene->_gameBoardSide[3]._outpostStation[i]._card.remove(); - - scene->_gameBoardSide[2]._outpostStation[i]._cardId = 0; - scene->_gameBoardSide[2]._outpostStation[i]._card.remove(); - - scene->_gameBoardSide[0]._outpostStation[i]._cardId = 0; - scene->_gameBoardSide[0]._outpostStation[i]._card.remove(); - - scene->_gameBoardSide[1]._outpostStation[i]._cardId = 0; - scene->_gameBoardSide[1]._outpostStation[i]._card.remove(); - } - - scene->_gameBoardSide[2]._delayCard._cardId = 0; - scene->_gameBoardSide[2]._delayCard._card.remove(); - - scene->_discardPile._cardId = 0; - scene->_discardPile._card.remove(); - - scene->_stockPile.remove(); - } - // No break on purpose - case 0: - R2_GLOBALS._sceneObjects->draw(); - signal(); - break; - case 12: - scene->suggestInstructions(); - remove(); - break; - default: - break; - } -} - -/** - * Shuffle cards animation - */ -void Scene1337::Action2::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: - scene->_shuffleAnimation._card.postInit(); - scene->_shuffleAnimation._card.setVisage(1332); - scene->_shuffleAnimation._card.setStrip(8); - scene->_shuffleAnimation._card.setFrame(1); - scene->_shuffleAnimation._card.fixPriority(300); - scene->_shuffleAnimation._card.setPosition(Common::Point(156, 108)); - - scene->_discardPile._card.remove(); - scene->_discardPile._cardId = 0; - - scene->_aSound1.play(60); - scene->_shuffleAnimation._card.animate(ANIM_MODE_5, this); - break; - case 1: - scene->_shuffleAnimation._card.setFrame(1); - - scene->_aSound1.play(60); - scene->_shuffleAnimation._card.animate(ANIM_MODE_5, this); - break; - case 2: { - Common::Point pt(156, 108); - NpcMover *mover = new NpcMover(); - scene->_shuffleAnimation._card.addMover(mover, &pt, this); - } - break; - case 3: - scene->_shuffleAnimation._card.remove(); - scene->_stockPile.setup(1332, 5, 1); - scene->_stockPile.setPosition(Common::Point(162, 95)); - scene->_stockPile.setPriority(110); - scene->_stockPile._effect = EFFECT_SHADED; - scene->_stockPile.show(); - scene->_shuffleEndedFl = true; - break; - default: - break; - } -} - -/** - * Deal cards - */ -void Scene1337::Action3::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - scene->_animatedCard._card.setPosition(Common::Point(162, 95), 0); - - switch (_actionIndex++) { - case 0: { - scene->_animatedCard._card._moveDiff = Common::Point(30, 30); - scene->_animatedCard._card.setVisage(1332); - scene->_animatedCard._card.setStrip(5); - scene->_animatedCard._card.setFrame(1); - scene->_animatedCard._card.fixPriority(400); - scene->_animatedCard._card.animate(ANIM_MODE_2, NULL); - scene->_aSound2.play(61); - - Common::Point pt(283, 146); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_animatedCard._card.show(); - scene->_gameBoardSide[1]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 1: { - scene->_gameBoardSide[1]._handCard[0]._card.postInit(); - scene->_gameBoardSide[1]._handCard[0]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[1]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[1]._handCard[0]._card.setPosition(scene->_gameBoardSide[1]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[1]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[1]._handCard[0]._card.setFrame(4); - scene->_gameBoardSide[1]._handCard[0]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(10, 174); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[2]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 2: { - scene->_gameBoardSide[2]._handCard[0]._card.postInit(); - scene->_gameBoardSide[2]._handCard[0]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[2]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[0]._card.setPosition(scene->_gameBoardSide[2]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[0]._card.fixPriority(170); - if (scene->_gameBoardSide[2]._handCard[0]._cardId > 25) { - scene->_gameBoardSide[2]._handCard[0]._card.setStrip(4); - scene->_gameBoardSide[2]._handCard[0]._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._cardId - 25); - } else if (scene->_gameBoardSide[2]._handCard[0]._cardId > 9) { - scene->_gameBoardSide[2]._handCard[0]._card.setStrip(3); - scene->_gameBoardSide[2]._handCard[0]._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._cardId - 9); - } else { - scene->_gameBoardSide[2]._handCard[0]._card.setStrip(2); - scene->_gameBoardSide[2]._handCard[0]._card.setFrame(scene->_gameBoardSide[2]._handCard[0]._cardId); - } - scene->_aSound2.play(61); - - Common::Point pt(14, 14); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[3]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 3: { - scene->_gameBoardSide[3]._handCard[0]._card.postInit(); - scene->_gameBoardSide[3]._handCard[0]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[3]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[3]._handCard[0]._card.setPosition(scene->_gameBoardSide[3]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[3]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[3]._handCard[0]._card.setFrame(3); - scene->_gameBoardSide[3]._handCard[0]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(280, 5); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[0]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 4: { - scene->_gameBoardSide[0]._handCard[0]._card.postInit(); - scene->_gameBoardSide[0]._handCard[0]._card._moveDiff = Common::Point(30,30); - scene->_gameBoardSide[0]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[0]._handCard[0]._card.setPosition(scene->_gameBoardSide[0]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[0]._handCard[0]._card.setStrip(5); - scene->_gameBoardSide[0]._handCard[0]._card.setFrame(1); - scene->_gameBoardSide[0]._handCard[0]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(283, 124); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[1]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 5: { - scene->_gameBoardSide[1]._handCard[1]._card.postInit(); - scene->_gameBoardSide[1]._handCard[1]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[1]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[1]._handCard[1]._card.setPosition(scene->_gameBoardSide[1]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[1]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[1]._handCard[1]._card.setFrame(4); - scene->_gameBoardSide[1]._handCard[1]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(37, 174); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[2]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 6: { - scene->_gameBoardSide[2]._handCard[1]._card.postInit(); - scene->_gameBoardSide[2]._handCard[1]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[2]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[1]._card.setPosition(scene->_gameBoardSide[2]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[1]._card.fixPriority(170); - - if (scene->_gameBoardSide[2]._handCard[1]._cardId > 25) { - scene->_gameBoardSide[2]._handCard[1]._card.setStrip(4); - scene->_gameBoardSide[2]._handCard[1]._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._cardId - 25); - } else if (scene->_gameBoardSide[2]._handCard[1]._cardId > 9) { - scene->_gameBoardSide[2]._handCard[1]._card.setStrip(3); - scene->_gameBoardSide[2]._handCard[1]._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._cardId - 9); - } else { - scene->_gameBoardSide[2]._handCard[1]._card.setStrip(2); - scene->_gameBoardSide[2]._handCard[1]._card.setFrame(scene->_gameBoardSide[2]._handCard[1]._cardId); - } - - scene->_aSound2.play(61); - - Common::Point pt(14, 36); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[3]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 7: { - scene->_gameBoardSide[3]._handCard[1]._card.postInit(); - scene->_gameBoardSide[3]._handCard[1]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[3]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[3]._handCard[1]._card.setPosition(scene->_gameBoardSide[3]._handCard[1]._stationPos); - scene->_gameBoardSide[3]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[3]._handCard[1]._card.setFrame(3); - scene->_gameBoardSide[3]._handCard[1]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(253, 5); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[0]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 8: { - scene->_gameBoardSide[0]._handCard[1]._card.postInit(); - scene->_gameBoardSide[0]._handCard[1]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[0]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[0]._handCard[1]._card.setPosition(scene->_gameBoardSide[0]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[0]._handCard[1]._card.setStrip(5); - scene->_gameBoardSide[0]._handCard[1]._card.setFrame(1); - scene->_gameBoardSide[0]._handCard[1]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(283, 102); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[1]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 9: { - scene->_gameBoardSide[1]._handCard[2]._card.postInit(); - scene->_gameBoardSide[1]._handCard[2]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[1]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[1]._handCard[2]._card.setPosition(scene->_gameBoardSide[1]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[1]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[1]._handCard[2]._card.setFrame(4); - scene->_gameBoardSide[1]._handCard[2]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(64, 174); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[2]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 10: { - scene->_gameBoardSide[2]._handCard[2]._card.postInit(); - scene->_gameBoardSide[2]._handCard[2]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[2]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[2]._handCard[2]._card.setPosition(scene->_gameBoardSide[2]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[2]._handCard[2]._card.fixPriority(170); - - if (scene->_gameBoardSide[2]._handCard[2]._cardId > 25) { - scene->_gameBoardSide[2]._handCard[2]._card.setStrip(4); - scene->_gameBoardSide[2]._handCard[2]._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._cardId - 25); - } else if (scene->_gameBoardSide[2]._handCard[2]._cardId > 9) { - scene->_gameBoardSide[2]._handCard[2]._card.setStrip(3); - scene->_gameBoardSide[2]._handCard[2]._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._cardId - 9); - } else { - scene->_gameBoardSide[2]._handCard[2]._card.setStrip(2); - scene->_gameBoardSide[2]._handCard[2]._card.setFrame(scene->_gameBoardSide[2]._handCard[2]._cardId); - } - - scene->_aSound2.play(61); - - Common::Point pt(14, 58); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[3]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 11: { - scene->_gameBoardSide[3]._handCard[2]._card.postInit(); - scene->_gameBoardSide[3]._handCard[2]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[3]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[3]._handCard[2]._card.setPosition(scene->_gameBoardSide[3]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[3]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[3]._handCard[2]._card.setFrame(3); - scene->_gameBoardSide[3]._handCard[2]._card.fixPriority(170); - scene->_aSound2.play(61); - - Common::Point pt(226, 5); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - - scene->_gameBoardSide[0]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - } - break; - case 12: - scene->_gameBoardSide[0]._handCard[2]._card.postInit(); - scene->_gameBoardSide[0]._handCard[2]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[0]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[0]._handCard[2]._card.setPosition(scene->_gameBoardSide[0]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[0]._handCard[2]._card.setStrip(5); - scene->_gameBoardSide[0]._handCard[2]._card.setFrame(1); - scene->_gameBoardSide[0]._handCard[2]._card.fixPriority(170); - scene->_animatedCard._card.hide(); - default: - break; - } - - if (_actionIndex > 12) { - scene->_currentPlayerNumb = 0; - R2_GLOBALS._sceneObjects->draw(); - scene->actionDisplay(1330, 0, 159, 10, 1, 200, 0, 7, 0, 154, 154); - scene->handleNextTurn(); - } else if (_actionIndex >= 1) { - scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; - scene->_cardsAvailableNumb--; - } -} - -/** - * Action used to handle the other players' turn - */ -void Scene1337::Action4::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: - if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._cardId == 0) - && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { - if (scene->_cardsAvailableNumb < 0) - scene->shuffleCards(); - scene->_animatedCard._card.setPosition(Common::Point(162, 95), 0); - scene->_animatedCard._card.show(); - scene->_aSound2.play(61); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card1Pos, this); - - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; - scene->_cardsAvailableNumb--; - - if (scene->_cardsAvailableNumb < 0) - scene->_stockPile.remove(); - } else { - // Self call, forcing next actionIndex - signal(); - } - break; - case 1: - if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card1Pos.x) - && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card1Pos.y) ) { - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.postInit(); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setVisage(1332); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._stationPos, 0); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setStrip(1); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._card.fixPriority(170); - } - - if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) - scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]); - - scene->_animatedCard._card.hide(); - if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[0]._cardId == 0) - && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { - if (scene->_cardsAvailableNumb < 0) - scene->shuffleCards(); - scene->_animatedCard._card.setPosition(Common::Point(162, 95)); - scene->_animatedCard._card.show(); - - scene->_aSound2.play(61); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card2Pos, this); - - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; - scene->_cardsAvailableNumb--; - if (scene->_cardsAvailableNumb < 0) - scene->_stockPile.remove(); - } else - signal(); - break; - case 2: - if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card2Pos.x) - && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card2Pos.y) ) { - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.postInit(); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setVisage(1332); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._stationPos, 0); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setStrip(1); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]._card.fixPriority(170); - } - - if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) - scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[1]); - - scene->_animatedCard._card.hide(); - if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._cardId == 0) - && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { - if (scene->_cardsAvailableNumb < 0) - scene->shuffleCards(); - scene->_animatedCard._card.setPosition(Common::Point(162, 95)); - scene->_animatedCard._card.show(); - - scene->_aSound2.play(61); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card3Pos, this); - - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; - scene->_cardsAvailableNumb--; - if (scene->_cardsAvailableNumb < 0) - scene->_stockPile.remove(); - } else - signal(); - break; - case 3: - if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card3Pos.x) - && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card3Pos.y) ) { - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.postInit(); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setVisage(1332); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._stationPos, 0); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setStrip(1); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]._card.fixPriority(170); - } - - if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) - scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[2]); - - scene->_animatedCard._card.hide(); - if ( (scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._cardId == 0) - && (!scene->isStationCard(scene->_gameBoardSide[scene->_currentPlayerNumb]._delayCard._cardId))) { - if (scene->_cardsAvailableNumb < 0) - scene->shuffleCards(); - scene->_animatedCard._card.setPosition(Common::Point(162, 95)); - scene->_animatedCard._card.show(); - - scene->_aSound2.play(61); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_gameBoardSide[scene->_currentPlayerNumb]._card4Pos, this); - - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._cardId = scene->_availableCardsPile[scene->_cardsAvailableNumb]; - scene->_availableCardsPile[scene->_cardsAvailableNumb] = 0; - scene->_cardsAvailableNumb--; - if (scene->_cardsAvailableNumb < 0) - scene->_stockPile.remove(); - } else - signal(); - break; - case 4: - if ( (scene->_animatedCard._card._position.x == scene->_gameBoardSide[scene->_currentPlayerNumb]._card4Pos.x) - && (scene->_animatedCard._card._position.y == scene->_gameBoardSide[scene->_currentPlayerNumb]._card4Pos.y) ) { - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.postInit(); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card._moveDiff = Common::Point(30, 30); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setVisage(1332); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setPosition(scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._stationPos, 0); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setStrip(1); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.setFrame(scene->_gameBoardSide[scene->_currentPlayerNumb]._frameNum); - scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]._card.fixPriority(170); - } - - if ((R2_GLOBALS._debugCardGame) || (scene->_currentPlayerNumb == 2)) - scene->setAnimationInfo(&scene->_gameBoardSide[scene->_currentPlayerNumb]._handCard[3]); - - scene->_animatedCard._card.hide(); - switch (scene->_currentPlayerNumb) { - case 0: - scene->handlePlayer0(); - break; - case 1: - scene->handlePlayer1(); - break; - case 2: - scene->handleAutoplayPlayer2(); - break; - case 3: - scene->handlePlayer3(); - break; - default: - break; - } - break; - default: - break; - } -} - -/** - * Animations for discarding a card - */ -void Scene1337::Action5::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard1->_cardId; - scene->_currentDiscardIndex--; - if (!g_globals->_sceneObjects->contains(&scene->_discardPile._card)) { - // The first discarded card makes the pile appear - scene->_discardPile._card.postInit(); - scene->_discardPile._card.hide(); - scene->_discardPile._card.setVisage(1332); - scene->_discardPile._card.setPosition(scene->_discardPile._stationPos, 0); - scene->_discardPile._card.fixPriority(170); - } - - scene->_discardPile._cardId = scene->_actionCard1->_cardId; - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - - if (scene->_actionCard1 == &scene->_selectedCard) { - scene->setCursorData(5, 1, 4); - scene->subC4CEC(); - } - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - Common::Point pt(128, 95); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &pt, this); - } - break; - case 1: - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(&scene->_discardPile); - scene->_aSound2.play(61); - scene->handleNextTurn(); - break; - default: - break; - } -} - -/** - * Animations for playing a platform card - */ -void Scene1337::Action6::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_actionCard2->_cardId = 1; - scene->_actionCard2->_card.postInit(); - scene->_actionCard2->_card.hide(); - scene->_actionCard2->_card.setVisage(1332); - scene->_actionCard2->_card.setPosition(scene->_actionCard2->_stationPos); - scene->_actionCard2->_card.fixPriority(170); - - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 1: - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(59); - if (scene->_actionCard1 == &scene->_selectedCard) { - scene->setCursorData(5, 1, 4); - scene->subC4CEC(); - } - scene->handleNextTurn(); - break; - default: - break; - } -} - -/** - * Upgrade platform to station by playing a station card on top of it - */ -void Scene1337::Action7::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; - - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 1: - if (scene->_actionCard1 == &scene->_selectedCard) { - scene->setCursorData(5, 1, 4); - scene->subC4CEC(); - } - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(59); - scene->_discardedPlatformCard._cardId = 1; - scene->_discardedPlatformCard._stationPos = scene->_actionCard2->_stationPos; - scene->_discardedPlatformCard._card.postInit(); - scene->_discardedPlatformCard._card.hide(); - scene->_discardedPlatformCard._card._flags = OBJFLAG_HIDING; - - scene->discardCard(&scene->_discardedPlatformCard); - break; - default: - break; - } -} - -void Scene1337::Action8::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; - scene->_currentDiscardIndex--; - - scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; - scene->_actionCard1->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 1: - scene->_animatedCard._card.hide(); - - if (scene->_actionCard1 == &scene->_selectedCard) { - scene->setCursorData(5, 1, 4); - scene->subC4CEC(); - } - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(58); - scene->discardCard(scene->_actionCard2); - break; - default: - break; - } -} - -// Play delay card -void Scene1337::Action9::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; - scene->_actionCard2->_card.postInit(); - scene->_actionCard2->_card.hide(); - scene->_actionCard2->_card.setVisage(1332); - scene->_actionCard2->_card.setPosition(scene->_actionCard2->_stationPos, 0); - scene->_actionCard2->_card.fixPriority(170); - - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 1: - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(57); - - if (scene->_actionCard1 == &scene->_selectedCard) { - scene->setCursorData(5, 1, 4); - scene->subC4CEC(); - } - - scene->handleNextTurn(); - break; - default: - break; - } -} - -// Counter a trick with a card -void Scene1337::Action10::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_actionCard3->_card.postInit(); - scene->_actionCard3->_card.hide(); - scene->_actionCard3->_card.setVisage(1332); - scene->_actionCard3->_card.setPosition(scene->_actionCard3->_stationPos, 0); - scene->_actionCard3->_card.fixPriority(170); - scene->_actionCard3->_cardId = scene->_actionCard1->_cardId; - - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - - if (scene->_actionCard1 == &scene->_selectedCard) { - scene->setCursorData(5, 1, 4); - scene->subC4CEC(); - } - - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard3->_stationPos, this); - } - break; - case 1: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(scene->_actionCard3); - scene->_aSound1.play(57); - - bool found = false; - int indexFound = -1; - - switch (scene->_actionIdx1) { - case 0: - for (indexFound = 0; indexFound < 3; indexFound++) { - if (scene->_gameBoardSide[0]._handCard[indexFound]._cardId == 29) { - found = true; - break; - } - } - break; - case 1: - for (indexFound = 0; indexFound < 3; indexFound++) { - if (scene->_gameBoardSide[1]._handCard[indexFound]._cardId == 29) { - found = true; - break; - } - } - break; - case 2: - for (indexFound = 0; indexFound < 3; indexFound++) { - if (scene->_gameBoardSide[2]._handCard[indexFound]._cardId == 29) { - found = true; - break; - } - } - break; - case 3: - for (indexFound = 0; indexFound < 3; indexFound++) { - if (scene->_gameBoardSide[3]._handCard[indexFound]._cardId == 29) { - found = true; - break; - } - } - break; - default: - break; - } - - bool found2 = false; - - if (found) { - switch (scene->_actionIdx1) { - case 0: - scene->subC51A0(&scene->_gameBoardSide[0]._handCard[indexFound], scene->_actionCard3); - found2 = true; - break; - case 1: - scene->subC51A0(&scene->_gameBoardSide[1]._handCard[indexFound], scene->_actionCard3); - found2 = true; - break; - case 2: - scene->subC4CD2(); - if (MessageDialog::show(USE_INTERCEPTOR, NO_MSG, YES_MSG) == 0) - scene->subC4CEC(); - else { - scene->subC51A0(&scene->_gameBoardSide[2]._handCard[indexFound], scene->_actionCard3); - found2 = true; - } - break; - case 3: - scene->subC51A0(&scene->_gameBoardSide[3]._handCard[indexFound], scene->_actionCard3); - found2 = true; - break; - default: - break; - } - } - - if (!found2) - break; - - if (scene->_actionIdx1 == 2) { - int j = 0; - for (int i = 0; i <= 7; i++) { - if (scene->_gameBoardSide[2]._outpostStation[i]._cardId != 0) - ++j; - } - - if (j <= 1) { - for (int i = 0; i <= 7; i++) { - if (scene->_gameBoardSide[2]._outpostStation[i]._cardId != 0) { - scene->_actionCard2 = &scene->_gameBoardSide[2]._outpostStation[i]; - break; - } - } - } else { - scene->subC4CD2(); - - found2 = false; - while (!found2) { - scene->actionDisplay(1330, 130, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - // Wait for a mouse or keypress - Event event; - while (!g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !g_vm->shouldQuit()) { - g_globals->_scenePalette.signalListeners(); - R2_GLOBALS._sceneObjects->draw(); - g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); - } - - scene->_selectedCard._stationPos = event.mousePos; - - for (int i = 0; i <= 7; i++) { - if (scene->_gameBoardSide[2]._outpostStation[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[2]._outpostStation[i]._cardId != 0)) { - scene->_actionCard2 = &scene->_gameBoardSide[2]._outpostStation[0]; - found2 = true; - break; - } - } - } - scene->subC4CEC(); - } - } - - scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; - scene->_currentDiscardIndex--; - scene->_actionCard2->_cardId = 0; - scene->_actionCard2->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard2->_stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard3->_stationPos, this); - } - break; - case 2: - scene->_animatedCard._card.hide(); - scene->discardCard(scene->_actionCard3); - break; - default: - break; - } -} - -// Use trick (card #25 - thieft ?) and pick a card from the opponent -void Scene1337::Action11::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_actionCard2->_card.postInit(); - scene->_actionCard2->_card.hide(); - scene->_actionCard2->_card.setVisage(1332); - scene->_actionCard2->_card.setPosition(scene->_actionCard2->_stationPos, 0); - scene->_actionCard2->_card.fixPriority(170); - scene->_actionCard2->_cardId = 25; - - if (scene->_actionIdx1 == 2) { - scene->_animatedCard._card.setPosition(scene->_actionCard2->_stationPos, 0); - scene->setCursorData(5, 1, 4); - } else { - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - } - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 1: { - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(57); - - bool found = false; - bool noAction = true; - - int i = -1; - - switch (scene->_actionIdx2) { - case 0: - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[0]._handCard[i]._cardId == 27) { - found = true; - break; - } - } - - if ((found) && (scene->getFreeHandCard(scene->_actionIdx1) != -1)) { - scene->_actionCard1 = &scene->_gameBoardSide[0]._handCard[i]; - scene->_actionCard2 = &scene->_gameBoardSide[0]._emptyStationPos; - if (scene->_actionIdx1 != 0) { - int tmpVal = scene->getFreeHandCard(scene->_actionIdx1); - scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionIdx1]._handCard[tmpVal]; - } - scene->_actionItem.setAction(&scene->_action12); - noAction = false; - } - break; - case 1: - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[1]._handCard[i]._cardId == 27) { - found = true; - break; - } - } - - if ((found) && (scene->getFreeHandCard(scene->_actionIdx1) != -1)) { - scene->_actionCard1 = &scene->_gameBoardSide[1]._handCard[i]; - scene->_actionCard2 = &scene->_gameBoardSide[1]._emptyStationPos; - if (scene->_actionIdx1 != 1) { - int tmpVal = scene->getFreeHandCard(scene->_actionIdx1); - scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionIdx1]._handCard[tmpVal]; - } - scene->_actionItem.setAction(&scene->_action12); - noAction = false; - } - break; - case 2: - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[2]._handCard[i]._cardId == 27) { - found = true; - break; - } - } - - if ((found) && (scene->getFreeHandCard(scene->_actionIdx1) != -1)) { - scene->subC4CD2(); - if (MessageDialog::show(USE_DOUBLE_AGENT, NO_MSG, YES_MSG) == 0) - scene->subC4CEC(); - else { - scene->subC4CEC(); - scene->_actionCard1 = &scene->_gameBoardSide[2]._handCard[i]; - scene->_actionCard2 = &scene->_gameBoardSide[2]._emptyStationPos; - if (scene->_actionIdx1 != 2) { - int tmpVal = scene->getFreeHandCard(scene->_actionIdx1); - scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionIdx1]._handCard[tmpVal]; - } - scene->_actionItem.setAction(&scene->_action12); - noAction = false; - } - } - break; - case 3: - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[3]._handCard[i]._cardId == 27) { - found = true; - break; - } - } - - if ((found) && (scene->getFreeHandCard(scene->_actionIdx1) != -1)) { - scene->_actionCard1 = &scene->_gameBoardSide[3]._handCard[i]; - scene->_actionCard2 = &scene->_gameBoardSide[3]._emptyStationPos; - if (scene->_actionIdx1 != 3) { - int tmpVal = scene->getFreeHandCard(scene->_actionIdx1); - scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionIdx1]._handCard[tmpVal]; - } - scene->_actionItem.setAction(&scene->_action12); - noAction = false; - } - break; - default: - break; - } - - if (!noAction) - return; - - if (scene->_actionIdx1 == 2) { - int count = 0; - if (scene->_actionIdx2 != 2) { - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[scene->_actionIdx2]._handCard[i]._cardId == 0) - ++count; - } - } - - if (count > 1) { - scene->subC4CD2(); - - found = false; - while (!found) { - switch (scene->_actionIdx2) { - case 0: - scene->actionDisplay(1330, 131, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 1: - scene->actionDisplay(1330, 132, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 3: - scene->actionDisplay(1330, 133, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - - Event event; - while (!g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !g_vm->shouldQuit()) { - g_globals->_scenePalette.signalListeners(); - R2_GLOBALS._sceneObjects->draw(); - g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); - } - - scene->_selectedCard._stationPos = event.mousePos; - - found = false; - - if (scene->_actionIdx2 != 2) { - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[scene->_actionIdx2]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[scene->_actionIdx2]._handCard[i]._cardId != 0)) { - scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionIdx2]._handCard[i]; - found = true; - break; - } - } - } - } // while - scene->_displayHelpFl = true; - scene->subC4CEC(); - } else if (scene->_actionIdx2 != 2) { - int tmpVal = scene->getFreeHandCard(scene->_actionIdx2); - scene->_actionCard3 = &scene->_gameBoardSide[scene->_actionIdx2]._handCard[tmpVal]; - } - } - - scene->_actionCard1->_card.postInit(); - scene->_actionCard1->_card.hide(); - scene->_actionCard1->_card.setVisage(1332); - scene->_actionCard1->_card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_actionCard1->_card.fixPriority(170); - scene->_actionCard1->_card.setStrip2(1); - scene->_actionCard1->_cardId = scene->_actionCard3->_cardId; - - scene->_actionCard3->_cardId = 0; - scene->_actionCard3->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard3->_stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard1->_stationPos, this); - } - break; - case 2: - scene->_animatedCard._card.hide(); - switch (scene->_actionIdx1) { - case 0: - scene->_actionCard1->_card.setFrame2(2); - scene->_actionCard1->_card.show(); - break; - case 1: - scene->_actionCard1->_card.setFrame2(4); - scene->_actionCard1->_card.show(); - break; - case 3: - scene->_actionCard1->_card.setFrame2(3); - scene->_actionCard1->_card.show(); - break; - default: - scene->setAnimationInfo(scene->_actionCard1); - break; - } - - scene->_currentPlayerNumb--; - scene->_showPlayerTurn = false; - scene->discardCard(scene->_actionCard2); - break; - default: - break; - } -} - -// Pick a card in opponent hand -void Scene1337::Action12::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: - signal(); - break; - case 1: { - scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; - scene->_currentDiscardIndex++; - scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 2: - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(58); - if (scene->_actionIdx2 == 2) { - int count = 0; - int i = -1; - switch (scene->_actionIdx1) { - case 0: - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[0]._handCard[i]._cardId != 0) - ++count; - } - break; - case 1: - for (i = 0; i <= 3; i++) { - // The original game was counting in the hand of player 3, which is obviously wrong - if (scene->_gameBoardSide[1]._handCard[i]._cardId != 0) - ++count; - } - break; - case 3: - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[3]._handCard[i]._cardId != 0) - ++count; - } - break; - default: - break; - } - - if (count > 1) { - scene->subC4CD2(); - - bool found = false; - - while (!found) { - switch (scene->_actionIdx1) { - case 0: - scene->actionDisplay(1330, 131, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 1: - scene->actionDisplay(1330, 132, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 3: - scene->actionDisplay(1330, 133, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - - Event event; - while (!g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS) && !g_vm->shouldQuit()) { - g_globals->_scenePalette.signalListeners(); - R2_GLOBALS._sceneObjects->draw(); - g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); - } - - scene->_selectedCard._stationPos = event.mousePos; - - if (scene->_actionIdx1 == 0) { - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[0]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[0]._handCard[i]._cardId != 0)) { - found = true; - scene->_actionCard3 = &scene->_gameBoardSide[0]._handCard[i]; - break; - } - } - } - - if (scene->_actionIdx1 == 3) { - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[3]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[3]._handCard[i]._cardId != 0)) { - found = true; - scene->_actionCard3 = &scene->_gameBoardSide[3]._handCard[i]; - break; - } - } - } - - if (scene->_actionIdx1 == 1) { - for (i = 0; i <= 3; i++) { - if (scene->_gameBoardSide[1]._handCard[i].isIn(scene->_selectedCard._stationPos) && (scene->_gameBoardSide[1]._handCard[i]._cardId != 0)) { - found = true; - scene->_actionCard3 = &scene->_gameBoardSide[1]._handCard[i]; - break; - } - } - } - } - scene->subC4CEC(); - } else if (scene->_actionIdx1 != 1) { - switch (scene->_actionIdx1) { - case 0: - scene->_actionCard3 = &scene->_gameBoardSide[0]._handCard[scene->getFreeHandCard(0)]; - break; - case 3: - scene->_actionCard3 = &scene->_gameBoardSide[3]._handCard[scene->getFreeHandCard(3)]; - break; - default: - break; - } - } else { - scene->_actionCard3 = &scene->_gameBoardSide[1]._handCard[scene->getFreeHandCard(1)]; - } - - scene->_actionCard1->_card.postInit(); - scene->_actionCard1->_card.hide(); - scene->_actionCard1->_card.setVisage(1332); - scene->_actionCard1->_card.setPosition(scene->_actionCard1->_stationPos); - scene->_actionCard1->_card.fixPriority(170); - scene->_actionCard1->_card.setStrip2(1); - scene->_actionCard1->_cardId = scene->_actionCard3->_cardId; - - scene->_actionCard3->_cardId = 0; - scene->_actionCard3->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard3->_stationPos); - scene->_animatedCard._card.show(); - scene->_aSound1.play(57); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard1->_stationPos, this); - } - break; - case 3: - scene->_animatedCard._card.hide(); - switch (scene->_actionIdx2) { - case 0: - scene->_actionCard1->_card.setFrame2(2); - scene->_actionCard1->_card.show(); - break; - case 1: - scene->_actionCard1->_card.setFrame2(4); - scene->_actionCard1->_card.show(); - break; - case 3: - scene->_actionCard1->_card.setFrame2(3); - scene->_actionCard1->_card.show(); - break; - default: - scene->setAnimationInfo(scene->_actionCard1); - break; - } - scene->discardCard(scene->_actionCard2); - scene->handleNextTurn(); - break; - default: - break; - } -} - -void Scene1337::Action13::signal() { - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - - switch (_actionIndex++) { - case 0: { - scene->_availableCardsPile[scene->_currentDiscardIndex] = scene->_actionCard2->_cardId; - scene->_currentDiscardIndex--; - - scene->_actionCard2->_cardId = scene->_actionCard1->_cardId; - - scene->_actionCard1->_cardId = 0; - scene->_actionCard1->_card.remove(); - - scene->_animatedCard._card.setPosition(scene->_actionCard1->_stationPos, 0); - scene->_animatedCard._card.show(); - - NpcMover *mover = new NpcMover(); - scene->_animatedCard._card.addMover(mover, &scene->_actionCard2->_stationPos, this); - } - break; - case 1: - scene->_animatedCard._card.hide(); - scene->setAnimationInfo(scene->_actionCard2); - scene->_aSound1.play(58); - signal(); - break; - case 2: - scene->discardCard(scene->_actionCard2); - break; - default: - break; - } -} - -void Scene1337::postInit(SceneObjectList *OwnerList) { -// In the original, may be found in subPostInit. -// Without it, enableControl asserts - loadScene(1330); - R2_GLOBALS._uiElements._active = false; - SceneExt::postInit(); -// - - // Hide the user interface - BF_GLOBALS._interfaceY = SCREEN_HEIGHT; - R2_GLOBALS._uiElements._visible = false; - - R2_GLOBALS._player.enableControl(); - R2_GLOBALS._player._canWalk = false; - R2_GLOBALS._player._uiEnabled = false; - - _delayedFunction = nullptr; - - _actionCard1 = nullptr; - _actionCard2 = nullptr; - _actionCard3 = nullptr; - - _gameBoardSide[2]._handCard[0]._stationPos = Common::Point(10, 174); - _gameBoardSide[2]._handCard[1]._stationPos = Common::Point(37, 174); - _gameBoardSide[2]._handCard[2]._stationPos = Common::Point(64, 174); - _gameBoardSide[2]._handCard[3]._stationPos = Common::Point(91, 174); - - _gameBoardSide[2]._outpostStation[0]._stationPos = Common::Point(119, 174); - _gameBoardSide[2]._outpostStation[1]._stationPos = Common::Point(119, 148); - _gameBoardSide[2]._outpostStation[2]._stationPos = Common::Point(119, 122); - _gameBoardSide[2]._outpostStation[3]._stationPos = Common::Point(145, 122); - _gameBoardSide[2]._outpostStation[4]._stationPos = Common::Point(171, 122); - _gameBoardSide[2]._outpostStation[5]._stationPos = Common::Point(171, 148); - _gameBoardSide[2]._outpostStation[6]._stationPos = Common::Point(171, 174); - _gameBoardSide[2]._outpostStation[7]._stationPos = Common::Point(145, 174); - - _gameBoardSide[2]._delayCard._stationPos = Common::Point(199, 174); - - _gameBoardSide[2]._emptyStationPos._stationPos = Common::Point(145, 148); - - _gameBoardSide[2]._card1Pos = Common::Point(10, 174); - _gameBoardSide[2]._card2Pos = Common::Point(37, 174); - _gameBoardSide[2]._card3Pos = Common::Point(64, 174); - _gameBoardSide[2]._card4Pos = Common::Point(91, 174); - _gameBoardSide[2]._frameNum = 2; - - _gameBoardSide[3]._handCard[0]._stationPos = Common::Point(14, 14); - _gameBoardSide[3]._handCard[1]._stationPos = Common::Point(14, 36); - _gameBoardSide[3]._handCard[2]._stationPos = Common::Point(14, 58); - _gameBoardSide[3]._handCard[3]._stationPos = Common::Point(14, 80); - - _gameBoardSide[3]._outpostStation[0]._stationPos = Common::Point(37, 66); - _gameBoardSide[3]._outpostStation[1]._stationPos = Common::Point(63, 66); - _gameBoardSide[3]._outpostStation[2]._stationPos = Common::Point(89, 66); - _gameBoardSide[3]._outpostStation[3]._stationPos = Common::Point(89, 92); - _gameBoardSide[3]._outpostStation[4]._stationPos = Common::Point(89, 118); - _gameBoardSide[3]._outpostStation[5]._stationPos = Common::Point(63, 118); - _gameBoardSide[3]._outpostStation[6]._stationPos = Common::Point(37, 118); - _gameBoardSide[3]._outpostStation[7]._stationPos = Common::Point(37, 92); - - _gameBoardSide[3]._delayCard._stationPos = Common::Point(37, 145); - - _gameBoardSide[3]._emptyStationPos._stationPos = Common::Point(63, 92); - - _gameBoardSide[3]._card1Pos = Common::Point(14, 14); - _gameBoardSide[3]._card2Pos = Common::Point(14, 36); - _gameBoardSide[3]._card3Pos = Common::Point(14, 58); - _gameBoardSide[3]._card4Pos = Common::Point(14, 80); - _gameBoardSide[3]._frameNum = 3; - - _gameBoardSide[0]._handCard[0]._stationPos = Common::Point(280, 5); - _gameBoardSide[0]._handCard[1]._stationPos = Common::Point(253, 5); - _gameBoardSide[0]._handCard[2]._stationPos = Common::Point(226, 5); - _gameBoardSide[0]._handCard[3]._stationPos = Common::Point(199, 5); - - _gameBoardSide[0]._outpostStation[0]._stationPos = Common::Point(171, 16); - _gameBoardSide[0]._outpostStation[1]._stationPos = Common::Point(171, 42); - _gameBoardSide[0]._outpostStation[2]._stationPos = Common::Point(171, 68); - _gameBoardSide[0]._outpostStation[3]._stationPos = Common::Point(145, 68); - _gameBoardSide[0]._outpostStation[4]._stationPos = Common::Point(119, 68); - _gameBoardSide[0]._outpostStation[5]._stationPos = Common::Point(119, 42); - _gameBoardSide[0]._outpostStation[6]._stationPos = Common::Point(119, 16); - _gameBoardSide[0]._outpostStation[7]._stationPos = Common::Point(145, 16); - - _gameBoardSide[0]._delayCard._stationPos = Common::Point(91, 16); - - _gameBoardSide[0]._emptyStationPos._stationPos = Common::Point(145, 42); - - _gameBoardSide[0]._card1Pos = Common::Point(280, 5); - _gameBoardSide[0]._card2Pos = Common::Point(253, 5); - _gameBoardSide[0]._card3Pos = Common::Point(226, 5); - _gameBoardSide[0]._card4Pos = Common::Point(199, 5); - _gameBoardSide[0]._frameNum = 2; - - _gameBoardSide[1]._handCard[0]._stationPos = Common::Point(283, 146); - _gameBoardSide[1]._handCard[1]._stationPos = Common::Point(283, 124); - _gameBoardSide[1]._handCard[2]._stationPos = Common::Point(283, 102); - _gameBoardSide[1]._handCard[3]._stationPos = Common::Point(283, 80); - - _gameBoardSide[1]._outpostStation[0]._stationPos = Common::Point(253, 122); - _gameBoardSide[1]._outpostStation[1]._stationPos = Common::Point(227, 122); - _gameBoardSide[1]._outpostStation[2]._stationPos = Common::Point(201, 122); - _gameBoardSide[1]._outpostStation[3]._stationPos = Common::Point(201, 96); - _gameBoardSide[1]._outpostStation[4]._stationPos = Common::Point(201, 70); - _gameBoardSide[1]._outpostStation[5]._stationPos = Common::Point(227, 70); - _gameBoardSide[1]._outpostStation[6]._stationPos = Common::Point(253, 70); - _gameBoardSide[1]._outpostStation[7]._stationPos = Common::Point(253, 96); - - _gameBoardSide[1]._delayCard._stationPos = Common::Point(253, 43); - - _gameBoardSide[1]._emptyStationPos._stationPos = Common::Point(227, 96); - - _gameBoardSide[1]._card1Pos = Common::Point(283, 146); - _gameBoardSide[1]._card2Pos = Common::Point(283, 124); - _gameBoardSide[1]._card3Pos = Common::Point(283, 102); - _gameBoardSide[1]._card4Pos = Common::Point(283, 80); - _gameBoardSide[1]._frameNum = 4; - - subPostInit(); - - _stockPile.postInit(); -} - -void Scene1337::remove() { - if (R2_GLOBALS._v57709 > 1) { - subD1917(); - subD1940(false); - } - - R2_GLOBALS._uiElements._active = true; - R2_GLOBALS._uiElements._visible = true; - SceneExt::remove(); -} - -void Scene1337::process(Event &event) { - if (event.eventType == EVENT_BUTTON_DOWN) { - if (event.btnState == BTNSHIFT_RIGHT) { - updateCursorId(R2_GLOBALS._mouseCursorId, true); - event.handled = true; - } else if (_delayedFunction) { - FunctionPtrType tmpFctPtr = _delayedFunction; - _delayedFunction = nullptr; - (this->*tmpFctPtr)(); - event.handled = true; - } - } else if (event.eventType == EVENT_KEYPRESS) { - if (event.kbd.keycode == Common::KEYCODE_SPACE) { - if (_delayedFunction) { - FunctionPtrType tmpFctPtr = _delayedFunction; - _delayedFunction = nullptr; - (this->*tmpFctPtr)(); - event.handled = true; - } - } else - warning("Fixme: Find proper keycode value"); - } - - if (!event.handled) - Scene::process(event); -} - -void Scene1337::dispatch() { - if (!_instructionsDisplayedFl) { - ++_instructionsWaitCount; - if (_instructionsWaitCount == 4) { - _instructionsDisplayedFl = true; - suggestInstructions(); - } - } - - // The following code is in the original in sceneHandler::process(), - // which is terrible as it's checked in every scene of the game. - setCursorData(5, _cursorCurStrip, _cursorCurFrame); - // - - Scene::dispatch(); -} - -void Scene1337::actionDisplay(int resNum, int lineNum, int x, int y, int keepOnScreen, int width, int textMode, int fontNum, int colFG, int colBGExt, int colFGExt) { - // TODO: Check if it's normal that arg5 is unused and replaced by an hardcoded 0 value - // May hide an original bug - - SceneItem::display(resNum, lineNum, SET_X, x, SET_Y, y, SET_KEEP_ONSCREEN, 0, - SET_WIDTH, width, SET_POS_MODE, -1, SET_TEXT_MODE, textMode, - SET_FONT, fontNum, SET_FG_COLOR, colFG, SET_EXT_BGCOLOR, colBGExt, - SET_EXT_FGCOLOR, colFGExt, LIST_END); -} - -void Scene1337::setAnimationInfo(Card *card) { - if (!card) - return; - - if (card->_cardId > 25) { - card->_card.setStrip2(4); - card->_card.setFrame(card->_cardId - 25); - } else if (card->_cardId > 9) { - card->_card.setStrip2(3); - card->_card.setFrame(card->_cardId - 9); - } else { - card->_card.setStrip2(2); - card->_card.setFrame(card->_cardId); - } - - card->_card.show(); - R2_GLOBALS._sceneObjects->draw(); -} - -void Scene1337::handleNextTurn() { - switch (_winnerId) { - case -1: - ++_currentPlayerNumb; - if (_currentPlayerNumb > 3) - _currentPlayerNumb = 0; - - if (_showPlayerTurn) { - _currentPlayerArrow.show(); - switch (_currentPlayerNumb) { - case 0: - _currentPlayerArrow.setStrip(3); - break; - case 1: - _currentPlayerArrow.setStrip(4); - break; - case 2: - subD1975(174, 107); - _currentPlayerArrow.setStrip(1); - break; - case 3: - subC4CEC(); - _currentPlayerArrow.setStrip(2); - break; - default: - break; - } - - if (!_autoplay) - _delayedFunction = &Scene1337::handlePlayerTurn; - else - handlePlayerTurn(); - } else { - handlePlayerTurn(); - } - break; - case 0: - _aSound2.play(62); - actionDisplay(1330, 135, 159, 10, 1, 200, 0, 7, 0, 154, 154); - actionDisplay(1330, 121, 20, 99, 1, 136, 0, 7, 0, 172, 172); - actionDisplay(1330, 122, 300, 99, 1, 136, 0, 7, 0, 117, 117); - R2_GLOBALS._sceneObjects->draw(); - actionDisplay(1330, 123, 159, 134, 1, 200, 0, 7, 0, 105, 105); - break; - case 1: - _aSound2.play(62); - actionDisplay(1330, 151, 300, 99, 1, 136, 0, 7, 0, 117, 117); - actionDisplay(1330, 118, 20, 99, 1, 136, 0, 7, 0, 172, 172); - actionDisplay(1330, 119, 159, 10, 1, 200, 0, 7, 0, 154, 154); - R2_GLOBALS._sceneObjects->draw(); - actionDisplay(1330, 120, 159, 134, 1, 200, 0, 7, 0, 105, 105); - break; - case 2: - _aSound2.play(62); - actionDisplay(1330, 134, 159, 134, 1, 200, 0, 7, 0, 105, 105); - actionDisplay(1330, 124, 20, 99, 1, 136, 0, 7, 0, 172, 172); - actionDisplay(1330, 126, 159, 10, 1, 200, 0, 7, 0, 154, 154); - R2_GLOBALS._sceneObjects->draw(); - actionDisplay(1330, 125, 300, 99, 1, 136, 0, 7, 0, 117, 117); - break; - case 3: - _aSound2.play(62); - actionDisplay(1330, 150, 20, 99, 1, 136, 0, 7, 0, 172, 172); - actionDisplay(1330, 115, 300, 99, 1, 136, 0, 7, 0, 117, 117); - actionDisplay(1330, 116, 159, 10, 1, 200, 0, 7, 0, 154, 154); - R2_GLOBALS._sceneObjects->draw(); - actionDisplay(1330, 117, 159, 134, 1, 200, 0, 7, 0, 105, 105); - break; - default: - break; - } - - if (_winnerId != -1) - R2_GLOBALS._sceneManager.changeScene(125); - -} - -void Scene1337::handlePlayerTurn() { - if (_showPlayerTurn) - _currentPlayerArrow.hide(); - - switch (_currentPlayerNumb) { - case 2: - subC4CD2(); - if (_displayHelpFl) - actionDisplay(1330, 114, 159, 10, 1, 200, 0, 7, 0, 154, 154); - _displayHelpFl = false; - // No break on purpose - case 0: - // No break on purpose - case 1: - // No break on purpose - case 3: - _actionItem.setAction(&_action4); - default: - break; - } - - _showPlayerTurn = true; - -} - -bool Scene1337::isStationCard(int cardId) { - switch (cardId) { - case 10: - // No break on purpose - case 12: - // No break on purpose - case 15: - // No break on purpose - case 17: - // No break on purpose - case 18: - // No break on purpose - case 19: - // No break on purpose - case 20: - // No break on purpose - case 21: - return true; - default: - return false; - } -} - -bool Scene1337::isStopConstructionCard(int cardId) { - switch (cardId) { - case 11: - // No break on purpose - case 14: - // No break on purpose - case 16: - // No break on purpose - case 24: - return true; - default: - return false; - } -} - -int Scene1337::getStationId(int playerId, int handCardId) { - if ((_gameBoardSide[playerId]._handCard[handCardId]._cardId > 1) && (_gameBoardSide[playerId]._handCard[handCardId]._cardId <= 9)) - return handCardId; - - return -1; -} - -int Scene1337::findPlatformCardInHand(int playerId) { - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 1) - return i; - } - - return -1; -} - -int Scene1337::findCard13InHand(int playerId) { - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 13) - return i; - } - - return -1; -} - -int Scene1337::checkThieftCard(int playerId) { - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 25) - return i; - } - - return -1; -} - -int Scene1337::isDelayCard(int cardId) { - switch (cardId) { - case 11: - // No break on purpose - case 14: - // No break on purpose - case 16: - // No break on purpose - case 24: - return cardId; - break; - default: - return -1; - break; - } -} - -int Scene1337::getStationCardId(int cardId) { - switch (cardId) { - case 10: - // No break on purpose - case 12: - // No break on purpose - case 15: - // No break on purpose - case 17: - // No break on purpose - case 18: - // No break on purpose - case 19: - // No break on purpose - case 20: - // No break on purpose - case 21: - return cardId; - default: - return -1; - } -} - -void Scene1337::handlePlayer01Discard(int playerId) { - switch (playerId) { - case 0: - for (int i = 0; i <= 3; i++) { - if (getStationCardId(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (isDelayCard(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if ((_gameBoardSide[playerId]._handCard[i]._cardId > 1) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 9)) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if ((_gameBoardSide[playerId]._handCard[i]._cardId >= 26) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 33)) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 1) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 25) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 13) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - break; - case 1: - for (int i = 0; i <= 3; i++) { - if ((_gameBoardSide[playerId]._handCard[i]._cardId >= 26) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 33)) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 1) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if ((_gameBoardSide[playerId]._handCard[i]._cardId > 1) && (_gameBoardSide[playerId]._handCard[i]._cardId <= 9)) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (getStationCardId(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (isDelayCard(_gameBoardSide[playerId]._handCard[i]._cardId) != -1) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 25) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (_gameBoardSide[playerId]._handCard[i]._cardId == 13) { - discardCard(&_gameBoardSide[playerId]._handCard[i]); - return; - } - } - - break; - default: - break; - } -} - -void Scene1337::playThieftCard(int playerId, Card *card, int victimId) { - _actionIdx1 = playerId; - _actionIdx2 = victimId; - - int randIndx; - - for (;;) { - randIndx = R2_GLOBALS._randomSource.getRandomNumber(3); - if (_gameBoardSide[victimId]._handCard[randIndx]._cardId != 0) - break; - } - - _actionCard1 = card; - _actionCard2 = &_gameBoardSide[victimId]._emptyStationPos; - _actionCard3 = &_gameBoardSide[victimId]._handCard[randIndx]; - - _actionItem.setAction(&_action11); -} - -int Scene1337::getPreventionCardId(int cardId) { - int retVal; - - switch (cardId) { - case 10: - retVal = 2; - break; - case 12: - retVal = 3; - break; - case 15: - retVal = 5; - break; - case 17: - retVal = 9; - break; - case 18: - retVal = 6; - break; - case 19: - retVal = 4; - break; - case 20: - retVal = 8; - break; - case 21: - retVal = 7; - break; - default: - retVal = -1; - } - - return retVal; -} - -bool Scene1337::isAttackPossible(int victimId, int cardId) { - if (victimId < 0 || victimId >= ARRAYSIZE(_gameBoardSide)) - error("Scene1337::isAttackPossible() victimId:%d out of range 0 to %d", victimId, ARRAYSIZE(_gameBoardSide)-1); - - for (int i = 0; i <= 7; i++) { - if (_gameBoardSide[victimId]._outpostStation[i]._cardId != 0) { - if (getPreventionCardId(cardId) == _gameBoardSide[victimId]._outpostStation[i]._cardId) - return false; - } - } - return true; -} - -int Scene1337::getPlayerWithOutpost(int playerId) { - int randPlayerId = R2_GLOBALS._randomSource.getRandomNumber(3); - - for (int i = 0; i <= 3; i++) { - if (randPlayerId != playerId) { - for (int j = 0; j <= 7; j++) { - if (_gameBoardSide[randPlayerId]._outpostStation[j]._cardId != 0) - return randPlayerId; - } - } - - if (playerId == 1) { - randPlayerId--; - if (randPlayerId < 0) - randPlayerId = 3; - } else { - ++randPlayerId; - if (randPlayerId > 3) - randPlayerId = 0; - } - } - - return -1; -} - -bool Scene1337::checkAntiDelayCard(int delayCardId, int cardId) { - if ((delayCardId == 11) && (cardId == 26)) - return true; - - if ((delayCardId == 14) && (cardId == 30)) - return true; - - if ((delayCardId == 16) && (cardId == 32)) - return true; - - if ((delayCardId == 24) && (cardId == 28)) - return true; - - return false; -} - -void Scene1337::playStationCard(Card *station, Card *platform) { - _actionCard1 = station; - _actionCard2 = platform; - _actionItem.setAction(&_action7); -} - -int Scene1337::getFreeHandCard(int playerId) { - if ( (_gameBoardSide[playerId]._handCard[0]._cardId == 0) - && (_gameBoardSide[playerId]._handCard[1]._cardId == 0) - && (_gameBoardSide[playerId]._handCard[2]._cardId == 0) - && (_gameBoardSide[playerId]._handCard[3]._cardId == 0)) - return -1; - - int randIndx; - for (;;) { - randIndx = R2_GLOBALS._randomSource.getRandomNumber(3); - if (_gameBoardSide[playerId]._handCard[randIndx]._cardId == 0) - break; - } - - return randIndx; -} - -void Scene1337::playPlatformCard(Card *card, Card *dest) { - _actionCard1 = card; - _actionCard2 = dest; - - _actionItem.setAction(&_action6); -} - -void Scene1337::playDelayCard(Card *card, Card *dest) { - _actionCard1 = card; - _actionCard2 = dest; - - _actionItem.setAction(&_action9); -} - -void Scene1337::playAntiDelayCard(Card *card, Card *dest) { - _actionCard1 = card; - _actionCard2 = dest; - - _actionItem.setAction(&_action8); - - handleNextTurn(); -} - - -Scene1337::Card *Scene1337::getStationCard(int playerId) { - for (int i = 0; i <= 7; i++) { - if ((_gameBoardSide[playerId]._outpostStation[i]._cardId >= 1) && (_gameBoardSide[playerId]._outpostStation[i]._cardId <= 9)) - return &_gameBoardSide[playerId]._outpostStation[i]; - } - - return nullptr; -} - -void Scene1337::playCounterTrickCard(Card *card, int playerId) { - _actionCard1 = card; - _actionCard2 = getStationCard(playerId); - _actionCard3 = &_gameBoardSide[playerId]._emptyStationPos; - _actionIdx1 = playerId; - _actionItem.setAction(&_action10); - handleNextTurn(); -} - -void Scene1337::discardCard(Card *card) { - _actionCard1 = card; - - _actionItem.setAction(&_action5); -} - -void Scene1337::subC4CD2() { - if (R2_GLOBALS._v57709 > 0) { - subD1917(); - subD1940(false); // _v5780C-- - } -} - -void Scene1337::subC4CEC() { - if (R2_GLOBALS._v57709 == 0) { - subD18F5(); - subD1940(true); // _v5780C++ - } -} - -void Scene1337::subC51A0(Card *subObj1, Card *subObj2) { - _actionCard1 = subObj1; - _actionCard2 = subObj2; - - _actionItem.setAction(&_action13); -} - -void Scene1337::displayDialog(int dialogNumb) { - switch (dialogNumb - 1) { - case 0: - actionDisplay(1330, 53, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 1: - actionDisplay(1330, 57, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 2: - actionDisplay(1330, 58, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 3: - actionDisplay(1330, 59, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 4: - actionDisplay(1330, 60, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 5: - actionDisplay(1330, 61, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 6: - actionDisplay(1330, 62, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 7: - actionDisplay(1330, 63, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 8: - actionDisplay(1330, 64, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 9: - actionDisplay(1330, 65, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 10: - actionDisplay(1330, 67, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 11: - actionDisplay(1330, 69, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 12: - actionDisplay(1330, 71, 159, 10, 1, 200, 0, 7, 0, 154, 154); - actionDisplay(1330, 72, 159, 10, 1, 200, 0, 7, 0, 154, 154); - actionDisplay(1330, 73, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 13: - actionDisplay(1330, 79, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 14: - actionDisplay(1330, 81, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - actionDisplay(1330, 83, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 16: - actionDisplay(1330, 85, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 17: - actionDisplay(1330, 87, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 18: - actionDisplay(1330, 89, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 19: - actionDisplay(1330, 91, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - actionDisplay(1330, 93, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 23: - actionDisplay(1330, 95, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 24: - actionDisplay(1330, 97, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 25: - actionDisplay(1330, 104, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 26: - actionDisplay(1330, 105, 159, 10, 1, 200, 0, 7, 0, 154, 154); - actionDisplay(1330, 106, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 27: - actionDisplay(1330, 110, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 28: - actionDisplay(1330, 108, 159, 10, 1, 200, 0, 7, 0, 154, 154); - actionDisplay(1330, 109, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 29: - actionDisplay(1330, 111, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 31: - actionDisplay(1330, 112, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } -} - -void Scene1337::subPostInit() { - R2_GLOBALS._v57709 = 0; - R2_GLOBALS._v5780C = 0; - updateCursorId(1, false); - subD1940(true); // _v5780C++ - subD18F5(); - -// loadScene(1330); -// SceneExt::postInit(); - - R2_GLOBALS._scenePalette.addRotation(224, 235, 1); - - _availableCardsPile[0] = 1; - _availableCardsPile[1] = 1; - _availableCardsPile[2] = 1; - _availableCardsPile[3] = 1; - _availableCardsPile[4] = 1; - _availableCardsPile[5] = 1; - _availableCardsPile[6] = 1; - _availableCardsPile[7] = 1; - _availableCardsPile[8] = 26; - _availableCardsPile[9] = 2; - _availableCardsPile[10] = 2; - _availableCardsPile[11] = 2; - _availableCardsPile[12] = 2; - _availableCardsPile[13] = 2; - _availableCardsPile[14] = 26; - _availableCardsPile[15] = 3; - _availableCardsPile[16] = 3; - _availableCardsPile[17] = 3; - _availableCardsPile[18] = 3; - _availableCardsPile[19] = 3; - _availableCardsPile[20] = 28; - _availableCardsPile[21] = 4; - _availableCardsPile[22] = 4; - _availableCardsPile[23] = 4; - _availableCardsPile[24] = 4; - _availableCardsPile[25] = 4; - _availableCardsPile[26] = 28; - _availableCardsPile[27] = 5; - _availableCardsPile[28] = 5; - _availableCardsPile[29] = 5; - _availableCardsPile[30] = 5; - _availableCardsPile[31] = 5; - _availableCardsPile[32] = 30; - _availableCardsPile[33] = 6; - _availableCardsPile[34] = 6; - _availableCardsPile[35] = 6; - _availableCardsPile[36] = 6; - _availableCardsPile[37] = 6; - _availableCardsPile[38] = 30; - _availableCardsPile[39] = 7; - _availableCardsPile[40] = 7; - _availableCardsPile[41] = 7; - _availableCardsPile[42] = 7; - _availableCardsPile[43] = 7; - _availableCardsPile[44] = 32; - _availableCardsPile[45] = 8; - _availableCardsPile[46] = 8; - _availableCardsPile[47] = 8; - _availableCardsPile[48] = 8; - _availableCardsPile[49] = 8; - _availableCardsPile[50] = 32; - _availableCardsPile[51] = 9; - _availableCardsPile[52] = 9; - _availableCardsPile[53] = 9; - _availableCardsPile[54] = 9; - _availableCardsPile[55] = 9; - _availableCardsPile[56] = 10; - _availableCardsPile[57] = 11; - _availableCardsPile[58] = 12; - _availableCardsPile[59] = 13; - _availableCardsPile[60] = 13; - _availableCardsPile[61] = 14; - _availableCardsPile[62] = 15; - _availableCardsPile[63] = 16; - _availableCardsPile[64] = 17; - _availableCardsPile[65] = 18; - _availableCardsPile[66] = 19; - _availableCardsPile[67] = 20; - _availableCardsPile[68] = 21; - _availableCardsPile[69] = 26; - _availableCardsPile[70] = 28; - _availableCardsPile[71] = 24; - _availableCardsPile[72] = 25; - _availableCardsPile[73] = 25; - _availableCardsPile[74] = 25; - _availableCardsPile[75] = 25; - _availableCardsPile[76] = 26; - _availableCardsPile[77] = 26; - _availableCardsPile[78] = 26; - _availableCardsPile[79] = 27; - _availableCardsPile[80] = 27; - _availableCardsPile[81] = 28; - _availableCardsPile[82] = 28; - _availableCardsPile[83] = 28; - _availableCardsPile[84] = 29; - _availableCardsPile[85] = 29; - _availableCardsPile[86] = 29; - _availableCardsPile[87] = 30; - _availableCardsPile[88] = 30; - _availableCardsPile[89] = 30; - _availableCardsPile[90] = 30; - _availableCardsPile[91] = 32; - _availableCardsPile[92] = 1; - _availableCardsPile[93] = 32; - _availableCardsPile[94] = 32; - _availableCardsPile[95] = 32; - _availableCardsPile[96] = 1; - _availableCardsPile[97] = 1; - _availableCardsPile[98] = 1; - _availableCardsPile[99] = 0; - - _cardsAvailableNumb = 98; - _currentDiscardIndex = 98; // CHECKME: Would make more sense at pos 99 - - _discardPile._cardId = 0; - _discardPile._stationPos = Common::Point(128, 95); - - _stockCard._cardId = 0; - _stockCard._stationPos = Common::Point(162, 95); - - _selectedCard._cardId = 0; - - _animatedCard._card.postInit(); - _animatedCard._card.setVisage(1332); - _animatedCard._card.setStrip(5); - _animatedCard._card.setFrame(1); - _animatedCard._card._moveDiff = Common::Point(10, 10); - _animatedCard._card.fixPriority(400); - _animatedCard._card.setPosition(Common::Point(128, 95), 0); - _animatedCard._card.animate(ANIM_MODE_2, NULL); - _animatedCard._card.hide(); - - _currentPlayerArrow.postInit(); - _currentPlayerArrow.setVisage(1334); - _currentPlayerArrow.setStrip(1); - _currentPlayerArrow.setFrame(1); - _currentPlayerArrow._numFrames = 12; - _currentPlayerArrow.fixPriority(500); - _currentPlayerArrow.setPosition(Common::Point(174, 107), 0); - _currentPlayerArrow.animate(ANIM_MODE_2, NULL); - _currentPlayerArrow.hide(); - - _showPlayerTurn = true; - _displayHelpFl = false; - _winnerId = -1; - - _helpIcon.postInit(); - _helpIcon.setup(9531, 1, 1); - _helpIcon.setPosition(Common::Point(249, 168)); - _helpIcon.setPriority(155); - _helpIcon._effect = EFFECT_NONE; - _helpIcon.show(); - - _autoplay = false; - _instructionsDisplayedFl = false; - _instructionsWaitCount = 0; -} - -void Scene1337::suggestInstructions() { - if (R2_GLOBALS._v57709 > 0) - subD1917(); - - if (MessageDialog::show(NEED_INSTRUCTIONS, NO_MSG, YES_MSG) == 0) { - if (R2_GLOBALS._v57709 == 0) - subD18F5(); - dealCards(); - } else { - if (R2_GLOBALS._v57709 == 0) - subD18F5(); - displayInstructions(); - } -} - -void Scene1337::displayInstructions() { - _actionItem.setAction(&_action1); -} - -void Scene1337::shuffleCards() { - R2_GLOBALS._sceneObjects->draw(); - - // Remove holes in card pile - for (int i = 0; i <= 98; i++) { - if (_availableCardsPile[i] == 0) { - for (int j = i + 1; j <= 98; j ++) { - if (_availableCardsPile[j] != 0) { - _availableCardsPile[i] = _availableCardsPile[j]; - _availableCardsPile[j] = 0; - break; - } - } - } - } - - // Compute the number of available cards - for (int i = 0; i <= 99; i ++) { - if (_availableCardsPile[i] == 0) { - // CHECKME: This will fail if i == 0, which shouldn't happen - // as we don't shuffle cards when no card is available. - _cardsAvailableNumb = i - 1; - _currentDiscardIndex = 98; // CHECKME: Would make more sense at pos 99 - break; - } - } - - for (int i = 0; i < 2000; i ++) { - int randIndx = R2_GLOBALS._randomSource.getRandomNumber(_cardsAvailableNumb); - int swap = _availableCardsPile[0]; - _availableCardsPile[0] = _availableCardsPile[randIndx]; - _availableCardsPile[randIndx] = swap; - } - - _shuffleEndedFl = false; - - // Shuffle cards - _animatedCard._card.setAction(&_action2); - - while(!_shuffleEndedFl && !g_vm->shouldQuit()) { - g_globals->_sceneObjects->recurse(SceneHandler::dispatchObject); - g_globals->_scenePalette.signalListeners(); - R2_GLOBALS._sceneObjects->draw(); - g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); - } -} - -void Scene1337::dealCards() { - _animatedCard._card._moveDiff = Common::Point(30, 30); - shuffleCards(); - - // Deal cards - _actionItem.setAction(&_action3); -} - -void Scene1337::showOptionsDialog() { - // Display menu with "Auto Play", "New Game", "Quit" and "Continue" - OptionsDialog::show(); -} - -void Scene1337::handleClick(int arg1, Common::Point pt) { - int curReg = R2_GLOBALS._sceneRegions.indexOf(g_globals->_events._mousePos); - - if (arg1 == 3) { - bool found = false; - int i; - for (i = 0; i <= 7; i++) { - if ( _gameBoardSide[2]._outpostStation[i].isIn(pt) - || _gameBoardSide[0]._outpostStation[i].isIn(pt) - || _gameBoardSide[1]._outpostStation[i].isIn(pt) - || _gameBoardSide[3]._outpostStation[i].isIn(pt) ) { - found = true; - break; - } - } - - if (found) { - switch (curReg) { - case 5: - if (_gameBoardSide[2]._outpostStation[i]._cardId != 0) - displayDialog(_gameBoardSide[2]._outpostStation[i]._cardId); - else - actionDisplay(1330, 20, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 10: - if (_gameBoardSide[3]._outpostStation[i]._cardId != 0) - displayDialog(_gameBoardSide[3]._outpostStation[i]._cardId); - else - actionDisplay(1330, 22, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - if (_gameBoardSide[0]._outpostStation[i]._cardId != 0) - displayDialog(_gameBoardSide[0]._outpostStation[i]._cardId); - else - actionDisplay(1330, 21, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - if (_gameBoardSide[1]._outpostStation[i]._cardId != 0) - displayDialog(_gameBoardSide[1]._outpostStation[i]._cardId); - else - actionDisplay(1330, 23, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } else if ( _gameBoardSide[2]._delayCard.isIn(pt) - || _gameBoardSide[0]._delayCard.isIn(pt) - || _gameBoardSide[1]._delayCard.isIn(pt) - || _gameBoardSide[3]._delayCard.isIn(pt) ) { - switch (curReg) { - case 5: - if (_gameBoardSide[2]._delayCard._cardId != 0) - displayDialog(_gameBoardSide[2]._delayCard._cardId); - else - actionDisplay(1330, 10, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 10: - if (_gameBoardSide[3]._delayCard._cardId != 0) - displayDialog(_gameBoardSide[3]._delayCard._cardId); - else - actionDisplay(1330, 16, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - if (_gameBoardSide[0]._delayCard._cardId != 0) - displayDialog(_gameBoardSide[0]._delayCard._cardId); - else - actionDisplay(1330, 13, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - if (_gameBoardSide[1]._delayCard._cardId != 0) - displayDialog(_gameBoardSide[1]._delayCard._cardId); - else - actionDisplay(1330, 18, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } else if (_discardPile.isIn(pt)) { - if (_discardPile._cardId != 0) - displayDialog(_discardPile._cardId); - else - actionDisplay(1330, 7, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (_helpIcon._bounds.contains(pt)) - actionDisplay(1330, 43, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else if (_stockCard.isIn(pt)) - actionDisplay(1330, 4, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else if ( (_gameBoardSide[2]._emptyStationPos.isIn(pt)) - || (_gameBoardSide[3]._emptyStationPos.isIn(pt)) - || (_gameBoardSide[0]._emptyStationPos.isIn(pt)) - || (_gameBoardSide[1]._emptyStationPos.isIn(pt)) ) - actionDisplay(1330, 32, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else if (_gameBoardSide[2]._handCard[0].isIn(pt)) - displayDialog(_gameBoardSide[2]._handCard[0]._cardId); - else if (_gameBoardSide[2]._handCard[1].isIn(pt)) - displayDialog(_gameBoardSide[2]._handCard[1]._cardId); - else if (_gameBoardSide[2]._handCard[2].isIn(pt)) - displayDialog(_gameBoardSide[2]._handCard[2]._cardId); - else if (_gameBoardSide[2]._handCard[3].isIn(pt)) - displayDialog(_gameBoardSide[2]._handCard[3]._cardId); - else if ((curReg >= 6) && (curReg <= 9)) - actionDisplay(1330, 29, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else if ((curReg >= 11) && (curReg <= 14)) - actionDisplay(1330, 31, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else if ((curReg >= 16) && (curReg <= 19)) - actionDisplay(1330, 30, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else { - switch (curReg) { - case 0: - actionDisplay(1330, 2, 159, 134, 1, 200, 0, 7, 0, 105, 105); - break; - case 5: - actionDisplay(1330, 25, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 10: - actionDisplay(1330, 27, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - actionDisplay(1330, 26, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - actionDisplay(1330, 28, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 21: - actionDisplay(1330, 24, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } - } - - if (arg1 != 1) - return; - - for (int i = 0; i <= 7; i++) { - if (_gameBoardSide[2]._outpostStation[i].isIn(pt)) { - switch (_gameBoardSide[2]._outpostStation[i]._cardId) { - case 0: - actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 1: - actionDisplay(1330, 54, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - actionDisplay(1330, 34, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - } - return; - } - if (_gameBoardSide[0]._outpostStation[i].isIn(pt)) { - switch (_gameBoardSide[0]._outpostStation[i]._cardId) { - case 0: - actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - actionDisplay(1330, 1, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - } - return; - } - if (_gameBoardSide[1]._outpostStation[i].isIn(pt)) { - switch (_gameBoardSide[1]._outpostStation[i]._cardId) { - case 0: - actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); - break; - default: - actionDisplay(1330, 144, 300, 99, 1, 136, 0, 7, 0, 117, 117); - break; - } - return; - } - if (_gameBoardSide[3]._outpostStation[i].isIn(pt)) { - switch (_gameBoardSide[3]._outpostStation[i]._cardId) { - case 0: - actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); - break; - default: - actionDisplay(1330, 145, 20, 99, 1, 136, 0, 7, 0, 172, 172); - break; - } - return; - } - } - - if (_gameBoardSide[2]._delayCard.isIn(pt)) { - // The original uses _gameBoardSide[0], which is obviously a bug. - if (_gameBoardSide[2]._delayCard._cardId != 0) - actionDisplay(1330, 39, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else - actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - return; - } - if (_gameBoardSide[3]._delayCard.isIn(pt)) { - if (_gameBoardSide[3]._delayCard._cardId != 0) - actionDisplay(1330, 145, 20, 99, 1, 136, 0, 7, 0, 172, 172); - else - actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); - - return; - } - if (_gameBoardSide[1]._delayCard.isIn(pt)) { - if (_gameBoardSide[1]._delayCard._cardId != 0) - actionDisplay(1330, 144, 300, 99, 1, 136, 0, 7, 0, 117, 117); - else - actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); - - return; - } - if (_gameBoardSide[0]._delayCard.isIn(pt)) { - if (_gameBoardSide[0]._delayCard._cardId != 0) - actionDisplay(1330, 1, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else - actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - - return; - } - if (_gameBoardSide[3]._emptyStationPos.isIn(pt)) { - actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); - return; - } - if (_gameBoardSide[1]._emptyStationPos.isIn(pt)) { - actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); - return; - } - if (_gameBoardSide[0]._emptyStationPos.isIn(pt)) { - actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - return; - } - - if (_helpIcon._bounds.contains(pt)) { - showOptionsDialog(); - return; - } - - if (_discardPile.isIn(pt)) - actionDisplay(1330, 9, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else if (_stockCard.isIn(pt)) - actionDisplay(1330, 5, 159, 10, 1, 200, 0, 7, 0, 154, 154); - else { - switch (curReg) { - case 0: - actionDisplay(1330, 3, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 6: - // no break on purpose - case 7: - // no break on purpose - case 8: - // no break on purpose - case 9: - actionDisplay(1330, 145, 20, 99, 1, 136, 0, 7, 0, 172, 172); - break; - case 10: - actionDisplay(1330, 147, 20, 99, 1, 136, 0, 7, 0, 172, 172); - break; - case 11: - // no break on purpose - case 12: - // no break on purpose - case 13: - // no break on purpose - case 14: - actionDisplay(1330, 1, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 16: - // no break on purpose - case 17: - // no break on purpose - case 18: - // no break on purpose - case 19: - actionDisplay(1330, 144, 300, 99, 1, 136, 0, 7, 0, 117, 117); - break; - case 20: - actionDisplay(1330, 146, 300, 99, 1, 136, 0, 7, 0, 117, 117); - break; - default: - actionDisplay(1330, 11, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - } - } -} - -void Scene1337::handlePlayer0() { - if (_gameBoardSide[0]._delayCard._cardId != 0) { - switch (_gameBoardSide[0]._delayCard._cardId) { - case 10: - //No break on purpose - case 12: - //No break on purpose - case 15: - //No break on purpose - case 17: - //No break on purpose - case 18: - //No break on purpose - case 19: - //No break on purpose - case 20: - //No break on purpose - case 21: - discardCard(&_gameBoardSide[0]._delayCard); - break; - default: - for (int i = 0; i <= 3; i++) { - if (checkAntiDelayCard(_gameBoardSide[0]._delayCard._cardId, _gameBoardSide[0]._handCard[i]._cardId)) { - playAntiDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[0]._delayCard); - return; - } - } - - break; - } - } - - for (int i = 0; i <= 3; i++) { - int tmpVal = getStationId(0, i); - - if (tmpVal != -1) { - bool stationAlreadyPresentFl = false; - for (int j = 0; j <= 7; j++) { - if (_gameBoardSide[0]._outpostStation[j]._cardId == _gameBoardSide[0]._handCard[tmpVal]._cardId) { - stationAlreadyPresentFl = true; - break; - } - } - - if (!stationAlreadyPresentFl) { - for (int j = 0; j <= 7; j++) { - if ((_gameBoardSide[0]._outpostStation[j]._cardId == 1) && !isStopConstructionCard(_gameBoardSide[0]._delayCard._cardId)) { - int stationCount = 0; - for (int k = 0; k <= 7; k++) { - if ((_gameBoardSide[0]._outpostStation[k]._cardId > 1) && (_gameBoardSide[0]._outpostStation[k]._cardId <= 9)) { - ++stationCount; - } - } - - if (stationCount == 7) - _winnerId = 0; - - playStationCard(&_gameBoardSide[0]._handCard[tmpVal], &_gameBoardSide[0]._outpostStation[j]); - return; - } - } - } - } - } - - int tmpVal = findPlatformCardInHand(0); - - if (tmpVal != -1) { - for (int i = 0; i <= 7; i++) { - if ((_gameBoardSide[0]._outpostStation[i]._cardId == 0) && !isStopConstructionCard(_gameBoardSide[0]._delayCard._cardId)) { - playPlatformCard(&_gameBoardSide[0]._handCard[tmpVal], &_gameBoardSide[0]._outpostStation[i]); - return; - } - } - } - - int card13Id = findCard13InHand(0); - if (card13Id != -1) { - for (int i = 0; i <= 7; i++) { - if (_gameBoardSide[2]._outpostStation[i]._cardId != 0) { - playCounterTrickCard(&_gameBoardSide[0]._handCard[card13Id], 2); - return; - } - } - } - - int thieftId = checkThieftCard(0); - if (thieftId != -1) { - if ( (_gameBoardSide[2]._handCard[0]._cardId != 0) - || (_gameBoardSide[2]._handCard[1]._cardId != 0) - || (_gameBoardSide[2]._handCard[2]._cardId != 0) - || (_gameBoardSide[2]._handCard[3]._cardId != 0) ) { - playThieftCard(0, &_gameBoardSide[0]._handCard[thieftId], 2); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if ((isDelayCard(_gameBoardSide[0]._handCard[i]._cardId) != -1) - && (_gameBoardSide[2]._delayCard._cardId == 0) - && isAttackPossible(2, _gameBoardSide[0]._handCard[i]._cardId)) { - playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[2]._delayCard); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if ((getStationCardId(_gameBoardSide[0]._handCard[i]._cardId) != -1) - && (_gameBoardSide[2]._delayCard._cardId == 0) - && isAttackPossible(2, _gameBoardSide[0]._handCard[i]._cardId)) { - playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[2]._delayCard); - return; - } - } - - card13Id = findCard13InHand(0); - int victimPlayerId = getPlayerWithOutpost(0); - - if ((card13Id != -1) && (victimPlayerId != -1)) { - playCounterTrickCard(&_gameBoardSide[0]._handCard[card13Id], victimPlayerId); - return; - } - - thieftId = checkThieftCard(0); - if (thieftId != -1) { - if ( (_gameBoardSide[1]._handCard[0]._cardId != 0) - || (_gameBoardSide[1]._handCard[1]._cardId != 0) - || (_gameBoardSide[1]._handCard[2]._cardId != 0) - || (_gameBoardSide[1]._handCard[3]._cardId != 0) ) { - playThieftCard(0, &_gameBoardSide[0]._handCard[thieftId], 1); - return; - } - } - - for (int i = 0; i <= 3; i++) { - if (getStationCardId(_gameBoardSide[0]._handCard[i]._cardId) != -1) { - if ((_gameBoardSide[1]._delayCard._cardId == 0) && isAttackPossible(1, _gameBoardSide[0]._handCard[i]._cardId)) { - playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[1]._delayCard); - return; - } - - if ((_gameBoardSide[3]._delayCard._cardId == 0) && isAttackPossible(3, _gameBoardSide[0]._handCard[i]._cardId)) { - playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[3]._delayCard); - return; - } - } - } - - for (int i = 0; i <= 3; i++) { - tmpVal = isDelayCard(_gameBoardSide[0]._handCard[i]._cardId); - if (tmpVal != -1) { - if ((_gameBoardSide[1]._delayCard._cardId == 0) && isAttackPossible(1, _gameBoardSide[0]._handCard[i]._cardId)) { - playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[1]._delayCard); - return; - } - - if ((_gameBoardSide[3]._delayCard._cardId == 0) && isAttackPossible(3, _gameBoardSide[0]._handCard[i]._cardId)) { - playDelayCard(&_gameBoardSide[0]._handCard[i], &_gameBoardSide[3]._delayCard); - return; - } - } - } - - handlePlayer01Discard(0); -} - -void Scene1337::handlePlayer1() { - if (this->_gameBoardSide[1]._delayCard._cardId != 0) { - switch (_gameBoardSide[1]._delayCard._cardId) { - case 10: - // No break on purpose - case 12: - // No break on purpose - case 15: - // No break on purpose - case 17: - // No break on purpose - case 18: - // No break on purpose - case 19: - // No break on purpose - case 20: - // No break on purpose - case 21: - discardCard(&_gameBoardSide[1]._delayCard); - return; - default: - for (int i = 0; i <= 3; i++) { - if (checkAntiDelayCard(_gameBoardSide[1]._delayCard._cardId, _gameBoardSide[1]._handCard[i]._cardId)) { - playAntiDelayCard(&_gameBoardSide[1]._handCard[i], &_gameBoardSide[1]._delayCard); - return; - } - } - break; - } - } - - for (int i = 0; i <= 3; i++) { - int tmpIndx = getStationId(1, i); - if (tmpIndx == -1) - break; - - int tmpVal = 0; - for (int j = 0; j <= 7; j++) { - if (_gameBoardSide[1]._outpostStation[j]._cardId == _gameBoardSide[1]._handCard[tmpIndx]._cardId) { - tmpVal = 1; - break; - } - } - - if (tmpVal == 0) - break; - - for (int j = 0; j <= 7; j++) { - if ((_gameBoardSide[1]._outpostStation[j]._cardId == 1) && !isStopConstructionCard(_gameBoardSide[1]._delayCard._cardId)) { - int stationCount = 0; - for (int k = 0; k <= 7; k++) { - if ((_gameBoardSide[1]._outpostStation[k]._cardId > 1) && (_gameBoardSide[1]._outpostStation[k]._cardId <= 9)) - ++stationCount; - } - - if (stationCount == 7) - _winnerId = 1; - - playStationCard(&_gameBoardSide[1]._handCard[tmpIndx], &_gameBoardSide[1]._outpostStation[j]); - return; - } - } - } - - int normalCardId = findPlatformCardInHand(1); - if (normalCardId != -1) { - for (int i = 0; i <= 7; i++) { - if ((_gameBoardSide[1]._outpostStation[i]._cardId == 0) && !isStopConstructionCard(_gameBoardSide[1]._delayCard._cardId)) { - playPlatformCard(&_gameBoardSide[1]._handCard[normalCardId], &_gameBoardSide[1]._outpostStation[i]); - return; - } - } - } - - int card13Id = findCard13InHand(1); - int tmpVal2 = getPlayerWithOutpost(1); - - if ((card13Id != -1) && (tmpVal2 != -1)) { - playCounterTrickCard(&_gameBoardSide[1]._handCard[card13Id], tmpVal2); - return; - } - - int thieftId = checkThieftCard(1); - if (thieftId != -1) { - int playerIdFound = -1; - int rndVal = R2_GLOBALS._randomSource.getRandomNumber(3); - for (int i = 0; i <= 3; i++) { - if (rndVal != 1) { - if ( (_gameBoardSide[rndVal]._handCard[0]._cardId != 0) - || (_gameBoardSide[rndVal]._handCard[1]._cardId != 0) - || (_gameBoardSide[rndVal]._handCard[2]._cardId != 0) - || (_gameBoardSide[rndVal]._handCard[3]._cardId == 0)) { - playerIdFound = rndVal; - break; - } - } - // The original was only updating in the rndVal block, - // which was a bug as the checks were stopping at this point - rndVal--; - if (rndVal < 0) - rndVal = 3; - } - - if (playerIdFound != -1) { - playThieftCard(1, &_gameBoardSide[1]._handCard[thieftId], playerIdFound); - return; - } - } - - int count = -1; - int i; - for (i = 0; i <= 3; i++) { - int tmpVal = isDelayCard(_gameBoardSide[1]._handCard[i]._cardId); - if (tmpVal != -1) { - int rndVal = R2_GLOBALS._randomSource.getRandomNumber(3); - - for (int j = 0; j <= 3; j++) { - //CHECKME: tmpVal or rndVal? - // FIXME: This is probably meant to be rndVal, but not clear... - if (tmpVal < 0 || tmpVal >= ARRAYSIZE(_gameBoardSide)) - error("Scene1337::handlePlayer1() tmpVal:%d out of range 0 to %d", tmpVal, ARRAYSIZE(_gameBoardSide)-1); - - if (tmpVal != 1) { - if ((_gameBoardSide[tmpVal]._delayCard._cardId == 0) && isAttackPossible(tmpVal, _gameBoardSide[1]._handCard[i]._cardId)) - count = tmpVal; - } - - if (count < 0 || count >= ARRAYSIZE(_gameBoardSide)) - error("Scene1337::handlePlayer1() count:%d out of range 0 to %d", count, ARRAYSIZE(_gameBoardSide)-1); - - if (count != -1) { - playDelayCard(&_gameBoardSide[1]._handCard[i], &_gameBoardSide[count]._delayCard); - return; - } else { - rndVal--; - if (rndVal < 0) - rndVal = 3; - } - } - } - } - - int j; - for (j = 0; j <= 3; j++) { - if (getStationCardId(_gameBoardSide[1]._handCard[j]._cardId) != -1) { - count = -1; - int rndVal = R2_GLOBALS._randomSource.getRandomNumber(3); - for (int l = 0; l <= 3; l++) { - if (rndVal != 1) { - if ((_gameBoardSide[rndVal]._delayCard._cardId == 0) && (_gameBoardSide[1]._handCard[j]._cardId == 1)) - count = rndVal; - } - if (count != -1) { - playDelayCard(&_gameBoardSide[1]._handCard[j], &_gameBoardSide[count]._delayCard); - return; - } else { - rndVal--; - if (rndVal < 0) - rndVal = 3; - } - } - } - } - - handlePlayer01Discard(1); -} - -void Scene1337::handlePlayer3() { - if (_gameBoardSide[3]._delayCard._cardId != 0) { - switch (_gameBoardSide[3]._delayCard._cardId) { - case 10: - // No break on purpose - case 12: - // No break on purpose - case 15: - // No break on purpose - case 17: - // No break on purpose - case 18: - // No break on purpose - case 19: - // No break on purpose - case 20: - // No break on purpose - case 21: - discardCard(&_gameBoardSide[3]._delayCard); - return; - default: - for (int i = 0; i <= 3; i++) { - if (checkAntiDelayCard(_gameBoardSide[3]._delayCard._cardId, _gameBoardSide[3]._handCard[i]._cardId)) { - playAntiDelayCard(&_gameBoardSide[3]._handCard[i], &_gameBoardSide[3]._delayCard); - return; - } - } - break; - } - } - - int randIndx = R2_GLOBALS._randomSource.getRandomNumber(3); - - if (_gameBoardSide[3]._handCard[randIndx]._cardId == 1) { - for (int i = 0; i <= 7; i++) { - if ((_gameBoardSide[3]._outpostStation[i]._cardId == 0) && !isStopConstructionCard(_gameBoardSide[3]._delayCard._cardId)) { - playPlatformCard(&_gameBoardSide[3]._handCard[randIndx], &_gameBoardSide[3]._outpostStation[i]); - return; - } - } - } else if (_gameBoardSide[3]._handCard[randIndx]._cardId <= 9) { - for (int i = 0; i <= 7; i++) { - if (_gameBoardSide[3]._outpostStation[i]._cardId == _gameBoardSide[3]._handCard[randIndx]._cardId) { - discardCard(&_gameBoardSide[3]._handCard[randIndx]); - return; - } - } - - for (int i = 0; i <= 7; i++) { - if ((_gameBoardSide[3]._outpostStation[i]._cardId == 1) && !isStopConstructionCard(_gameBoardSide[3]._delayCard._cardId)) { - int stationCount = 0; - for (int j = 0; j <= 7; j++) { - if ((_gameBoardSide[3]._outpostStation[j]._cardId > 1) && (_gameBoardSide[3]._outpostStation[j]._cardId <= 9)) - ++stationCount; - } - - if (stationCount == 7) - _winnerId = 3; - - playStationCard(&_gameBoardSide[3]._handCard[randIndx], &_gameBoardSide[3]._outpostStation[i]); - return; - } - } - } else if (_gameBoardSide[3]._handCard[randIndx]._cardId == 13) { - int victimId = getPlayerWithOutpost(3); - - if (victimId != -1) { - playCounterTrickCard(&_gameBoardSide[3]._handCard[randIndx], victimId); - return; - } - } else if (_gameBoardSide[3]._handCard[randIndx]._cardId == 25) { - int victimId = -1; - int tmpRandIndx = R2_GLOBALS._randomSource.getRandomNumber(3); - - for (int i = 0; i <= 3; i++) { - if ( (tmpRandIndx != 3) - && ( (_gameBoardSide[tmpRandIndx]._handCard[0]._cardId != 0) - || (_gameBoardSide[tmpRandIndx]._handCard[1]._cardId != 0) - || (_gameBoardSide[tmpRandIndx]._handCard[2]._cardId != 0) - || (_gameBoardSide[tmpRandIndx]._handCard[3]._cardId != 0) )) { - victimId = tmpRandIndx; - break; - } - - ++tmpRandIndx; - if (tmpRandIndx > 3) - tmpRandIndx = 0; - } - - if (victimId != -1) { - playThieftCard(3, &_gameBoardSide[3]._handCard[randIndx], victimId); - return; - } - } else { - switch (_gameBoardSide[3]._handCard[randIndx]._cardId) { - case 10: - // No break on purpose - case 11: - // No break on purpose - case 12: - // No break on purpose - case 14: - // No break on purpose - case 15: - // No break on purpose - case 16: - // No break on purpose - case 17: - // No break on purpose - case 18: - // No break on purpose - case 19: - // No break on purpose - case 20: - // No break on purpose - case 21: - // No break on purpose - case 24: { - int victimId = -1; - int tmpRandIndx = R2_GLOBALS._randomSource.getRandomNumber(3); - - for (int i = 0; i <= 3; i++) { - if (tmpRandIndx != 3) { - if ((_gameBoardSide[tmpRandIndx]._delayCard._cardId == 0) - && isAttackPossible(tmpRandIndx, _gameBoardSide[3]._handCard[randIndx]._cardId)) - victimId = tmpRandIndx; - } - - ++tmpRandIndx; - if (tmpRandIndx > 3) - tmpRandIndx = 0; - - if (victimId != -1) - break; - } - - if (victimId != -1) { - // Useless second identical check skipped - playDelayCard(&_gameBoardSide[3]._handCard[randIndx], &_gameBoardSide[victimId]._delayCard); - return; - } - } - default: - break; - } - } - - discardCard(&_gameBoardSide[3]._handCard[randIndx]); -} - -void Scene1337::handleAutoplayPlayer2() { - if (getStationCardId(this->_gameBoardSide[2]._delayCard._cardId) == -1) - _delayedFunction = &Scene1337::handlePlayer2; - else - discardCard(&_gameBoardSide[2]._delayCard); -} - -void Scene1337::handlePlayer2() { - _selectedCard._stationPos = g_globals->_events._mousePos; - - if (R2_GLOBALS._v57810 == 200) { - // Hand - int i; - for (i = 0; i < 4; i++) { - if ((_gameBoardSide[2]._handCard[i].isIn(_selectedCard._stationPos)) && (_gameBoardSide[2]._handCard[i]._cardId != 0)) { - Card *handcard = &_gameBoardSide[2]._handCard[i]; - _selectedCard._cardId = handcard->_cardId; - _selectedCard._stationPos = handcard->_stationPos; - //warning("_selectedCard._actorName = handcard->_actorName;"); - //warning("_selectedCard._fieldE = handcard->_fieldE;"); - //warning("_selectedCard._field10 = handcard->_field10;"); - //warning("_selectedCard._field12 = handcard->_field12;"); - //warning("_selectedCard._field14 = handcard->_field14;"); - //warning("_selectedCard._field16 = handcard->_field16;"); - _selectedCard._sceneRegionId = handcard->_sceneRegionId; - _selectedCard._position = handcard->_position; - _selectedCard._yDiff = handcard->_yDiff; - _selectedCard._bounds = handcard->_bounds; - _selectedCard._resNum = handcard->_resNum; - _selectedCard._lookLineNum = handcard->_lookLineNum; - _selectedCard._talkLineNum = handcard->_talkLineNum; - _selectedCard._useLineNum = handcard->_useLineNum; - _selectedCard._action = handcard->_action; - //warning("_selectedCard._field0 = handcard->_field0;"); - _selectedCard._card._updateStartFrame = handcard->_card._updateStartFrame; - _selectedCard._card._walkStartFrame = handcard->_card._walkStartFrame; - // _field2E is named _field3C in R2R - _selectedCard._card._oldPosition = handcard->_card._oldPosition; - _selectedCard._card._percent = handcard->_card._percent; - _selectedCard._card._priority = handcard->_card._priority; - _selectedCard._card._angle = handcard->_card._angle; - _selectedCard._card._flags = handcard->_card._flags; - _selectedCard._card._xe = handcard->_card._xe; - _selectedCard._card._xs = handcard->_card._xs; - _selectedCard._card._paneRects[0] = handcard->_card._paneRects[0]; - _selectedCard._card._paneRects[1] = handcard->_card._paneRects[1]; - _selectedCard._card._visage = handcard->_card._visage; - _selectedCard._card._objectWrapper = handcard->_card._objectWrapper; - _selectedCard._card._strip = handcard->_card._strip; - _selectedCard._card._animateMode = handcard->_card._animateMode; - _selectedCard._card._frame = handcard->_card._frame; - _selectedCard._card._endFrame = handcard->_card._endFrame; - // _field68 is named _field76 in R2R - _selectedCard._card._loopCount = handcard->_card._loopCount; - _selectedCard._card._frameChange = handcard->_card._frameChange; - _selectedCard._card._numFrames = handcard->_card._numFrames; - _selectedCard._card._regionIndex = handcard->_card._regionIndex; - _selectedCard._card._mover = handcard->_card._mover; - _selectedCard._card._moveDiff = handcard->_card._moveDiff; - _selectedCard._card._moveRate = handcard->_card._moveRate; - _selectedCard._card._actorDestPos = handcard->_card._actorDestPos; - _selectedCard._card._endAction = handcard->_card._endAction; - _selectedCard._card._regionBitList = handcard->_card._regionBitList; - // _selectedCard._object1._actorName = handcard->_object1._actorName; - //warning("_selectedCard._card._fieldE = handcard->_card._fieldE;"); - //warning("_selectedCard._card._field10 = handcard->_card._field10;"); - //warning("_selectedCard._card._field12 = handcard->_card._field12;"); - //warning("_selectedCard._card._field14 = handcard->_card._field14;"); - //warning("_selectedCard._card._field16 = handcard->_card._field16;"); - - _gameBoardSide[2]._handCard[i]._cardId = 0; - _gameBoardSide[2]._handCard[i]._card.remove(); - break; - } - } - - if (i == 4) { - handleClick(1, _selectedCard._stationPos); - handleAutoplayPlayer2(); - return; - } else { - setCursorData(1332, _selectedCard._card._strip, _selectedCard._card._frame); - R2_GLOBALS._sceneObjects->draw(); - } - } else if (R2_GLOBALS._v57810 == 300) { - // Eye - handleClick(3, _selectedCard._stationPos); - handleAutoplayPlayer2(); - return; - } else { - // The original code is calling a function full of dead code. - // Only this message remains after a cleanup. - MessageDialog::show(WRONG_ANSWER_MSG, OK_BTN_STRING); - // - handleAutoplayPlayer2(); - return; - } - - Event event; - bool found; - for (;;) { - if ( ((g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN)) && (event.btnState == BTNSHIFT_RIGHT)) - || (g_globals->_events.getEvent(event, EVENT_KEYPRESS)) ){ - _selectedCard._stationPos = g_globals->_events._mousePos; - found = false; - - for (int i = 0; i <= 3; i ++) { - if (_gameBoardSide[2]._handCard[i].isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if (_gameBoardSide[2]._handCard[i]._cardId == 0) { - _gameBoardSide[2]._handCard[i]._cardId = _selectedCard._cardId; - _gameBoardSide[2]._handCard[i]._card.postInit(); - _gameBoardSide[2]._handCard[i]._card.hide(); - _gameBoardSide[2]._handCard[i]._card.setVisage(1332); - _gameBoardSide[2]._handCard[i]._card.setPosition(_gameBoardSide[2]._handCard[i]._stationPos, 0); - _gameBoardSide[2]._handCard[i]._card.fixPriority(170); - setAnimationInfo(&_gameBoardSide[2]._handCard[i]); - setCursorData(5, 1, 4); - _currentPlayerNumb--; - _showPlayerTurn = false; - handleNextTurn(); - return; - } else { - actionDisplay(1330, 127, 159, 10, 1, 200, 0, 7, 0, 154, 154); - found = true; - } - break; - } - } - - if (!found) { - if (_discardPile.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - discardCard(&_selectedCard); - return; - } else if (_selectedCard._cardId == 1) { - bool isInCardFl = false; - int i; - for (i = 0; i <= 7; i++) { - if (_gameBoardSide[2]._outpostStation[i].isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - isInCardFl = true; - break; - } - } - - if ((isInCardFl) && (_gameBoardSide[2]._outpostStation[i]._cardId == 0)) { - if (isDelayCard(_gameBoardSide[2]._delayCard._cardId) != -1) { - actionDisplay(1330, 55, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else { - playPlatformCard(&_selectedCard, &_gameBoardSide[2]._outpostStation[i]); - return; - } - } else { - actionDisplay(1330, 56, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else if (_selectedCard._cardId <= 9) { - bool isInCardFl = false; - int i; - for (i = 0; i <= 7; i++) { - if (_gameBoardSide[2]._outpostStation[i].isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - isInCardFl = true; - break; - } - } - if ((isInCardFl) && (_gameBoardSide[2]._outpostStation[i]._cardId == 1)) { - isInCardFl = false; - for (int j = 0; j <= 7; j++) { - if (_selectedCard._cardId == _gameBoardSide[2]._outpostStation[j]._cardId) { - isInCardFl = true; - break; - } - } - if (isInCardFl) { - // This station is already in place - actionDisplay(1330, 34, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (isDelayCard(_gameBoardSide[2]._delayCard._cardId) != -1) { - // You must eliminate your delay before you can play a station - actionDisplay(1330, 35, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else { - int stationCount = 0; - for (int k = 0; k <= 7; k++) { - if ((_gameBoardSide[2]._outpostStation[k]._cardId > 1) && (_gameBoardSide[2]._outpostStation[k]._cardId <= 9)) - ++stationCount; - } - - if (stationCount == 7) - _winnerId = 2; - - playStationCard(&_selectedCard, &_gameBoardSide[2]._outpostStation[i]); - return; - } - } else { - actionDisplay(1330, 37, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else if ((_selectedCard._cardId == 26) || (_selectedCard._cardId == 30) ||(_selectedCard._cardId == 32) || (_selectedCard._cardId == 28)) { - // Check anti-delay card - if (_gameBoardSide[2]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - actionDisplay(1330, 42, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (checkAntiDelayCard(_gameBoardSide[2]._delayCard._cardId, _selectedCard._cardId)) { - playAntiDelayCard(&_selectedCard, &_gameBoardSide[2]._delayCard); - return; - } else { - if (_gameBoardSide[2]._delayCard._cardId != 0) { - switch (_gameBoardSide[2]._delayCard._cardId) { - case 11: - actionDisplay(1330, 68, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 14: - actionDisplay(1330, 80, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 16: - actionDisplay(1330, 84, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 24: - actionDisplay(1330, 96, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } else { - actionDisplay(1330, 41, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } - } else if ((getStationCardId(_selectedCard._cardId) == -1) && (isDelayCard(_selectedCard._cardId) == -1)) { - if (_selectedCard._cardId == 13) { - if (_gameBoardSide[0]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - for (int k = 0; k <= 7; k++) { - if (_gameBoardSide[0]._outpostStation[k]._cardId != 0) { - playCounterTrickCard(&_selectedCard, 0); - return; - } - } - actionDisplay(1330, 74, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (_gameBoardSide[3]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - for (int k = 0; k <= 7; k++) { - if (_gameBoardSide[3]._outpostStation[k]._cardId != 0) { - playCounterTrickCard(&_selectedCard, 3); - return; - } - } - actionDisplay(1330, 74, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (_gameBoardSide[1]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - for (int k = 0; k <= 7; k++) { - if (_gameBoardSide[1]._outpostStation[k]._cardId == 0) { - playCounterTrickCard(&_selectedCard, 1); - return; - } - } - actionDisplay(1330, 74, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else { - actionDisplay(1330, 128, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else if (_selectedCard._cardId == 25) { - if (_gameBoardSide[0]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if ( (_gameBoardSide[0]._handCard[0]._cardId != 0) - || (_gameBoardSide[0]._handCard[1]._cardId != 0) - || (_gameBoardSide[0]._handCard[2]._cardId != 0) - || (_gameBoardSide[0]._handCard[3]._cardId != 0) ) { - int k; - for (k = 0; k <= 3; k++){ - if (_gameBoardSide[2]._handCard[k]._cardId == 0) - break; - } - playThieftCard(2, &_gameBoardSide[2]._handCard[k], 0); - return; - } else { - actionDisplay(1330, 99, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else if (_gameBoardSide[1]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if ( (_gameBoardSide[1]._handCard[0]._cardId != 0) - || (_gameBoardSide[1]._handCard[1]._cardId != 0) - || (_gameBoardSide[1]._handCard[2]._cardId != 0) - || (_gameBoardSide[1]._handCard[3]._cardId != 0) ) { - int k; - for (k = 0; k <= 3; k++){ - if (_gameBoardSide[2]._handCard[k]._cardId == 0) - break; - } - playThieftCard(2, &_gameBoardSide[2]._handCard[k], 1); - return; - } else { - actionDisplay(1330, 99, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } - - if (_gameBoardSide[3]._emptyStationPos.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if ( (_gameBoardSide[3]._handCard[0]._cardId != 0) - || (_gameBoardSide[3]._handCard[1]._cardId != 0) - || (_gameBoardSide[3]._handCard[2]._cardId != 0) - || (_gameBoardSide[3]._handCard[3]._cardId != 0) ) { - int k; - for (k = 0; k <= 3; k++){ - if (_gameBoardSide[2]._handCard[k]._cardId == 0) - break; - } - playThieftCard(2, &_gameBoardSide[2]._handCard[k], 3); - return; - } else { - actionDisplay(1330, 99, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else { - actionDisplay(1330, 129, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else if (_selectedCard._cardId == 29) { - // Interceptor cards are used to prevent collision - actionDisplay(1330, 136, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (_selectedCard._cardId == 27) { - actionDisplay(1330, 137, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } else if (_gameBoardSide[0]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if (_gameBoardSide[0]._delayCard._cardId != 0) { - actionDisplay(1330, 15, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (!isAttackPossible(0, _selectedCard._cardId)) { - switch (_selectedCard._cardId) { - case 10: - actionDisplay(1330, 66, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 12: - actionDisplay(1330, 70, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - actionDisplay(1330, 82, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 17: - actionDisplay(1330, 86, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 18: - actionDisplay(1330, 88, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 19: - actionDisplay(1330, 90, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - actionDisplay(1330, 92, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 21: - actionDisplay(1330, 94, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } else { - playDelayCard(&_selectedCard, &_gameBoardSide[0]._delayCard); - return; - } - } else if (_gameBoardSide[3]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if (_gameBoardSide[3]._delayCard._cardId != 0) { - actionDisplay(1330, 17, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (!isAttackPossible(3, _selectedCard._cardId)) { - switch (_selectedCard._cardId) { - case 10: - actionDisplay(1330, 66, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 12: - actionDisplay(1330, 70, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - actionDisplay(1330, 82, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 17: - actionDisplay(1330, 86, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 18: - actionDisplay(1330, 88, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 19: - actionDisplay(1330, 90, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - actionDisplay(1330, 92, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 21: - actionDisplay(1330, 94, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } else { - playDelayCard(&_selectedCard, &_gameBoardSide[3]._delayCard); - return; - } - } else if (_gameBoardSide[1]._delayCard.isIn(Common::Point(_selectedCard._stationPos.x + 12, _selectedCard._stationPos.y + 12))) { - if (_gameBoardSide[1]._delayCard._cardId != 0) { - actionDisplay(1330, 19, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } else if (!isAttackPossible(1, _selectedCard._cardId)) { - switch (_selectedCard._cardId) { - case 10: - actionDisplay(1330, 66, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 12: - actionDisplay(1330, 70, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 15: - actionDisplay(1330, 82, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 17: - actionDisplay(1330, 86, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 18: - actionDisplay(1330, 88, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 19: - actionDisplay(1330, 90, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 20: - actionDisplay(1330, 92, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - case 21: - actionDisplay(1330, 94, 159, 10, 1, 200, 0, 7, 0, 154, 154); - break; - default: - break; - } - } else { - playDelayCard(&_selectedCard, &_gameBoardSide[1]._delayCard); - return; - } - } else { - actionDisplay(1330, 38, 159, 10, 1, 200, 0, 7, 0, 154, 154); - } - } - } else { - g_globals->_scenePalette.signalListeners(); - R2_GLOBALS._sceneObjects->draw(); - g_globals->_events.delay(g_globals->_sceneHandler->_delayTicks); - } - - g_globals->_sceneObjects->recurse(SceneHandler::dispatchObject); - } -} - -void Scene1337::updateCursorId(int cursorId, bool updateFl) { - if ((R2_GLOBALS._v57709 != 0) || (R2_GLOBALS._v5780C != 0)) - return; - - R2_GLOBALS._mouseCursorId = cursorId; - - if (updateFl) { - R2_GLOBALS._mouseCursorId++; - - if (R2_GLOBALS._mouseCursorId < 1) - R2_GLOBALS._mouseCursorId = 2; - - if (R2_GLOBALS._mouseCursorId > 2) - R2_GLOBALS._mouseCursorId = 1; - } - - // The original was using an intermediate function to call setCursorData. - // It has been removed to improve readability - if (R2_GLOBALS._mouseCursorId == 1) { - R2_GLOBALS._v57810 = 200; - setCursorData(5, 1, 4); - } else if (R2_GLOBALS._mouseCursorId == 2) { - R2_GLOBALS._v57810 = 300; - setCursorData(5, 1, 5); - } else { - R2_GLOBALS._v57810 = 0; - setCursorData(5, 0, 0); - } -} - -void Scene1337::setCursorData(int resNum, int rlbNum, int frameNum) { - _cursorCurRes = resNum; - _cursorCurStrip = rlbNum; - _cursorCurFrame = frameNum; - - if (!frameNum) { - // Should be a hardcoded cursor displaying only a dot. - // FIXME: Use another cursor when possible - R2_GLOBALS._events.setCursor(CURSOR_CROSSHAIRS); - } else { - // TODO: The original was using some ressource caching, which was useless and complex - // and which has been removed. This cursor behavior clearly made intensive use of this caching... - // We now have to find a way to cache these cursor pointers and avoid loading them multiple times per seconds - uint size; - byte *cursor = g_resourceManager->getSubResource(resNum, rlbNum, frameNum, &size); - // Decode the cursor - GfxSurface s = surfaceFromRes(cursor); - - Graphics::Surface surface = s.lockSurface(); - const byte *cursorData = (const byte *)surface.getPixels(); - CursorMan.replaceCursor(cursorData, surface.w, surface.h, s._centroid.x, s._centroid.y, s._transColor); - s.unlockSurface(); - - DEALLOCATE(cursor); - } -} - -void Scene1337::subD18F5() { - if (R2_GLOBALS._v57709 == 0) - R2_GLOBALS._events.setCursor(CURSOR_CROSSHAIRS); - - ++R2_GLOBALS._v57709; -} - -void Scene1337::subD1917() { - if (R2_GLOBALS._v57709 != 0) { - R2_GLOBALS._v57709--; - if (R2_GLOBALS._v57709 != 0) { - // The original was using an intermediate function to call setCursorData. - // It has been removed to improve readability - setCursorData(5, _cursorCurStrip, _cursorCurFrame); - } - } -} - -void Scene1337::subD1940(bool flag) { - if (flag) - ++R2_GLOBALS._v5780C; - else if (R2_GLOBALS._v5780C != 0) - --R2_GLOBALS._v5780C; -} - -void Scene1337::subD1975(int arg1, int arg2) { - warning("STUBBED lvl2 Scene1337::subD1975()"); -} - -void Scene1337::OptionsDialog::show() { - OptionsDialog *dlg = new OptionsDialog(); - dlg->draw(); - - // Show the dialog - GfxButton *btn = dlg->execute(NULL); - - // Figure out the new selected character - if (btn == &dlg->_quitGame) - R2_GLOBALS._sceneManager.changeScene(125); - else if (btn == &dlg->_restartGame) - R2_GLOBALS._sceneManager.changeScene(1330); - - // Remove the dialog - dlg->remove(); - delete dlg; -} - -Scene1337::OptionsDialog::OptionsDialog() { - // Set the elements text - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - _autoplay.setText(scene->_autoplay ? AUTO_PLAY_ON : AUTO_PLAY_OFF); - _restartGame.setText(START_NEW_CARD_GAME); - _quitGame.setText(QUIT_CARD_GAME); - _continueGame.setText(CONTINUE_CARD_GAME); - - // Set position of the elements - _autoplay._bounds.moveTo(5, 2); - _restartGame._bounds.moveTo(5, _autoplay._bounds.bottom + 2); - _quitGame._bounds.moveTo(5, _restartGame._bounds.bottom + 2); - _continueGame._bounds.moveTo(5, _quitGame._bounds.bottom + 2); - - // Add the items to the dialog - addElements(&_autoplay, &_restartGame, &_quitGame, &_continueGame, NULL); - - // Set the dialog size and position - frame(); - _bounds.collapse(-6, -6); - setCenter(160, 100); -} - -GfxButton *Scene1337::OptionsDialog::execute(GfxButton *defaultButton) { - _gfxManager.activate(); - - // Event loop - GfxButton *selectedButton = NULL; - - bool breakFlag = false; - while (!g_vm->shouldQuit() && !breakFlag) { - Event event; - while (g_globals->_events.getEvent(event) && !breakFlag) { - // Adjust mouse positions to be relative within the dialog - event.mousePos.x -= _gfxManager._bounds.left; - event.mousePos.y -= _gfxManager._bounds.top; - - for (GfxElementList::iterator i = _elements.begin(); i != _elements.end(); ++i) { - if ((*i)->process(event)) - selectedButton = static_cast<GfxButton *>(*i); - } - - if (selectedButton == &_autoplay) { - // Toggle Autoplay - selectedButton = NULL; - Scene1337 *scene = (Scene1337 *)R2_GLOBALS._sceneManager._scene; - scene->_autoplay = !scene->_autoplay; - - _autoplay.setText(scene->_autoplay ? AUTO_PLAY_ON : AUTO_PLAY_OFF); - _autoplay.draw(); - } else if (selectedButton) { - breakFlag = true; - break; - } else if (!event.handled) { - if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) { - selectedButton = NULL; - breakFlag = true; - break; - } - } - } - - g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); - } - - _gfxManager.deactivate(); - return selectedButton; -} - -/*-------------------------------------------------------------------------- * Scene 1500 - Cutscene: Ship landing * *--------------------------------------------------------------------------*/ @@ -13433,1796 +7970,5 @@ void Scene1945::signal() { R2_GLOBALS._player._canWalk = false; } -/*-------------------------------------------------------------------------- - * Scene 1950 - Flup Tube Corridor Maze - * - *--------------------------------------------------------------------------*/ - -Scene1950::KeypadWindow::KeypadWindow() { - _buttonIndex = 0; -} - -void Scene1950::KeypadWindow::synchronize(Serializer &s) { - SceneArea::synchronize(s); - - s.syncAsSint16LE(_buttonIndex); -} - -Scene1950::KeypadWindow::KeypadButton::KeypadButton() { - _buttonIndex = 0; - _pressed = false; - _toggled = false; -} - -void Scene1950::KeypadWindow::KeypadButton::synchronize(Serializer &s) { - SceneActor::synchronize(s); - - s.syncAsSint16LE(_buttonIndex); - s.syncAsSint16LE(_pressed); - s.syncAsSint16LE(_toggled); -} - -void Scene1950::KeypadWindow::KeypadButton::init(int indx) { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _buttonIndex = indx; - _pressed = false; - _toggled = false; - - postInit(); - setup(1971, 2, 1); - fixPriority(249); - setPosition(Common::Point(((_buttonIndex % 4) * 22) + 127, ((_buttonIndex / 4) * 19) + 71)); - scene->_sceneAreas.push_front(this); -} - -void Scene1950::KeypadWindow::KeypadButton::process(Event &event) { - if ((event.eventType == EVENT_BUTTON_DOWN) && (R2_GLOBALS._events.getCursor() == CURSOR_USE) - && (_bounds.contains(event.mousePos)) && !_pressed) { - R2_GLOBALS._sound2.play(227); - if (!_toggled) { - setFrame(2); - _toggled = true; - } else { - setFrame(1); - _toggled = false; - } - _pressed = true; - event.handled = true; - } - - if ((event.eventType == EVENT_BUTTON_UP) && _pressed) { - _pressed = false; - event.handled = true; - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - scene->doButtonPress(_buttonIndex); - } -} - -bool Scene1950::KeypadWindow::KeypadButton::startAction(CursorType action, Event &event) { - if (action == CURSOR_USE) - return false; - return SceneActor::startAction(action, event); -} - -void Scene1950::KeypadWindow::remove() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - for (_buttonIndex = 0; _buttonIndex < 16; ++_buttonIndex) { - scene->_sceneAreas.remove(&_buttons[_buttonIndex]); - _buttons[_buttonIndex].remove(); - } - - ModalWindow::remove(); - - if (!R2_GLOBALS.getFlag(37)) - R2_GLOBALS._sound2.play(278); - - R2_GLOBALS._player.disableControl(CURSOR_WALK); - scene->_eastExit._enabled = true; - - if (!R2_GLOBALS.getFlag(37)) { - if (R2_GLOBALS.getFlag(36)) { - scene->_sceneMode = 1964; - scene->setAction(&scene->_sequenceManager, scene, 1964, &R2_GLOBALS._player, NULL); - } else { - scene->_sceneMode = 1965; - scene->setAction(&scene->_sequenceManager, scene, 1965, &R2_GLOBALS._player, NULL); - } - } -} - -void Scene1950::KeypadWindow::setup2(int visage, int stripFrameNum, int frameNum, int posX, int posY) { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - if (R2_GLOBALS._player._mover) - R2_GLOBALS._player.addMover(NULL); - R2_GLOBALS._player._canWalk = false; - - ModalWindow::setup2(visage, stripFrameNum, frameNum, posX, posY); - - _object1.fixPriority(248); - scene->_eastExit._enabled = false; - setup3(1950, 27, 28, 27); - - for (_buttonIndex = 0; _buttonIndex < 16; _buttonIndex++) - _buttons[_buttonIndex].init(_buttonIndex); -} - -void Scene1950::KeypadWindow::setup3(int resNum, int lookLineNum, int talkLineNum, int useLineNum) { - // Copy of Scene1200::LaserPanel::proc13() - _areaActor.setDetails(resNum, lookLineNum, talkLineNum, useLineNum, 2, (SceneItem *) NULL); -} - -/*--------------------------------------------------------------------------*/ - -bool Scene1950::Keypad::startAction(CursorType action, Event &event) { - if ((action != CURSOR_USE) || (R2_GLOBALS.getFlag(37))) - return SceneHotspot::startAction(action, event); - - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - R2_GLOBALS._player.disableControl(); - if (R2_GLOBALS.getFlag(36)) { - scene->_sceneMode = 1962; - scene->setAction(&scene->_sequenceManager, scene, 1962, &R2_GLOBALS._player, NULL); - } else { - scene->_sceneMode = 1963; - scene->setAction(&scene->_sequenceManager, scene, 1963, &R2_GLOBALS._player, NULL); - } - return true; -} - -bool Scene1950::Door::startAction(CursorType action, Event &event) { - if (action != R2_SCRITH_KEY) - return SceneActor::startAction(action, event); - - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - R2_GLOBALS._player.disableControl(); - R2_INVENTORY.setObjectScene(R2_SCRITH_KEY, 0); - scene->_sceneMode = 1958; - scene->setAction(&scene->_sequenceManager, scene, 1958, &R2_GLOBALS._player, &scene->_door, NULL); - return true; -} - -bool Scene1950::Scrolls::startAction(CursorType action, Event &event) { - if ((action != CURSOR_USE) || (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) != 1950)) - return SceneActor::startAction(action, event); - - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - R2_GLOBALS._player.disableControl(); - scene->_sceneMode = 1968; - scene->setAction(&scene->_sequenceManager, scene, 1968, &R2_GLOBALS._player, NULL); - - return true; -} - -bool Scene1950::Gem::startAction(CursorType action, Event &event) { - if ((action != CURSOR_USE) || (!R2_GLOBALS.getFlag(37))) - return SceneActor::startAction(action, event); - - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - R2_GLOBALS._player.disableControl(); - scene->_sceneMode = 1967; - scene->setAction(&scene->_sequenceManager, scene, 1967, &R2_GLOBALS._player, NULL); - - return true; -} - -/*--------------------------------------------------------------------------*/ - -Scene1950::Vampire::Vampire() { - _deadPosition = Common::Point(0, 0); - _deltaX = 0; - _deltaY = 0; - _vampireMode = 0; -} - -void Scene1950::Vampire::synchronize(Serializer &s) { - SceneActor::synchronize(s); - - s.syncAsSint16LE(_deadPosition.x); - s.syncAsSint16LE(_deadPosition.y); - s.syncAsSint16LE(_deltaX); - s.syncAsSint16LE(_deltaY); - s.syncAsSint16LE(_vampireMode); -} - -void Scene1950::Vampire::signal() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - switch (_vampireMode) { - case 19: { - _vampireMode = 0; - setVisage(1960); - if (R2_GLOBALS._flubMazeEntryDirection == 3) - setStrip(2); - else - setStrip(1); - - NpcMover *mover = new NpcMover(); - addMover(mover, &scene->_vampireDestPos, scene); - } - break; - case 20: { - // Non fatal shot - _vampireMode = 19; - R2_GLOBALS._player.setVisage(22); - if (R2_GLOBALS._flubMazeEntryDirection == 3) - R2_GLOBALS._player.setStrip(1); - else - R2_GLOBALS._player.setStrip(2); - R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); - R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._shotsRequired--; - - if (R2_GLOBALS._flubMazeEntryDirection == 3) - _deadPosition.x = _position.x + 10; - else - _deadPosition.x = _position.x - 10; - _deadPosition.y = _position.y - 4; - - setVisage(1961); - - if (R2_GLOBALS._flubMazeEntryDirection == 3) - setStrip(2); - else - setStrip(1); - - animate(ANIM_MODE_2, NULL); - Common::Point pt = _deadPosition; - PlayerMover *mover = new PlayerMover(); - addMover(mover, &pt, this); - - R2_GLOBALS._player.enableControl(); - } - break; - case 21: { - // Fatal shot - R2_GLOBALS._player.setVisage(22); - if (R2_GLOBALS._flubMazeEntryDirection == 3) - R2_GLOBALS._player.setStrip(1); - else - R2_GLOBALS._player.setStrip(2); - R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); - - setVisage(1961); - if (R2_GLOBALS._flubMazeEntryDirection == 3) - setStrip(4); - else - setStrip(3); - setDetails(1950, 15, -1, 17, 2, (SceneItem *) NULL); - addMover(NULL); - _numFrames = 8; - R2_GLOBALS._sound2.play(226); - animate(ANIM_MODE_5, NULL); - fixPriority(10); - - R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._isAlive = false; - R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._shotsRequired--; - R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._position = _position; - _deltaX = (_position.x - R2_GLOBALS._player._position.x) / 2; - _deltaY = (_position.y - R2_GLOBALS._player._position.y) / 2; - - byte vampireCount = 0; - for (byte i = 0; i < 18; ++i) { - if (!R2_GLOBALS._vampireData[i]._isAlive) - ++vampireCount; - } - - if (vampireCount == 18) { - R2_GLOBALS.setFlag(36); - _vampireMode = 23; - Common::Point pt(R2_GLOBALS._player._position.x + _deltaX, R2_GLOBALS._player._position.y + _deltaY); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } else if (vampireCount == 1) { - _vampireMode = 22; - Common::Point pt(R2_GLOBALS._player._position.x + _deltaX, R2_GLOBALS._player._position.y + _deltaY); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } else { - R2_GLOBALS._player.enableControl(CURSOR_WALK); - } - - if (R2_GLOBALS._flubMazeEntryDirection == 3) - scene->_eastExit._enabled = true; - else - scene->_westExit._enabled = true; - - scene->_vampireActive = false; - } - break; - case 22: - SceneItem::display(1950, 18, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - R2_GLOBALS._player.enableControl(CURSOR_WALK); - break; - case 23: - SceneItem::display(1950, 25, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - scene->_sceneMode = R2_GLOBALS._flubMazeEntryDirection; - scene->setAction(&scene->_sequenceManager, scene, 1960, &R2_GLOBALS._player, NULL); - break; - default: - break; - } -} - -bool Scene1950::Vampire::startAction(CursorType action, Event &event) { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - if (!R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._isAlive || - (action != R2_PHOTON_STUNNER)) - return SceneActor::startAction(action, event); - - R2_GLOBALS._player.disableControl(); - - if (R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._shotsRequired <= 1) - _vampireMode = 21; - else - _vampireMode = 20; - - R2_GLOBALS._player.setVisage(25); - if (R2_GLOBALS._flubMazeEntryDirection == 3) - R2_GLOBALS._player.setStrip(2); - else - R2_GLOBALS._player.setStrip(1); - R2_GLOBALS._player.animate(ANIM_MODE_5, this); - R2_GLOBALS._sound3.play(99); - - return true; -} - -/*--------------------------------------------------------------------------*/ - -void Scene1950::NorthExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 1; - scene->_sceneMode = 11; - - Common::Point pt(160, 127); - PlayerMover *mover = new PlayerMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); -} - -void Scene1950::UpExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 2; - scene->_sceneMode = 12; - - if (!scene->_upExitStyle) { - if (R2_GLOBALS.getFlag(36)) - scene->setAction(&scene->_sequenceManager, scene, 1953, &R2_GLOBALS._player, NULL); - else - scene->setAction(&scene->_sequenceManager, scene, 1970, &R2_GLOBALS._player, NULL); - } else { - if (R2_GLOBALS.getFlag(36)) - scene->setAction(&scene->_sequenceManager, scene, 1952, &R2_GLOBALS._player, NULL); - else - scene->setAction(&scene->_sequenceManager, scene, 1969, &R2_GLOBALS._player, NULL); - } -} - -void Scene1950::EastExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 3; - scene->_sceneMode = 13; - - if (scene->_vampireActive) - R2_GLOBALS._player.animate(ANIM_MODE_9); - - Common::Point pt(340, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); -} - -void Scene1950::DownExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 4; - scene->_sceneMode = 14; - - if (R2_GLOBALS.getFlag(36)) - scene->setAction(&scene->_sequenceManager, scene, 1956, &R2_GLOBALS._player, NULL); - else - scene->setAction(&scene->_sequenceManager, scene, 1973, &R2_GLOBALS._player, NULL); -} - -void Scene1950::SouthExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 5; - scene->_sceneMode = 15; - - Common::Point pt(160, 213); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); -} - -void Scene1950::WestExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 6; - - if (R2_GLOBALS._flubMazeArea == 2) { - // In the very first corridor area after the Scrith Door - if ((R2_GLOBALS.getFlag(36)) && (R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 2) && (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) == 2)) { - scene->_sceneMode = 1961; - Common::Point pt(-20, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); - } else { - if (!R2_GLOBALS.getFlag(36)) - SceneItem::display(1950, 33, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - if ((R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 1950) || (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) == 1950)) - SceneItem::display(1950, 34, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - scene->_sceneMode = 0; - Common::Point pt(30, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); - } - } else { - if (scene->_vampireActive) - R2_GLOBALS._player.animate(ANIM_MODE_9); - - scene->_sceneMode = 16; - Common::Point pt(-20, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); - } -} - -void Scene1950::ShaftExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 0; - scene->_sceneMode = 1951; - scene->setAction(&scene->_sequenceManager, scene, 1951, &R2_GLOBALS._player, NULL); -} - -void Scene1950::DoorExit::changeScene() { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - - _enabled = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._flubMazeEntryDirection = 3; - if (R2_GLOBALS._player._visage == 22) { - scene->_sceneMode = 1975; - scene->setAction(&scene->_sequenceManager, scene, 1975, &R2_GLOBALS._player, NULL); - } else { - SceneItem::display(1950, 22, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - R2_GLOBALS._flubMazeEntryDirection = 0; - scene->_sceneMode = 0; - Common::Point pt(250, 150); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, scene); - _enabled = true; - } -} - -/*--------------------------------------------------------------------------*/ - -Scene1950::Scene1950() { - _upExitStyle = false; - _removeFlag = false; - _vampireActive = false; - _vampireDestPos = Common::Point(0, 0); - _vampireIndex = 0; -} - -void Scene1950::synchronize(Serializer &s) { - SceneExt::synchronize(s); - - s.syncAsSint16LE(_upExitStyle); - s.syncAsSint16LE(_removeFlag); - s.syncAsSint16LE(_vampireActive); - s.syncAsSint16LE(_vampireDestPos.x); - s.syncAsSint16LE(_vampireDestPos.y); - s.syncAsSint16LE(_vampireIndex); -} - -void Scene1950::initArea() { - _northExit._enabled = false; - _upExit._enabled = false; - _eastExit._enabled = false; - _downExit._enabled = false; - _southExit._enabled = false; - _westExit._enabled = false; - _shaftExit._enabled = false; - _doorExit._enabled = false; - _northExit._insideArea = false; - _upExit._insideArea = false; - _eastExit._insideArea = false; - _downExit._insideArea = false; - _southExit._insideArea = false; - _westExit._insideArea = false; - _shaftExit._insideArea = false; - _doorExit._insideArea = false; - _northExit._moving = false; - _upExit._moving = false; - _eastExit._moving = false; - _downExit._moving = false; - _southExit._moving = false; - _westExit._moving = false; - _shaftExit._moving = false; - _doorExit._moving = false; - _upExitStyle = false; - - switch (R2_GLOBALS._flubMazeArea - 1) { - case 0: - loadScene(1948); - break; - case 1: - // No break on purpose - case 8: - // No break on purpose - case 10: - // No break on purpose - case 12: - // No break on purpose - case 16: - // No break on purpose - case 19: - // No break on purpose - case 23: - // No break on purpose - case 30: - // No break on purpose - case 44: - // No break on purpose - case 72: - // No break on purpose - case 74: - // No break on purpose - case 86: - // No break on purpose - case 96: - // No break on purpose - case 103: - loadScene(1950); - break; - case 2: - // No break on purpose - case 29: - loadScene(1965); - break; - case 3: - // No break on purpose - case 9: - // No break on purpose - case 11: - // No break on purpose - case 15: - // No break on purpose - case 24: - // No break on purpose - case 39: - // No break on purpose - case 45: - // No break on purpose - case 71: - // No break on purpose - case 73: - // No break on purpose - case 75: - // No break on purpose - case 79: - // No break on purpose - case 85: - // No break on purpose - case 87: - // No break on purpose - case 95: - loadScene(1955); - break; - case 4: - // No break on purpose - case 6: - // No break on purpose - case 13: - // No break on purpose - case 27: - // No break on purpose - case 41: - // No break on purpose - case 48: - // No break on purpose - case 50: - // No break on purpose - case 54: - // No break on purpose - case 76: - // No break on purpose - case 80: - // No break on purpose - case 90: - // No break on purpose - case 104: - loadScene(1975); - break; - case 5: - // No break on purpose - case 7: - // No break on purpose - case 14: - // No break on purpose - case 28: - // No break on purpose - case 32: - // No break on purpose - case 47: - // No break on purpose - case 53: - loadScene(1997); - break; - case 17: - // No break on purpose - case 20: - // No break on purpose - case 25: - // No break on purpose - case 31: - // No break on purpose - case 33: - // No break on purpose - case 46: - loadScene(1995); - break; - case 18: - // No break on purpose - case 22: - // No break on purpose - case 26: - // No break on purpose - case 36: - // No break on purpose - case 38: - // No break on purpose - case 43: - // No break on purpose - case 51: - // No break on purpose - case 70: - // No break on purpose - case 78: - // No break on purpose - case 84: - // No break on purpose - case 89: - // No break on purpose - case 101: - loadScene(1970); - break; - case 21: - // No break on purpose - case 34: - // No break on purpose - case 57: - // No break on purpose - case 58: - // No break on purpose - case 59: - // No break on purpose - case 62: - // No break on purpose - case 65: - loadScene(1980); - break; - case 35: - // No break on purpose - case 61: - // No break on purpose - case 77: - // No break on purpose - case 83: - loadScene(1982); - break; - case 37: - // No break on purpose - case 52: - // No break on purpose - case 82: - // No break on purpose - case 88: - // No break on purpose - case 92: - // No break on purpose - case 97: - // No break on purpose - case 100: - loadScene(1962); - break; - case 40: - // No break on purpose - case 102: - loadScene(1960); - break; - case 42: - // No break on purpose - case 55: - // No break on purpose - case 60: - // No break on purpose - case 66: - // No break on purpose - case 68: - // No break on purpose - case 69: - // No break on purpose - case 93: - // No break on purpose - case 98: - loadScene(1990); - break; - case 49: - // No break on purpose - case 81: - // No break on purpose - case 91: - // No break on purpose - case 94: - // No break on purpose - case 99: - loadScene(1967); - break; - case 56: - // No break on purpose - case 63: - // No break on purpose - case 64: - // No break on purpose - case 67: - loadScene(1985); - _upExitStyle = true; - break; - default: - break; - } - - if (R2_GLOBALS._flubMazeArea != 1) - R2_GLOBALS._walkRegions.load(1950); - - switch (R2_GLOBALS._flubMazeArea - 1) { - case 0: - _shaftExit._enabled = true; - if ((R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) == 0) && (R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 1950)) - _doorExit._enabled = true; - R2_GLOBALS._walkRegions.disableRegion(2); - R2_GLOBALS._walkRegions.disableRegion(3); - R2_GLOBALS._walkRegions.disableRegion(4); - R2_GLOBALS._walkRegions.disableRegion(5); - R2_GLOBALS._walkRegions.disableRegion(6); - break; - case 1: - // No break on purpose - case 2: - // No break on purpose - case 3: - // No break on purpose - case 8: - // No break on purpose - case 9: - // No break on purpose - case 10: - // No break on purpose - case 11: - // No break on purpose - case 12: - // No break on purpose - case 15: - // No break on purpose - case 16: - // No break on purpose - case 19: - // No break on purpose - case 23: - // No break on purpose - case 24: - // No break on purpose - case 29: - // No break on purpose - case 30: - // No break on purpose - case 39: - // No break on purpose - case 40: - // No break on purpose - case 44: - // No break on purpose - case 45: - // No break on purpose - case 71: - // No break on purpose - case 72: - // No break on purpose - case 73: - // No break on purpose - case 74: - // No break on purpose - case 75: - // No break on purpose - case 79: - // No break on purpose - case 85: - // No break on purpose - case 86: - // No break on purpose - case 87: - // No break on purpose - case 95: - // No break on purpose - case 96: - // No break on purpose - case 102: - // No break on purpose - case 103: - _eastExit._enabled = true; - _westExit._enabled = true; - break; - case 4: - // No break on purpose - case 6: - // No break on purpose - case 13: - // No break on purpose - case 17: - // No break on purpose - case 20: - // No break on purpose - case 25: - // No break on purpose - case 27: - // No break on purpose - case 31: - // No break on purpose - case 33: - // No break on purpose - case 37: - // No break on purpose - case 41: - // No break on purpose - case 46: - // No break on purpose - case 48: - // No break on purpose - case 50: - // No break on purpose - case 52: - // No break on purpose - case 54: - // No break on purpose - case 76: - // No break on purpose - case 80: - // No break on purpose - case 82: - // No break on purpose - case 88: - // No break on purpose - case 90: - // No break on purpose - case 92: - // No break on purpose - case 97: - // No break on purpose - case 100: - // No break on purpose - case 104: - _westExit._enabled = true; - R2_GLOBALS._walkRegions.disableRegion(6); - R2_GLOBALS._walkRegions.disableRegion(9); - break; - case 5: - // No break on purpose - case 7: - // No break on purpose - case 14: - // No break on purpose - case 18: - // No break on purpose - case 22: - // No break on purpose - case 26: - // No break on purpose - case 28: - // No break on purpose - case 32: - // No break on purpose - case 36: - // No break on purpose - case 38: - // No break on purpose - case 43: - // No break on purpose - case 47: - // No break on purpose - case 49: - // No break on purpose - case 51: - // No break on purpose - case 53: - // No break on purpose - case 70: - // No break on purpose - case 78: - // No break on purpose - case 81: - // No break on purpose - case 84: - // No break on purpose - case 89: - // No break on purpose - case 91: - // No break on purpose - case 94: - // No break on purpose - case 99: - // No break on purpose - case 101: - _eastExit._enabled = true; - R2_GLOBALS._walkRegions.disableRegion(1); - R2_GLOBALS._walkRegions.disableRegion(7); - R2_GLOBALS._walkRegions.disableRegion(13); - break; - default: - R2_GLOBALS._walkRegions.disableRegion(1); - R2_GLOBALS._walkRegions.disableRegion(6); - R2_GLOBALS._walkRegions.disableRegion(7); - R2_GLOBALS._walkRegions.disableRegion(9); - R2_GLOBALS._walkRegions.disableRegion(13); - break; - } - - _northDoorway.remove(); - _northDoorway.removeObject(); - _southDoorway.remove(); - - switch (R2_GLOBALS._flubMazeArea - 4) { - case 0: - // No break on purpose - case 3: - // No break on purpose - case 16: - // No break on purpose - case 22: - // No break on purpose - case 24: - // No break on purpose - case 32: - // No break on purpose - case 33: - // No break on purpose - case 45: - // No break on purpose - case 46: - // No break on purpose - case 48: - // No break on purpose - case 51: - // No break on purpose - case 56: - // No break on purpose - case 59: - // No break on purpose - case 67: - // No break on purpose - case 68: - // No break on purpose - case 70: - // No break on purpose - case 73: - // No break on purpose - case 82: - // No break on purpose - case 90: - _northExit._enabled = true; - _northDoorway.setup(1950, (R2_GLOBALS._flubMazeArea % 2) + 1, 1, 160, 137, 25); - //visage,strip,frame,px,py,priority,effect - _southDoorway.postInit(); - _southDoorway.setVisage(1950); - _southDoorway.setStrip((((R2_GLOBALS._flubMazeArea - 1) / 35) % 2) + 1); - _southDoorway.setFrame(2); - _southDoorway.setPosition(Common::Point(160, 167)); - _southDoorway.fixPriority(220); - R2_GLOBALS._walkRegions.disableRegion(3); - R2_GLOBALS._walkRegions.disableRegion(4); - break; - case 7: - // No break on purpose - case 10: - // No break on purpose - case 23: - // No break on purpose - case 29: - // No break on purpose - case 31: - // No break on purpose - case 39: - // No break on purpose - case 40: - // No break on purpose - case 52: - // No break on purpose - case 53: - // No break on purpose - case 55: - // No break on purpose - case 63: - // No break on purpose - case 65: - // No break on purpose - case 66: - // No break on purpose - case 75: - // No break on purpose - case 77: - // No break on purpose - case 81: - // No break on purpose - case 87: - // No break on purpose - case 89: - // No break on purpose - case 97: - _southExit._enabled = true; - - _southDoorway.postInit(); - _southDoorway.setVisage(1950); - _southDoorway.setStrip((((R2_GLOBALS._flubMazeArea - 1) / 35) % 2) + 1); - _southDoorway.setFrame(3); - _southDoorway.setPosition(Common::Point(160, 167)); - _southDoorway.fixPriority(220); - break; - case 58: - // No break on purpose - case 74: - // No break on purpose - case 80: - _northExit._enabled = true; - _southExit._enabled = true; - - _northDoorway.setup(1950, (R2_GLOBALS._flubMazeArea % 2) + 1, 1, 160, 137, 25); - - _southDoorway.postInit(); - _southDoorway.setVisage(1950); - _southDoorway.setStrip((((R2_GLOBALS._flubMazeArea - 1) / 35) % 2) + 1); - _southDoorway.setFrame(3); - _southDoorway.setPosition(Common::Point(160, 167)); - _southDoorway.fixPriority(220); - R2_GLOBALS._walkRegions.disableRegion(3); - R2_GLOBALS._walkRegions.disableRegion(4); - break; - default: - _southDoorway.postInit(); - _southDoorway.setVisage(1950); - _southDoorway.setStrip(((R2_GLOBALS._flubMazeArea - 1) / 35) % 2 + 1); - _southDoorway.setFrame(2); - _southDoorway.setPosition(Common::Point(160, 167)); - _southDoorway.fixPriority(220); - break; - } - - switch (R2_GLOBALS._flubMazeArea - 3) { - case 0: - // No break on purpose - case 3: - // No break on purpose - case 5: - // No break on purpose - case 12: - // No break on purpose - case 15: - // No break on purpose - case 18: - // No break on purpose - case 19: - // No break on purpose - case 23: - // No break on purpose - case 26: - // No break on purpose - case 27: - // No break on purpose - case 29: - // No break on purpose - case 30: - // No break on purpose - case 31: - // No break on purpose - case 32: - // No break on purpose - case 44: - // No break on purpose - case 45: - // No break on purpose - case 51: - // No break on purpose - case 55: - // No break on purpose - case 56: - // No break on purpose - case 57: - // No break on purpose - case 60: - // No break on purpose - case 63: - _upExit._enabled = true; - break; - case 54: - // No break on purpose - case 61: - // No break on purpose - case 62: - // No break on purpose - case 65: - _upExit._enabled = true; - // No break on purpose - case 35: - // No break on purpose - case 38: - // No break on purpose - case 40: - // No break on purpose - case 47: - // No break on purpose - case 50: - // No break on purpose - case 53: - // No break on purpose - case 58: - // No break on purpose - case 64: - // No break on purpose - case 66: - // No break on purpose - case 67: - // No break on purpose - case 79: - // No break on purpose - case 80: - // No break on purpose - case 86: - // No break on purpose - case 89: - // No break on purpose - case 90: - // No break on purpose - case 91: - // No break on purpose - case 92: - // No break on purpose - case 95: - // No break on purpose - case 96: - // No break on purpose - case 97: - // No break on purpose - case 98: - // No break on purpose - case 100: - _downExit._enabled = true; - R2_GLOBALS._walkRegions.disableRegion(4); - R2_GLOBALS._walkRegions.disableRegion(5); - R2_GLOBALS._walkRegions.disableRegion(6); - R2_GLOBALS._walkRegions.disableRegion(10); - R2_GLOBALS._walkRegions.disableRegion(11); - default: - break; - } - R2_GLOBALS._uiElements.draw(); -} - -void Scene1950::enterArea() { - R2_GLOBALS._player.disableControl(); - R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); - - _vampire.remove(); - _door.remove(); - _scrolls.remove(); - - _vampireActive = false; - _vampireIndex = 0; - - // Certain areas have a vampire in them - switch (R2_GLOBALS._flubMazeArea) { - case 10: - _vampireIndex = 1; - break; - case 13: - _vampireIndex = 2; - break; - case 16: - _vampireIndex = 3; - break; - case 17: - _vampireIndex = 4; - break; - case 24: - _vampireIndex = 5; - break; - case 25: - _vampireIndex = 6; - break; - case 31: - _vampireIndex = 7; - break; - case 40: - _vampireIndex = 8; - break; - case 45: - _vampireIndex = 9; - break; - case 46: - _vampireIndex = 10; - break; - case 73: - _vampireIndex = 11; - break; - case 75: - _vampireIndex = 12; - break; - case 80: - _vampireIndex = 13; - break; - case 87: - _vampireIndex = 14; - break; - case 88: - _vampireIndex = 15; - break; - case 96: - _vampireIndex = 16; - break; - case 97: - _vampireIndex = 17; - break; - case 104: - _vampireIndex = 18; - break; - default: - break; - } - - if (_vampireIndex != 0) { - _vampire.postInit(); - _vampire._numFrames = 6; - _vampire._moveRate = 6; - _vampire._moveDiff = Common::Point(3, 2); - _vampire._effect = EFFECT_SHADED; - - if (!R2_GLOBALS._vampireData[_vampireIndex - 1]._isAlive) { - // Show vampire ashes - _vampire.setPosition(Common::Point(R2_GLOBALS._vampireData[_vampireIndex - 1]._position)); - _vampire.animate(ANIM_MODE_NONE, NULL); - _vampire.addMover(NULL); - _vampire.setVisage(1961); - _vampire.setStrip(4); - _vampire.setFrame(10); - _vampire.fixPriority(10); - _vampire.setDetails(1950, 15, -1, 17, 2, (SceneItem *) NULL); - } else { - // Start the vampire - _vampire.setVisage(1960); - _vampire.setPosition(Common::Point(160, 130)); - _vampire.animate(ANIM_MODE_2, NULL); - _vampire.setDetails(1950, 12, -1, 14, 2, (SceneItem *) NULL); - _vampireActive = true; - } - } - if ((R2_GLOBALS._flubMazeArea == 1) && (R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) != 0)) { - // Show doorway at the right hand side of the very first flub corridor - _door.postInit(); - _door.setVisage(1948); - _door.setStrip(3); - _door.setPosition(Common::Point(278, 155)); - _door.fixPriority(100); - _door.setDetails(1950, 19, 20, 23, 2, (SceneItem *) NULL); - } - - if (R2_GLOBALS._flubMazeArea == 102) { - R2_GLOBALS._walkRegions.load(1951); - R2_GLOBALS._walkRegions.disableRegion(1); - R2_GLOBALS._walkRegions.disableRegion(5); - R2_GLOBALS._walkRegions.disableRegion(6); - R2_GLOBALS._walkRegions.disableRegion(7); - - _cube.postInit(); - _cube.setVisage(1970); - _cube.setStrip(1); - if (R2_GLOBALS.getFlag(37)) - _cube.setFrame(3); - else - _cube.setFrame(1); - _cube.setPosition(Common::Point(193, 158)); - _cube.setDetails(1950, 3, 4, 5, 2, (SceneItem *) NULL); - - _pulsingLights.postInit(); - _pulsingLights.setVisage(1970); - _pulsingLights.setStrip(3); - _pulsingLights.animate(ANIM_MODE_2, NULL); - _pulsingLights._numFrames = 6; - _pulsingLights.setPosition(Common::Point(194, 158)); - _pulsingLights.fixPriority(159); - - _keypad.setDetails(Rect(188, 124, 199, 133), 1950, 27, 28, -1, 2, NULL); - - if (R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 1950) { - _gem.postInit(); - _gem.setVisage(1970); - _gem.setStrip(1); - _gem.setFrame(2); - _gem.fixPriority(160); - } - - if (R2_GLOBALS.getFlag(37)) { - _gem.setPosition(Common::Point(192, 118)); - _gem.setDetails(1950, 9, 4, -1, 2, (SceneItem *) NULL); - } else { - _containmentField.postInit(); - _containmentField.setVisage(1970); - _containmentField.setStrip(4); - _containmentField._numFrames = 4; - _containmentField.animate(ANIM_MODE_8, 0, NULL); - _containmentField.setPosition(Common::Point(192, 121)); - _containmentField.fixPriority(159); - _containmentField.setDetails(1950, 6, 7, 8, 2, (SceneItem *) NULL); - - _gem.setPosition(Common::Point(192, 109)); - _gem.setDetails(1950, 9, 7, 8, 2, (SceneItem *) NULL); - } - - _scrolls.postInit(); - _scrolls.setVisage(1972); - _scrolls.setStrip(1); - _scrolls.setPosition(Common::Point(76, 94)); - _scrolls.fixPriority(25); - _scrolls.setDetails(1950, 30, -1, -1, 2, (SceneItem *) NULL); - if (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) == 2) - _scrolls.setFrame(2); - else - _scrolls.setFrame(1); - - _removeFlag = true; - } else if (_removeFlag) { - _cube.remove(); - _containmentField.remove(); - _gem.remove(); - _pulsingLights.remove(); - _scrolls.remove(); - - R2_GLOBALS._sceneItems.remove(&_background); - _background.setDetails(Rect(0, 0, 320, 200), 1950, 0, 1, 2, 2, NULL); - - _removeFlag = false; - } - - switch (R2_GLOBALS._flubMazeEntryDirection) { - case 0: - _sceneMode = 1950; - if (R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) == 0) - // The original uses CURSOR_ARROW. CURSOR_WALK is much more coherent - R2_GLOBALS._player.enableControl(CURSOR_WALK); - else - setAction(&_sequenceManager, this, 1950, &R2_GLOBALS._player, NULL); - - break; - case 1: { - _sceneMode = R2_GLOBALS._flubMazeEntryDirection; - R2_GLOBALS._player.setPosition(Common::Point(160, 213)); - Common::Point pt(160, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } - break; - case 2: - _sceneMode = R2_GLOBALS._flubMazeEntryDirection; - if (R2_GLOBALS.getFlag(36)) - setAction(&_sequenceManager, this, 1957, &R2_GLOBALS._player, NULL); - else - setAction(&_sequenceManager, this, 1974, &R2_GLOBALS._player, NULL); - break; - case 3: - // Entering from the left - if (!_vampireActive) { - _sceneMode = R2_GLOBALS._flubMazeEntryDirection; - R2_GLOBALS._player.setPosition(Common::Point(-20, 160)); - Common::Point pt(30, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } else { - _sceneMode = 18; - _eastExit._enabled = false; - _vampireDestPos = Common::Point(60, 152); - R2_GLOBALS._player.enableControl(CURSOR_USE); - R2_GLOBALS._player._canWalk = false; - - _vampire.setStrip(2); - NpcMover *mover = new NpcMover(); - _vampire.addMover(mover, &_vampireDestPos, this); - - R2_GLOBALS._player.setPosition(Common::Point(-20, 160)); - Common::Point pt2(30, 160); - NpcMover *mover2 = new NpcMover(); - R2_GLOBALS._player.addMover(mover2, &pt2, NULL); - } - break; - case 4: - _sceneMode = R2_GLOBALS._flubMazeEntryDirection; - if (!_upExitStyle) { - if (R2_GLOBALS.getFlag(36)) - setAction(&_sequenceManager, this, 1955, &R2_GLOBALS._player, NULL); - else - setAction(&_sequenceManager, this, 1972, &R2_GLOBALS._player, NULL); - } else { - if (R2_GLOBALS.getFlag(36)) - setAction(&_sequenceManager, this, 1954, &R2_GLOBALS._player, NULL); - else - setAction(&_sequenceManager, this, 1971, &R2_GLOBALS._player, NULL); - } - break; - case 5: { - _sceneMode = R2_GLOBALS._flubMazeEntryDirection; - R2_GLOBALS._player.setPosition(Common::Point(160, 127)); - Common::Point pt(160, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } - break; - case 6: - // Entering from the right - if (!_vampireActive) { - _sceneMode = R2_GLOBALS._flubMazeEntryDirection; - if (R2_GLOBALS._flubMazeArea == 1) { - setAction(&_sequenceManager, this, 1961, &R2_GLOBALS._player, NULL); - } else { - R2_GLOBALS._player.setPosition(Common::Point(340, 160)); - Common::Point pt(289, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } - } else { - _sceneMode = 17; - _westExit._enabled = false; - _vampireDestPos = Common::Point(259, 152); - - R2_GLOBALS._player.enableControl(CURSOR_USE); - R2_GLOBALS._player._canWalk = false; - - _vampire.setStrip(1); - NpcMover *mover = new NpcMover(); - _vampire.addMover(mover, &_vampireDestPos, this); - - R2_GLOBALS._player.setPosition(Common::Point(340, 160)); - Common::Point pt2(289, 160); - NpcMover *mover2 = new NpcMover(); - R2_GLOBALS._player.addMover(mover2, &pt2, NULL); - } - break; - default: - break; - } -} - -void Scene1950::doButtonPress(int indx) { - Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; - R2_GLOBALS._player.disableControl(); - - int prevIndex = indx - 1; - if ((indx / 4) == (prevIndex / 4)) { - if (prevIndex < 0) - prevIndex = 3; - } else { - prevIndex += 4; - } - - assert(prevIndex >= 0 && prevIndex < 16); - if (!_KeypadWindow._buttons[prevIndex]._toggled) { - _KeypadWindow._buttons[prevIndex].setFrame(2); - _KeypadWindow._buttons[prevIndex]._toggled = true; - } else { - _KeypadWindow._buttons[prevIndex].setFrame(1); - _KeypadWindow._buttons[prevIndex]._toggled = false; - } - - prevIndex = indx + 1; - if ((indx / 4) == (prevIndex / 4)) { - if (prevIndex > 15) - prevIndex = 12; - } else { - prevIndex -= 4; - } - - assert(prevIndex >= 0 && prevIndex < 16); - if (!_KeypadWindow._buttons[prevIndex]._toggled) { - _KeypadWindow._buttons[prevIndex].setFrame(2); - _KeypadWindow._buttons[prevIndex]._toggled = true; - } else { - _KeypadWindow._buttons[prevIndex].setFrame(1); - _KeypadWindow._buttons[prevIndex]._toggled = false; - } - - prevIndex = indx - 4; - if (prevIndex < 0) - prevIndex += 16; - - assert(prevIndex >= 0 && prevIndex < 16); - if (!_KeypadWindow._buttons[prevIndex]._toggled) { - _KeypadWindow._buttons[prevIndex].setFrame(2); - _KeypadWindow._buttons[prevIndex]._toggled = true; - } else { - _KeypadWindow._buttons[prevIndex].setFrame(1); - _KeypadWindow._buttons[prevIndex]._toggled = false; - } - - prevIndex = indx + 4; - if (prevIndex > 15) - prevIndex -= 16; - - assert(prevIndex >= 0 && prevIndex < 16); - if (!_KeypadWindow._buttons[prevIndex]._toggled) { - _KeypadWindow._buttons[prevIndex].setFrame(2); - _KeypadWindow._buttons[prevIndex]._toggled = true; - } else { - _KeypadWindow._buttons[prevIndex].setFrame(1); - _KeypadWindow._buttons[prevIndex]._toggled = false; - } - - // Check whether all the buttons are highlighted - int cpt = 0; - for (prevIndex = 0; prevIndex < 16; prevIndex++) { - if (_KeypadWindow._buttons[prevIndex]._toggled) - ++cpt; - } - - if (cpt != 16) { - R2_GLOBALS._player.enableControl(); - R2_GLOBALS._player._canWalk = false; - } else { - R2_GLOBALS.setFlag(37); - _sceneMode = 24; - setAction(&_sequenceManager, scene, 1976, NULL); - } -} - -void Scene1950::postInit(SceneObjectList *OwnerList) { - _upExitStyle = false; - _removeFlag = false; - _vampireActive = false; - _vampireIndex = 0; - if (R2_GLOBALS._sceneManager._previousScene == 300) - R2_GLOBALS._flubMazeArea = 103; - - initArea(); - SceneExt::postInit(); - R2_GLOBALS._sound1.play(105); - - _northExit.setDetails(Rect(130, 46, 189, 135), SHADECURSOR_UP, 1950); - _northExit.setDest(Common::Point(160, 145)); - - _upExit.setDetails(Rect(208, 0, 255, 73), EXITCURSOR_N, 1950); - _upExit.setDest(Common::Point(200, 151)); - - _eastExit.setDetails(Rect(305, 95, 320, 147), EXITCURSOR_E, 1950); - _eastExit.setDest(Common::Point(312, 160)); - - _downExit.setDetails(Rect(208, 99, 255, 143), EXITCURSOR_S, 1950); - _downExit.setDest(Common::Point(200, 151)); - - _southExit.setDetails(Rect(113, 154, 206, 168), SHADECURSOR_DOWN, 1950); - _southExit.setDest(Common::Point(160, 165)); - - _westExit.setDetails(Rect(0, 95, 14, 147), EXITCURSOR_W, 1950); - _westExit.setDest(Common::Point(7, 160)); - - _shaftExit.setDetails(Rect(72, 54, 120, 128), EXITCURSOR_NW, 1950); - _shaftExit.setDest(Common::Point(120, 140)); - - _doorExit.setDetails(Rect(258, 60, 300, 145), EXITCURSOR_NE, 1950); - _doorExit.setDest(Common::Point(268, 149)); - - R2_GLOBALS._player.postInit(); - if ( (R2_INVENTORY.getObjectScene(R2_TANNER_MASK) == 0) && (R2_INVENTORY.getObjectScene(R2_PURE_GRAIN_ALCOHOL) == 0) - && (R2_INVENTORY.getObjectScene(R2_SOAKED_FACEMASK) == 0) && (!R2_GLOBALS.getFlag(36)) ) - R2_GLOBALS._player.setVisage(22); - else - R2_GLOBALS._player.setVisage(20); - - R2_GLOBALS._player._moveDiff = Common::Point(5, 3); - _background.setDetails(Rect(0, 0, 320, 200), 1950, 0, 1, 2, 1, NULL); - - enterArea(); -} - -void Scene1950::remove() { - R2_GLOBALS._sound1.stop(); - R2_GLOBALS._sound2.fadeOut2(NULL); - SceneExt::remove(); -} - -void Scene1950::signal() { - switch (_sceneMode) { - case 11: - R2_GLOBALS._flubMazeArea += 7; - initArea(); - enterArea(); - break; - case 12: - // Moving up a ladder within the Flub maze - R2_GLOBALS._flubMazeArea += 35; - initArea(); - enterArea(); - break; - case 1975: - SceneItem::display(1950, 21, SET_WIDTH, 280, SET_X, 160, SET_POS_MODE, 1, - SET_Y, 20, SET_EXT_BGCOLOR, 7, LIST_END); - // No break on purpose - case 13: - // Moving east within the Flub maze - ++R2_GLOBALS._flubMazeArea; - initArea(); - enterArea(); - break; - case 14: - // Moving down a ladder within the Flub maze - R2_GLOBALS._flubMazeArea -= 35; - initArea(); - enterArea(); - break; - case 15: - R2_GLOBALS._flubMazeArea -= 7; - initArea(); - enterArea(); - break; - case 16: - // Moving west within the Flub maze - // No break on purpose - case 1961: - --R2_GLOBALS._flubMazeArea; - initArea(); - enterArea(); - break; - case 17: { - _sceneMode = 13; - R2_GLOBALS._flubMazeEntryDirection = 3; - _vampireActive = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._player._canWalk = true; - R2_GLOBALS._player.setVisage(22); - R2_GLOBALS._player.animate(ANIM_MODE_9); - Common::Point pt(340, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - Common::Point pt2(289, 160); - NpcMover *mover2 = new NpcMover(); - _vampire.addMover(mover2, &pt2, NULL); - } - break; - case 18: { - _sceneMode = 16; - R2_GLOBALS._flubMazeEntryDirection = 6; - _vampireActive = false; - R2_GLOBALS._player.disableControl(CURSOR_WALK); - R2_GLOBALS._player._canWalk = true; - R2_GLOBALS._player.setVisage(22); - R2_GLOBALS._player.animate(ANIM_MODE_9); - Common::Point pt(-20, 160); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - Common::Point pt2(30, 160); - NpcMover *mover2 = new NpcMover(); - _vampire.addMover(mover2, &pt2, NULL); - } - break; - case 24: - _KeypadWindow.remove(); - _sceneMode = 1966; - _cube.setFrame(3); - setAction(&_sequenceManager, this, 1966, &_containmentField, &_gem, NULL); - break; - case 1951: - R2_GLOBALS._sound1.fadeOut2(NULL); - R2_GLOBALS._sceneManager.changeScene(1945); - break; - case 1958: - SceneItem::display(1950, 24, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - R2_GLOBALS._player.enableControl(CURSOR_WALK); - _doorExit._enabled = true; - break; - case 1959: - R2_INVENTORY.setObjectScene(R2_SOAKED_FACEMASK, 0); - R2_GLOBALS._player.enableControl(CURSOR_WALK); - _doorExit._enabled = true; - break; - case 1962: - // No break on purpose - case 1963: - R2_GLOBALS._player.enableControl(); - _KeypadWindow.setup2(1971, 1, 1, 160, 135); - break; - case 1964: - // No break on purpose - case 1965: - if (!R2_GLOBALS.getFlag(37)) - SceneItem::display(1950, 26, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); - - R2_GLOBALS._player.enableControl(); - break; - case 1966: - _containmentField.remove(); - if (R2_GLOBALS.getFlag(36)) { - _sceneMode = 1964; - setAction(&_sequenceManager, this, 1964, &R2_GLOBALS._player, NULL); - } else { - _sceneMode = 1965; - setAction(&_sequenceManager, this, 1965, &R2_GLOBALS._player, NULL); - } - _gem.setDetails(1950, 9, -1, -1, 2, (SceneItem *) NULL); - break; - case 1967: { - _sceneMode = 0; - R2_INVENTORY.setObjectScene(R2_SAPPHIRE_BLUE, 2); - _gem.remove(); - if (R2_GLOBALS.getFlag(36)) - R2_GLOBALS._player.setVisage(20); - else - R2_GLOBALS._player.setVisage(22); - - R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); - // This is a hack to work around a pathfinding issue. original destination is (218, 165) - Common::Point pt(128, 165); - NpcMover *mover = new NpcMover(); - R2_GLOBALS._player.addMover(mover, &pt, this); - } - break; - case 1968: - R2_GLOBALS._player.enableControl(); - R2_INVENTORY.setObjectScene(R2_ANCIENT_SCROLLS, 2); - _scrolls.setFrame(2); - if (R2_GLOBALS.getFlag(36)) - R2_GLOBALS._player.setVisage(20); - else - R2_GLOBALS._player.setVisage(22); - R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); - break; - default: - R2_GLOBALS._player.enableControl(CURSOR_WALK); - break; - } -} - -void Scene1950::process(Event &event) { - if ( (event.eventType == EVENT_BUTTON_DOWN) - && (R2_GLOBALS._player._uiEnabled) - && (R2_GLOBALS._events.getCursor() == R2_SOAKED_FACEMASK) - && (R2_GLOBALS._player._bounds.contains(event.mousePos)) - && (R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) == 0)) { - event.handled = true; - R2_GLOBALS._player.disableControl(); - _shaftExit._enabled = false; - _doorExit._enabled = false; - _sceneMode = 1959; - setAction(&_sequenceManager, this, 1959, &R2_GLOBALS._player, NULL); - } - - Scene::process(event); -} - } // End of namespace Ringworld2 } // End of namespace TsAGE diff --git a/engines/tsage/ringworld2/ringworld2_scenes1.h b/engines/tsage/ringworld2/ringworld2_scenes1.h index 91c4b88391..e6f5e0ab97 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes1.h +++ b/engines/tsage/ringworld2/ringworld2_scenes1.h @@ -135,270 +135,6 @@ public: virtual void saveCharacter(int characterIndex); }; -class Scene1200 : public SceneExt { - enum CrawlDirection { CRAWL_EAST = 1, CRAWL_WEST = 2, CRAWL_SOUTH = 3, CRAWL_NORTH = 4 }; - - class LaserPanel: public ModalWindow { - public: - class Jumper : public SceneActorExt { - public: - void init(int state); - virtual bool startAction(CursorType action, Event &event); - }; - - Jumper _jumper1; - Jumper _jumper2; - Jumper _jumper3; - - LaserPanel(); - - virtual void postInit(SceneObjectList *OwnerList = NULL); - virtual void remove(); - }; - -public: - NamedHotspot _item1; - SceneActor _actor1; - LaserPanel _laserPanel; - MazeUI _mazeUI; - SequenceManager _sequenceManager; - - int _nextCrawlDirection; - int _field414; - int _field416; - int _field418; - int _field41A; - bool _fixupMaze; - - Scene1200(); - void synchronize(Serializer &s); - - void startCrawling(CrawlDirection dir); - - virtual void postInit(SceneObjectList *OwnerList = NULL); - virtual void signal(); - virtual void process(Event &event); - virtual void dispatch(); - virtual void saveCharacter(int characterIndex); -}; - -class Scene1337 : public SceneExt { - class OptionsDialog: public GfxDialog { - private: - GfxButton _autoplay; - GfxButton _restartGame; - GfxButton _quitGame; - GfxButton _continueGame; - - OptionsDialog(); - virtual ~OptionsDialog() {} - virtual GfxButton *execute(GfxButton *defaultButton); - public: - static void show(); - }; - - class Card: public SceneHotspot { - public: - SceneObject _card; - - int _cardId; - Common::Point _stationPos; - - Card(); - void synchronize(Serializer &s); - bool isIn(Common::Point pt); - }; - - class GameBoardSide: public SceneHotspot { - public: - Card _handCard[4]; - Card _outpostStation[8]; - Card _delayCard; - Card _emptyStationPos; - - Common::Point _card1Pos; - Common::Point _card2Pos; - Common::Point _card3Pos; - Common::Point _card4Pos; - int _frameNum; - - GameBoardSide(); - void synchronize(Serializer &s); - }; - - class Action1337: public Action { - public: - void waitFrames(int32 frameCount); - }; - - class Action1: public Action1337 { - public: - void signal(); - }; - class Action2: public Action1337 { - public: - void signal(); - }; - class Action3: public Action1337 { - public: - void signal(); - }; - class Action4: public Action1337 { - public: - void signal(); - }; - class Action5: public Action1337 { - public: - void signal(); - }; - class Action6: public Action1337 { - public: - void signal(); - }; - class Action7: public Action1337 { - public: - void signal(); - }; - class Action8: public Action1337 { - public: - void signal(); - }; - class Action9: public Action1337 { - public: - void signal(); - }; - class Action10: public Action1337 { - public: - void signal(); - }; - class Action11: public Action1337 { - public: - void signal(); - }; - class Action12: public Action1337 { - public: - void signal(); - }; - class Action13: public Action1337 { - public: - void signal(); - }; -public: - Action1 _action1; - Action2 _action2; - Action3 _action3; - Action4 _action4; - Action5 _action5; - Action6 _action6; - Action7 _action7; - Action8 _action8; - Action9 _action9; - Action10 _action10; - Action11 _action11; - Action12 _action12; - Action13 _action13; - - typedef void (Scene1337::*FunctionPtrType)(); - FunctionPtrType _delayedFunction; - - bool _autoplay; - bool _shuffleEndedFl; - bool _showPlayerTurn; - bool _displayHelpFl; - bool _instructionsDisplayedFl; - - // Discarded cards are put in the available cards pile, with an higher index so there no conflict - int _currentDiscardIndex; - int _availableCardsPile[100]; - int _cardsAvailableNumb; - int _currentPlayerNumb; - int _actionIdx1; - int _actionIdx2; - int _winnerId; - int _instructionsWaitCount; - int _cursorCurRes; - int _cursorCurStrip; - int _cursorCurFrame; - - ASound _aSound1; - ASound _aSound2; - GameBoardSide _gameBoardSide[4]; - SceneActor _helpIcon; - SceneActor _stockPile; - SceneItem _actionItem; - SceneObject _currentPlayerArrow; - - Card *_actionCard1; - Card *_actionCard2; - Card *_actionCard3; - Card _animatedCard; - Card _shuffleAnimation; - Card _discardedPlatformCard; - Card _selectedCard; - Card _discardPile; - Card _stockCard; - - SceneObject _upperDisplayCard[8]; - SceneObject _lowerDisplayCard[8]; - - Scene1337(); - virtual void synchronize(Serializer &s); - - void actionDisplay(int resNum, int lineNum, int x, int y, int keepOnScreen, int width, int textMode, int fontNum, int colFG, int colBGExt, int colFGExt); - void setAnimationInfo(Card *card); - void handleNextTurn(); - void handlePlayerTurn(); - bool isStationCard(int cardId); - bool isStopConstructionCard(int cardId); - int getStationId(int playerId, int handCardId); - int findPlatformCardInHand(int playerId); - int findCard13InHand(int playerId); - int checkThieftCard(int playerId); - int isDelayCard(int cardId); - int getStationCardId(int cardId); - void handlePlayer01Discard(int playerId); - void playThieftCard(int playerId, Card *card, int victimId); - int getPreventionCardId(int cardId); - bool isAttackPossible(int victimId, int cardId); - int getPlayerWithOutpost(int playerId); - bool checkAntiDelayCard(int delayCardId, int cardId); - void playStationCard(Card *station, Card *platform); - void playDelayCard(Card *card, Card *dest); - void playPlatformCard(Card *card, Card *dest); - void playAntiDelayCard(Card *card, Card *dest); - Card *getStationCard(int arg1); - void playCounterTrickCard(Card *card, int playerId); - int getFreeHandCard(int playerId); - void discardCard(Card *card); - void subC4CD2(); - void subC4CEC(); - void subC51A0(Card *subObj1, Card *subObj2); - void displayDialog(int dialogNumb); - void subPostInit(); - void displayInstructions(); - void suggestInstructions(); - void shuffleCards(); - void dealCards(); - void showOptionsDialog(); - void handleClick(int arg1, Common::Point pt); - void handlePlayer0(); - void handlePlayer1(); - void handlePlayer2(); - void handlePlayer3(); - void handleAutoplayPlayer2(); - void updateCursorId(int arg1, bool arg2); - void setCursorData(int resNum, int rlbNum, int frameNum); - void subD18F5(); - void subD1917(); - void subD1940(bool flag); - void subD1975(int arg1, int arg2); - - virtual void postInit(SceneObjectList *OwnerList = NULL); - virtual void remove(); - virtual void process(Event &event); - virtual void dispatch(); -}; - class Scene1500 : public SceneExt { public: SceneActor _starship; @@ -1097,143 +833,6 @@ public: virtual void signal(); }; -class Scene1950 : public SceneExt { - /* Windows */ - class KeypadWindow: public ModalWindow { - public: - class KeypadButton : public SceneActor { - public: - int _buttonIndex; - bool _pressed; - bool _toggled; - - KeypadButton(); - void synchronize(Serializer &s); - - void init(int indx); - virtual void process(Event &event); - virtual bool startAction(CursorType action, Event &event); - }; - - SceneActor _areaActor; - KeypadButton _buttons[16]; - - int _buttonIndex; - - KeypadWindow(); - virtual void synchronize(Serializer &s); - virtual void remove(); - virtual void setup2(int visage, int stripFrameNum, int frameNum, int posX, int posY); - virtual void setup3(int resNum, int lookLineNum, int talkLineNum, int useLineNum); - }; - - class Keypad : public NamedHotspot { - public: - virtual bool startAction(CursorType action, Event &event); - }; - - /* Actors */ - class Door : public SceneActor { - public: - virtual bool startAction(CursorType action, Event &event); - }; - class Scrolls : public SceneActor { - public: - virtual bool startAction(CursorType action, Event &event); - }; - class Gem : public SceneActor { - public: - virtual bool startAction(CursorType action, Event &event); - }; - class Vampire : public SceneActor { - public: - Common::Point _deadPosition; - int _deltaX; - int _deltaY; - int _vampireMode; - - Vampire(); - void synchronize(Serializer &s); - - virtual void signal(); - virtual bool startAction(CursorType action, Event &event); - }; - - /* Exits */ - class NorthExit : public SceneExit { - public: - virtual void changeScene(); - }; - class UpExit : public SceneExit { - public: - virtual void changeScene(); - }; - class EastExit : public SceneExit { - public: - virtual void changeScene(); - }; - class DownExit : public SceneExit { - public: - virtual void changeScene(); - }; - class SouthExit : public SceneExit { - public: - virtual void changeScene(); - }; - class WestExit : public SceneExit { - public: - virtual void changeScene(); - }; - class ShaftExit : public SceneExit { - public: - virtual void changeScene(); - }; - class DoorExit : public SceneExit { - public: - virtual void changeScene(); - }; -private: - void initArea(); - void enterArea(); - void doButtonPress(int indx); -public: - NamedHotspot _background; - Keypad _keypad; - SceneActor _southDoorway; - SceneObject _northDoorway; - Door _door; - Scrolls _scrolls; - SceneActor _containmentField; - Gem _gem; - SceneActor _cube; - SceneActor _pulsingLights; - Vampire _vampire; - KeypadWindow _KeypadWindow; - NorthExit _northExit; - UpExit _upExit; - EastExit _eastExit; - DownExit _downExit; - SouthExit _southExit; - WestExit _westExit; - ShaftExit _shaftExit; - DoorExit _doorExit; - SequenceManager _sequenceManager; - - bool _upExitStyle; - bool _removeFlag; - bool _vampireActive; - Common::Point _vampireDestPos; - int _vampireIndex; - - Scene1950(); - void synchronize(Serializer &s); - - virtual void postInit(SceneObjectList *OwnerList = NULL); - virtual void remove(); - virtual void signal(); - virtual void process(Event &event); -}; - } // End of namespace Ringworld2 } // End of namespace TsAGE diff --git a/engines/tsage/ringworld2/ringworld2_scenes3.cpp b/engines/tsage/ringworld2/ringworld2_scenes3.cpp index 9eaead630b..8610e0c8bc 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes3.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes3.cpp @@ -3878,7 +3878,7 @@ void Scene3500::dispatch() { Scene::dispatch(); // WORKAROUND: The _mazeUI wasn't originally added to the scene in postInit. - // This is only needed to fix old savegames + // This is only needed to fix old savegames if (!R2_GLOBALS._sceneObjects->contains(&_mazeUI)) _mazeUI.draw(); diff --git a/engines/tsage/ringworld2/ringworld2_vampire.cpp b/engines/tsage/ringworld2/ringworld2_vampire.cpp new file mode 100644 index 0000000000..9d3b7f91a5 --- /dev/null +++ b/engines/tsage/ringworld2/ringworld2_vampire.cpp @@ -0,0 +1,1821 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "tsage/ringworld2/ringworld2_vampire.h" + +namespace TsAGE { + +namespace Ringworld2 { + +/*-------------------------------------------------------------------------- + * Scene 1950 - Flup Tube Corridor Maze + * + *--------------------------------------------------------------------------*/ + +Scene1950::KeypadWindow::KeypadWindow() { + _buttonIndex = 0; +} + +void Scene1950::KeypadWindow::synchronize(Serializer &s) { + SceneArea::synchronize(s); + + s.syncAsSint16LE(_buttonIndex); +} + +Scene1950::KeypadWindow::KeypadButton::KeypadButton() { + _buttonIndex = 0; + _pressed = false; + _toggled = false; +} + +void Scene1950::KeypadWindow::KeypadButton::synchronize(Serializer &s) { + SceneActor::synchronize(s); + + s.syncAsSint16LE(_buttonIndex); + s.syncAsSint16LE(_pressed); + s.syncAsSint16LE(_toggled); +} + +void Scene1950::KeypadWindow::KeypadButton::init(int indx) { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _buttonIndex = indx; + _pressed = false; + _toggled = false; + + postInit(); + setup(1971, 2, 1); + fixPriority(249); + setPosition(Common::Point(((_buttonIndex % 4) * 22) + 127, ((_buttonIndex / 4) * 19) + 71)); + scene->_sceneAreas.push_front(this); +} + +void Scene1950::KeypadWindow::KeypadButton::process(Event &event) { + if ((event.eventType == EVENT_BUTTON_DOWN) && (R2_GLOBALS._events.getCursor() == CURSOR_USE) + && (_bounds.contains(event.mousePos)) && !_pressed) { + R2_GLOBALS._sound2.play(227); + if (!_toggled) { + setFrame(2); + _toggled = true; + } else { + setFrame(1); + _toggled = false; + } + _pressed = true; + event.handled = true; + } + + if ((event.eventType == EVENT_BUTTON_UP) && _pressed) { + _pressed = false; + event.handled = true; + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + scene->doButtonPress(_buttonIndex); + } +} + +bool Scene1950::KeypadWindow::KeypadButton::startAction(CursorType action, Event &event) { + if (action == CURSOR_USE) + return false; + return SceneActor::startAction(action, event); +} + +void Scene1950::KeypadWindow::remove() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + for (_buttonIndex = 0; _buttonIndex < 16; ++_buttonIndex) { + scene->_sceneAreas.remove(&_buttons[_buttonIndex]); + _buttons[_buttonIndex].remove(); + } + + ModalWindow::remove(); + + if (!R2_GLOBALS.getFlag(37)) + R2_GLOBALS._sound2.play(278); + + R2_GLOBALS._player.disableControl(CURSOR_WALK); + scene->_eastExit._enabled = true; + + if (!R2_GLOBALS.getFlag(37)) { + if (R2_GLOBALS.getFlag(36)) { + scene->_sceneMode = 1964; + scene->setAction(&scene->_sequenceManager, scene, 1964, &R2_GLOBALS._player, NULL); + } else { + scene->_sceneMode = 1965; + scene->setAction(&scene->_sequenceManager, scene, 1965, &R2_GLOBALS._player, NULL); + } + } +} + +void Scene1950::KeypadWindow::setup2(int visage, int stripFrameNum, int frameNum, int posX, int posY) { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + if (R2_GLOBALS._player._mover) + R2_GLOBALS._player.addMover(NULL); + R2_GLOBALS._player._canWalk = false; + + ModalWindow::setup2(visage, stripFrameNum, frameNum, posX, posY); + + _object1.fixPriority(248); + scene->_eastExit._enabled = false; + setup3(1950, 27, 28, 27); + + for (_buttonIndex = 0; _buttonIndex < 16; _buttonIndex++) + _buttons[_buttonIndex].init(_buttonIndex); +} + +void Scene1950::KeypadWindow::setup3(int resNum, int lookLineNum, int talkLineNum, int useLineNum) { + // Copy of Scene1200::LaserPanel::proc13() + _areaActor.setDetails(resNum, lookLineNum, talkLineNum, useLineNum, 2, (SceneItem *) NULL); +} + +/*--------------------------------------------------------------------------*/ + +bool Scene1950::Keypad::startAction(CursorType action, Event &event) { + if ((action != CURSOR_USE) || (R2_GLOBALS.getFlag(37))) + return SceneHotspot::startAction(action, event); + + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + R2_GLOBALS._player.disableControl(); + if (R2_GLOBALS.getFlag(36)) { + scene->_sceneMode = 1962; + scene->setAction(&scene->_sequenceManager, scene, 1962, &R2_GLOBALS._player, NULL); + } else { + scene->_sceneMode = 1963; + scene->setAction(&scene->_sequenceManager, scene, 1963, &R2_GLOBALS._player, NULL); + } + return true; +} + +bool Scene1950::Door::startAction(CursorType action, Event &event) { + if (action != R2_SCRITH_KEY) + return SceneActor::startAction(action, event); + + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + R2_GLOBALS._player.disableControl(); + R2_INVENTORY.setObjectScene(R2_SCRITH_KEY, 0); + scene->_sceneMode = 1958; + scene->setAction(&scene->_sequenceManager, scene, 1958, &R2_GLOBALS._player, &scene->_door, NULL); + return true; +} + +bool Scene1950::Scrolls::startAction(CursorType action, Event &event) { + if ((action != CURSOR_USE) || (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) != 1950)) + return SceneActor::startAction(action, event); + + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + R2_GLOBALS._player.disableControl(); + scene->_sceneMode = 1968; + scene->setAction(&scene->_sequenceManager, scene, 1968, &R2_GLOBALS._player, NULL); + + return true; +} + +bool Scene1950::Gem::startAction(CursorType action, Event &event) { + if ((action != CURSOR_USE) || (!R2_GLOBALS.getFlag(37))) + return SceneActor::startAction(action, event); + + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + R2_GLOBALS._player.disableControl(); + scene->_sceneMode = 1967; + scene->setAction(&scene->_sequenceManager, scene, 1967, &R2_GLOBALS._player, NULL); + + return true; +} + +/*--------------------------------------------------------------------------*/ + +Scene1950::Vampire::Vampire() { + _deadPosition = Common::Point(0, 0); + _deltaX = 0; + _deltaY = 0; + _vampireMode = 0; +} + +void Scene1950::Vampire::synchronize(Serializer &s) { + SceneActor::synchronize(s); + + s.syncAsSint16LE(_deadPosition.x); + s.syncAsSint16LE(_deadPosition.y); + s.syncAsSint16LE(_deltaX); + s.syncAsSint16LE(_deltaY); + s.syncAsSint16LE(_vampireMode); +} + +void Scene1950::Vampire::signal() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + switch (_vampireMode) { + case 19: { + _vampireMode = 0; + setVisage(1960); + if (R2_GLOBALS._flubMazeEntryDirection == 3) + setStrip(2); + else + setStrip(1); + + NpcMover *mover = new NpcMover(); + addMover(mover, &scene->_vampireDestPos, scene); + } + break; + case 20: { + // Non fatal shot + _vampireMode = 19; + R2_GLOBALS._player.setVisage(22); + if (R2_GLOBALS._flubMazeEntryDirection == 3) + R2_GLOBALS._player.setStrip(1); + else + R2_GLOBALS._player.setStrip(2); + R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); + R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._shotsRequired--; + + if (R2_GLOBALS._flubMazeEntryDirection == 3) + _deadPosition.x = _position.x + 10; + else + _deadPosition.x = _position.x - 10; + _deadPosition.y = _position.y - 4; + + setVisage(1961); + + if (R2_GLOBALS._flubMazeEntryDirection == 3) + setStrip(2); + else + setStrip(1); + + animate(ANIM_MODE_2, NULL); + Common::Point pt = _deadPosition; + PlayerMover *mover = new PlayerMover(); + addMover(mover, &pt, this); + + R2_GLOBALS._player.enableControl(); + } + break; + case 21: { + // Fatal shot + R2_GLOBALS._player.setVisage(22); + if (R2_GLOBALS._flubMazeEntryDirection == 3) + R2_GLOBALS._player.setStrip(1); + else + R2_GLOBALS._player.setStrip(2); + R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); + + setVisage(1961); + if (R2_GLOBALS._flubMazeEntryDirection == 3) + setStrip(4); + else + setStrip(3); + setDetails(1950, 15, -1, 17, 2, (SceneItem *) NULL); + addMover(NULL); + _numFrames = 8; + R2_GLOBALS._sound2.play(226); + animate(ANIM_MODE_5, NULL); + fixPriority(10); + + R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._isAlive = false; + R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._shotsRequired--; + R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._position = _position; + _deltaX = (_position.x - R2_GLOBALS._player._position.x) / 2; + _deltaY = (_position.y - R2_GLOBALS._player._position.y) / 2; + + byte vampireCount = 0; + for (byte i = 0; i < 18; ++i) { + if (!R2_GLOBALS._vampireData[i]._isAlive) + ++vampireCount; + } + + if (vampireCount == 18) { + R2_GLOBALS.setFlag(36); + _vampireMode = 23; + Common::Point pt(R2_GLOBALS._player._position.x + _deltaX, R2_GLOBALS._player._position.y + _deltaY); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } else if (vampireCount == 1) { + _vampireMode = 22; + Common::Point pt(R2_GLOBALS._player._position.x + _deltaX, R2_GLOBALS._player._position.y + _deltaY); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } else { + R2_GLOBALS._player.enableControl(CURSOR_WALK); + } + + if (R2_GLOBALS._flubMazeEntryDirection == 3) + scene->_eastExit._enabled = true; + else + scene->_westExit._enabled = true; + + scene->_vampireActive = false; + } + break; + case 22: + SceneItem::display(1950, 18, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + R2_GLOBALS._player.enableControl(CURSOR_WALK); + break; + case 23: + SceneItem::display(1950, 25, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + scene->_sceneMode = R2_GLOBALS._flubMazeEntryDirection; + scene->setAction(&scene->_sequenceManager, scene, 1960, &R2_GLOBALS._player, NULL); + break; + default: + break; + } +} + +bool Scene1950::Vampire::startAction(CursorType action, Event &event) { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + if (!R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._isAlive || + (action != R2_PHOTON_STUNNER)) + return SceneActor::startAction(action, event); + + R2_GLOBALS._player.disableControl(); + + if (R2_GLOBALS._vampireData[scene->_vampireIndex - 1]._shotsRequired <= 1) + _vampireMode = 21; + else + _vampireMode = 20; + + R2_GLOBALS._player.setVisage(25); + if (R2_GLOBALS._flubMazeEntryDirection == 3) + R2_GLOBALS._player.setStrip(2); + else + R2_GLOBALS._player.setStrip(1); + R2_GLOBALS._player.animate(ANIM_MODE_5, this); + R2_GLOBALS._sound3.play(99); + + return true; +} + +/*--------------------------------------------------------------------------*/ + +void Scene1950::NorthExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 1; + scene->_sceneMode = 11; + + Common::Point pt(160, 127); + PlayerMover *mover = new PlayerMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); +} + +void Scene1950::UpExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 2; + scene->_sceneMode = 12; + + if (!scene->_upExitStyle) { + if (R2_GLOBALS.getFlag(36)) + scene->setAction(&scene->_sequenceManager, scene, 1953, &R2_GLOBALS._player, NULL); + else + scene->setAction(&scene->_sequenceManager, scene, 1970, &R2_GLOBALS._player, NULL); + } else { + if (R2_GLOBALS.getFlag(36)) + scene->setAction(&scene->_sequenceManager, scene, 1952, &R2_GLOBALS._player, NULL); + else + scene->setAction(&scene->_sequenceManager, scene, 1969, &R2_GLOBALS._player, NULL); + } +} + +void Scene1950::EastExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 3; + scene->_sceneMode = 13; + + if (scene->_vampireActive) + R2_GLOBALS._player.animate(ANIM_MODE_9); + + Common::Point pt(340, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); +} + +void Scene1950::DownExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 4; + scene->_sceneMode = 14; + + if (R2_GLOBALS.getFlag(36)) + scene->setAction(&scene->_sequenceManager, scene, 1956, &R2_GLOBALS._player, NULL); + else + scene->setAction(&scene->_sequenceManager, scene, 1973, &R2_GLOBALS._player, NULL); +} + +void Scene1950::SouthExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 5; + scene->_sceneMode = 15; + + Common::Point pt(160, 213); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); +} + +void Scene1950::WestExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 6; + + if (R2_GLOBALS._flubMazeArea == 2) { + // In the very first corridor area after the Scrith Door + if ((R2_GLOBALS.getFlag(36)) && (R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 2) && (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) == 2)) { + scene->_sceneMode = 1961; + Common::Point pt(-20, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); + } else { + if (!R2_GLOBALS.getFlag(36)) + SceneItem::display(1950, 33, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + if ((R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 1950) || (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) == 1950)) + SceneItem::display(1950, 34, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + scene->_sceneMode = 0; + Common::Point pt(30, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); + } + } else { + if (scene->_vampireActive) + R2_GLOBALS._player.animate(ANIM_MODE_9); + + scene->_sceneMode = 16; + Common::Point pt(-20, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); + } +} + +void Scene1950::ShaftExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 0; + scene->_sceneMode = 1951; + scene->setAction(&scene->_sequenceManager, scene, 1951, &R2_GLOBALS._player, NULL); +} + +void Scene1950::DoorExit::changeScene() { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + + _enabled = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._flubMazeEntryDirection = 3; + if (R2_GLOBALS._player._visage == 22) { + scene->_sceneMode = 1975; + scene->setAction(&scene->_sequenceManager, scene, 1975, &R2_GLOBALS._player, NULL); + } else { + SceneItem::display(1950, 22, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + R2_GLOBALS._flubMazeEntryDirection = 0; + scene->_sceneMode = 0; + Common::Point pt(250, 150); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, scene); + _enabled = true; + } +} + +/*--------------------------------------------------------------------------*/ + +Scene1950::Scene1950() { + _upExitStyle = false; + _removeFlag = false; + _vampireActive = false; + _vampireDestPos = Common::Point(0, 0); + _vampireIndex = 0; +} + +void Scene1950::synchronize(Serializer &s) { + SceneExt::synchronize(s); + + s.syncAsSint16LE(_upExitStyle); + s.syncAsSint16LE(_removeFlag); + s.syncAsSint16LE(_vampireActive); + s.syncAsSint16LE(_vampireDestPos.x); + s.syncAsSint16LE(_vampireDestPos.y); + s.syncAsSint16LE(_vampireIndex); +} + +void Scene1950::initArea() { + _northExit._enabled = false; + _upExit._enabled = false; + _eastExit._enabled = false; + _downExit._enabled = false; + _southExit._enabled = false; + _westExit._enabled = false; + _shaftExit._enabled = false; + _doorExit._enabled = false; + _northExit._insideArea = false; + _upExit._insideArea = false; + _eastExit._insideArea = false; + _downExit._insideArea = false; + _southExit._insideArea = false; + _westExit._insideArea = false; + _shaftExit._insideArea = false; + _doorExit._insideArea = false; + _northExit._moving = false; + _upExit._moving = false; + _eastExit._moving = false; + _downExit._moving = false; + _southExit._moving = false; + _westExit._moving = false; + _shaftExit._moving = false; + _doorExit._moving = false; + _upExitStyle = false; + + switch (R2_GLOBALS._flubMazeArea - 1) { + case 0: + loadScene(1948); + break; + case 1: + // No break on purpose + case 8: + // No break on purpose + case 10: + // No break on purpose + case 12: + // No break on purpose + case 16: + // No break on purpose + case 19: + // No break on purpose + case 23: + // No break on purpose + case 30: + // No break on purpose + case 44: + // No break on purpose + case 72: + // No break on purpose + case 74: + // No break on purpose + case 86: + // No break on purpose + case 96: + // No break on purpose + case 103: + loadScene(1950); + break; + case 2: + // No break on purpose + case 29: + loadScene(1965); + break; + case 3: + // No break on purpose + case 9: + // No break on purpose + case 11: + // No break on purpose + case 15: + // No break on purpose + case 24: + // No break on purpose + case 39: + // No break on purpose + case 45: + // No break on purpose + case 71: + // No break on purpose + case 73: + // No break on purpose + case 75: + // No break on purpose + case 79: + // No break on purpose + case 85: + // No break on purpose + case 87: + // No break on purpose + case 95: + loadScene(1955); + break; + case 4: + // No break on purpose + case 6: + // No break on purpose + case 13: + // No break on purpose + case 27: + // No break on purpose + case 41: + // No break on purpose + case 48: + // No break on purpose + case 50: + // No break on purpose + case 54: + // No break on purpose + case 76: + // No break on purpose + case 80: + // No break on purpose + case 90: + // No break on purpose + case 104: + loadScene(1975); + break; + case 5: + // No break on purpose + case 7: + // No break on purpose + case 14: + // No break on purpose + case 28: + // No break on purpose + case 32: + // No break on purpose + case 47: + // No break on purpose + case 53: + loadScene(1997); + break; + case 17: + // No break on purpose + case 20: + // No break on purpose + case 25: + // No break on purpose + case 31: + // No break on purpose + case 33: + // No break on purpose + case 46: + loadScene(1995); + break; + case 18: + // No break on purpose + case 22: + // No break on purpose + case 26: + // No break on purpose + case 36: + // No break on purpose + case 38: + // No break on purpose + case 43: + // No break on purpose + case 51: + // No break on purpose + case 70: + // No break on purpose + case 78: + // No break on purpose + case 84: + // No break on purpose + case 89: + // No break on purpose + case 101: + loadScene(1970); + break; + case 21: + // No break on purpose + case 34: + // No break on purpose + case 57: + // No break on purpose + case 58: + // No break on purpose + case 59: + // No break on purpose + case 62: + // No break on purpose + case 65: + loadScene(1980); + break; + case 35: + // No break on purpose + case 61: + // No break on purpose + case 77: + // No break on purpose + case 83: + loadScene(1982); + break; + case 37: + // No break on purpose + case 52: + // No break on purpose + case 82: + // No break on purpose + case 88: + // No break on purpose + case 92: + // No break on purpose + case 97: + // No break on purpose + case 100: + loadScene(1962); + break; + case 40: + // No break on purpose + case 102: + loadScene(1960); + break; + case 42: + // No break on purpose + case 55: + // No break on purpose + case 60: + // No break on purpose + case 66: + // No break on purpose + case 68: + // No break on purpose + case 69: + // No break on purpose + case 93: + // No break on purpose + case 98: + loadScene(1990); + break; + case 49: + // No break on purpose + case 81: + // No break on purpose + case 91: + // No break on purpose + case 94: + // No break on purpose + case 99: + loadScene(1967); + break; + case 56: + // No break on purpose + case 63: + // No break on purpose + case 64: + // No break on purpose + case 67: + loadScene(1985); + _upExitStyle = true; + break; + default: + break; + } + + if (R2_GLOBALS._flubMazeArea != 1) + R2_GLOBALS._walkRegions.load(1950); + + switch (R2_GLOBALS._flubMazeArea - 1) { + case 0: + _shaftExit._enabled = true; + if ((R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) == 0) && (R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 1950)) + _doorExit._enabled = true; + R2_GLOBALS._walkRegions.disableRegion(2); + R2_GLOBALS._walkRegions.disableRegion(3); + R2_GLOBALS._walkRegions.disableRegion(4); + R2_GLOBALS._walkRegions.disableRegion(5); + R2_GLOBALS._walkRegions.disableRegion(6); + break; + case 1: + // No break on purpose + case 2: + // No break on purpose + case 3: + // No break on purpose + case 8: + // No break on purpose + case 9: + // No break on purpose + case 10: + // No break on purpose + case 11: + // No break on purpose + case 12: + // No break on purpose + case 15: + // No break on purpose + case 16: + // No break on purpose + case 19: + // No break on purpose + case 23: + // No break on purpose + case 24: + // No break on purpose + case 29: + // No break on purpose + case 30: + // No break on purpose + case 39: + // No break on purpose + case 40: + // No break on purpose + case 44: + // No break on purpose + case 45: + // No break on purpose + case 71: + // No break on purpose + case 72: + // No break on purpose + case 73: + // No break on purpose + case 74: + // No break on purpose + case 75: + // No break on purpose + case 79: + // No break on purpose + case 85: + // No break on purpose + case 86: + // No break on purpose + case 87: + // No break on purpose + case 95: + // No break on purpose + case 96: + // No break on purpose + case 102: + // No break on purpose + case 103: + _eastExit._enabled = true; + _westExit._enabled = true; + break; + case 4: + // No break on purpose + case 6: + // No break on purpose + case 13: + // No break on purpose + case 17: + // No break on purpose + case 20: + // No break on purpose + case 25: + // No break on purpose + case 27: + // No break on purpose + case 31: + // No break on purpose + case 33: + // No break on purpose + case 37: + // No break on purpose + case 41: + // No break on purpose + case 46: + // No break on purpose + case 48: + // No break on purpose + case 50: + // No break on purpose + case 52: + // No break on purpose + case 54: + // No break on purpose + case 76: + // No break on purpose + case 80: + // No break on purpose + case 82: + // No break on purpose + case 88: + // No break on purpose + case 90: + // No break on purpose + case 92: + // No break on purpose + case 97: + // No break on purpose + case 100: + // No break on purpose + case 104: + _westExit._enabled = true; + R2_GLOBALS._walkRegions.disableRegion(6); + R2_GLOBALS._walkRegions.disableRegion(9); + break; + case 5: + // No break on purpose + case 7: + // No break on purpose + case 14: + // No break on purpose + case 18: + // No break on purpose + case 22: + // No break on purpose + case 26: + // No break on purpose + case 28: + // No break on purpose + case 32: + // No break on purpose + case 36: + // No break on purpose + case 38: + // No break on purpose + case 43: + // No break on purpose + case 47: + // No break on purpose + case 49: + // No break on purpose + case 51: + // No break on purpose + case 53: + // No break on purpose + case 70: + // No break on purpose + case 78: + // No break on purpose + case 81: + // No break on purpose + case 84: + // No break on purpose + case 89: + // No break on purpose + case 91: + // No break on purpose + case 94: + // No break on purpose + case 99: + // No break on purpose + case 101: + _eastExit._enabled = true; + R2_GLOBALS._walkRegions.disableRegion(1); + R2_GLOBALS._walkRegions.disableRegion(7); + R2_GLOBALS._walkRegions.disableRegion(13); + break; + default: + R2_GLOBALS._walkRegions.disableRegion(1); + R2_GLOBALS._walkRegions.disableRegion(6); + R2_GLOBALS._walkRegions.disableRegion(7); + R2_GLOBALS._walkRegions.disableRegion(9); + R2_GLOBALS._walkRegions.disableRegion(13); + break; + } + + _northDoorway.remove(); + _northDoorway.removeObject(); + _southDoorway.remove(); + + switch (R2_GLOBALS._flubMazeArea - 4) { + case 0: + // No break on purpose + case 3: + // No break on purpose + case 16: + // No break on purpose + case 22: + // No break on purpose + case 24: + // No break on purpose + case 32: + // No break on purpose + case 33: + // No break on purpose + case 45: + // No break on purpose + case 46: + // No break on purpose + case 48: + // No break on purpose + case 51: + // No break on purpose + case 56: + // No break on purpose + case 59: + // No break on purpose + case 67: + // No break on purpose + case 68: + // No break on purpose + case 70: + // No break on purpose + case 73: + // No break on purpose + case 82: + // No break on purpose + case 90: + _northExit._enabled = true; + _northDoorway.setup(1950, (R2_GLOBALS._flubMazeArea % 2) + 1, 1, 160, 137, 25); + //visage,strip,frame,px,py,priority,effect + _southDoorway.postInit(); + _southDoorway.setVisage(1950); + _southDoorway.setStrip((((R2_GLOBALS._flubMazeArea - 1) / 35) % 2) + 1); + _southDoorway.setFrame(2); + _southDoorway.setPosition(Common::Point(160, 167)); + _southDoorway.fixPriority(220); + R2_GLOBALS._walkRegions.disableRegion(3); + R2_GLOBALS._walkRegions.disableRegion(4); + break; + case 7: + // No break on purpose + case 10: + // No break on purpose + case 23: + // No break on purpose + case 29: + // No break on purpose + case 31: + // No break on purpose + case 39: + // No break on purpose + case 40: + // No break on purpose + case 52: + // No break on purpose + case 53: + // No break on purpose + case 55: + // No break on purpose + case 63: + // No break on purpose + case 65: + // No break on purpose + case 66: + // No break on purpose + case 75: + // No break on purpose + case 77: + // No break on purpose + case 81: + // No break on purpose + case 87: + // No break on purpose + case 89: + // No break on purpose + case 97: + _southExit._enabled = true; + + _southDoorway.postInit(); + _southDoorway.setVisage(1950); + _southDoorway.setStrip((((R2_GLOBALS._flubMazeArea - 1) / 35) % 2) + 1); + _southDoorway.setFrame(3); + _southDoorway.setPosition(Common::Point(160, 167)); + _southDoorway.fixPriority(220); + break; + case 58: + // No break on purpose + case 74: + // No break on purpose + case 80: + _northExit._enabled = true; + _southExit._enabled = true; + + _northDoorway.setup(1950, (R2_GLOBALS._flubMazeArea % 2) + 1, 1, 160, 137, 25); + + _southDoorway.postInit(); + _southDoorway.setVisage(1950); + _southDoorway.setStrip((((R2_GLOBALS._flubMazeArea - 1) / 35) % 2) + 1); + _southDoorway.setFrame(3); + _southDoorway.setPosition(Common::Point(160, 167)); + _southDoorway.fixPriority(220); + R2_GLOBALS._walkRegions.disableRegion(3); + R2_GLOBALS._walkRegions.disableRegion(4); + break; + default: + _southDoorway.postInit(); + _southDoorway.setVisage(1950); + _southDoorway.setStrip(((R2_GLOBALS._flubMazeArea - 1) / 35) % 2 + 1); + _southDoorway.setFrame(2); + _southDoorway.setPosition(Common::Point(160, 167)); + _southDoorway.fixPriority(220); + break; + } + + switch (R2_GLOBALS._flubMazeArea - 3) { + case 0: + // No break on purpose + case 3: + // No break on purpose + case 5: + // No break on purpose + case 12: + // No break on purpose + case 15: + // No break on purpose + case 18: + // No break on purpose + case 19: + // No break on purpose + case 23: + // No break on purpose + case 26: + // No break on purpose + case 27: + // No break on purpose + case 29: + // No break on purpose + case 30: + // No break on purpose + case 31: + // No break on purpose + case 32: + // No break on purpose + case 44: + // No break on purpose + case 45: + // No break on purpose + case 51: + // No break on purpose + case 55: + // No break on purpose + case 56: + // No break on purpose + case 57: + // No break on purpose + case 60: + // No break on purpose + case 63: + _upExit._enabled = true; + break; + case 54: + // No break on purpose + case 61: + // No break on purpose + case 62: + // No break on purpose + case 65: + _upExit._enabled = true; + // No break on purpose + case 35: + // No break on purpose + case 38: + // No break on purpose + case 40: + // No break on purpose + case 47: + // No break on purpose + case 50: + // No break on purpose + case 53: + // No break on purpose + case 58: + // No break on purpose + case 64: + // No break on purpose + case 66: + // No break on purpose + case 67: + // No break on purpose + case 79: + // No break on purpose + case 80: + // No break on purpose + case 86: + // No break on purpose + case 89: + // No break on purpose + case 90: + // No break on purpose + case 91: + // No break on purpose + case 92: + // No break on purpose + case 95: + // No break on purpose + case 96: + // No break on purpose + case 97: + // No break on purpose + case 98: + // No break on purpose + case 100: + _downExit._enabled = true; + R2_GLOBALS._walkRegions.disableRegion(4); + R2_GLOBALS._walkRegions.disableRegion(5); + R2_GLOBALS._walkRegions.disableRegion(6); + R2_GLOBALS._walkRegions.disableRegion(10); + R2_GLOBALS._walkRegions.disableRegion(11); + default: + break; + } + R2_GLOBALS._uiElements.draw(); +} + +void Scene1950::enterArea() { + R2_GLOBALS._player.disableControl(); + R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); + + _vampire.remove(); + _door.remove(); + _scrolls.remove(); + + _vampireActive = false; + _vampireIndex = 0; + + // Certain areas have a vampire in them + switch (R2_GLOBALS._flubMazeArea) { + case 10: + _vampireIndex = 1; + break; + case 13: + _vampireIndex = 2; + break; + case 16: + _vampireIndex = 3; + break; + case 17: + _vampireIndex = 4; + break; + case 24: + _vampireIndex = 5; + break; + case 25: + _vampireIndex = 6; + break; + case 31: + _vampireIndex = 7; + break; + case 40: + _vampireIndex = 8; + break; + case 45: + _vampireIndex = 9; + break; + case 46: + _vampireIndex = 10; + break; + case 73: + _vampireIndex = 11; + break; + case 75: + _vampireIndex = 12; + break; + case 80: + _vampireIndex = 13; + break; + case 87: + _vampireIndex = 14; + break; + case 88: + _vampireIndex = 15; + break; + case 96: + _vampireIndex = 16; + break; + case 97: + _vampireIndex = 17; + break; + case 104: + _vampireIndex = 18; + break; + default: + break; + } + + if (_vampireIndex != 0) { + _vampire.postInit(); + _vampire._numFrames = 6; + _vampire._moveRate = 6; + _vampire._moveDiff = Common::Point(3, 2); + _vampire._effect = EFFECT_SHADED; + + if (!R2_GLOBALS._vampireData[_vampireIndex - 1]._isAlive) { + // Show vampire ashes + _vampire.setPosition(Common::Point(R2_GLOBALS._vampireData[_vampireIndex - 1]._position)); + _vampire.animate(ANIM_MODE_NONE, NULL); + _vampire.addMover(NULL); + _vampire.setVisage(1961); + _vampire.setStrip(4); + _vampire.setFrame(10); + _vampire.fixPriority(10); + _vampire.setDetails(1950, 15, -1, 17, 2, (SceneItem *) NULL); + } else { + // Start the vampire + _vampire.setVisage(1960); + _vampire.setPosition(Common::Point(160, 130)); + _vampire.animate(ANIM_MODE_2, NULL); + _vampire.setDetails(1950, 12, -1, 14, 2, (SceneItem *) NULL); + _vampireActive = true; + } + } + if ((R2_GLOBALS._flubMazeArea == 1) && (R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) != 0)) { + // Show doorway at the right hand side of the very first flub corridor + _door.postInit(); + _door.setVisage(1948); + _door.setStrip(3); + _door.setPosition(Common::Point(278, 155)); + _door.fixPriority(100); + _door.setDetails(1950, 19, 20, 23, 2, (SceneItem *) NULL); + } + + if (R2_GLOBALS._flubMazeArea == 102) { + R2_GLOBALS._walkRegions.load(1951); + R2_GLOBALS._walkRegions.disableRegion(1); + R2_GLOBALS._walkRegions.disableRegion(5); + R2_GLOBALS._walkRegions.disableRegion(6); + R2_GLOBALS._walkRegions.disableRegion(7); + + _cube.postInit(); + _cube.setVisage(1970); + _cube.setStrip(1); + if (R2_GLOBALS.getFlag(37)) + _cube.setFrame(3); + else + _cube.setFrame(1); + _cube.setPosition(Common::Point(193, 158)); + _cube.setDetails(1950, 3, 4, 5, 2, (SceneItem *) NULL); + + _pulsingLights.postInit(); + _pulsingLights.setVisage(1970); + _pulsingLights.setStrip(3); + _pulsingLights.animate(ANIM_MODE_2, NULL); + _pulsingLights._numFrames = 6; + _pulsingLights.setPosition(Common::Point(194, 158)); + _pulsingLights.fixPriority(159); + + _keypad.setDetails(Rect(188, 124, 199, 133), 1950, 27, 28, -1, 2, NULL); + + if (R2_INVENTORY.getObjectScene(R2_SAPPHIRE_BLUE) == 1950) { + _gem.postInit(); + _gem.setVisage(1970); + _gem.setStrip(1); + _gem.setFrame(2); + _gem.fixPriority(160); + } + + if (R2_GLOBALS.getFlag(37)) { + _gem.setPosition(Common::Point(192, 118)); + _gem.setDetails(1950, 9, 4, -1, 2, (SceneItem *) NULL); + } else { + _containmentField.postInit(); + _containmentField.setVisage(1970); + _containmentField.setStrip(4); + _containmentField._numFrames = 4; + _containmentField.animate(ANIM_MODE_8, 0, NULL); + _containmentField.setPosition(Common::Point(192, 121)); + _containmentField.fixPriority(159); + _containmentField.setDetails(1950, 6, 7, 8, 2, (SceneItem *) NULL); + + _gem.setPosition(Common::Point(192, 109)); + _gem.setDetails(1950, 9, 7, 8, 2, (SceneItem *) NULL); + } + + _scrolls.postInit(); + _scrolls.setVisage(1972); + _scrolls.setStrip(1); + _scrolls.setPosition(Common::Point(76, 94)); + _scrolls.fixPriority(25); + _scrolls.setDetails(1950, 30, -1, -1, 2, (SceneItem *) NULL); + if (R2_INVENTORY.getObjectScene(R2_ANCIENT_SCROLLS) == 2) + _scrolls.setFrame(2); + else + _scrolls.setFrame(1); + + _removeFlag = true; + } else if (_removeFlag) { + _cube.remove(); + _containmentField.remove(); + _gem.remove(); + _pulsingLights.remove(); + _scrolls.remove(); + + R2_GLOBALS._sceneItems.remove(&_background); + _background.setDetails(Rect(0, 0, 320, 200), 1950, 0, 1, 2, 2, NULL); + + _removeFlag = false; + } + + switch (R2_GLOBALS._flubMazeEntryDirection) { + case 0: + _sceneMode = 1950; + if (R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) == 0) + // The original uses CURSOR_ARROW. CURSOR_WALK is much more coherent + R2_GLOBALS._player.enableControl(CURSOR_WALK); + else + setAction(&_sequenceManager, this, 1950, &R2_GLOBALS._player, NULL); + + break; + case 1: { + _sceneMode = R2_GLOBALS._flubMazeEntryDirection; + R2_GLOBALS._player.setPosition(Common::Point(160, 213)); + Common::Point pt(160, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } + break; + case 2: + _sceneMode = R2_GLOBALS._flubMazeEntryDirection; + if (R2_GLOBALS.getFlag(36)) + setAction(&_sequenceManager, this, 1957, &R2_GLOBALS._player, NULL); + else + setAction(&_sequenceManager, this, 1974, &R2_GLOBALS._player, NULL); + break; + case 3: + // Entering from the left + if (!_vampireActive) { + _sceneMode = R2_GLOBALS._flubMazeEntryDirection; + R2_GLOBALS._player.setPosition(Common::Point(-20, 160)); + Common::Point pt(30, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } else { + _sceneMode = 18; + _eastExit._enabled = false; + _vampireDestPos = Common::Point(60, 152); + R2_GLOBALS._player.enableControl(CURSOR_USE); + R2_GLOBALS._player._canWalk = false; + + _vampire.setStrip(2); + NpcMover *mover = new NpcMover(); + _vampire.addMover(mover, &_vampireDestPos, this); + + R2_GLOBALS._player.setPosition(Common::Point(-20, 160)); + Common::Point pt2(30, 160); + NpcMover *mover2 = new NpcMover(); + R2_GLOBALS._player.addMover(mover2, &pt2, NULL); + } + break; + case 4: + _sceneMode = R2_GLOBALS._flubMazeEntryDirection; + if (!_upExitStyle) { + if (R2_GLOBALS.getFlag(36)) + setAction(&_sequenceManager, this, 1955, &R2_GLOBALS._player, NULL); + else + setAction(&_sequenceManager, this, 1972, &R2_GLOBALS._player, NULL); + } else { + if (R2_GLOBALS.getFlag(36)) + setAction(&_sequenceManager, this, 1954, &R2_GLOBALS._player, NULL); + else + setAction(&_sequenceManager, this, 1971, &R2_GLOBALS._player, NULL); + } + break; + case 5: { + _sceneMode = R2_GLOBALS._flubMazeEntryDirection; + R2_GLOBALS._player.setPosition(Common::Point(160, 127)); + Common::Point pt(160, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } + break; + case 6: + // Entering from the right + if (!_vampireActive) { + _sceneMode = R2_GLOBALS._flubMazeEntryDirection; + if (R2_GLOBALS._flubMazeArea == 1) { + setAction(&_sequenceManager, this, 1961, &R2_GLOBALS._player, NULL); + } else { + R2_GLOBALS._player.setPosition(Common::Point(340, 160)); + Common::Point pt(289, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } + } else { + _sceneMode = 17; + _westExit._enabled = false; + _vampireDestPos = Common::Point(259, 152); + + R2_GLOBALS._player.enableControl(CURSOR_USE); + R2_GLOBALS._player._canWalk = false; + + _vampire.setStrip(1); + NpcMover *mover = new NpcMover(); + _vampire.addMover(mover, &_vampireDestPos, this); + + R2_GLOBALS._player.setPosition(Common::Point(340, 160)); + Common::Point pt2(289, 160); + NpcMover *mover2 = new NpcMover(); + R2_GLOBALS._player.addMover(mover2, &pt2, NULL); + } + break; + default: + break; + } +} + +void Scene1950::doButtonPress(int indx) { + Scene1950 *scene = (Scene1950 *)R2_GLOBALS._sceneManager._scene; + R2_GLOBALS._player.disableControl(); + + int prevIndex = indx - 1; + if ((indx / 4) == (prevIndex / 4)) { + if (prevIndex < 0) + prevIndex = 3; + } else { + prevIndex += 4; + } + + assert(prevIndex >= 0 && prevIndex < 16); + if (!_KeypadWindow._buttons[prevIndex]._toggled) { + _KeypadWindow._buttons[prevIndex].setFrame(2); + _KeypadWindow._buttons[prevIndex]._toggled = true; + } else { + _KeypadWindow._buttons[prevIndex].setFrame(1); + _KeypadWindow._buttons[prevIndex]._toggled = false; + } + + prevIndex = indx + 1; + if ((indx / 4) == (prevIndex / 4)) { + if (prevIndex > 15) + prevIndex = 12; + } else { + prevIndex -= 4; + } + + assert(prevIndex >= 0 && prevIndex < 16); + if (!_KeypadWindow._buttons[prevIndex]._toggled) { + _KeypadWindow._buttons[prevIndex].setFrame(2); + _KeypadWindow._buttons[prevIndex]._toggled = true; + } else { + _KeypadWindow._buttons[prevIndex].setFrame(1); + _KeypadWindow._buttons[prevIndex]._toggled = false; + } + + prevIndex = indx - 4; + if (prevIndex < 0) + prevIndex += 16; + + assert(prevIndex >= 0 && prevIndex < 16); + if (!_KeypadWindow._buttons[prevIndex]._toggled) { + _KeypadWindow._buttons[prevIndex].setFrame(2); + _KeypadWindow._buttons[prevIndex]._toggled = true; + } else { + _KeypadWindow._buttons[prevIndex].setFrame(1); + _KeypadWindow._buttons[prevIndex]._toggled = false; + } + + prevIndex = indx + 4; + if (prevIndex > 15) + prevIndex -= 16; + + assert(prevIndex >= 0 && prevIndex < 16); + if (!_KeypadWindow._buttons[prevIndex]._toggled) { + _KeypadWindow._buttons[prevIndex].setFrame(2); + _KeypadWindow._buttons[prevIndex]._toggled = true; + } else { + _KeypadWindow._buttons[prevIndex].setFrame(1); + _KeypadWindow._buttons[prevIndex]._toggled = false; + } + + // Check whether all the buttons are highlighted + int cpt = 0; + for (prevIndex = 0; prevIndex < 16; prevIndex++) { + if (_KeypadWindow._buttons[prevIndex]._toggled) + ++cpt; + } + + if (cpt != 16) { + R2_GLOBALS._player.enableControl(); + R2_GLOBALS._player._canWalk = false; + } else { + R2_GLOBALS.setFlag(37); + _sceneMode = 24; + setAction(&_sequenceManager, scene, 1976, NULL); + } +} + +void Scene1950::postInit(SceneObjectList *OwnerList) { + _upExitStyle = false; + _removeFlag = false; + _vampireActive = false; + _vampireIndex = 0; + if (R2_GLOBALS._sceneManager._previousScene == 300) + R2_GLOBALS._flubMazeArea = 103; + + initArea(); + SceneExt::postInit(); + R2_GLOBALS._sound1.play(105); + + _northExit.setDetails(Rect(130, 46, 189, 135), SHADECURSOR_UP, 1950); + _northExit.setDest(Common::Point(160, 145)); + + _upExit.setDetails(Rect(208, 0, 255, 73), EXITCURSOR_N, 1950); + _upExit.setDest(Common::Point(200, 151)); + + _eastExit.setDetails(Rect(305, 95, 320, 147), EXITCURSOR_E, 1950); + _eastExit.setDest(Common::Point(312, 160)); + + _downExit.setDetails(Rect(208, 99, 255, 143), EXITCURSOR_S, 1950); + _downExit.setDest(Common::Point(200, 151)); + + _southExit.setDetails(Rect(113, 154, 206, 168), SHADECURSOR_DOWN, 1950); + _southExit.setDest(Common::Point(160, 165)); + + _westExit.setDetails(Rect(0, 95, 14, 147), EXITCURSOR_W, 1950); + _westExit.setDest(Common::Point(7, 160)); + + _shaftExit.setDetails(Rect(72, 54, 120, 128), EXITCURSOR_NW, 1950); + _shaftExit.setDest(Common::Point(120, 140)); + + _doorExit.setDetails(Rect(258, 60, 300, 145), EXITCURSOR_NE, 1950); + _doorExit.setDest(Common::Point(268, 149)); + + R2_GLOBALS._player.postInit(); + if ( (R2_INVENTORY.getObjectScene(R2_TANNER_MASK) == 0) && (R2_INVENTORY.getObjectScene(R2_PURE_GRAIN_ALCOHOL) == 0) + && (R2_INVENTORY.getObjectScene(R2_SOAKED_FACEMASK) == 0) && (!R2_GLOBALS.getFlag(36)) ) + R2_GLOBALS._player.setVisage(22); + else + R2_GLOBALS._player.setVisage(20); + + R2_GLOBALS._player._moveDiff = Common::Point(5, 3); + _background.setDetails(Rect(0, 0, 320, 200), 1950, 0, 1, 2, 1, NULL); + + enterArea(); +} + +void Scene1950::remove() { + R2_GLOBALS._sound1.stop(); + R2_GLOBALS._sound2.fadeOut2(NULL); + SceneExt::remove(); +} + +void Scene1950::signal() { + switch (_sceneMode) { + case 11: + R2_GLOBALS._flubMazeArea += 7; + initArea(); + enterArea(); + break; + case 12: + // Moving up a ladder within the Flub maze + R2_GLOBALS._flubMazeArea += 35; + initArea(); + enterArea(); + break; + case 1975: + SceneItem::display(1950, 21, SET_WIDTH, 280, SET_X, 160, SET_POS_MODE, 1, + SET_Y, 20, SET_EXT_BGCOLOR, 7, LIST_END); + // No break on purpose + case 13: + // Moving east within the Flub maze + ++R2_GLOBALS._flubMazeArea; + initArea(); + enterArea(); + break; + case 14: + // Moving down a ladder within the Flub maze + R2_GLOBALS._flubMazeArea -= 35; + initArea(); + enterArea(); + break; + case 15: + R2_GLOBALS._flubMazeArea -= 7; + initArea(); + enterArea(); + break; + case 16: + // Moving west within the Flub maze + // No break on purpose + case 1961: + --R2_GLOBALS._flubMazeArea; + initArea(); + enterArea(); + break; + case 17: { + _sceneMode = 13; + R2_GLOBALS._flubMazeEntryDirection = 3; + _vampireActive = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._player._canWalk = true; + R2_GLOBALS._player.setVisage(22); + R2_GLOBALS._player.animate(ANIM_MODE_9); + Common::Point pt(340, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + Common::Point pt2(289, 160); + NpcMover *mover2 = new NpcMover(); + _vampire.addMover(mover2, &pt2, NULL); + } + break; + case 18: { + _sceneMode = 16; + R2_GLOBALS._flubMazeEntryDirection = 6; + _vampireActive = false; + R2_GLOBALS._player.disableControl(CURSOR_WALK); + R2_GLOBALS._player._canWalk = true; + R2_GLOBALS._player.setVisage(22); + R2_GLOBALS._player.animate(ANIM_MODE_9); + Common::Point pt(-20, 160); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + Common::Point pt2(30, 160); + NpcMover *mover2 = new NpcMover(); + _vampire.addMover(mover2, &pt2, NULL); + } + break; + case 24: + _KeypadWindow.remove(); + _sceneMode = 1966; + _cube.setFrame(3); + setAction(&_sequenceManager, this, 1966, &_containmentField, &_gem, NULL); + break; + case 1951: + R2_GLOBALS._sound1.fadeOut2(NULL); + R2_GLOBALS._sceneManager.changeScene(1945); + break; + case 1958: + SceneItem::display(1950, 24, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + R2_GLOBALS._player.enableControl(CURSOR_WALK); + _doorExit._enabled = true; + break; + case 1959: + R2_INVENTORY.setObjectScene(R2_SOAKED_FACEMASK, 0); + R2_GLOBALS._player.enableControl(CURSOR_WALK); + _doorExit._enabled = true; + break; + case 1962: + // No break on purpose + case 1963: + R2_GLOBALS._player.enableControl(); + _KeypadWindow.setup2(1971, 1, 1, 160, 135); + break; + case 1964: + // No break on purpose + case 1965: + if (!R2_GLOBALS.getFlag(37)) + SceneItem::display(1950, 26, 0, 280, 1, 160, 9, 1, 2, 20, 7, 7, LIST_END); + + R2_GLOBALS._player.enableControl(); + break; + case 1966: + _containmentField.remove(); + if (R2_GLOBALS.getFlag(36)) { + _sceneMode = 1964; + setAction(&_sequenceManager, this, 1964, &R2_GLOBALS._player, NULL); + } else { + _sceneMode = 1965; + setAction(&_sequenceManager, this, 1965, &R2_GLOBALS._player, NULL); + } + _gem.setDetails(1950, 9, -1, -1, 2, (SceneItem *) NULL); + break; + case 1967: { + _sceneMode = 0; + R2_INVENTORY.setObjectScene(R2_SAPPHIRE_BLUE, 2); + _gem.remove(); + if (R2_GLOBALS.getFlag(36)) + R2_GLOBALS._player.setVisage(20); + else + R2_GLOBALS._player.setVisage(22); + + R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); + // This is a hack to work around a pathfinding issue. original destination is (218, 165) + Common::Point pt(128, 165); + NpcMover *mover = new NpcMover(); + R2_GLOBALS._player.addMover(mover, &pt, this); + } + break; + case 1968: + R2_GLOBALS._player.enableControl(); + R2_INVENTORY.setObjectScene(R2_ANCIENT_SCROLLS, 2); + _scrolls.setFrame(2); + if (R2_GLOBALS.getFlag(36)) + R2_GLOBALS._player.setVisage(20); + else + R2_GLOBALS._player.setVisage(22); + R2_GLOBALS._player.animate(ANIM_MODE_1, NULL); + break; + default: + R2_GLOBALS._player.enableControl(CURSOR_WALK); + break; + } +} + +void Scene1950::process(Event &event) { + if ( (event.eventType == EVENT_BUTTON_DOWN) + && (R2_GLOBALS._player._uiEnabled) + && (R2_GLOBALS._events.getCursor() == R2_SOAKED_FACEMASK) + && (R2_GLOBALS._player._bounds.contains(event.mousePos)) + && (R2_INVENTORY.getObjectScene(R2_SCRITH_KEY) == 0)) { + event.handled = true; + R2_GLOBALS._player.disableControl(); + _shaftExit._enabled = false; + _doorExit._enabled = false; + _sceneMode = 1959; + setAction(&_sequenceManager, this, 1959, &R2_GLOBALS._player, NULL); + } + + Scene::process(event); +} + +} // End of namespace Ringworld2 +} // End of namespace TsAGE diff --git a/engines/tsage/ringworld2/ringworld2_vampire.h b/engines/tsage/ringworld2/ringworld2_vampire.h new file mode 100644 index 0000000000..ca7aa34544 --- /dev/null +++ b/engines/tsage/ringworld2/ringworld2_vampire.h @@ -0,0 +1,179 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef TSAGE_RINGWORLD2_VAMPIRE_H +#define TSAGE_RINGWORLD2_VAMPIRE_H + +#include "tsage/events.h" +#include "tsage/core.h" +#include "tsage/scenes.h" +#include "tsage/globals.h" +#include "tsage/sound.h" +#include "tsage/ringworld2/ringworld2_logic.h" + +namespace TsAGE { + +namespace Ringworld2 { + +using namespace TsAGE; + +class Scene1950 : public SceneExt { + /* Windows */ + class KeypadWindow: public ModalWindow { + public: + class KeypadButton : public SceneActor { + public: + int _buttonIndex; + bool _pressed; + bool _toggled; + + KeypadButton(); + void synchronize(Serializer &s); + + void init(int indx); + virtual void process(Event &event); + virtual bool startAction(CursorType action, Event &event); + }; + + SceneActor _areaActor; + KeypadButton _buttons[16]; + + int _buttonIndex; + + KeypadWindow(); + virtual void synchronize(Serializer &s); + virtual void remove(); + virtual void setup2(int visage, int stripFrameNum, int frameNum, int posX, int posY); + virtual void setup3(int resNum, int lookLineNum, int talkLineNum, int useLineNum); + }; + + class Keypad : public NamedHotspot { + public: + virtual bool startAction(CursorType action, Event &event); + }; + + /* Actors */ + class Door : public SceneActor { + public: + virtual bool startAction(CursorType action, Event &event); + }; + class Scrolls : public SceneActor { + public: + virtual bool startAction(CursorType action, Event &event); + }; + class Gem : public SceneActor { + public: + virtual bool startAction(CursorType action, Event &event); + }; + class Vampire : public SceneActor { + public: + Common::Point _deadPosition; + int _deltaX; + int _deltaY; + int _vampireMode; + + Vampire(); + void synchronize(Serializer &s); + + virtual void signal(); + virtual bool startAction(CursorType action, Event &event); + }; + + /* Exits */ + class NorthExit : public SceneExit { + public: + virtual void changeScene(); + }; + class UpExit : public SceneExit { + public: + virtual void changeScene(); + }; + class EastExit : public SceneExit { + public: + virtual void changeScene(); + }; + class DownExit : public SceneExit { + public: + virtual void changeScene(); + }; + class SouthExit : public SceneExit { + public: + virtual void changeScene(); + }; + class WestExit : public SceneExit { + public: + virtual void changeScene(); + }; + class ShaftExit : public SceneExit { + public: + virtual void changeScene(); + }; + class DoorExit : public SceneExit { + public: + virtual void changeScene(); + }; +private: + void initArea(); + void enterArea(); + void doButtonPress(int indx); +public: + NamedHotspot _background; + Keypad _keypad; + SceneActor _southDoorway; + SceneObject _northDoorway; + Door _door; + Scrolls _scrolls; + SceneActor _containmentField; + Gem _gem; + SceneActor _cube; + SceneActor _pulsingLights; + Vampire _vampire; + KeypadWindow _KeypadWindow; + NorthExit _northExit; + UpExit _upExit; + EastExit _eastExit; + DownExit _downExit; + SouthExit _southExit; + WestExit _westExit; + ShaftExit _shaftExit; + DoorExit _doorExit; + SequenceManager _sequenceManager; + + bool _upExitStyle; + bool _removeFlag; + bool _vampireActive; + Common::Point _vampireDestPos; + int _vampireIndex; + + Scene1950(); + void synchronize(Serializer &s); + + virtual void postInit(SceneObjectList *OwnerList = NULL); + virtual void remove(); + virtual void signal(); + virtual void process(Event &event); +}; + +} // End of namespace Ringworld2 +} // End of namespace TsAGE + +#endif diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp index fee1bd752b..b95b614f09 100644 --- a/engines/tsage/sound.cpp +++ b/engines/tsage/sound.cpp @@ -164,7 +164,7 @@ Common::List<SoundDriverEntry> &SoundManager::buildDriverList(bool detectFlag) { sd._status = detectFlag ? SNDSTATUS_DETECTED : SNDSTATUS_SKIPPED; sd._field2 = 0; sd._field6 = 15000; - sd._shortDescription = "Adlib or SoundBlaster"; + sd._shortDescription = "AdLib or SoundBlaster"; sd._longDescription = "3812fm"; _availableDrivers.push_back(sd); diff --git a/engines/wintermute/POTFILES b/engines/wintermute/POTFILES new file mode 100644 index 0000000000..e9422415b2 --- /dev/null +++ b/engines/wintermute/POTFILES @@ -0,0 +1 @@ +engines/wintermute/detection.cpp diff --git a/engines/wintermute/base/base_engine.cpp b/engines/wintermute/base/base_engine.cpp index 7c2e9c8468..2166a3e070 100644 --- a/engines/wintermute/base/base_engine.cpp +++ b/engines/wintermute/base/base_engine.cpp @@ -61,10 +61,11 @@ BaseEngine::~BaseEngine() { delete _classReg; } -void BaseEngine::createInstance(const Common::String &targetName, const Common::String &gameId, Common::Language lang) { +void BaseEngine::createInstance(const Common::String &targetName, const Common::String &gameId, Common::Language lang, WMETargetExecutable targetExecutable) { instance()._targetName = targetName; instance()._gameId = gameId; instance()._language = lang; + instance()._targetExecutable = targetExecutable; instance().init(); } diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h index dd82cf9c29..0f4a6b0775 100644 --- a/engines/wintermute/base/base_engine.h +++ b/engines/wintermute/base/base_engine.h @@ -34,6 +34,8 @@ #include "common/random.h" #include "common/language.h" +#include "engines/wintermute/game_description.h" + namespace Wintermute { class BaseFileManager; @@ -53,10 +55,12 @@ class BaseEngine : public Common::Singleton<Wintermute::BaseEngine> { Common::RandomSource *_rnd; SystemClassRegistry *_classReg; Common::Language _language; + WMETargetExecutable _targetExecutable; public: BaseEngine(); ~BaseEngine(); - static void createInstance(const Common::String &targetName, const Common::String &gameId, Common::Language lang); + static void createInstance(const Common::String &targetName, const Common::String &gameId, Common::Language lang, WMETargetExecutable targetExecutable = LATEST_VERSION); + void setGameRef(BaseGame *gameRef) { _gameRef = gameRef; } Common::RandomSource *getRandomSource() { return _rnd; } @@ -73,6 +77,9 @@ public: const char *getGameTargetName() const { return _targetName.c_str(); } Common::String getGameId() const { return _gameId; } Common::Language getLanguage() const { return _language; } + WMETargetExecutable getTargetExecutable() const { + return _targetExecutable; + } }; } // End of namespace Wintermute diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp index d37a22e2a6..668053bb3a 100644 --- a/engines/wintermute/base/base_game.cpp +++ b/engines/wintermute/base/base_game.cpp @@ -3110,6 +3110,10 @@ bool BaseGame::persist(BasePersistenceManager *persistMgr) { persistMgr->transferUint32(TMEMBER(_autoSaveSlot)); persistMgr->transferBool(TMEMBER(_cursorHidden)); + if (persistMgr->checkVersion(1, 3, 1)) { + _settings->persist(persistMgr); + } + if (!persistMgr->getIsSaving()) { _quitting = false; } @@ -3892,6 +3896,11 @@ void BaseGame::expandStringByStringTable(char **str) const { _settings->expandStringByStringTable(str); } +////////////////////////////////////////////////////////////////////////// +void BaseGame::expandStringByStringTable(Common::String &str) const { + _settings->expandStringByStringTable(str); +} + char *BaseGame::getKeyFromStringTable(const char *str) const { return _settings->getKeyFromStringTable(str); } diff --git a/engines/wintermute/base/base_game.h b/engines/wintermute/base/base_game.h index cdbbff6c93..e535cc9618 100644 --- a/engines/wintermute/base/base_game.h +++ b/engines/wintermute/base/base_game.h @@ -123,6 +123,7 @@ public: inline BaseObject *getMainObject() { return _mainObject; } inline BaseFont *getSystemFont() { return _systemFont; } + inline BaseFont *getVideoFont() { return _videoFont; } bool initInput(); bool initLoop(); @@ -140,6 +141,7 @@ public: // String Table void expandStringByStringTable(char **str) const; + void expandStringByStringTable(Common::String &str) const; char *getKeyFromStringTable(const char *str) const; void LOG(bool res, const char *fmt, ...); diff --git a/engines/wintermute/base/base_game_settings.cpp b/engines/wintermute/base/base_game_settings.cpp index 3b54384cc7..996bada997 100644 --- a/engines/wintermute/base/base_game_settings.cpp +++ b/engines/wintermute/base/base_game_settings.cpp @@ -215,8 +215,17 @@ void BaseGameSettings::expandStringByStringTable(char **str) const { _stringTable->expand(str); } +////////////////////////////////////////////////////////////////////////// +void BaseGameSettings::expandStringByStringTable(Common::String &str) const { + _stringTable->expand(str); +} + char *BaseGameSettings::getKeyFromStringTable(const char *str) const { return _stringTable->getKey(str); } +bool BaseGameSettings::persist(BasePersistenceManager *persistMgr) { + return _stringTable->persist(persistMgr); +} + } // End of namespace Wintermute diff --git a/engines/wintermute/base/base_game_settings.h b/engines/wintermute/base/base_game_settings.h index fe0e9907e6..15afb06450 100644 --- a/engines/wintermute/base/base_game_settings.h +++ b/engines/wintermute/base/base_game_settings.h @@ -34,6 +34,7 @@ namespace Wintermute { class BaseStringTable; class BaseGame; +class BasePersistenceManager; class BaseGameSettings { public: const char *getGameFile() const { return (_gameFile ? _gameFile : "default.game"); } @@ -45,7 +46,10 @@ public: bool loadSettings(const char *filename); bool loadStringTable(const char *filename, bool clearOld); void expandStringByStringTable(char **str) const; + void expandStringByStringTable(Common::String &str) const; char *getKeyFromStringTable(const char *str) const; + + bool persist(BasePersistenceManager *persistMgr); private: char *_gameFile; int _resWidth; diff --git a/engines/wintermute/base/base_keyboard_state.cpp b/engines/wintermute/base/base_keyboard_state.cpp index 61087c5836..0babc07586 100644 --- a/engines/wintermute/base/base_keyboard_state.cpp +++ b/engines/wintermute/base/base_keyboard_state.cpp @@ -278,10 +278,24 @@ uint32 BaseKeyboardState::keyCodeToVKey(Common::Event *event) { enum VKeyCodes { kVkEscape = 27, kVkSpace = 32, + kVkHome = 36, kVkLeft = 37, kVkUp = 38, kVkRight = 39, - kVkDown = 40 + kVkDown = 40, + + kVkF1 = 112, + kVkF2 = 113, + kVkF3 = 114, + kVkF4 = 115, + kVkF5 = 116, + kVkF6 = 117, + kVkF7 = 118, + kVkF8 = 119, + kVkF9 = 120, + kVkF10 = 121, + kVkF11 = 122, + kVkF12 = 123 }; ////////////////////////////////////////////////////////////////////////// @@ -290,22 +304,42 @@ Common::KeyCode BaseKeyboardState::vKeyToKeyCode(uint32 vkey) { switch (vkey) { case kVkEscape: return Common::KEYCODE_ESCAPE; - break; case kVkSpace: return Common::KEYCODE_SPACE; - break; + case kVkHome: + return Common::KEYCODE_HOME; case kVkLeft: return Common::KEYCODE_LEFT; - break; case kVkRight: return Common::KEYCODE_RIGHT; - break; case kVkUp: return Common::KEYCODE_UP; - break; case kVkDown: return Common::KEYCODE_DOWN; - break; + case kVkF1: + return Common::KEYCODE_F1; + case kVkF2: + return Common::KEYCODE_F2; + case kVkF3: + return Common::KEYCODE_F3; + case kVkF4: + return Common::KEYCODE_F4; + case kVkF5: + return Common::KEYCODE_F5; + case kVkF6: + return Common::KEYCODE_F6; + case kVkF7: + return Common::KEYCODE_F7; + case kVkF8: + return Common::KEYCODE_F8; + case kVkF9: + return Common::KEYCODE_F9; + case kVkF10: + return Common::KEYCODE_F10; + case kVkF11: + return Common::KEYCODE_F11; + case kVkF12: + return Common::KEYCODE_F12; default: warning("Unknown VKEY: %d", vkey); return (Common::KeyCode)vkey; diff --git a/engines/wintermute/base/base_sprite.cpp b/engines/wintermute/base/base_sprite.cpp index 04060bff32..09e138a1fd 100644 --- a/engines/wintermute/base/base_sprite.cpp +++ b/engines/wintermute/base/base_sprite.cpp @@ -41,6 +41,7 @@ #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/game_description.h" namespace Wintermute { @@ -347,9 +348,17 @@ void BaseSprite::reset() { } else { _currentFrame = -1; } - - killAllSounds(); - + if (BaseEngine::instance().getTargetExecutable() >= WME_1_8_6) { + /* + * This was added in WME 1.8.6 + * + * 5MA and possibly other games ship with pre-1.8.6 WME, and + * depends (e.g.: menu sounds, etc) on this not being triggered. + * + * See bug #6647 + */ + killAllSounds(); + } _lastFrameTime = 0; _finished = false; _moveX = _moveY = 0; diff --git a/engines/wintermute/base/base_string_table.cpp b/engines/wintermute/base/base_string_table.cpp index 9adbbdf7be..4c750ebc93 100644 --- a/engines/wintermute/base/base_string_table.cpp +++ b/engines/wintermute/base/base_string_table.cpp @@ -147,6 +147,15 @@ void BaseStringTable::expand(char **str) const { } } +////////////////////////////////////////////////////////////////////////// +void BaseStringTable::expand(Common::String &str) const { + char *tmp = new char[str.size()+1]; + strcpy(tmp, str.c_str()); + expand(&tmp); + str = tmp; + delete[] tmp; +} + ////////////////////////////////////////////////////////////////////////// const char *BaseStringTable::expandStatic(const char *string) const { @@ -189,8 +198,10 @@ bool BaseStringTable::loadFile(const char *filename, bool clearOld) { BaseEngine::LOG(0, "Loading string table..."); if (clearOld) { + _filenames.clear(); _strings.clear(); } + _filenames.push_back(Common::String(filename)); uint32 size; byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size); @@ -253,4 +264,27 @@ bool BaseStringTable::loadFile(const char *filename, bool clearOld) { return STATUS_OK; } +bool BaseStringTable::persist(BasePersistenceManager *persistMgr) { + // Do nothing if the save game is too old. + if (!persistMgr->checkVersion(1, 3, 1)) { + return true; + } + uint32 numFiles = _filenames.size(); + persistMgr->transferUint32("NumFiles", &numFiles); + if (persistMgr->getIsSaving()) { + for (uint i = 0; i < numFiles; i++) { + persistMgr->transferString("Filename", &_filenames[i]); + } + } else { + _strings.clear(); + _filenames.clear(); + for (uint i = 0; i < numFiles; i++) { + Common::String filename = ""; + persistMgr->transferString("Filename", &filename); + loadFile(filename.c_str(), false); + } + } + return true; +} + } // End of namespace Wintermute diff --git a/engines/wintermute/base/base_string_table.h b/engines/wintermute/base/base_string_table.h index 9e915a1ad9..cfa3eeb226 100644 --- a/engines/wintermute/base/base_string_table.h +++ b/engines/wintermute/base/base_string_table.h @@ -35,17 +35,22 @@ namespace Wintermute { +class BasePersistenceManager; + class BaseStringTable : public BaseClass { public: bool loadFile(const char *filename, bool deleteAll = true); void expand(char **str) const; + void expand(Common::String &str) const; const char *expandStatic(const char *string) const; bool addString(const char *key, const char *val, bool reportDuplicities = true); BaseStringTable(BaseGame *inGame); virtual ~BaseStringTable(); char *getKey(const char *str) const; + bool persist(BasePersistenceManager *persistMgr); private: Common::HashMap<Common::String, Common::String> _strings; + Common::Array<Common::String> _filenames; typedef Common::HashMap<Common::String, Common::String>::const_iterator StringsIter; }; diff --git a/engines/wintermute/base/gfx/base_renderer.h b/engines/wintermute/base/gfx/base_renderer.h index 42ff2cb9e1..6b1a4f97f4 100644 --- a/engines/wintermute/base/gfx/base_renderer.h +++ b/engines/wintermute/base/gfx/base_renderer.h @@ -72,7 +72,6 @@ public: * Fade the screen to black * * @param alpha amount to fade by (alpha value of black) - * @return */ virtual void fade(uint16 alpha) = 0; /** diff --git a/engines/wintermute/base/sound/base_sound.cpp b/engines/wintermute/base/sound/base_sound.cpp index fa452cc0d6..b5b12d55f9 100644 --- a/engines/wintermute/base/sound/base_sound.cpp +++ b/engines/wintermute/base/sound/base_sound.cpp @@ -89,7 +89,7 @@ bool BaseSound::setSoundSimple() { _sound->setLooping(_soundLooping); _sound->setPrivateVolume(_soundPrivateVolume); _sound->setLoopStart(_soundLoopStart); - _sound->_freezePaused = _soundFreezePaused; + _sound->setFreezePaused(_soundFreezePaused); if (_soundPlaying) { return _sound->resume(); } else { @@ -130,7 +130,7 @@ bool BaseSound::pause(bool freezePaused) { if (_sound) { _soundPaused = true; if (freezePaused) { - _sound->_freezePaused = true; + _sound->setFreezePaused(true); } return _sound->pause(); } else { @@ -150,13 +150,13 @@ bool BaseSound::resume() { bool BaseSound::persist(BasePersistenceManager *persistMgr) { if (persistMgr->getIsSaving() && _sound) { _soundPlaying = _sound->isPlaying(); - _soundLooping = _sound->_looping; - _soundPrivateVolume = _sound->_privateVolume; + _soundLooping = _sound->isLooping(); + _soundPrivateVolume = _sound->getPrivateVolume(); if (_soundPlaying) { _soundPosition = _sound->getPosition(); } - _soundLoopStart = _sound->_loopStart; - _soundFreezePaused = _sound->_freezePaused; + _soundLoopStart = _sound->getLoopStart(); + _soundFreezePaused = _sound->isFreezePaused(); } if (persistMgr->getIsSaving()) { @@ -232,7 +232,7 @@ bool BaseSound::setPrivateVolume(int volume) { if (!_sound) { return STATUS_FAILED; } else { - _sound->_privateVolume = volume; + _sound->setPrivateVolume(volume); return STATUS_OK; } } @@ -241,7 +241,7 @@ int BaseSound::getVolumePercent() { if (!_sound) { return 0; } else { - return _sound->_privateVolume * 100 / 255; + return _sound->getPrivateVolume() * 100 / 255; } } @@ -249,7 +249,7 @@ int BaseSound::getVolume() { if (!_sound) { return 0; } else { - return _sound->_privateVolume; + return _sound->getPrivateVolume(); } } diff --git a/engines/wintermute/base/sound/base_sound_buffer.cpp b/engines/wintermute/base/sound/base_sound_buffer.cpp index 7ec68ea752..5fdac12cef 100644 --- a/engines/wintermute/base/sound/base_sound_buffer.cpp +++ b/engines/wintermute/base/sound/base_sound_buffer.cpp @@ -143,8 +143,13 @@ bool BaseSoundBuffer::play(bool looping, uint32 startSample) { _stream->seek(startSample); _handle = new Audio::SoundHandle; if (_looping) { - Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); - g_system->getMixer()->playStream(_type, _handle, loopStream, -1, _volume, _pan, DisposeAfterUse::YES); + if (_loopStart != 0) { + Audio::AudioStream *loopStream = new Audio::SubLoopingAudioStream(_stream, 0, Audio::Timestamp(_loopStart, _stream->getRate()), _stream->getLength(), DisposeAfterUse::NO); + g_system->getMixer()->playStream(_type, _handle, loopStream, -1, _volume, _pan, DisposeAfterUse::YES); + } else { + Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + g_system->getMixer()->playStream(_type, _handle, loopStream, -1, _volume, _pan, DisposeAfterUse::YES); + } } else { g_system->getMixer()->playStream(_type, _handle, _stream, -1, _volume, _pan, DisposeAfterUse::NO); } @@ -296,4 +301,24 @@ bool BaseSoundBuffer::applyFX(TSFXType type, float param1, float param2, float p return STATUS_OK; } +int32 BaseSoundBuffer::getPrivateVolume() const { + return _privateVolume; +} + +bool BaseSoundBuffer::isLooping() const { + return _looping; +} + +bool BaseSoundBuffer::isFreezePaused() const { + return _freezePaused; +} + +void BaseSoundBuffer::setFreezePaused(bool freezePaused) { + _freezePaused = freezePaused; +} + +Audio::Mixer::SoundType BaseSoundBuffer::getType() const { + return _type; +} + } // End of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound_buffer.h b/engines/wintermute/base/sound/base_sound_buffer.h index 94bc8dc6ad..b3f3046674 100644 --- a/engines/wintermute/base/sound/base_sound_buffer.h +++ b/engines/wintermute/base/sound/base_sound_buffer.h @@ -71,23 +71,26 @@ public: void updateVolume(); void setType(Audio::Mixer::SoundType Type); + Audio::Mixer::SoundType getType() const; bool loadFromFile(const Common::String &filename, bool forceReload = false); void setStreaming(bool streamed, uint32 numBlocks = 0, uint32 blockSize = 0); bool applyFX(TSFXType type, float param1, float param2, float param3, float param4); - + int32 getPrivateVolume() const; + void setFreezePaused(bool freezePaused); + bool isFreezePaused() const; + bool isLooping() const; //HSTREAM _stream; //HSYNC _sync; + +private: + Audio::Mixer::SoundType _type; Audio::SeekableAudioStream *_stream; Audio::SoundHandle *_handle; - bool _freezePaused; - uint32 _loopStart; - Audio::Mixer::SoundType _type; bool _looping; - int32 _privateVolume; -private: + uint32 _loopStart; uint32 _startPos; Common::String _filename; bool _streamed; diff --git a/engines/wintermute/base/sound/base_sound_manager.cpp b/engines/wintermute/base/sound/base_sound_manager.cpp index 41cfe5ea62..f1e0c3b1f9 100644 --- a/engines/wintermute/base/sound/base_sound_manager.cpp +++ b/engines/wintermute/base/sound/base_sound_manager.cpp @@ -254,9 +254,9 @@ byte BaseSoundMgr::getMasterVolume() { bool BaseSoundMgr::pauseAll(bool includingMusic) { for (uint32 i = 0; i < _sounds.size(); i++) { - if (_sounds[i]->isPlaying() && (_sounds[i]->_type != Audio::Mixer::kMusicSoundType || includingMusic)) { + if (_sounds[i]->isPlaying() && (_sounds[i]->getType() != Audio::Mixer::kMusicSoundType || includingMusic)) { _sounds[i]->pause(); - _sounds[i]->_freezePaused = true; + _sounds[i]->setFreezePaused(true); } } @@ -268,9 +268,9 @@ bool BaseSoundMgr::pauseAll(bool includingMusic) { bool BaseSoundMgr::resumeAll() { for (uint32 i = 0; i < _sounds.size(); i++) { - if (_sounds[i]->_freezePaused) { + if (_sounds[i]->isFreezePaused()) { _sounds[i]->resume(); - _sounds[i]->_freezePaused = false; + _sounds[i]->setFreezePaused(false); } } diff --git a/engines/wintermute/dcgf.h b/engines/wintermute/dcgf.h index 78503b8c3b..c919180e45 100644 --- a/engines/wintermute/dcgf.h +++ b/engines/wintermute/dcgf.h @@ -32,8 +32,8 @@ ////////////////////////////////////////////////////////////////////////// #define DCGF_VER_MAJOR 1 -#define DCGF_VER_MINOR 2 -#define DCGF_VER_BUILD 2 +#define DCGF_VER_MINOR 3 +#define DCGF_VER_BUILD 1 #define DCGF_VER_SUFFIX "ScummVM" #define DCGF_VER_BETA true diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp index a659c434d0..aca682ae99 100644 --- a/engines/wintermute/detection.cpp +++ b/engines/wintermute/detection.cpp @@ -74,7 +74,7 @@ static const char *directoryGlobs[] = { class WintermuteMetaEngine : public AdvancedMetaEngine { public: - WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(ADGameDescription), Wintermute::wintermuteGames, gameGuiOptions) { + WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(WMEGameDescription), Wintermute::wintermuteGames, gameGuiOptions) { _singleid = "wintermute"; _guioptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_SHOW_FPS); _maxScanDepth = 2; @@ -127,8 +127,8 @@ public: virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { assert(syst); assert(engine); - - *engine = new Wintermute::WintermuteEngine(syst, desc); + const WMEGameDescription *gd = (const WMEGameDescription *)desc; + *engine = new Wintermute::WintermuteEngine(syst, gd); return true; } diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h index 8206ca9643..4e3320159a 100644 --- a/engines/wintermute/detection_tables.h +++ b/engines/wintermute/detection_tables.h @@ -90,1342 +90,458 @@ static const PlainGameDescriptor wintermuteGames[] = { {0, 0} }; -static const ADGameDescription gameDescriptions[] = { +// Duplicates WME_ENTRY1s, for consistency +#define WME_ENTRY1s(f1, h1, s1) { {f1, 0, h1, s1}, AD_LISTEND } +#define WME_ENTRY2s(f1, h1, s1, f2, h2, s2) { {f1, 0, h1, s1}, {f2, 0, h2, s2}, AD_LISTEND } +#define WME_ENTRY3s(f1, h1, s1, f2, h2, s2, f3, h3, s3) { {f1, 0, h1, s1}, {f2, 0, h2, s2}, {f3, 0, h3, s3}, AD_LISTEND } + +#define WME_PLATENTRY(shortName, extraName, hashEntry, lang, plat, status, version) \ + { \ + { \ + shortName, \ + extraName, \ + hashEntry, \ + lang, \ + plat, \ + status, \ + GUIO0(), \ + }, \ + version \ + } + +// Convenience variant, as most of the games are Windows-games +#define WME_WINENTRY(shortName, extraName, hashEntry, lang, status, version) \ + { \ + { \ + shortName, \ + extraName, \ + hashEntry, \ + lang, \ + Common::kPlatformWindows, \ + status, \ + GUIO0(), \ + }, \ + version \ + } + +/* To add new entries: + * Make sure you have a target name defined at the top of the file + * + * If the game has only one language, and can be detected using only one file, + * then use WME_WINENTRY, with WME_ENTRY1s as exemplified below. + * + * If the game has more than one language, and the main data file is common across + * the versions, then you should use WME_WINENTRY with WME_ENTRY2s/WME_ENTRY3s, with + * the language file as the first hit, and the data file as the second. (Make sure to + * NOT create a WME_ENTRY1s matching the same data file as the 2/3 file match) + */ + +static const WMEGameDescription gameDescriptions[] = { // Five Lethal Demons - { - "5ld", - "", - AD_ENTRY1s("data.dcp", "1037a77cbd001e0644898addc022322c", 15407750), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("5ld", "", + WME_ENTRY1s("data.dcp", "1037a77cbd001e0644898addc022322c", 15407750), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Five Magical Amulets - { - "5ma", - "", - AD_ENTRY1s("data.dcp", "0134e92bcd5fd2837df3971087e96067", 163316498), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("5ma", "", + WME_ENTRY1s("data.dcp", "0134e92bcd5fd2837df3971087e96067", 163316498), Common::EN_ANY, ADGF_UNSTABLE, WME_1_7_0), // Actual Destination - { - "actualdest", - "", - AD_ENTRY1s("data.dcp", "6926f44b26f21ceb1d840eaab9aeb510", 9081740), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("actualdest", "", + WME_ENTRY1s("data.dcp", "6926f44b26f21ceb1d840eaab9aeb510", 9081740), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Boredom of Agustin Cordes - { - "agustin", - "", - AD_ENTRY1s("data.dcp", "abb79c16c9b92e9b06525a4c7c3f5861", 2461949), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("agustin", "", + WME_ENTRY1s("data.dcp", "abb79c16c9b92e9b06525a4c7c3f5861", 2461949), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Beyond the Threshold - { - "bthreshold", - "", - AD_ENTRY1s("data.dcp", "d49bf9ccb2e74507447c82d6ad3e2bc4", 12773712), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("bthreshold", "", + WME_ENTRY1s("data.dcp", "d49bf9ccb2e74507447c82d6ad3e2bc4", 12773712), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Bickadoodle - { - "bickadoodle", - "", - AD_ENTRY1s("data.dcp", "84db4d1594cac95e25614985775d10a8", 35303844), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("bickadoodle", "", + WME_ENTRY1s("data.dcp", "84db4d1594cac95e25614985775d10a8", 35303844), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Bickadoodle (Ver 1.1) - { - "bickadoodle", - "Version 1.1", - AD_ENTRY1s("data.dcp", "8bb52ac9a9ee129c5059e8e808b669d7", 35337760), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("bickadoodle", "Version 1.1", + WME_ENTRY1s("data.dcp", "8bb52ac9a9ee129c5059e8e808b669d7", 35337760), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), + // Bickadoodle (Ver 1.2) + WME_WINENTRY("bickadoodle", "Version 1.2", + WME_ENTRY1s("data.dcp", "1796a48f3ed72dd785ce93334ab883cc", 35337760), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Bickadoodle (download from http://aethericgames.com/games/bickadoodle/download-bickadoodle/) - { - "bickadoodle", - "", - AD_ENTRY1s("data.dcp", "1584d83577c32add0fce27fae91141a2", 35337728), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("bickadoodle", "", + WME_ENTRY1s("data.dcp", "1584d83577c32add0fce27fae91141a2", 35337728), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Book of Gron Part One - { - "bookofgron", - "", - AD_ENTRY1s("data.dcp", "e61b2ebee044a82fa0f8ca0fce2c8946", 83129531), - Common::RU_RUS, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("bookofgron", "", + WME_ENTRY1s("data.dcp", "e61b2ebee044a82fa0f8ca0fce2c8946", 83129531), Common::RU_RUS, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 4 - East Side Story (Demo) - { - "carolreed4", - "Demo", - AD_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("carolreed4", "Demo", + WME_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 4 - East Side Story - { - "carolreed4", - "", - AD_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("carolreed4", "", + WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 5 - The Colour of Murder - { - "carolreed5", - "", - AD_ENTRY1s("data.dcp", "3fcfca44209545d0e26774156427b494", 603660415), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("carolreed5", "", + WME_ENTRY1s("data.dcp", "3fcfca44209545d0e26774156427b494", 603660415), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 6 - Black Circle - { - "carolreed6", - "", - AD_ENTRY1s("data.dcp", "0e4c532beecf23d85012168753f41189", 456258147), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("carolreed6", "", + WME_ENTRY1s("data.dcp", "0e4c532beecf23d85012168753f41189", 456258147), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 7 - Blue Madonna (Demo) - { - "carolreed7", - "Demo", - AD_ENTRY1s("data.dcp", "0372ad0c775266f6355e9e8ae397a2f1", 103719442), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("carolreed7", "Demo", + WME_ENTRY1s("data.dcp", "0372ad0c775266f6355e9e8ae397a2f1", 103719442), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 7 - Blue Madonna - { - "carolreed7", - "", - AD_ENTRY1s("data.dcp", "24e3db3e2fabfc956713796d87a3efb0", 495471147), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("carolreed7", "", + WME_ENTRY1s("data.dcp", "24e3db3e2fabfc956713796d87a3efb0", 495471147), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 8 - Amber's Blood - { - "carolreed8", - "", - AD_ENTRY1s("data.dcp", "859d16b0d5b9b255e470cbded2c6cedc", 502714557), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("carolreed8", "", + WME_ENTRY1s("data.dcp", "859d16b0d5b9b255e470cbded2c6cedc", 502714557), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 9 - Cold Case Summer - { - "carolreed9", - "", - AD_ENTRY1s("data.dcp", "2b343b48a7aee508d728a546b414a255", 620005266), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("carolreed9", "", + WME_ENTRY1s("data.dcp", "2b343b48a7aee508d728a546b414a255", 620005266), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Chivalry is Not Dead - { - "chivalry", - "", - AD_ENTRY1s("data.dcp", "ebd0915d9a12df5224be22f53bb23eb6", 7278306), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_TESTING, - GUIO0() - }, + WME_WINENTRY("chivalry", "", + WME_ENTRY1s("data.dcp", "ebd0915d9a12df5224be22f53bb23eb6", 7278306), Common::EN_ANY, ADGF_TESTING, LATEST_VERSION), // Chivalry is Not Dead (Version from deirdrakai.com) - { - "chivalry", - "", - AD_ENTRY1s("data.dcp", "ae6d91b9517f4d2851a8ad94c96951c8", 7278302), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_TESTING, - GUIO0() - }, + WME_WINENTRY("chivalry", "", + WME_ENTRY1s("data.dcp", "ae6d91b9517f4d2851a8ad94c96951c8", 7278302), Common::EN_ANY, ADGF_TESTING, LATEST_VERSION), // Conspiracao Dumont - { - "conspiracao", - "", - AD_ENTRY1s("ConspiracaoDumont.exe", "106f3f2c8f18bb5ffffeed634ace256c", 32908032), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("conspiracao", "", + WME_ENTRY1s("ConspiracaoDumont.exe", "106f3f2c8f18bb5ffffeed634ace256c", 32908032), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Corrosion: Cold Winter Waiting - { - "corrosion", - "", - AD_ENTRY1s("data.dcp", "ae885b1a8faa0b27f43c0e8f0df02fc9", 525931618), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_TESTING, - GUIO0() - }, + WME_WINENTRY("corrosion", "", + WME_ENTRY1s("data.dcp", "ae885b1a8faa0b27f43c0e8f0df02fc9", 525931618), Common::EN_ANY, ADGF_TESTING, LATEST_VERSION), // Dead City (Czech) - { - "deadcity", - "", - { - // The Czech data are in data.dcp, so in this case we'll have to - // just detect the english version twice, to give the user a choice. - {"english.dcp", 0, "c591046d6de7e381d76f70e0787b2b1f", 415935}, - {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, - AD_LISTEND - }, - Common::CZ_CZE, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + // The Czech data are in data.dcp, so in this case we'll have to + // just detect the english version twice, to give the user a choice. + WME_WINENTRY("deadcity", "", + WME_ENTRY2s("english.dcp", "c591046d6de7e381d76f70e0787b2b1f", 415935, + "data.dcp", "7ebfd50d1a22370ed7b079bcaa631d62", 9070205), Common::CZ_CZE, ADGF_UNSTABLE, LATEST_VERSION), // Dead City (English) - { - "deadcity", - "", - { - {"english.dcp", 0, "c591046d6de7e381d76f70e0787b2b1f", 415935}, - {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("deadcity", "", + WME_ENTRY2s("english.dcp", "c591046d6de7e381d76f70e0787b2b1f", 415935, + "data.dcp", "7ebfd50d1a22370ed7b079bcaa631d62", 9070205), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Dead City (Italian) - { - "deadcity", - "", - { - {"italian.dcp", 0, "92d8efb94436bec7bd1b7fe0b548192e", 454037}, - {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, - AD_LISTEND - }, - Common::IT_ITA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("deadcity", "", + WME_ENTRY2s("italian.dcp", "92d8efb94436bec7bd1b7fe0b548192e", 454037, + "data.dcp", "7ebfd50d1a22370ed7b079bcaa631d62", 9070205), Common::IT_ITA, ADGF_UNSTABLE, LATEST_VERSION), // Dead City (Russian) - { - "deadcity", - "", - { - {"russian.dcp", 0, "a0ae71e9e1185596fffb07ad2c951eb9", 653317}, - {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, - AD_LISTEND - }, - Common::RU_RUS, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("deadcity", "", + WME_ENTRY2s("russian.dcp", "a0ae71e9e1185596fffb07ad2c951eb9", 653317, + "data.dcp", "7ebfd50d1a22370ed7b079bcaa631d62", 9070205), Common::RU_RUS, ADGF_UNSTABLE, LATEST_VERSION), // Dirty Split (Czech) - { - "dirtysplit", - "", - { - {"czech.dcp", 0, "08a71446467cf8f9444cfea446b46ad6", 127697934}, - {"data.dcp", 0, "8b4b81b718bf65f30a67fc0b1e329eb5", 88577623}, - }, - Common::CZ_CZE, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dirtysplit", "", + WME_ENTRY2s("czech.dcp", "08a71446467cf8f9444cfea446b46ad6", 127697934, + "data.dcp", "8b4b81b718bf65f30a67fc0b1e329eb5", 88577623), Common::CZ_CZE, ADGF_UNSTABLE, LATEST_VERSION), // Dirty Split (English) - { - "dirtysplit", - "", - AD_ENTRY1s("data.dcp", "8f3dae199361ece0f59fb20cfff6eed3", 88577621), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dirtysplit", "", + WME_ENTRY1s("data.dcp", "8f3dae199361ece0f59fb20cfff6eed3", 88577621), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Dirty Split (French) - { - "dirtysplit", - "", - { - {"french.dcp", 0, "a0508dedebd0fe478d0158fa4c2a1136", 125534323}, - {"data.dcp", 0, "e6d70c7f5d181b761cfcf974adf9186a", 88577623}, - AD_LISTEND - }, - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dirtysplit", "", + WME_ENTRY2s("french.dcp", "a0508dedebd0fe478d0158fa4c2a1136", 125534323, + "data.dcp", "e6d70c7f5d181b761cfcf974adf9186a", 88577623), Common::FR_FRA, ADGF_UNSTABLE, LATEST_VERSION), // Dirty Split (German) - { - "dirtysplit", - "", - AD_ENTRY1s("data.dcp", "139d8a25579e969f8b37d20e6e3de5f9", 92668291), - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dirtysplit", "", + WME_ENTRY1s("data.dcp", "139d8a25579e969f8b37d20e6e3de5f9", 92668291), Common::DE_DEU, ADGF_UNSTABLE, LATEST_VERSION), // Dirty Split (Italian) - { - "dirtysplit", - "", - { - {"italian.dcp", 0, "8108807fbd8af70be1ec452d0fd1131b", 125513726}, - {"data.dcp", 0, "35a150e22af274185883fdbb142c6fb1", 88577623}, - }, - Common::IT_ITA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dirtysplit", "", + WME_ENTRY2s("italian.dcp", "8108807fbd8af70be1ec452d0fd1131b", 125513726, + "data.dcp", "35a150e22af274185883fdbb142c6fb1", 88577623), Common::IT_ITA, ADGF_UNSTABLE, LATEST_VERSION), // Dirty Split (Spanish) - { - "dirtysplit", - "", - { - {"spanish.dcp", 0, "b3982c0a5e85b42e1e38240fef004aa4", 164428596}, - {"data.dcp", 0, "63766d6c68b9f00b632ea1736fc8a95c", 88577621}, - }, - Common::ES_ESP, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dirtysplit", "", + WME_ENTRY2s("spanish.dcp", "b3982c0a5e85b42e1e38240fef004aa4", 164428596, + "data.dcp", "63766d6c68b9f00b632ea1736fc8a95c", 88577621), Common::ES_ESP, ADGF_UNSTABLE, LATEST_VERSION), // Des Reves Elastiques Avec Mille Insectes Nommes Georges - { - "dreaming", - "", - AD_ENTRY1s("data.dcp", "4af26d97ea063fc1277ce30ae431de90", 8804073), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dreaming", "", + WME_ENTRY1s("data.dcp", "4af26d97ea063fc1277ce30ae431de90", 8804073), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Dreamscape - { - "dreamscape", - "", - AD_ENTRY1s("data.dcp", "7a5752ed4446c862be9f02d7932acf54", 17034377), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("dreamscape", "", + WME_ENTRY1s("data.dcp", "7a5752ed4446c862be9f02d7932acf54", 17034377), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Escape from the Mansion - { - "escapemansion", - "Beta 1", - AD_ENTRY1s("data.dcp", "d8e348b2312cc36a929cad75f12e0b3a", 21452380), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("escapemansion", "Beta 1", + WME_ENTRY1s("data.dcp", "d8e348b2312cc36a929cad75f12e0b3a", 21452380), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Escape from the Mansion - { - "escapemansion", - "Beta 2", - AD_ENTRY1s("data.dcp", "ded5fa6c5f2afdaf2cafb53e52cd3dd8", 21455763), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("escapemansion", "Beta 2", + WME_ENTRY1s("data.dcp", "ded5fa6c5f2afdaf2cafb53e52cd3dd8", 21455763), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Escape from the Mansion - { - "escapemansion", - "1.3", - AD_ENTRY1s("data.dcp", "1e5d231b56c8a228cd15cb690f50253e", 29261972), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("escapemansion", "1.3", + WME_ENTRY1s("data.dcp", "1e5d231b56c8a228cd15cb690f50253e", 29261972), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Four - { - "four", - "", - AD_ENTRY1s("data.dcp", "ec05cd5e37c9a524053b8859635a4234", 62599855), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("four", "", + WME_ENTRY1s("data.dcp", "ec05cd5e37c9a524053b8859635a4234", 62599855), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Framed - { - "framed", - "", - AD_ENTRY1s("data.dcp", "e7259fb36f2c6f9f28242291e0c3de98", 34690568), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("framed", "", + WME_ENTRY1s("data.dcp", "e7259fb36f2c6f9f28242291e0c3de98", 34690568), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Ghost in the Sheet - { - "ghostsheet", - "", - { - {"english.dcp", 0, "e6d0aad2c89996bcabe416105a3d6d3a", 12221017}, - {"data.dcp", 0, "b2f8b05328e4881e15e98e845b63f451", 168003}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("ghostsheet", "", + WME_ENTRY2s("english.dcp", "e6d0aad2c89996bcabe416105a3d6d3a", 12221017, + "data.dcp", "b2f8b05328e4881e15e98e845b63f451", 168003), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Ghost in the Sheet (Demo) - { - "ghostsheet", - "Demo", - AD_ENTRY1s("data.dcp", "dc1f6595f412ac25a52eaf47dad4ab81", 169083), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("ghostsheet", "Demo", + WME_ENTRY1s("data.dcp", "dc1f6595f412ac25a52eaf47dad4ab81", 169083), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Hamlet or the last game without MMORPS features, shaders and product placement - { - "hamlet", - "", - AD_ENTRY1s("data.dcp", "f624add957a77c9930529fb28cc2450f", 88183022), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("hamlet", "", + + WME_ENTRY1s("data.dcp", "f624add957a77c9930529fb28cc2450f", 88183022), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Helga Deep In Trouble (English) - { - "helga", - "", - { - {"english.dcp", 0, "bfa136b21bdbc7d8691c0770a6d40bc3", 135931}, - {"data.dcp", 0, "25cb955a60b58326f2eeda1ce288fb37", 183251259}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("helga", "", + WME_ENTRY2s("english.dcp", "bfa136b21bdbc7d8691c0770a6d40bc3", 135931, + "data.dcp", "25cb955a60b58326f2eeda1ce288fb37", 183251259), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Helga Deep In Trouble (Demo) (English) - { - "helga", - "Demo", - { - {"english.dcp", 0, "b3a93e678f0ef97200f691cd1724643f", 135864}, - {"data.dcp", 0, "45134ed93bc391edf148b79cdcbf2a09", 154266028}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("helga", "Demo", + WME_ENTRY2s("english.dcp", "b3a93e678f0ef97200f691cd1724643f", 135864, + "data.dcp", "45134ed93bc391edf148b79cdcbf2a09", 154266028), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // James Peris: No License Nor Control (English) - { - "jamesperis", - "", - AD_ENTRY1s("data.dcp", "a420961e170cb7d168a0d2bae2fe5218", 225294032), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("jamesperis", "", + WME_ENTRY1s("data.dcp", "a420961e170cb7d168a0d2bae2fe5218", 225294032), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // James Peris: No License Nor Control (Spanish) - { - "jamesperis", - "", - AD_ENTRY1s("data.dcp", "a420961e170cb7d168a0d2bae2fe5218", 225294032), - Common::ES_ESP, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("jamesperis", "", + WME_ENTRY1s("data.dcp", "a420961e170cb7d168a0d2bae2fe5218", 225294032), Common::ES_ESP, ADGF_UNSTABLE, LATEST_VERSION), // James Peris: No License Nor Control (Demo) (English) - { - "jamesperis", - "Demo", - AD_ENTRY1s("data.dcp", "edb9f9c7a08993c1e28f4e477b5f9830", 116113507), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("jamesperis", "Demo", + WME_ENTRY1s("data.dcp", "edb9f9c7a08993c1e28f4e477b5f9830", 116113507), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // James Peris: No License Nor Control (Demo) (Spanish) - { - "jamesperis", - "Demo", - AD_ENTRY1s("data.dcp", "edb9f9c7a08993c1e28f4e477b5f9830", 116113507), - Common::ES_ESP, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("jamesperis", "Demo", + WME_ENTRY1s("data.dcp", "edb9f9c7a08993c1e28f4e477b5f9830", 116113507), Common::ES_ESP, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // J.U.L.I.A. (English) - { - "julia", - "", - AD_ENTRY1s("data.dcp", "c2264b4f8fcd132d2913ff5b6076a24f", 10109741), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("julia", "", + WME_ENTRY1s("data.dcp", "c2264b4f8fcd132d2913ff5b6076a24f", 10109741), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // J.U.L.I.A. (English, Bundle in a box-version) - { - "julia", - "Version 1.2", - AD_ENTRY1s("data.dcp", "fe90023ccc22f35185b40b910e0d03a2", 10101373), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("julia", "Version 1.2", + WME_ENTRY1s("data.dcp", "fe90023ccc22f35185b40b910e0d03a2", 10101373), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // J.U.L.I.A. (English) (Demo) - { - "julia", - "Demo", - AD_ENTRY1s("data.dcp", "f0bbc3394555a9811f6050dae428cab6", 7655237), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("julia", "Demo", + WME_ENTRY1s("data.dcp", "f0bbc3394555a9811f6050dae428cab6", 7655237), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // J.U.L.I.A. (English) (Greenlight Demo) - { - "julia", - "Greenlight Demo", - AD_ENTRY1s("data.dcp", "4befd448d36b0dae9c3ab1aa7cb8b78d", 7271886), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("julia", "Greenlight Demo", + WME_ENTRY1s("data.dcp", "4befd448d36b0dae9c3ab1aa7cb8b78d", 7271886), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Kulivocko (Czech) - { - "kulivocko", - "", - AD_ENTRY1s("data.dcp", "44306dc470e9b27474043932eccee02f", 155106392), - Common::CZ_CZE, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("kulivocko", "", + WME_ENTRY1s("data.dcp", "44306dc470e9b27474043932eccee02f", 155106392), Common::CZ_CZE, ADGF_UNSTABLE, LATEST_VERSION), // Kulivocko (Czech) (Demo) - { - "kulivocko", - "Demo", - AD_ENTRY1s("data.dcp", "63b164bdfadecbb0deb5da691afb8154", 48362234), - Common::CZ_CZE, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("kulivocko", "Demo", + WME_ENTRY1s("data.dcp", "63b164bdfadecbb0deb5da691afb8154", 48362234), Common::CZ_CZE, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Life In 3 Minutes - { - "lifein3minutes", - "", - AD_ENTRY1s("data.dcp", "c6368950e37a95bf098b02b4eaa5b929", 141787214), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("lifein3minutes", "", + WME_ENTRY1s("data.dcp", "c6368950e37a95bf098b02b4eaa5b929", 141787214), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Looky Demo (English) - { - "looky", - "Demo", - { - {"english.dcp", 0, "1388e1dd320f4d553dea3b0316812f9d", 1358442}, - {"data.dcp", 0, "7074bcd7bc7ad7eb04c271aafb964c32", 13815660}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("looky", "Demo", + WME_ENTRY2s("english.dcp", "1388e1dd320f4d553dea3b0316812f9d", 1358442, + "data.dcp", "7074bcd7bc7ad7eb04c271aafb964c32", 13815660), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Looky Demo (German) - { - "looky", - "Demo", - { - {"german.dcp", 0, "606c048426dfbe94442b59fd34a5c76e", 14339496}, - {"data.dcp", 0, "7074bcd7bc7ad7eb04c271aafb964c32", 13815660}, - AD_LISTEND - }, - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("looky", "Demo", + WME_ENTRY2s("german.dcp", "606c048426dfbe94442b59fd34a5c76e", 14339496, + "data.dcp", "7074bcd7bc7ad7eb04c271aafb964c32", 13815660), Common::DE_DEU, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Looky (German) - { - "looky", - "", - { - {"german.dcp", 0, "bf4c2b8c26342342441a6d64934ab832", 107027865}, - {"data.dcp", 0, "50de0beaa5ad621aa9f020df901d1e74", 1342214}, - AD_LISTEND - }, - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("looky", "", + WME_ENTRY2s("german.dcp", "bf4c2b8c26342342441a6d64934ab832", 107027865, + "data.dcp", "50de0beaa5ad621aa9f020df901d1e74", 1342214), Common::DE_DEU, ADGF_UNSTABLE, LATEST_VERSION), // Mirage - { - "mirage", - "", - AD_ENTRY1s("data.dcp", "d230b0b99c0aa77b9ecd094d8ee5573b", 17844056), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("mirage", "", + WME_ENTRY1s("data.dcp", "d230b0b99c0aa77b9ecd094d8ee5573b", 17844056), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Oknytt - { - "oknytt", - "Version 1.0", - AD_ENTRY1s("data.dcp", "6456cf8f429905c83f07509f9da536dd", 109502959), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("oknytt", "Version 1.0", + WME_ENTRY1s("data.dcp", "6456cf8f429905c83f07509f9da536dd", 109502959), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Night Train Demo - { - "nighttrain", - "", - AD_ENTRY1s("data.dcp", "5a027ef84b083a730c9a4c85ec1d3a32", 131760816), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("nighttrain", "", + WME_ENTRY1s("data.dcp", "5a027ef84b083a730c9a4c85ec1d3a32", 131760816), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Paintaria - { - "paintaria", - "", - AD_ENTRY1s("data.dcp", "354c08440c98150ff0d4008dd2865880", 48326040), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("paintaria", "", + WME_ENTRY1s("data.dcp", "354c08440c98150ff0d4008dd2865880", 48326040), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Pigeons in the Park - { - "pigeons", - "", - AD_ENTRY1s("data.dcp", "9143a5b6ff8206aefe3c4c643add3ec7", 2611100), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("pigeons", "", + WME_ENTRY1s("data.dcp", "9143a5b6ff8206aefe3c4c643add3ec7", 2611100), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Project: Doom - { - "projectdoom", - "", - AD_ENTRY1s("data.dcp", "d5894b65a40706845434b99870bcab92", 99223761), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("projectdoom", "", + WME_ENTRY1s("data.dcp", "d5894b65a40706845434b99870bcab92", 99223761), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Project Joe - { - "projectjoe", - "", - AD_ENTRY1s("data.dcp", "ada3c08542901295076b5349e655e73f", 160780037), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("projectjoe", "", + WME_ENTRY1s("data.dcp", "ada3c08542901295076b5349e655e73f", 160780037), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Project Lonely Robot - { - "lonelyrobot", - "beta", - AD_ENTRY1s("data.dcp", "a0cf7ad5bab957416dcda454e9f28ef0", 3420120), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("lonelyrobot", "beta", + WME_ENTRY1s("data.dcp", "a0cf7ad5bab957416dcda454e9f28ef0", 3420120), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Reversion: The Escape Version 1.0 - { - "reversion1", - "Version 1.0", - AD_ENTRY1s("data.dcp", "cd616f98ebfd047e0c540b50b4b70761", 254384531), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.0", + WME_ENTRY1s("data.dcp", "cd616f98ebfd047e0c540b50b4b70761", 254384531), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.1 (Chinese) - { - "reversion1", - "Version 1.1", - { - {"chinese.dcp", 0, "cf97150739499a4c15f51dc534ff85a1", 6330561}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::ZH_CNA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.1", + WME_ENTRY2s("chinese.dcp", "cf97150739499a4c15f51dc534ff85a1", 6330561, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::ZH_CNA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.1 (English) - { - "reversion1", - "Version 1.1", - { - {"english.dcp", 0, "7b2f061d7c91365c5d04605f1de032b3", 5702699}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.1", + WME_ENTRY2s("english.dcp", "7b2f061d7c91365c5d04605f1de032b3", 5702699, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.1 (French) - { - "reversion1", - "Version 1.1", - { - {"french.dcp", 0, "214204b6022c5ed67fada44557690faf", 6327400}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.1", + WME_ENTRY2s("french.dcp", "214204b6022c5ed67fada44557690faf", 6327400, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::FR_FRA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.1 (German) - { - "reversion1", - "Version 1.1", - { - {"german.dcp", 0, "96677823b36d580a4a29e3659071071c", 6340699}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.1", + WME_ENTRY2s("german.dcp", "96677823b36d580a4a29e3659071071c", 6340699, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::DE_DEU, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.1 (Italian) - { - "reversion1", - "Version 1.1", - { - {"italian.dcp", 0, "9ce80c1835108f10170a02969f71efe1", 6301836}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::IT_ITA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.1", + WME_ENTRY2s("italian.dcp", "9ce80c1835108f10170a02969f71efe1", 6301836, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::IT_ITA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.1 (Portuguese) - { - "reversion1", - "Version 1.1", - { - {"portugues.dcp", 0, "8772501afa2c630a7c697eb99e9c7bda", 5053303}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::PT_BRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.1", + WME_ENTRY2s("portugues.dcp", "8772501afa2c630a7c697eb99e9c7bda", 5053303, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::PT_BRA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3 (Chinese) - { - "reversion1", - "Version 1.3", - { - {"xlanguage_nz.dcp", 0, "92c4065156e464211685bf799b3279fd", 5130600}, - {"data.dcp", 0, "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907}, - AD_LISTEND - }, - Common::ZH_CNA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3", + WME_ENTRY2s("xlanguage_nz.dcp", "92c4065156e464211685bf799b3279fd", 5130600, + "data.dcp", "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907), Common::ZH_CNA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3 (English) - { - "reversion1", - "Version 1.3", - { - {"xlanguage_en.dcp", 0, "05845e1283920a6e4044f2a54f7a9519", 4818543}, - {"data.dcp", 0, "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3", + WME_ENTRY2s("xlanguage_en.dcp", "05845e1283920a6e4044f2a54f7a9519", 4818543, + "data.dcp", "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3 (French) - { - "reversion1", - "Version 1.3", - { - {"xlanguage_fr.dcp", 0, "441795490e9307eb2ed07830779881ac", 5425959}, - {"data.dcp", 0, "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907}, - AD_LISTEND - }, - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3", + WME_ENTRY2s("xlanguage_fr.dcp", "441795490e9307eb2ed07830779881ac", 5425959, + "data.dcp", "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907), Common::FR_FRA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3 (German) - { - "reversion1", - "Version 1.3", - { - {"xlanguage_de.dcp", 0, "b588041015b93e54b4c246ca77d01e76", 5423798}, - {"data.dcp", 0, "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907}, - AD_LISTEND - }, - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3", + WME_ENTRY2s("xlanguage_de.dcp", "b588041015b93e54b4c246ca77d01e76", 5423798, + "data.dcp", "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907), Common::DE_DEU, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3 (Italian) - { - "reversion1", - "Version 1.3", - { - {"xlanguage_it.dcp", 0, "a1f4199079b75ee10cded41f05b45d5f", 5386424}, - {"data.dcp", 0, "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907}, - AD_LISTEND - }, - Common::IT_ITA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3", + WME_ENTRY2s("xlanguage_it.dcp", "a1f4199079b75ee10cded41f05b45d5f", 5386424, + "data.dcp", "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907), Common::IT_ITA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3 (Portuguese) - { - "reversion1", - "Version 1.3", - { - {"xlanguage_pt.dcp", 0, "3d653debd37e56756a79401e1004c4d2", 4149165}, - {"data.dcp", 0, "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907}, - AD_LISTEND - }, - Common::PT_BRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3", + WME_ENTRY2s("xlanguage_pt.dcp", "3d653debd37e56756a79401e1004c4d2", 4149165, + "data.dcp", "9ebb12f6fd7c038d079f81beb3bd96d5", 254185907), Common::PT_BRA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (Chinese) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_nz.dcp", 0, "7146dfa43ffdf0886e034fffe2c8a0c0", 13722261}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::ZH_CNA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_nz.dcp", "7146dfa43ffdf0886e034fffe2c8a0c0", 13722261, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::ZH_CNA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (English) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_en.dcp", 0, "64b6fa7eedc09c231f6ce046e77fee05", 11339619}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_en.dcp", "64b6fa7eedc09c231f6ce046e77fee05", 11339619, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (French) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_fr.dcp", 0, "d561d562224afea809153a1fd9fdb0c0", 11963210}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_fr.dcp", "d561d562224afea809153a1fd9fdb0c0", 11963210, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::FR_FRA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (German) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_de.dcp", 0, "4e3f614c36bd6bae74b8cc83e663a8f0", 14040310}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_de.dcp", "4e3f614c36bd6bae74b8cc83e663a8f0", 14040310, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::DE_DEU, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (Italian) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_it.dcp", 0, "10d09b7fe61946f09dd91d5e8d090f94", 11913752}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::IT_ITA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_it.dcp", "10d09b7fe61946f09dd91d5e8d090f94", 11913752, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::IT_ITA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (Latvian) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_lv.dcp", 0, "704359ab5040b0dab6545064d7aa6eb9", 11414925}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::LV_LAT, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_lv.dcp", "704359ab5040b0dab6545064d7aa6eb9", 11414925, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::LV_LAT, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (Polish) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_pl.dcp", 0, "c4ad33f57e1e998169552d521c1d6638", 11532215}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::PL_POL, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_pl.dcp", "c4ad33f57e1e998169552d521c1d6638", 11532215, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::PL_POL, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Escape Version 1.3.2369 (Portuguese) - { - "reversion1", - "Version 1.3.2369", - { - {"xlanguage_pt.dcp", 0, "886886b6b14aadac844078de856799a6", 10620797}, - {"data.dcp", 0, "aecb5deeea7b0baa871fbd0cef35a648", 254219204}, - AD_LISTEND - }, - Common::PT_BRA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion1", "Version 1.3.2369", + WME_ENTRY2s("xlanguage_pt.dcp", "886886b6b14aadac844078de856799a6", 10620797, + "data.dcp", "aecb5deeea7b0baa871fbd0cef35a648", 254219204), Common::PT_BRA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Meeting (Chinese) - { - "reversion2", - "", - { - {"xlanguage_nz.dcp", 0, "8c3709474a87a7876109025dff41ff3f", 8746015}, - {"data.dcp", 0, "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032}, - AD_LISTEND - }, - Common::ZH_CNA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion2", "", + WME_ENTRY2s("xlanguage_nz.dcp", "8c3709474a87a7876109025dff41ff3f", 8746015, + "data.dcp", "cb9865dc7e1db2990a8cf4bc13cf4999", 257643032), Common::ZH_CNA, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Meeting (English) - { - "reversion2", - "", - { - {"xlanguage_en.dcp", 0, "ca357d86618d1ab76a21c913f4403cbd", 8414976}, - {"data.dcp", 0, "f7938cbfdc48f07934550245a3286921", 255672016}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion2", "", + WME_ENTRY2s("xlanguage_en.dcp", "ca357d86618d1ab76a21c913f4403cbd", 8414976, + "data.dcp", "f7938cbfdc48f07934550245a3286921", 255672016), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Meeting (Spanish) - { - "reversion2", - "", - AD_ENTRY1s("data.dcp", "f7938cbfdc48f07934550245a3286921", 255672016), - Common::ES_ESP, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("reversion2", "", + WME_ENTRY1s("data.dcp", "f7938cbfdc48f07934550245a3286921", 255672016), Common::ES_ESP, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Meeting Version 2.0.2412 (Chinese) - { - "reversion2", - "Version 2.0.2412", - { - {"data.dcp", 0, "f4ffc4df24b7bebad56a24930f33a2bc", 255766600}, - {"xlanguage_nz.dcp", 0, "17c79af4928e24484bee77a7e807cc2a", 10737127}, - {"Linux.dcp", 0, "21858bd77dc86b03f701fd47900e2f51", 984535}, - AD_LISTEND - }, - Common::ZH_CNA, - Common::kPlatformLinux, - ADGF_UNSTABLE, - GUIO0() - }, + WME_PLATENTRY("reversion2", "Version 2.0.2412", + WME_ENTRY3s("data.dcp", "f4ffc4df24b7bebad56a24930f33a2bc", 255766600, + "xlanguage_nz.dcp", "17c79af4928e24484bee77a7e807cc2a", 10737127, + "Linux.dcp", "21858bd77dc86b03f701fd47900e2f51", 984535), Common::ZH_CNA, Common::kPlatformLinux, ADGF_UNSTABLE, LATEST_VERSION), // Reversion: The Meeting Version 2.0.2412 (English) - { - "reversion2", - "Version 2.0.2412", - { - {"data.dcp", 0, "f4ffc4df24b7bebad56a24930f33a2bc", 255766600}, - {"xlanguage_en.dcp", 0, "0598bf752ce93b42bcaf1094df537c7b", 8533057}, - {"Linux.dcp", 0, "21858bd77dc86b03f701fd47900e2f51", 984535}, - AD_LISTEND - }, - Common::EN_ANY, - Common::kPlatformLinux, - ADGF_UNSTABLE, - GUIO0() - }, + WME_PLATENTRY("reversion2", "Version 2.0.2412", + WME_ENTRY3s("data.dcp", "f4ffc4df24b7bebad56a24930f33a2bc", 255766600, + "xlanguage_en.dcp", "0598bf752ce93b42bcaf1094df537c7b", 8533057, + "Linux.dcp", "21858bd77dc86b03f701fd47900e2f51", 984535), Common::EN_ANY, Common::kPlatformLinux, ADGF_UNSTABLE, LATEST_VERSION), // Rhiannon: Curse of the four Branches - { - "rhiannon", - "", - AD_ENTRY1s("data.dcp", "870f348900b735f1cc79c0608ce32b0e", 1046169851), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("rhiannon", "", + WME_ENTRY1s("data.dcp", "870f348900b735f1cc79c0608ce32b0e", 1046169851), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Rhiannon: Curse of the four Branches (English PC DVD) - { - "rhiannon", - "DVD", - AD_ENTRY1s("data.dcp", "6736bbc921bb6ce5161b3ad095a97bd4", 1053441028), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("rhiannon", "DVD", + WME_ENTRY1s("data.dcp", "6736bbc921bb6ce5161b3ad095a97bd4", 1053441028), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // 1 1/2 Ritter: Auf der Suche nach der hinreissenden Herzelinde - { - "ritter", - "", - AD_ENTRY1s("data.dcp", "5ac416cee605d3a30f4d59687b1cdab2", 364260278), - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("ritter", "", + WME_ENTRY1s("data.dcp", "5ac416cee605d3a30f4d59687b1cdab2", 364260278), Common::DE_DEU, ADGF_UNSTABLE, LATEST_VERSION), // Satan and Son - { - "satanandson", - "", - AD_ENTRY1s("data.dcp", "16a6ba8174b697bbba9299619d1e20c4", 67539054), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("satanandson", "", + WME_ENTRY1s("data.dcp", "16a6ba8174b697bbba9299619d1e20c4", 67539054), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Rosemary - { - "rosemary", - "", - AD_ENTRY1s("data.dcp", "4f2631138bd4d27587d9043f8aeff3df", 29483643), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("rosemary", "", + WME_ENTRY1s("data.dcp", "4f2631138bd4d27587d9043f8aeff3df", 29483643), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Securanote - { - "securanote", - "", - AD_ENTRY1s("data.dcp", "5213d3e59b9e95b7fbd5c56f7de5341a", 2625554), - Common::EN_ANY, - Common::kPlatformIOS, - ADGF_UNSTABLE, - GUIO0() - }, + WME_PLATENTRY("securanote", "", + WME_ENTRY1s("data.dcp", "5213d3e59b9e95b7fbd5c56f7de5341a", 2625554), Common::EN_ANY, Common::kPlatformIOS, ADGF_UNSTABLE, LATEST_VERSION), // Shaban - { - "shaban", - "", - AD_ENTRY1s("data.dcp", "35f702ca9baabc5c620e0be230195c8a", 755388466), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("shaban", "", + WME_ENTRY1s("data.dcp", "35f702ca9baabc5c620e0be230195c8a", 755388466), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // The Shine of a Star - { - "shinestar", - "", - AD_ENTRY1s("data.dcp", "f05abe9e2427a5e4f73648fa09c4ba8e", 94113060), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("shinestar", "", + WME_ENTRY1s("data.dcp", "f05abe9e2427a5e4f73648fa09c4ba8e", 94113060), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Sofia's Debt - { - "sofiasdebt", - "", - AD_ENTRY1s("SD.exe", "e9515f9ba1a2925bb6733476a826a650", 9915047), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("sofiasdebt", "", + WME_ENTRY1s("SD.exe", "e9515f9ba1a2925bb6733476a826a650", 9915047), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Space Invaders (Demo) - { - "spaceinvaders", - "Demo", - AD_ENTRY1s("data.dcp", "3f27adefdf72f2c1601cf555c80a509f", 1308361), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("spaceinvaders", "Demo", + WME_ENTRY1s("data.dcp", "3f27adefdf72f2c1601cf555c80a509f", 1308361), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Space Madness - { - "spacemadness", - "1.0.2", - AD_ENTRY1s("data.dcp", "b9b83135dc7a9e1b4b5f50195dbeb630", 39546622), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("spacemadness", "1.0.2", + WME_ENTRY1s("data.dcp", "b9b83135dc7a9e1b4b5f50195dbeb630", 39546622), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // The Ancient Mark - Episode 1 - { - "theancientmark1", - "", - AD_ENTRY1s("data.dcp", "ca04c26f03b2bd307368b306b297ddd7", 364664692), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("theancientmark1", "", + WME_ENTRY1s("data.dcp", "ca04c26f03b2bd307368b306b297ddd7", 364664692), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // The Box - { - "thebox", - "", - AD_ENTRY1s("data.dcp", "ec5f0c7e8174e307701447b53afe7e2f", 108372483), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("thebox", "", + WME_ENTRY1s("data.dcp", "ec5f0c7e8174e307701447b53afe7e2f", 108372483), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // The Kite (Version 1.1) - { - "thekite", - "Version 1.1", - AD_ENTRY1s("data.dcp", "92d29428f464469bda2d81b03d4d5c3e", 47332296), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("thekite", "Version 1.1", + WME_ENTRY1s("data.dcp", "92d29428f464469bda2d81b03d4d5c3e", 47332296), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // The Kite (Version 1.2.e) - { - "thekite", - "Version 1.2.e", - AD_ENTRY1s("data.dcp", "92451578b1bdd2b32a1db592a4f6d5fc", 47360539), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("thekite", "Version 1.2.e", + WME_ENTRY1s("data.dcp", "92451578b1bdd2b32a1db592a4f6d5fc", 47360539), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // The Kite (Version 1.2.i) (Italian) - { - "thekite", - "Version 1.2.i", - AD_ENTRY1s("data.dcp", "d3435b106a1b3b4c1df8ad596d271586", 47509274), - Common::IT_ITA, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("thekite", "Version 1.2.i", + WME_ENTRY1s("data.dcp", "d3435b106a1b3b4c1df8ad596d271586", 47509274), Common::IT_ITA, ADGF_UNSTABLE, LATEST_VERSION), // The Kite (Version 1.2.r) (Russian) - { - "thekite", - "Version 1.2.r", - AD_ENTRY1s("data.dcp", "d531e097dd884737469da014ed882cde", 47554582 ), - Common::RU_RUS, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("thekite", "Version 1.2.r", + WME_ENTRY1s("data.dcp", "d531e097dd884737469da014ed882cde", 47554582 ), Common::RU_RUS, ADGF_UNSTABLE, LATEST_VERSION), // The Kite (Version 1.3.e) - { - "thekite", - "Version 1.3.e", - AD_ENTRY1s("data.dcp", "9761827b51370263b7623721545d7627", 47382987), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("thekite", "Version 1.3.e", + WME_ENTRY1s("data.dcp", "9761827b51370263b7623721545d7627", 47382987), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Fairy Tales About Toshechka and Boshechka - { - "tib", - "", - AD_ENTRY1s("data.dcp", "87d296ef3f46570ed18f000d3885db77", 340264526), - Common::RU_RUS, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("tib", "", + WME_ENTRY1s("data.dcp", "87d296ef3f46570ed18f000d3885db77", 340264526), Common::RU_RUS, ADGF_UNSTABLE, LATEST_VERSION), // The Trader of Stories - { - "tradestory", - "Demo", - AD_ENTRY1s("data.dcp", "0a0b51191636cc8ead89b905281c3218", 40401902), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("tradestory", "Demo", + WME_ENTRY1s("data.dcp", "0a0b51191636cc8ead89b905281c3218", 40401902), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // the white chamber (multi-language) - { - "twc", - "", - AD_ENTRY1s("data.dcp", "0011d01142547c61e51ba24dc42b579e", 186451273), - Common::UNK_LANG, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("twc", "", + WME_ENTRY1s("data.dcp", "0011d01142547c61e51ba24dc42b579e", 186451273), Common::UNK_LANG, ADGF_UNSTABLE, LATEST_VERSION), // Vsevolod Prologue (Demo) - { - "vsevolod", - "Prologue", - AD_ENTRY1s("data.dcp", "f2dcffd2692dbfcc9371fa1a87970fe7", 388669493), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE | - ADGF_DEMO, - GUIO0() - }, + WME_WINENTRY("vsevolod", "Prologue", + WME_ENTRY1s("data.dcp", "f2dcffd2692dbfcc9371fa1a87970fe7", 388669493), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // War - { - "war", - "", - AD_ENTRY1s("data.dcp", "003e317cda6d0137bbd5e5d7f089ee4d", 32591890), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("war", "", + WME_ENTRY1s("data.dcp", "003e317cda6d0137bbd5e5d7f089ee4d", 32591890), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Wilma Tetris - { - "wtetris", - "", - AD_ENTRY1s("data.dcp", "946e3a0496e6c12fb344c9ed861ff015", 2780093), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, + WME_WINENTRY("wtetris", "", + WME_ENTRY1s("data.dcp", "946e3a0496e6c12fb344c9ed861ff015", 2780093), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Zilm: A Game of Reflex 1.0 + WME_WINENTRY("Zilm", "1.0", + WME_ENTRY1s("data.dcp", "098dffaf03d8adbb4cb5633e4733e63c", 351726), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), { - "Zilm", - "1.0", - AD_ENTRY1s("data.dcp", "098dffaf03d8adbb4cb5633e4733e63c", 351726), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_UNSTABLE, - GUIO0() - }, - AD_TABLE_END_MARKER + AD_TABLE_END_MARKER, + LATEST_VERSION + } }; } // End of namespace Wintermute +#undef WEM_ENTRY1s +#undef WEM_ENTRY2s +#undef WEM_ENTRY3s +#undef WME_WINENTRY +#undef WME_PLATENTRY + diff --git a/engines/wintermute/game_description.h b/engines/wintermute/game_description.h new file mode 100644 index 0000000000..313fff8bbf --- /dev/null +++ b/engines/wintermute/game_description.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef WINTERMUTE_GAME_DESCRIPTION_H +#define WINTERMUTE_GAME_DESCRIPTION_H + +#include "engines/advancedDetector.h" + +namespace Wintermute { + +enum WMETargetExecutable { + OLDEST_VERSION, + WME_1_0_0, + WME_1_1_0, + WME_1_2_0, + WME_1_3_0, + WME_1_4_0, + WME_1_5_0, + WME_1_6_0, + WME_1_7_0, + WME_1_8_0, + WME_1_8_6, + WME_1_9_0, + LATEST_VERSION +}; + +struct WMEGameDescription { + ADGameDescription adDesc; + WMETargetExecutable targetExecutable; +}; + +} + +#endif /* WINTERMUTE_GAME_DESCRIPTION_H_ */ diff --git a/engines/wintermute/math/rect32.h b/engines/wintermute/math/rect32.h index 93b5c68a30..00326d6747 100644 --- a/engines/wintermute/math/rect32.h +++ b/engines/wintermute/math/rect32.h @@ -50,7 +50,7 @@ struct Point32 { y -= delta.y; return *this; } - + operator FloatPoint() { return FloatPoint(x,y); } diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk index 1b6c52e0b7..4c95314a02 100644 --- a/engines/wintermute/module.mk +++ b/engines/wintermute/module.mk @@ -108,7 +108,9 @@ MODULE_OBJS := \ utils/path_util.o \ utils/string_util.o \ utils/utils.o \ + video/subtitle_card.o \ video/video_player.o \ + video/video_subtitler.o \ video/video_theora_player.o \ debugger.o \ wintermute.o \ diff --git a/engines/wintermute/utils/utils.cpp b/engines/wintermute/utils/utils.cpp index d592019418..dc6476d4ea 100644 --- a/engines/wintermute/utils/utils.cpp +++ b/engines/wintermute/utils/utils.cpp @@ -32,11 +32,6 @@ namespace Wintermute { -////////////////////////////////////////////////////////////////////// -static inline unsigned Sqr(int x) { - return (x * x); -} - ////////////////////////////////////////////////////////////////////////////////// // Swap - swaps two integers ////////////////////////////////////////////////////////////////////////////////// diff --git a/engines/wintermute/video/subtitle_card.cpp b/engines/wintermute/video/subtitle_card.cpp new file mode 100644 index 0000000000..5d882502fd --- /dev/null +++ b/engines/wintermute/video/subtitle_card.cpp @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/video/subtitle_card.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +SubtitleCard::SubtitleCard(BaseGame *inGame, + const Common::String &text, + const uint &startFrame, + const uint &endFrame) : _gameRef(inGame), + _startFrame(startFrame), + _endFrame(endFrame) { + _text = text; + _gameRef->expandStringByStringTable(_text); +} + +uint32 SubtitleCard::getStartFrame() const { + return _startFrame; +} + +uint32 SubtitleCard::getEndFrame() const { + return _endFrame; +} + +Common::String SubtitleCard::getText() const { + return _text; +} + +} // End of namespace Wintermute diff --git a/engines/wintermute/video/subtitle_card.h b/engines/wintermute/video/subtitle_card.h new file mode 100644 index 0000000000..629df77287 --- /dev/null +++ b/engines/wintermute/video/subtitle_card.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SUBTITLECARD_H +#define WINTERMUTE_SUBTITLECARD_H + +#include "common/str.h" + +namespace Wintermute { + +class BaseGame; + +class SubtitleCard { +public: + SubtitleCard(BaseGame *inGame, const Common::String &text, const uint &startFrame, const uint &endFrame); + uint32 getEndFrame() const; + uint32 getStartFrame() const; + Common::String getText() const; +private: + BaseGame *_gameRef; + uint32 _endFrame; + uint32 _startFrame; + Common::String _text; +}; + +} // End of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_subtitler.cpp b/engines/wintermute/video/video_subtitler.cpp new file mode 100644 index 0000000000..95d938574b --- /dev/null +++ b/engines/wintermute/video/video_subtitler.cpp @@ -0,0 +1,266 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/video/video_subtitler.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" + +namespace Wintermute { + +VideoSubtitler::VideoSubtitler(BaseGame *inGame): BaseClass(inGame) { + _lastSample = -1; + _currentSubtitle = 0; + _showSubtitle = false; +} + +VideoSubtitler::~VideoSubtitler(void) { + _subtitles.clear(); +} + +bool VideoSubtitler::loadSubtitles(const Common::String &filename, const Common::String &subtitleFile) { + if (filename.size() == 0) { + return false; + } + + _subtitles.clear(); + + _lastSample = -1; + _currentSubtitle = 0; + _showSubtitle = false; + + Common::String newFile; + + /* + * Okay, the expected behaviour is this: either we are + * provided with a subtitle file to use by the script when + * calling PlayTheora(), or we try to autodetect a suitable + * one which, for /some/path/movie/ogg is to be called + * /some/path/movie.sub + */ + if (subtitleFile.size() != 0) { + newFile = subtitleFile; + } else { + Common::String path = PathUtil::getDirectoryName(filename); + Common::String name = PathUtil::getFileNameWithoutExtension(filename); + Common::String ext = ".SUB"; + newFile = PathUtil::combine(path, name + ext); + } + + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(newFile, true, false); + + if (file == nullptr) { + return false; // no subtitles + } + + int fileSize = file->size(); + char *buffer = new char[fileSize]; + + file->read(buffer, fileSize); + + /* This is where we parse .sub files. + * Subtitles cards are in the form + * {StartFrame}{EndFrame} FirstLine | SecondLine \n + */ + int pos = 0; + + while (pos < fileSize) { + char *tokenStart = 0; + int tokenLength = 0; + int tokenPos = -1; + int lineLength = 0; + int start = -1; + int end = -1; + bool inToken = false; + + while (pos + lineLength < fileSize && + buffer[pos + lineLength] != '\n' && + buffer[pos + lineLength] != '\0') { + // Measure the line until we hit EOL, EOS or just hit the boundary + lineLength++; + } + + int realLength; + + if (pos + lineLength >= fileSize) { + realLength = lineLength - 0; + } else { + // If we got here the above loop exited after hitting "\0" "\n" + realLength = lineLength - 1; + } + + Common::String cardText; + char *fileLine = (char *)&buffer[pos]; + + for (int i = 0; i < realLength; i++) { + if (fileLine[i] == '{') { + if (!inToken) { + // We've hit the start of a Start/EndFrame token + inToken = true; + tokenStart = fileLine + i + 1; + tokenLength = 0; + tokenPos++; + } else { + // Actually, we were already inside an (invalid) one. + tokenLength++; + } + } else if (fileLine[i] == '}') { + if (inToken) { + // we were /inside/ a {.*} token, so this is the end of the block + inToken = false; + char *token = new char[tokenLength + 1]; + strncpy(token, tokenStart, tokenLength); + token[tokenLength] = '\0'; + if (tokenPos == 0) { + // Was this StartFrame... + start = atoi(token); + } else if (tokenPos == 1) { + // Or the EndFrame? + end = atoi(token); + } + delete[] token; + } else { + // This char is part of the plain text, just append it + cardText += fileLine[i]; + } + } else { + if (inToken) { + tokenLength++; + } else { + if (fileLine[i] == '|') { + // The pipe character signals a linebreak in the text + cardText += '\n'; + } else { + // This char is part of the plain text, just append it + cardText += fileLine[i]; + } + } + } + } + + if (start != -1 && cardText.size() > 0 && (start != 1 || end != 1)){ + // Add a subtitlecard based on the line we have just parsed + _subtitles.push_back(SubtitleCard(_gameRef, cardText, start, end)); + } + + pos += lineLength + 1; + } + + delete[] buffer; + // Succeeded loading subtitles! + + return true; +} + +void VideoSubtitler::display() { + if (_showSubtitle) { + + BaseFont *font; + + if (_gameRef->getVideoFont() == nullptr) { + font = _gameRef->getSystemFont(); + } else { + font = _gameRef->getVideoFont(); + } + + int textHeight = font->getTextHeight( + (const byte *)_subtitles[_currentSubtitle].getText().c_str(), + _gameRef->_renderer->getWidth()); + + font->drawText( + (const byte *)_subtitles[_currentSubtitle].getText().c_str(), + 0, + (_gameRef->_renderer->getHeight() - textHeight - 5), + (_gameRef->_renderer->getWidth()), + TAL_CENTER); + } +} + +void VideoSubtitler::update(uint32 frame) { + if (_subtitles.size() == 0) { + // Edge case: we have loaded subtitles early on... from a blank file. + return; + } + + if ((int32)frame != _lastSample) { + /* + * If the frame count hasn't advanced the previous state still matches + * the current frame (obviously). + */ + + _lastSample = frame; + // Otherwise, we update _lastSample; see above. + + _showSubtitle = false; + + bool overdue = (frame > _subtitles[_currentSubtitle].getEndFrame()); + bool hasNext = (_currentSubtitle + 1 < _subtitles.size()); + bool nextStarted = false; + if (hasNext) { + nextStarted = (_subtitles[_currentSubtitle + 1].getStartFrame() <= frame); + } + + while (_currentSubtitle < _subtitles.size() && + overdue && hasNext && nextStarted) { + /* + * We advance until we get past all overdue subtitles. + * We should exit the cycle when we either reach the first + * subtitle which is not overdue whose subsequent subtitle + * has not started yet (aka the one we must display now or + * the one which WILL be displayed when its time comes) + * and / or when we reach the last one. + */ + + _currentSubtitle++; + + overdue = (frame > _subtitles[_currentSubtitle].getEndFrame()); + hasNext = (_currentSubtitle + 1 < _subtitles.size()); + if (hasNext) { + nextStarted = (_subtitles[_currentSubtitle + 1].getStartFrame() <= frame); + } else { + nextStarted = false; + } + } + + bool currentValid = (_subtitles[_currentSubtitle].getEndFrame() != 0); + /* + * No idea why we do this check, carried over from Mnemonic's code. + * Possibly a workaround for buggy subtitles or some kind of sentinel? :-\ + */ + + bool currentStarted = frame >= _subtitles[_currentSubtitle].getStartFrame(); + + if (currentStarted && !overdue && currentValid) { + _showSubtitle = true; + } + } +} + +} // End of namespace Wintermute diff --git a/engines/wintermute/video/video_subtitler.h b/engines/wintermute/video/video_subtitler.h new file mode 100644 index 0000000000..94f22909a1 --- /dev/null +++ b/engines/wintermute/video/video_subtitler.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VIDSUBTITLER_H +#define WINTERMUTE_VIDSUBTITLER_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/video/subtitle_card.h" + +namespace Wintermute { + +class VideoSubtitler : public BaseClass { +public: + VideoSubtitler(BaseGame *inGame); + virtual ~VideoSubtitler(void); + bool loadSubtitles(const Common::String &filename, const Common::String &subtitleFile); + void display(); + void update(uint32 frame); +private: + Common::Array<SubtitleCard> _subtitles; + int32 _lastSample; + bool _showSubtitle; + uint32 _currentSubtitle; +}; + +} // End of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp index e1553580ec..22c235c848 100644 --- a/engines/wintermute/video/video_theora_player.cpp +++ b/engines/wintermute/video/video_theora_player.cpp @@ -85,14 +85,14 @@ void VideoTheoraPlayer::SetDefaults() { _volume = 100; _theoraDecoder = nullptr; - // TODO: Add subtitles-support - //_subtitler = nullptr; + _subtitler = new VideoSubtitler(_gameRef); + _foundSubtitles = false; } ////////////////////////////////////////////////////////////////////////// VideoTheoraPlayer::~VideoTheoraPlayer(void) { cleanup(); -// SAFE_DELETE(_subtitler); + delete _subtitler; } ////////////////////////////////////////////////////////////////////////// @@ -130,6 +130,9 @@ bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common: warning("VideoTheoraPlayer::initialize - Theora support not compiled in, video will be skipped: %s", filename.c_str()); return STATUS_FAILED; #endif + + _foundSubtitles = _subtitler->loadSubtitles(_filename, subtitleFile); + _theoraDecoder->loadStream(_file); if (!_theoraDecoder->isVideoLoaded()) { @@ -214,7 +217,10 @@ bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, _state = THEORA_STATE_PLAYING; _looping = looping; _playbackType = type; - + if (_subtitler && _foundSubtitles && _gameRef->_subtitles) { + _subtitler->update(_theoraDecoder->getFrameCount()); + _subtitler->display(); + } _startTime = startTime; _volume = volume; _posX = x; @@ -256,7 +262,7 @@ bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, #if 0 // Stubbed for now as theora isn't seekable if (StartTime) SeekToTime(StartTime); - Update(); + update(); #endif return STATUS_FAILED; } @@ -289,6 +295,10 @@ bool VideoTheoraPlayer::update() { } if (_theoraDecoder) { + if (_subtitler && _foundSubtitles && _gameRef->_subtitles) { + _subtitler->update(_theoraDecoder->getCurFrame()); + } + if (_theoraDecoder->endOfVideo() && _looping) { warning("Should loop movie %s, hacked for now", _filename.c_str()); _theoraDecoder->rewind(); @@ -412,11 +422,10 @@ bool VideoTheoraPlayer::display(uint32 alpha) { } else { res = STATUS_FAILED; } - // TODO: Add subtitles-support -/* if (m_Subtitler && _gameRef->m_VideoSubtitles) { - m_Subtitler->display(); - }*/ + if (_subtitler && _foundSubtitles && _gameRef->_subtitles) { + _subtitler->display(); + } return res; } diff --git a/engines/wintermute/video/video_theora_player.h b/engines/wintermute/video/video_theora_player.h index 8274a1444f..0b9b3d487a 100644 --- a/engines/wintermute/video/video_theora_player.h +++ b/engines/wintermute/video/video_theora_player.h @@ -31,6 +31,7 @@ #include "engines/wintermute/base/base.h" #include "engines/wintermute/persistent.h" +#include "engines/wintermute/video/video_subtitler.h" #include "video/video_decoder.h" #include "common/stream.h" #include "graphics/surface.h" @@ -59,7 +60,7 @@ public: Common::String _filename; BaseSurface *_texture; - //CVidSubtitler *_subtitler; + VideoSubtitler *_subtitler; // control methods bool initialize(const Common::String &filename, const Common::String &subtitleFile = Common::String()); @@ -137,9 +138,10 @@ private: bool _playbackStarted; + bool _foundSubtitles; + // helpers void SetDefaults(); - }; } // End of namespace Wintermute diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index 81b6e53c9f..e35bb60c3d 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -53,7 +53,7 @@ WintermuteEngine::WintermuteEngine() : Engine(g_system) { _gameDescription = nullptr; } -WintermuteEngine::WintermuteEngine(OSystem *syst, const ADGameDescription *desc) +WintermuteEngine::WintermuteEngine(OSystem *syst, const WMEGameDescription *desc) : Engine(syst), _gameDescription(desc) { // Put your engine in a sane state, but do nothing big yet; // in particular, do not load data from files; rather, if you @@ -133,7 +133,7 @@ Common::Error WintermuteEngine::run() { } int WintermuteEngine::init() { - BaseEngine::createInstance(_targetName, _gameDescription->gameid, _gameDescription->language); + BaseEngine::createInstance(_targetName, _gameDescription->adDesc.gameid, _gameDescription->adDesc.language, _gameDescription->targetExecutable); _game = new AdGame(_targetName); if (!_game) { return 1; diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h index 017809d56a..f8f5fc7deb 100644 --- a/engines/wintermute/wintermute.h +++ b/engines/wintermute/wintermute.h @@ -26,6 +26,7 @@ #include "engines/engine.h" #include "engines/advancedDetector.h" #include "gui/debugger.h" +#include "engines/wintermute/game_description.h" namespace Wintermute { @@ -44,7 +45,7 @@ enum { class WintermuteEngine : public Engine { public: - WintermuteEngine(OSystem *syst, const ADGameDescription *desc); + WintermuteEngine(OSystem *syst, const WMEGameDescription *desc); WintermuteEngine(); ~WintermuteEngine(); @@ -67,7 +68,7 @@ private: int messageLoop(); GUI::Debugger *_debugger; BaseGame *_game; - const ADGameDescription *_gameDescription; + const WMEGameDescription *_gameDescription; friend class Console; }; diff --git a/engines/zvision/POTFILES b/engines/zvision/POTFILES new file mode 100644 index 0000000000..48e2782648 --- /dev/null +++ b/engines/zvision/POTFILES @@ -0,0 +1 @@ +engines/zvision/detection.cpp diff --git a/engines/zvision/archives/zfs_archive.cpp b/engines/zvision/archives/zfs_archive.cpp index f5fa6fc9bf..d18cc9966b 100644 --- a/engines/zvision/archives/zfs_archive.cpp +++ b/engines/zvision/archives/zfs_archive.cpp @@ -79,7 +79,7 @@ void ZfsArchive::readHeaders(Common::SeekableReadStream *stream) { // Read in each entry header for (uint32 i = 0; i < _header.filesPerBlock; ++i) { ZfsEntryHeader entryHeader; - + entryHeader.name = readEntryName(stream); entryHeader.offset = stream->readUint32LE(); entryHeader.id = stream->readUint32LE(); diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/core/save_manager.cpp index 07fb7637e7..e10201e024 100644 --- a/engines/zvision/core/save_manager.cpp +++ b/engines/zvision/core/save_manager.cpp @@ -106,7 +106,7 @@ void SaveManager::autoSave() { void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { // Create a thumbnail and save it Graphics::saveThumbnail(*file); - + // Write out the save date/time TimeDate td; g_system->getTimeAndDate(td); @@ -171,7 +171,7 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea warning("File is not a ZVision save file. Aborting load"); return false; } - + // Read in the version header.version = in->readByte(); diff --git a/engines/zvision/fonts/truetype_font.cpp b/engines/zvision/fonts/truetype_font.cpp index ba4d72bde8..45eaeeb2b4 100644 --- a/engines/zvision/fonts/truetype_font.cpp +++ b/engines/zvision/fonts/truetype_font.cpp @@ -95,6 +95,7 @@ Graphics::Surface *TruetypeFont::drawTextToSurface(const Common::String &text, u lines.pop_back(); } if (lines.size() == 0) { + delete surface; return nullptr; } diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index 517278e155..219f418b13 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -123,7 +123,7 @@ ActionDisableControl::ActionDisableControl(const Common::String &line) { bool ActionDisableControl::execute(ZVision *engine) { debug("Disabling control %u", _key); - + ScriptManager *scriptManager = engine->getScriptManager(); scriptManager->setStateFlags(_key, scriptManager->getStateFlags(_key) | ScriptManager::DISABLED); @@ -194,7 +194,7 @@ bool ActionMusic::execute(ZVision *engine) { } else { audioStream = makeRawZorkStream(_fileName, engine); } - + if (_loop) { Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); engine->_mixer->playStream(_soundType, 0, loopingAudioStream, -1, _volume); @@ -327,7 +327,7 @@ ActionSetPartialScreen::ActionSetPartialScreen(const Common::String &line) { bool ActionSetPartialScreen::execute(ZVision *engine) { RenderManager *renderManager = engine->getRenderManager(); - + if (_backgroundColor > 0) { renderManager->clearWorkingWindowTo555Color(_backgroundColor); } diff --git a/engines/zvision/scripting/controls/animation_control.h b/engines/zvision/scripting/controls/animation_control.h index 6c4d6dfcf7..bdb07d39ea 100644 --- a/engines/zvision/scripting/controls/animation_control.h +++ b/engines/zvision/scripting/controls/animation_control.h @@ -56,7 +56,7 @@ private: private: uint32 _animationKey; - + union { RlfAnimation *rlf; Video::VideoDecoder *avi; diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp index 9724e661b7..c029a2a7a1 100644 --- a/engines/zvision/scripting/controls/lever_control.cpp +++ b/engines/zvision/scripting/controls/lever_control.cpp @@ -84,7 +84,7 @@ LeverControl::~LeverControl() { } else if (_fileType == RLF) { delete _animation.rlf; } - + delete[] _frameInfo; } @@ -194,7 +194,7 @@ void LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common if (!_enabled) { return; } - + if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { _mouseIsCaptured = true; _lastMousePos = backgroundImageSpacePos; @@ -205,7 +205,7 @@ void LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common:: if (!_enabled) { return; } - + if (_mouseIsCaptured) { _mouseIsCaptured = false; _engine->getScriptManager()->setStateValue(_key, _currentFrame); @@ -220,7 +220,7 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common if (!_enabled) { return false; } - + bool cursorWasChanged = false; if (_mouseIsCaptured) { @@ -276,7 +276,7 @@ bool LeverControl::process(uint32 deltaTimeInMillis) { renderFrame(_returnRoutesCurrentFrame); } } - + return false; } @@ -387,7 +387,7 @@ void LeverControl::renderFrame(uint frameNumber) { // getFrameData() will automatically optimize to getNextFrame() / getPreviousFrame() if it can frameData = (const uint16 *)_animation.rlf->getFrameData(frameNumber)->getPixels(); width = _animation.rlf->width(); // Use the animation width instead of _animationCoords.width() - height = _animation.rlf->height(); // Use the animation height instead of _animationCoords.height() + height = _animation.rlf->height(); // Use the animation height instead of _animationCoords.height() } else if (_fileType == AVI) { _animation.avi->seekToFrame(frameNumber); const Graphics::Surface *surface = _animation.avi->decodeNextFrame(); diff --git a/engines/zvision/scripting/controls/push_toggle_control.cpp b/engines/zvision/scripting/controls/push_toggle_control.cpp index 82736b7576..a96c95b377 100644 --- a/engines/zvision/scripting/controls/push_toggle_control.cpp +++ b/engines/zvision/scripting/controls/push_toggle_control.cpp @@ -76,7 +76,7 @@ void PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Com if (!_enabled) { return; } - + if (_hotspot.contains(backgroundImageSpacePos)) { _engine->getScriptManager()->setStateValue(_key, 1); } @@ -86,7 +86,7 @@ bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const C if (!_enabled) { return false; } - + if (_hotspot.contains(backgroundImageSpacePos)) { _engine->getCursorManager()->changeCursor(_hoverCursor); return true; diff --git a/engines/zvision/scripting/controls/timer_node.cpp b/engines/zvision/scripting/controls/timer_node.cpp index c8c8a85d34..6f88b056a1 100644 --- a/engines/zvision/scripting/controls/timer_node.cpp +++ b/engines/zvision/scripting/controls/timer_node.cpp @@ -31,7 +31,7 @@ namespace ZVision { - + TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) : Control(engine, key) { if (_engine->getGameId() == GID_NEMESIS) { diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp index 753ce4ac6a..416bac00f3 100644 --- a/engines/zvision/scripting/scr_file_handling.cpp +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -236,7 +236,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (line.matchString("*:ttytext*", true)) { // TODO: Implement ActionTTYText } else if (line.matchString("*:universe_music*", true)) { - // TODO: Implement ActionUniverseMusic + // TODO: Implement ActionUniverseMusic } else if (line.matchString("*:copy_file*", true)) { // Not used. Purposely left empty } else { diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index 41b835e550..da82308051 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -260,7 +260,7 @@ Control *ScriptManager::getControl(uint32 key) { void ScriptManager::focusControl(uint32 key) { for (ControlList::iterator iter = _activeControls.begin(); iter != _activeControls.end(); ++iter) { uint32 controlKey = (*iter)->getKey(); - + if (controlKey == key) { (*iter)->focus(); } else if (controlKey == _currentlyFocusedControl) { diff --git a/engines/zvision/utility/utility.cpp b/engines/zvision/utility/utility.cpp index 905bc4513a..2079d23733 100644 --- a/engines/zvision/utility/utility.cpp +++ b/engines/zvision/utility/utility.cpp @@ -203,7 +203,7 @@ void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Com return; Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine); - + Common::DumpFile output; output.open(outputFile); |