ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

myst3.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/debug-channels.h"
00024 #include "common/events.h"
00025 #include "common/error.h"
00026 #include "common/config-manager.h"
00027 #include "common/file.h"
00028 #include "common/util.h"
00029 #include "common/textconsole.h"
00030 #include "common/translation.h"
00031 
00032 #include "gui/debugger.h"
00033 #include "gui/error.h"
00034 
00035 #include "engines/engine.h"
00036 
00037 #include "engines/myst3/archive.h"
00038 #include "engines/myst3/console.h"
00039 #include "engines/myst3/database.h"
00040 #include "engines/myst3/effects.h"
00041 #include "engines/myst3/myst3.h"
00042 #include "engines/myst3/nodecube.h"
00043 #include "engines/myst3/nodeframe.h"
00044 #include "engines/myst3/scene.h"
00045 #include "engines/myst3/state.h"
00046 #include "engines/myst3/cursor.h"
00047 #include "engines/myst3/inventory.h"
00048 #include "engines/myst3/script.h"
00049 #include "engines/myst3/menu.h"
00050 #include "engines/myst3/movie.h"
00051 #include "engines/myst3/sound.h"
00052 #include "engines/myst3/ambient.h"
00053 #include "engines/myst3/transition.h"
00054 
00055 #include "image/jpeg.h"
00056 
00057 #include "graphics/conversion.h"
00058 #include "graphics/renderer.h"
00059 #include "graphics/yuv_to_rgb.h"
00060 
00061 #include "math/vector2d.h"
00062 
00063 namespace Myst3 {
00064 
00065 Myst3Engine::Myst3Engine(OSystem *syst, const Myst3GameDescription *version) :
00066         Engine(syst), _system(syst), _gameDescription(version),
00067         _db(0), _console(0), _scriptEngine(0),
00068         _state(0), _node(0), _scene(0), _archiveNode(0),
00069         _cursor(0), _inventory(0), _gfx(0), _menu(0),
00070         _rnd(0), _sound(0), _ambient(0),
00071         _inputSpacePressed(false), _inputEnterPressed(false),
00072         _inputEscapePressed(false), _inputTildePressed(false),
00073         _inputEscapePressedNotConsumed(false),
00074         _interactive(false), _lastSaveTime(0),
00075         _menuAction(0), _projectorBackground(0),
00076         _shakeEffect(0), _rotationEffect(0),
00077         _backgroundSoundScriptLastRoomId(0),
00078         _backgroundSoundScriptLastAgeId(0),
00079         _transition(0), _frameLimiter(0), _inventoryManualHide(false) {
00080     DebugMan.addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses");
00081     DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function");
00082     DebugMan.addDebugChannel(kDebugScript, "Script", "Track Script Execution");
00083     DebugMan.addDebugChannel(kDebugNode, "Node", "Track Node Changes");
00084 
00085     // Add subdirectories to the search path to allow running from a full HDD install
00086     const Common::FSNode gameDataDir(ConfMan.get("path"));
00087     SearchMan.addSubDirectoryMatching(gameDataDir, "bin");
00088     SearchMan.addSubDirectoryMatching(gameDataDir, "M3Data");
00089     SearchMan.addSubDirectoryMatching(gameDataDir, "M3Data/TEXT");
00090     SearchMan.addSubDirectoriesMatching(gameDataDir, "EXILE Disc ?/Data", true);
00091 
00092     // Win DVD version directories
00093     SearchMan.addSubDirectoryMatching(gameDataDir, "English");
00094     SearchMan.addSubDirectoryMatching(gameDataDir, "Data");
00095 
00096     // Mac DVD version directories
00097     SearchMan.addSubDirectoryMatching(gameDataDir, "Exile DVD");
00098     SearchMan.addSubDirectoryMatching(gameDataDir, "Exile DVD/data");
00099 
00100     // PS2 version directories
00101     SearchMan.addSubDirectoryMatching(gameDataDir, "GAMEDATA");
00102     SearchMan.addSubDirectoryMatching(gameDataDir, "GAMEDATA/WORLD");
00103     SearchMan.addSubDirectoryMatching(gameDataDir, "GAMEDATA/WORLD/SOUND");
00104     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN");
00105     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN/DISCS");
00106     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN/DISCS/DATA");
00107     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN/M3DATA");
00108     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN/M3DATA/TEXT");
00109     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN/M3DATA/TEXT/NTSC");
00110     SearchMan.addSubDirectoryMatching(gameDataDir, "MYST3BIN/M3DATA/TEXT/PAL");
00111 }
00112 
00113 Myst3Engine::~Myst3Engine() {
00114     DebugMan.clearAllDebugChannels();
00115 
00116     closeArchives();
00117 
00118     delete _menu;
00119     delete _inventory;
00120     delete _cursor;
00121     delete _scene;
00122     delete _archiveNode;
00123     delete _db;
00124     delete _scriptEngine;
00125     delete _console;
00126     delete _state;
00127     delete _rnd;
00128     delete _sound;
00129     delete _ambient;
00130     delete _frameLimiter;
00131     delete _gfx;
00132 }
00133 
00134 bool Myst3Engine::hasFeature(EngineFeature f) const {
00135     // The TinyGL renderer does not support arbitrary resolutions for now
00136     Common::String rendererConfig = ConfMan.get("renderer");
00137     Graphics::RendererType desiredRendererType = Graphics::parseRendererTypeCode(rendererConfig);
00138     Graphics::RendererType matchingRendererType = Graphics::getBestMatchingAvailableRendererType(desiredRendererType);
00139     bool softRenderer = matchingRendererType == Graphics::kRendererTypeTinyGL;
00140 
00141     return
00142         (f == kSupportsRTL) ||
00143         (f == kSupportsLoadingDuringRuntime) ||
00144         (f == kSupportsSavingDuringRuntime) ||
00145         (f == kSupportsArbitraryResolutions && !softRenderer);
00146 }
00147 
00148 Common::Error Myst3Engine::run() {
00149     if (!checkDatafiles()) {
00150         // An error message has already been displayed
00151         return Common::kUserCanceled;
00152     }
00153 
00154     _gfx = createRenderer(_system);
00155     _gfx->init();
00156     _gfx->clear();
00157 
00158     _frameLimiter = new FrameLimiter(_system, ConfMan.getInt("engine_speed"));
00159     _sound = new Sound(this);
00160     _ambient = new Ambient(this);
00161     _rnd = new Common::RandomSource("sprint");
00162     _console = new Console(this);
00163     _scriptEngine = new Script(this);
00164     _db = new Database(getPlatform(), getGameLanguage(), getGameLocalizationType());
00165     _state = new GameState(getPlatform(), _db);
00166     _scene = new Scene(this);
00167     if (getPlatform() == Common::kPlatformXbox) {
00168         _menu = new AlbumMenu(this);
00169     } else {
00170         _menu = new PagingMenu(this);
00171     }
00172     _archiveNode = new Archive();
00173 
00174     _system->showMouse(false);
00175 
00176     openArchives();
00177 
00178     _cursor = new Cursor(this);
00179     _inventory = new Inventory(this);
00180 
00181     settingsInitDefaults();
00182     syncSoundSettings();
00183 
00184     // Init the font
00185     Graphics::Surface *font = loadTexture(1206);
00186     _gfx->initFont(font);
00187     font->free();
00188     delete font;
00189 
00190     if (ConfMan.hasKey("save_slot")) {
00191         // Load game from specified slot, if any
00192         loadGameState(ConfMan.getInt("save_slot"));
00193     } else {
00194         if (getPlatform() == Common::kPlatformXbox) {
00195             // Play the logo videos
00196             loadNode(kNodeLogoPlay, kLogo, 11);
00197         }
00198 
00199         // Game init script, loads the menu
00200         loadNode(kNodeSharedInit, kRoomShared, 1);
00201     }
00202 
00203     while (!shouldQuit()) {
00204         runNodeBackgroundScripts();
00205         processInput(true);
00206         updateCursor();
00207 
00208         if (_menuAction) {
00209             _menu->updateMainMenu(_menuAction);
00210             _menuAction = 0;
00211         }
00212 
00213         drawFrame();
00214     }
00215 
00216     tryAutoSaving(); //Attempt to autosave before exiting
00217     unloadNode();
00218 
00219     _archiveNode->close();
00220     _gfx->freeFont();
00221 
00222     // Make sure the mouse is unlocked
00223     _system->lockMouse(false);
00224 
00225     return Common::kNoError;
00226 }
00227 
00228 bool Myst3Engine::addArchive(const Common::String &file, bool mandatory) {
00229     Archive *archive = new Archive();
00230     bool opened = archive->open(file.c_str(), 0);
00231 
00232     if (opened) {
00233         _archivesCommon.push_back(archive);
00234     } else {
00235         delete archive;
00236         if (mandatory)
00237             error("Unable to open archive %s", file.c_str());
00238     }
00239 
00240     return opened;
00241 }
00242 
00243 void Myst3Engine::openArchives() {
00244     // The language of the menus is always the same as the executable
00245     // The English CD version can only display English text
00246     // The non English CD versions can display their localized language and English
00247     // The DVD version can display 6 different languages
00248 
00249     Common::String menuLanguage;
00250     Common::String textLanguage;
00251 
00252     switch (getGameLanguage()) {
00253     case Common::NL_NLD:
00254         menuLanguage = "DUTCH";
00255         break;
00256     case Common::FR_FRA:
00257         menuLanguage = "FRENCH";
00258         break;
00259     case Common::DE_DEU:
00260         menuLanguage = "GERMAN";
00261         break;
00262     case Common::HE_ISR:
00263         menuLanguage = "HEBREW";
00264         break;
00265     case Common::IT_ITA:
00266         menuLanguage = "ITALIAN";
00267         break;
00268     case Common::ES_ESP:
00269         menuLanguage = "SPANISH";
00270         break;
00271     case Common::JA_JPN:
00272         menuLanguage = "JAPANESE";
00273         break;
00274     case Common::PL_POL:
00275         menuLanguage = "POLISH";
00276         break;
00277     case Common::EN_ANY:
00278     case Common::RU_RUS:
00279     default:
00280         menuLanguage = "ENGLISH";
00281         break;
00282     }
00283 
00284     if (getGameLocalizationType() == kLocMulti6) {
00285         switch (ConfMan.getInt("text_language")) {
00286         case kDutch:
00287             textLanguage = "DUTCH";
00288             break;
00289         case kFrench:
00290             textLanguage = "FRENCH";
00291             break;
00292         case kGerman:
00293             textLanguage = "GERMAN";
00294             break;
00295         case kItalian:
00296             textLanguage = "ITALIAN";
00297             break;
00298         case kSpanish:
00299             textLanguage = "SPANISH";
00300             break;
00301         case kEnglish:
00302         default:
00303             textLanguage = "ENGLISH";
00304             break;
00305         }
00306     } else if (getGameLanguage() == Common::HE_ISR) {
00307         textLanguage = "ENGLISH"; // The Hebrew version does not have a "HEBREW.m3t" file
00308     } else {
00309         if (getGameLocalizationType() == kLocMonolingual || ConfMan.getInt("text_language")) {
00310             textLanguage = menuLanguage;
00311         } else {
00312             textLanguage = "ENGLISH";
00313         }
00314     }
00315 
00316     if (getGameLocalizationType() != kLocMonolingual && getPlatform() != Common::kPlatformXbox && textLanguage == "ENGLISH") {
00317         textLanguage = "ENGLISHjp";
00318     }
00319 
00320     if (getPlatform() == Common::kPlatformXbox) {
00321         menuLanguage += "X";
00322         textLanguage += "X";
00323     }
00324 
00325     // Load all the override files in the search path
00326     Common::ArchiveMemberList overrides;
00327     SearchMan.listMatchingMembers(overrides, "*.m3o");
00328     for (Common::ArchiveMemberList::const_iterator it = overrides.begin(); it != overrides.end(); it++) {
00329         addArchive(it->get()->getName(), false);
00330     }
00331 
00332     addArchive(textLanguage + ".m3t", true);
00333 
00334     if (getGameLocalizationType() != kLocMonolingual || getPlatform() == Common::kPlatformXbox || getGameLanguage() == Common::HE_ISR) {
00335         addArchive(menuLanguage + ".m3u", true);
00336     }
00337 
00338     addArchive("RSRC.m3r", true);
00339 }
00340 
00341 bool Myst3Engine::isTextLanguageEnglish() const {
00342     if (getGameLocalizationType() == kLocMonolingual && getGameLanguage() == Common::EN_ANY) {
00343         return true;
00344     }
00345 
00346     return getGameLocalizationType() != kLocMonolingual && ConfMan.getInt("text_language") == kEnglish;
00347 }
00348 
00349 void Myst3Engine::closeArchives() {
00350     for (uint i = 0; i < _archivesCommon.size(); i++)
00351         delete _archivesCommon[i];
00352 
00353     _archivesCommon.clear();
00354 }
00355 
00356 bool Myst3Engine::checkDatafiles() {
00357     if (!SearchMan.hasFile("OVER101.m3o")) {
00358         warning("Unable to open the update game archive 'OVER101.m3o'");
00359         static const char *updateMessage =
00360                 _("This version of Myst III has not been updated with the latest official patch.\n"
00361                           "Please install the official update corresponding to your game's language.\n"
00362                           "The updates can be downloaded from:\n"
00363                           "http://www.residualvm.org/downloads/");
00364         warning("%s", updateMessage);
00365         GUI::displayErrorDialog(updateMessage);
00366         return false;
00367     }
00368 
00369     return true;
00370 }
00371 
00372 HotSpot *Myst3Engine::getHoveredHotspot(NodePtr nodeData, uint16 var) {
00373     _state->setHotspotHovered(false);
00374     _state->setHotspotActiveRect(0);
00375 
00376     if (_state->getViewType() == kCube) {
00377         float pitch, heading;
00378         _cursor->getDirection(pitch, heading);
00379 
00380         for (uint j = 0; j < nodeData->hotspots.size(); j++) {
00381             int32 hitRect = nodeData->hotspots[j].isPointInRectsCube(pitch, heading);
00382             if (hitRect >= 0 && nodeData->hotspots[j].isEnabled(_state, var)) {
00383                 if (nodeData->hotspots[j].rects.size() > 1) {
00384                     _state->setHotspotHovered(true);
00385                     _state->setHotspotActiveRect(hitRect);
00386                 }
00387                 return &nodeData->hotspots[j];
00388             }
00389         }
00390     } else {
00391         // get the mouse position in original game window coordinates
00392         Common::Point mouse = _cursor->getPosition(false);
00393         mouse = _scene->scalePoint(mouse);
00394 
00395         for (uint j = 0; j < nodeData->hotspots.size(); j++) {
00396             int32 hitRect = nodeData->hotspots[j].isPointInRectsFrame(_state, mouse);
00397             if (hitRect >= 0 && nodeData->hotspots[j].isEnabled(_state, var)) {
00398                 if (nodeData->hotspots[j].rects.size() > 1) {
00399                     _state->setHotspotHovered(true);
00400                     _state->setHotspotActiveRect(hitRect);
00401                 }
00402                 return &nodeData->hotspots[j];
00403             }
00404         }
00405     }
00406 
00407     return 0;
00408 }
00409 
00410 void Myst3Engine::updateCursor() {
00411     if (!_inventory->isMouseInside()) {
00412         _inventoryManualHide = false;
00413     }
00414 
00415     if (isInventoryVisible() && _inventory->isMouseInside()) {
00416         _inventory->updateCursor();
00417         return;
00418     }
00419 
00420     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
00421 
00422     _state->setHotspotIgnoreClick(true);
00423     HotSpot *hovered = getHoveredHotspot(nodeData);
00424     _state->setHotspotIgnoreClick(false);
00425 
00426     if (hovered) {
00427         _cursor->changeCursor(hovered->cursor);
00428     } else {
00429         _cursor->changeCursor(8);
00430     }
00431 }
00432 
00433 void Myst3Engine::processInput(bool interactive) {
00434     _interactive = interactive;
00435 
00436     if (_state->hasVarGamePadUpPressed()) {
00437         // Reset the gamepad directions once they had a chance to be read by the scripts
00438         // This combined with keyboard repeat ensures the menu does not scroll too fast
00439         _state->setGamePadUpPressed(false);
00440         _state->setGamePadDownPressed(false);
00441         _state->setGamePadLeftPressed(false);
00442         _state->setGamePadRightPressed(false);
00443     }
00444 
00445     bool shouldInteractWithHoveredElement = false;
00446 
00447     // Process events
00448     Common::Event event;
00449     while (getEventManager()->pollEvent(event)) {
00450         if (_state->hasVarGamePadUpPressed()) {
00451             processEventForGamepad(event);
00452         }
00453 
00454         processEventForKeyboardState(event);
00455 
00456         if (event.type == Common::EVENT_MOUSEMOVE) {
00457             if (_state->getViewType() == kCube
00458                     && _cursor->isPositionLocked()) {
00459                 _scene->updateCamera(event.relMouse);
00460             }
00461 
00462             _cursor->updatePosition(event.mouse);
00463 
00464         } else if (event.type == Common::EVENT_LBUTTONDOWN) {
00465             shouldInteractWithHoveredElement = true;
00466         } else if (event.type == Common::EVENT_RBUTTONDOWN) {
00467             // Skip the event when in non-interactive mode
00468             if (!interactive)
00469                 continue;
00470             // Nothing to do if not in cube view
00471             if (_state->getViewType() != kCube)
00472                 continue;
00473             // Don't unlock if the cursor is transparent
00474             if (!_state->getCursorTransparency())
00475                 continue;
00476 
00477             bool cursorLocked = _cursor->isPositionLocked();
00478             _cursor->lockPosition(!cursorLocked);
00479 
00480         } else if (event.type == Common::EVENT_KEYDOWN) {
00481             // Save file name input
00482             if (_menu->handleInput(event.kbd)) {
00483                 continue;
00484             }
00485 
00486             if (event.kbdRepeat) {
00487                 // Ignore keyboard repeat except when entering save names
00488                 continue;
00489             }
00490 
00491             switch (event.kbd.keycode) {
00492             case Common::KEYCODE_ESCAPE:
00493                 _inputEscapePressedNotConsumed = true;
00494                 break;
00495             case Common::KEYCODE_RETURN:
00496             case Common::KEYCODE_KP_ENTER:
00497                 if (event.kbd.hasFlags(Common::KBD_ALT)) {
00498                     _gfx->toggleFullscreen();
00499                 } else {
00500                     shouldInteractWithHoveredElement = true;
00501                 }
00502                 break;
00503             case Common::KEYCODE_F5:
00504                 // Open main menu
00505                 if (_cursor->isVisible() && interactive) {
00506                     if (_state->getLocationRoom() != kRoomMenu)
00507                         _menu->goToNode(kNodeMenuMain);
00508                 }
00509                 break;
00510             case Common::KEYCODE_d:
00511                 if (event.kbd.flags & Common::KBD_CTRL) {
00512                     _console->attach();
00513                     _console->onFrame();
00514                 }
00515                 break;
00516             case Common::KEYCODE_i:
00517                 if (event.kbd.flags & Common::KBD_CTRL) {
00518                     bool mouseInverted = ConfMan.getBool("mouse_inverted");
00519                     mouseInverted = !mouseInverted;
00520                     ConfMan.setBool("mouse_inverted", mouseInverted);
00521                 }
00522                 break;
00523             default:
00524                 break;
00525             }
00526         } else if (event.type == Common::EVENT_SCREEN_CHANGED) {
00527             _gfx->computeScreenViewport();
00528             _cursor->updatePosition(_eventMan->getMousePos());
00529             _inventory->reflow();
00530         }
00531     }
00532 
00533     // The input state variables need to be set before calling the scripts
00534     updateInputState();
00535 
00536     if (shouldInteractWithHoveredElement && interactive) {
00537         interactWithHoveredElement();
00538     }
00539 
00540     if (shouldPerformAutoSave(_lastSaveTime)) {
00541         tryAutoSaving();
00542     }
00543 
00544     // Open main menu
00545     // This is not checked directly in the event handling code
00546     // because menu open requests done while in lookOnly mode
00547     // need to be honored after leaving the inner script loop,
00548     // especially when the script loop was cancelled due to pressing
00549     // escape.
00550     if (_inputEscapePressedNotConsumed && interactive) {
00551         _inputEscapePressedNotConsumed = false;
00552         if (_cursor->isVisible() && _state->hasVarMenuEscapePressed()) {
00553             if (_state->getLocationRoom() != kRoomMenu)
00554                 _menu->goToNode(kNodeMenuMain);
00555             else
00556                 _state->setMenuEscapePressed(1);
00557         }
00558     }
00559 }
00560 
00561 void Myst3Engine::processEventForKeyboardState(const Common::Event &event) {
00562     if (event.type == Common::EVENT_KEYDOWN) {
00563         if (event.kbdRepeat) {
00564             // Ignore keyboard repeat except when entering save names
00565             return;
00566         }
00567 
00568         switch (event.kbd.keycode) {
00569             case Common::KEYCODE_ESCAPE:
00570                 _inputEscapePressed = true;
00571                 break;
00572             case Common::KEYCODE_RETURN:
00573             case Common::KEYCODE_KP_ENTER:
00574                 if (!event.kbd.hasFlags(Common::KBD_ALT)) {
00575                     _inputEnterPressed = true;
00576                 }
00577                 break;
00578             case Common::KEYCODE_SPACE:
00579                 _inputSpacePressed = true;
00580                 break;
00581             case Common::KEYCODE_BACKQUOTE: // tilde, used to trigger the easter eggs
00582                 _inputTildePressed = true;
00583                 break;
00584             default:
00585                 break;
00586         }
00587     } else if (event.type == Common::EVENT_KEYUP) {
00588         switch (event.kbd.keycode) {
00589             case Common::KEYCODE_ESCAPE:
00590                 _inputEscapePressed = false;
00591                 _inputEscapePressedNotConsumed = false;
00592                 break;
00593             case Common::KEYCODE_RETURN:
00594             case Common::KEYCODE_KP_ENTER:
00595                 _inputEnterPressed = false;
00596                 break;
00597             case Common::KEYCODE_SPACE:
00598                 _inputSpacePressed = false;
00599                 break;
00600             case Common::KEYCODE_BACKQUOTE:
00601                 _inputTildePressed = false;
00602                 break;
00603             default:
00604                 break;
00605         }
00606     }
00607 }
00608 
00609 void Myst3Engine::processEventForGamepad(const Common::Event &event) {
00610     if (event.type == Common::EVENT_LBUTTONDOWN) {
00611         _state->setGamePadActionPressed(true);
00612     } else if (event.type == Common::EVENT_LBUTTONUP) {
00613         _state->setGamePadActionPressed(false);
00614     } else if (event.type == Common::EVENT_KEYDOWN) {
00615         if (event.kbdRepeat) return;
00616 
00617         switch (event.kbd.keycode) {
00618         case Common::KEYCODE_RETURN:
00619         case Common::KEYCODE_KP_ENTER:
00620             _state->setGamePadActionPressed(true);
00621             break;
00622         case Common::KEYCODE_UP:
00623             _state->setGamePadUpPressed(true);
00624             break;
00625         case Common::KEYCODE_DOWN:
00626             _state->setGamePadDownPressed(true);
00627             break;
00628         case Common::KEYCODE_LEFT:
00629             _state->setGamePadLeftPressed(true);
00630             break;
00631         case Common::KEYCODE_RIGHT:
00632             _state->setGamePadRightPressed(true);
00633             break;
00634         case Common::KEYCODE_ESCAPE:
00635             _state->setGamePadCancelPressed(true);
00636             break;
00637         default:
00638             break;
00639         }
00640     } else if (event.type == Common::EVENT_KEYUP) {
00641         switch (event.kbd.keycode) {
00642         case Common::KEYCODE_RETURN:
00643         case Common::KEYCODE_KP_ENTER:
00644             _state->setGamePadActionPressed(false);
00645             break;
00646         case Common::KEYCODE_ESCAPE:
00647             _state->setGamePadCancelPressed(false);
00648             break;
00649         default:
00650             break;
00651         }
00652     }
00653 }
00654 
00655 void Myst3Engine::updateInputState() {
00656     _state->setInputMousePressed(inputValidatePressed());
00657     _state->setInputTildePressed(_inputTildePressed);
00658     _state->setInputSpacePressed(_inputSpacePressed);
00659     _state->setInputEscapePressed(_inputEscapePressed);
00660 }
00661 
00662 void Myst3Engine::interactWithHoveredElement() {
00663     if (isInventoryVisible() && _inventory->isMouseInside()) {
00664         uint16 hoveredInventory = _inventory->hoveredItem();
00665         if (hoveredInventory > 0) {
00666             _inventory->useItem(hoveredInventory);
00667         } else {
00668             if (isWideScreenModEnabled()) {
00669                 _inventoryManualHide = true;
00670             }
00671         }
00672         return;
00673     }
00674 
00675     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
00676     HotSpot *hovered = getHoveredHotspot(nodeData);
00677 
00678     if (hovered) {
00679         _scriptEngine->run(&hovered->script);
00680         return;
00681     }
00682 
00683     // Bad click
00684     _sound->playEffect(697, 5);
00685 }
00686 
00687 void Myst3Engine::drawFrame(bool noSwap) {
00688     _sound->update();
00689     _gfx->clear();
00690 
00691     if (_state->getViewType() == kCube) {
00692         float pitch = _state->getLookAtPitch();
00693         float heading = _state->getLookAtHeading();
00694         float fov = _state->getLookAtFOV();
00695 
00696         // Apply the rotation effect
00697         if (_rotationEffect) {
00698             _rotationEffect->update();
00699 
00700             heading += _rotationEffect->getHeadingOffset();
00701             _state->lookAt(pitch, heading);
00702         }
00703 
00704         // Apply the shake effect
00705         if (_shakeEffect) {
00706             _shakeEffect->update();
00707             pitch += _shakeEffect->getPitchOffset();
00708             heading += _shakeEffect->getHeadingOffset();
00709         }
00710 
00711         _gfx->setupCameraPerspective(pitch, heading, fov);
00712     }
00713 
00714     if (_node) {
00715         _node->update();
00716         _gfx->renderDrawable(_node, _scene);
00717     }
00718 
00719     for (int i = _movies.size() - 1; i >= 0 ; i--) {
00720         _movies[i]->update();
00721         _gfx->renderDrawable(_movies[i], _scene);
00722     }
00723 
00724     if (_state->getViewType() == kMenu) {
00725         _gfx->renderDrawable(_menu, _scene);
00726     }
00727 
00728     for (uint i = 0; i < _drawables.size(); i++) {
00729         _gfx->renderDrawable(_drawables[i], _scene);
00730     }
00731 
00732     if (_state->getViewType() != kMenu) {
00733         float pitch = _state->getLookAtPitch();
00734         float heading = _state->getLookAtHeading();
00735         SunSpot flare = computeSunspotsIntensity(pitch, heading);
00736         if (flare.intensity >= 0)
00737             _scene->drawSunspotFlare(flare);
00738     }
00739 
00740     if (isInventoryVisible()) {
00741         _gfx->renderWindow(_inventory);
00742     }
00743 
00744     // Draw overlay 2D movies
00745     for (int i = _movies.size() - 1; i >= 0 ; i--) {
00746         _gfx->renderDrawableOverlay(_movies[i], _scene);
00747     }
00748 
00749     for (uint i = 0; i < _drawables.size(); i++) {
00750         _gfx->renderDrawableOverlay(_drawables[i], _scene);
00751     }
00752 
00753     // Draw spot subtitles
00754     if (_node) {
00755         _gfx->renderDrawableOverlay(_node, _scene);
00756     }
00757 
00758     bool cursorVisible = _cursor->isVisible();
00759 
00760     if (getPlatform() == Common::kPlatformXbox) {
00761         // The cursor is not drawn in the Xbox version menus and journals
00762         cursorVisible &= !(_state->getLocationRoom() == kRoomMenu || _state->getLocationRoom() == kRoomJournals);
00763     }
00764 
00765     if (cursorVisible)
00766         _gfx->renderDrawable(_cursor, _scene);
00767 
00768     _gfx->flipBuffer();
00769 
00770     if (!noSwap) {
00771         _frameLimiter->delayBeforeSwap();
00772         _system->updateScreen();
00773         _state->updateFrameCounters();
00774         _frameLimiter->startFrame();
00775     }
00776 }
00777 
00778 bool Myst3Engine::isInventoryVisible() {
00779     if (_state->getViewType() == kMenu)
00780         return false;
00781 
00782     if (_node && _node->hasSubtitlesToDraw())
00783         return false;
00784 
00785     if (_inventoryManualHide) {
00786         return false;
00787     }
00788 
00789     // Only draw the inventory when the mouse is inside its area
00790     if (isWideScreenModEnabled() && !_inventory->isMouseInside()) {
00791         return false;
00792     }
00793 
00794     return true;
00795 }
00796 
00797 void Myst3Engine::setupTransition() {
00798     delete _transition;
00799     _transition = new Transition(this);
00800 }
00801 
00802 void Myst3Engine::drawTransition(TransitionType transitionType) {
00803     if (_transition) {
00804         _interactive = false; // Don't allow loading while drawing transitions
00805         _transition->draw(transitionType);
00806         delete _transition;
00807         _transition = nullptr;
00808     }
00809 }
00810 
00811 void Myst3Engine::goToNode(uint16 nodeID, TransitionType transitionType) {
00812     uint16 node = _state->getLocationNextNode();
00813     if (!node)
00814         node = nodeID;
00815 
00816     uint16 room = _state->getLocationNextRoom();
00817     uint16 age = _state->getLocationNextAge();
00818 
00819     setupTransition();
00820 
00821     ViewType sourceViewType = _state->getViewType();
00822     if (sourceViewType == kCube) {
00823         // The lookat direction in the next node should be
00824         // the direction of the mouse cursor
00825         float pitch, heading;
00826         _cursor->getDirection(pitch, heading);
00827         _state->lookAt(pitch, heading);
00828     }
00829 
00830     loadNode(node, room, age);
00831 
00832     _state->setLocationNextNode(0);
00833     _state->setLocationNextRoom(0);
00834     _state->setLocationNextAge(0);
00835 
00836     if (_state->getAmbiantPreviousFadeOutDelay() > 0) {
00837         _ambient->playCurrentNode(100, _state->getAmbiantPreviousFadeOutDelay());
00838     }
00839 
00840     drawTransition(transitionType);
00841 }
00842 
00843 void Myst3Engine::loadNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
00844     unloadNode();
00845 
00846     _scriptEngine->run(&_db->getNodeInitScript());
00847 
00848     if (nodeID)
00849         _state->setLocationNode(_state->valueOrVarValue(nodeID));
00850 
00851     if (roomID)
00852         _state->setLocationRoom(_state->valueOrVarValue(roomID));
00853     else
00854         roomID = _state->getLocationRoom();
00855 
00856     if (ageID)
00857         _state->setLocationAge(_state->valueOrVarValue(ageID));
00858     else
00859         ageID = _state->getLocationAge();
00860 
00861     _db->cacheRoom(roomID, ageID);
00862 
00863     Common::String newRoomName = _db->getRoomName(roomID, ageID);
00864     if ((!_archiveNode || _archiveNode->getRoomName() != newRoomName) && !_db->isCommonRoom(roomID, ageID)) {
00865 
00866         Common::String nodeFile = Common::String::format("%snodes.m3a", newRoomName.c_str());
00867 
00868         _archiveNode->close();
00869         if (!_archiveNode->open(nodeFile.c_str(), newRoomName.c_str())) {
00870             error("Unable to open archive %s", nodeFile.c_str());
00871         }
00872     }
00873 
00874     runNodeInitScripts();
00875 
00876     // The effects can only be created after running the node init scripts
00877     _node->initEffects();
00878     _shakeEffect = ShakeEffect::create(this);
00879     _rotationEffect = RotationEffect::create(this);
00880 
00881     // WORKAROUND: In Narayan, the scripts in node NACH 9 test on var 39
00882     // without first reinitializing it leading to Saavedro not always giving
00883     // Releeshan to the player when he is trapped between both shields.
00884     if (nodeID == 9 && roomID == kRoomNarayan)
00885         _state->setVar(39, 0);
00886 }
00887 
00888 void Myst3Engine::unloadNode() {
00889     if (!_node)
00890         return;
00891 
00892     // Delete all movies
00893     removeMovie(0);
00894 
00895     // Remove all sunspots
00896     for (uint i = 0; i < _sunspots.size(); i++)
00897         delete _sunspots[i];
00898 
00899     _sunspots.clear();
00900 
00901     // Clean up the effects
00902     delete _shakeEffect;
00903     _shakeEffect = nullptr;
00904     _state->setShakeEffectAmpl(0);
00905     delete _rotationEffect;
00906     _rotationEffect = nullptr;
00907 
00908     delete _node;
00909     _node = nullptr;
00910 }
00911 
00912 void Myst3Engine::runNodeInitScripts() {
00913     NodePtr nodeData = _db->getNodeData(
00914             _state->getLocationNode(),
00915             _state->getLocationRoom(),
00916             _state->getLocationAge());
00917 
00918     NodePtr nodeDataInit = _db->getNodeData(
00919             32765,
00920             _state->getLocationRoom(),
00921             _state->getLocationAge());
00922 
00923     if (nodeDataInit)
00924         runScriptsFromNode(32765);
00925 
00926     if (!nodeData)
00927         error("Node %d unknown in the database", _state->getLocationNode());
00928 
00929     for (uint j = 0; j < nodeData->scripts.size(); j++) {
00930         if (_state->evaluate(nodeData->scripts[j].condition)) {
00931             _scriptEngine->run(&nodeData->scripts[j].script);
00932         }
00933     }
00934 
00935     // Mark the node as a reachable zip destination
00936     _state->markNodeAsVisited(
00937             _state->getLocationNode(),
00938             _state->getLocationRoom(),
00939             _state->getLocationAge());
00940 }
00941 
00942 void Myst3Engine::runNodeBackgroundScripts() {
00943     NodePtr nodeDataRoom = _db->getNodeData(32765, _state->getLocationRoom(), _state->getLocationAge());
00944 
00945     if (nodeDataRoom) {
00946         for (uint j = 0; j < nodeDataRoom->hotspots.size(); j++) {
00947             if (nodeDataRoom->hotspots[j].condition == -1) {
00948                 if (!_scriptEngine->run(&nodeDataRoom->hotspots[j].script))
00949                     break;
00950             }
00951         }
00952     }
00953 
00954     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
00955 
00956     for (uint j = 0; j < nodeData->hotspots.size(); j++) {
00957         if (nodeData->hotspots[j].condition == -1) {
00958             if (!_scriptEngine->run(&nodeData->hotspots[j].script))
00959                 break;
00960         }
00961     }
00962 }
00963 
00964 void Myst3Engine::loadNodeCubeFaces(uint16 nodeID) {
00965     _state->setViewType(kCube);
00966 
00967     _cursor->lockPosition(true);
00968     updateCursor();
00969 
00970     _node = new NodeCube(this, nodeID);
00971 }
00972 
00973 void Myst3Engine::loadNodeFrame(uint16 nodeID) {
00974     _state->setViewType(kFrame);
00975 
00976     _cursor->lockPosition(false);
00977     updateCursor();
00978 
00979     _node = new NodeFrame(this, nodeID);
00980 }
00981 
00982 void Myst3Engine::loadNodeMenu(uint16 nodeID) {
00983     _state->setViewType(kMenu);
00984 
00985     _cursor->lockPosition(false);
00986     updateCursor();
00987 
00988     _node = new NodeFrame(this, nodeID);
00989 }
00990 
00991 void Myst3Engine::runScriptsFromNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
00992     if (roomID == 0)
00993         roomID = _state->getLocationRoom();
00994 
00995     if (ageID == 0)
00996         ageID = _state->getLocationAge();
00997 
00998     NodePtr nodeData = _db->getNodeData(nodeID, roomID, ageID);
00999 
01000     for (uint j = 0; j < nodeData->scripts.size(); j++) {
01001         if (_state->evaluate(nodeData->scripts[j].condition)) {
01002             if (!_scriptEngine->run(&nodeData->scripts[j].script))
01003                 break;
01004         }
01005     }
01006 }
01007 
01008 void Myst3Engine::runBackgroundSoundScriptsFromNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
01009     if (_state->getSoundScriptsSuspended())
01010         return;
01011 
01012     if (roomID == 0)
01013         roomID = _state->getLocationRoom();
01014 
01015     if (ageID == 0)
01016         ageID = _state->getLocationAge();
01017 
01018     NodePtr nodeData = _db->getNodeData(nodeID, roomID, ageID);
01019 
01020     if (!nodeData) return;
01021 
01022     if (_backgroundSoundScriptLastRoomId != roomID || _backgroundSoundScriptLastAgeId != ageID) {
01023         bool sameScript;
01024         if (   _backgroundSoundScriptLastRoomId != 0 && roomID != 0
01025             && _backgroundSoundScriptLastAgeId  != 0 && ageID  != 0) {
01026             sameScript = _db->areRoomsScriptsEqual(_backgroundSoundScriptLastRoomId, _backgroundSoundScriptLastAgeId,
01027                                                    roomID, ageID, kScriptTypeBackgroundSound);
01028         } else {
01029             sameScript = false;
01030         }
01031 
01032         // Stop previous music when the music script changes
01033         if (!sameScript
01034             && _backgroundSoundScriptLastRoomId != kRoomMenu
01035             && _backgroundSoundScriptLastRoomId != kRoomJournals
01036             && roomID != kRoomMenu
01037             && roomID != kRoomJournals) {
01038 
01039             _sound->stopMusic(_state->getSoundScriptFadeOutDelay());
01040 
01041             if (!nodeData->backgroundSoundScripts.empty()) {
01042                 _state->setSoundScriptsPaused(1);
01043                 _state->setSoundScriptsTimer(0);
01044             }
01045         }
01046 
01047         _backgroundSoundScriptLastRoomId = roomID;
01048         _backgroundSoundScriptLastAgeId  = ageID;
01049     }
01050 
01051     for (uint j = 0; j < nodeData->backgroundSoundScripts.size(); j++) {
01052         if (_state->evaluate(nodeData->backgroundSoundScripts[j].condition)) {
01053             if (!_scriptEngine->run(&nodeData->backgroundSoundScripts[j].script))
01054                 break;
01055         }
01056     }
01057 }
01058 
01059 void Myst3Engine::runAmbientScripts(uint32 node) {
01060     uint32 room = _ambient->_scriptRoom;
01061     uint32 age = _ambient->_scriptAge;
01062 
01063     if (room == 0)
01064         room = _state->getLocationRoom();
01065 
01066     if (age == 0)
01067         age = _state->getLocationAge();
01068 
01069     NodePtr nodeData = _db->getNodeData(node, room, age);
01070 
01071     if (!nodeData) return;
01072 
01073     for (uint j = 0; j < nodeData->soundScripts.size(); j++)
01074         if (_state->evaluate(nodeData->soundScripts[j].condition))
01075             _scriptEngine->run(&nodeData->soundScripts[j].script);
01076 }
01077 
01078 void Myst3Engine::loadMovie(uint16 id, uint16 condition, bool resetCond, bool loop) {
01079     ScriptedMovie *movie;
01080 
01081     if (!_state->getMovieUseBackground()) {
01082         movie = new ScriptedMovie(this, id);
01083     } else {
01084         movie = new ProjectorMovie(this, id, _projectorBackground);
01085         _projectorBackground = 0;
01086         _state->setMovieUseBackground(0);
01087     }
01088 
01089     movie->setCondition(condition);
01090     movie->setDisableWhenComplete(resetCond);
01091     movie->setLoop(loop);
01092 
01093     if (_state->getMovieScriptDriven()) {
01094         movie->setScriptDriven(_state->getMovieScriptDriven());
01095         _state->setMovieScriptDriven(0);
01096     }
01097 
01098     if (_state->getMovieStartFrameVar()) {
01099         movie->setStartFrameVar(_state->getMovieStartFrameVar());
01100         _state->setMovieStartFrameVar(0);
01101     }
01102 
01103     if (_state->getMovieEndFrameVar()) {
01104         movie->setEndFrameVar(_state->getMovieEndFrameVar());
01105         _state->setMovieEndFrameVar(0);
01106     }
01107 
01108     if (_state->getMovieStartFrame()) {
01109         movie->setStartFrame(_state->getMovieStartFrame());
01110         _state->setMovieStartFrame(0);
01111     }
01112 
01113     if (_state->getMovieEndFrame()) {
01114         movie->setEndFrame(_state->getMovieEndFrame());
01115         _state->setMovieEndFrame(0);
01116     }
01117 
01118     if (_state->getMovieNextFrameGetVar()) {
01119         movie->setNextFrameReadVar(_state->getMovieNextFrameGetVar());
01120         _state->setMovieNextFrameGetVar(0);
01121     }
01122 
01123     if (_state->getMovieNextFrameSetVar()) {
01124         movie->setNextFrameWriteVar(_state->getMovieNextFrameSetVar());
01125         _state->setMovieNextFrameSetVar(0);
01126     }
01127 
01128     if (_state->getMoviePlayingVar()) {
01129         movie->setPlayingVar(_state->getMoviePlayingVar());
01130         _state->setMoviePlayingVar(0);
01131     }
01132 
01133     if (_state->getMovieOverridePosition()) {
01134         movie->setPosU(_state->getMovieOverridePosU());
01135         movie->setPosV(_state->getMovieOverridePosV());
01136         _state->setMovieOverridePosition(0);
01137     }
01138 
01139     if (_state->getMovieUVar()) {
01140         movie->setPosUVar(_state->getMovieUVar());
01141         _state->setMovieUVar(0);
01142     }
01143 
01144     if (_state->getMovieVVar()) {
01145         movie->setPosVVar(_state->getMovieVVar());
01146         _state->setMovieVVar(0);
01147     }
01148 
01149     if (_state->getMovieOverrideCondition()) {
01150         movie->setCondition(_state->getMovieOverrideCondition());
01151         _state->setMovieOverrideCondition(0);
01152     }
01153 
01154     if (_state->getMovieConditionBit()) {
01155         movie->setConditionBit(_state->getMovieConditionBit());
01156         _state->setMovieConditionBit(0);
01157     }
01158 
01159     if (_state->getMovieForce2d()) {
01160         movie->setForce2d(_state->getMovieForce2d());
01161         _state->setMovieForce2d(0);
01162     }
01163 
01164     if (_state->getMovieVolume1()) {
01165         movie->setVolume(_state->getMovieVolume1());
01166         _state->setMovieVolume1(0);
01167     } else {
01168         movie->setVolume(_state->getMovieVolume2());
01169     }
01170 
01171     if (_state->getMovieVolumeVar()) {
01172         movie->setVolumeVar(_state->getMovieVolumeVar());
01173         _state->setMovieVolumeVar(0);
01174     }
01175 
01176     if (_state->getMovieSoundHeading()) {
01177         movie->setSoundHeading(_state->getMovieSoundHeading());
01178         _state->setMovieSoundHeading(0);
01179     }
01180 
01181     if (_state->getMoviePanningStrenght()) {
01182         movie->setSoundAttenuation(_state->getMoviePanningStrenght());
01183         _state->setMoviePanningStrenght(0);
01184     }
01185 
01186     if (_state->getMovieAdditiveBlending()) {
01187         movie->setAdditiveBlending(true);
01188         _state->setMovieAdditiveBlending(0);
01189     }
01190 
01191     if (_state->getMovieTransparency()) {
01192         movie->setTransparency(_state->getMovieTransparency());
01193         _state->setMovieTransparency(0);
01194     } else {
01195         movie->setTransparency(100);
01196     }
01197 
01198     if (_state->getMovieTransparencyVar()) {
01199         movie->setTransparencyVar(_state->getMovieTransparencyVar());
01200         _state->setMovieTransparencyVar(0);
01201     }
01202 
01203     _movies.push_back(movie);
01204 }
01205 
01206 void Myst3Engine::playSimpleMovie(uint16 id, bool fullframe, bool refreshAmbientSounds) {
01207     SimpleMovie movie(this, id);
01208 
01209     if (!movie.isVideoLoaded()) {
01210         // The video was not loaded and it was optional, just do nothing
01211         return;
01212     }
01213 
01214     if (_state->getMovieSynchronized()) {
01215         movie.setSynchronized(_state->getMovieSynchronized());
01216         _state->setMovieSynchronized(0);
01217     }
01218 
01219     if (_state->getMovieStartFrame()) {
01220         movie.setStartFrame(_state->getMovieStartFrame());
01221         _state->setMovieStartFrame(0);
01222     }
01223 
01224     if (_state->getMovieEndFrame()) {
01225         movie.setEndFrame(_state->getMovieEndFrame());
01226         _state->setMovieEndFrame(0);
01227     }
01228 
01229     if (_state->getMovieVolume1()) {
01230         movie.setVolume(_state->getMovieVolume1());
01231         _state->setMovieVolume1(0);
01232     } else {
01233         movie.setVolume(_state->getMovieVolume2());
01234     }
01235 
01236     if (fullframe) {
01237         movie.setForce2d(_state->getViewType() == kCube);
01238         movie.setForceOpaque(true);
01239         movie.setPosU(0);
01240         movie.setPosV(_state->getViewType() == kMenu ? Renderer::kTopBorderHeight : 0);
01241     }
01242 
01243     movie.play();
01244 
01245     if (refreshAmbientSounds) {
01246         movie.refreshAmbientSounds();
01247     }
01248 
01249     _drawables.push_back(&movie);
01250 
01251     while (!shouldQuit() && !movie.endOfVideo()) {
01252         movie.update();
01253 
01254         // Process events
01255         processInput(false);
01256 
01257         // Handle skipping
01258         if (_inputSpacePressed || _inputEscapePressed) {
01259             // Consume the escape key press so the menu does not open
01260             _inputEscapePressedNotConsumed = false;
01261             break;
01262         }
01263 
01264         drawFrame();
01265     }
01266 
01267     _drawables.pop_back();
01268 
01269     // Reset the movie script so that the next movie will not try to run them
01270     // when the user has skipped this one before the script is triggered.
01271     _state->setMovieScriptStartFrame(0);
01272     _state->setMovieScript(0);
01273     _state->setMovieAmbiantScriptStartFrame(0);
01274     _state->setMovieAmbiantScript(0);
01275 }
01276 
01277 void Myst3Engine::removeMovie(uint16 id) {
01278     if (id == 0) {
01279         for (uint i = 0; i < _movies.size(); i++)
01280             delete _movies[i];
01281 
01282         _movies.clear();
01283         return;
01284     } else {
01285         for (uint i = 0; i < _movies.size(); i++)
01286             if (_movies[i]->getId() == id) {
01287                 delete _movies[i];
01288                 _movies.remove_at(i);
01289                 break;
01290             }
01291     }
01292 }
01293 
01294 void Myst3Engine::setMovieLooping(uint16 id, bool loop) {
01295     for (uint i = 0; i < _movies.size(); i++) {
01296         if (_movies[i]->getId() == id) {
01297             // Enable or disable looping
01298             _movies[i]->setLoop(loop);
01299             _movies[i]->setDisableWhenComplete(!loop);
01300             break;
01301         }
01302     }
01303 }
01304 
01305 void Myst3Engine::addSpotItem(uint16 id, int16 condition, bool fade) {
01306     assert(_node);
01307 
01308     _node->loadSpotItem(id, condition, fade);
01309 }
01310 
01311 SpotItemFace *Myst3Engine::addMenuSpotItem(uint16 id, int16 condition, const Common::Rect &rect) {
01312     assert(_node);
01313 
01314     SpotItemFace *face = _node->loadMenuSpotItem(condition, rect);
01315 
01316     _menu->setSaveLoadSpotItem(id, face);
01317 
01318     return face;
01319 }
01320 
01321 void Myst3Engine::loadNodeSubtitles(uint32 id) {
01322     assert(_node);
01323 
01324     _node->loadSubtitles(id);
01325 }
01326 
01327 const DirectorySubEntry *Myst3Engine::getFileDescription(const Common::String &room, uint32 index, uint16 face,
01328                                                          DirectorySubEntry::ResourceType type) {
01329     Common::String archiveRoom = room;
01330     if (archiveRoom == "") {
01331         archiveRoom = _db->getRoomName(_state->getLocationRoom(), _state->getLocationAge());
01332     }
01333 
01334     const DirectorySubEntry *desc = 0;
01335 
01336     // Search common archives
01337     uint i = 0;
01338     while (!desc && i < _archivesCommon.size()) {
01339         desc = _archivesCommon[i]->getDescription(archiveRoom, index, face, type);
01340         i++;
01341     }
01342 
01343     // Search currently loaded node archive
01344     if (!desc && _archiveNode)
01345         desc = _archiveNode->getDescription(archiveRoom, index, face, type);
01346 
01347     return desc;
01348 }
01349 
01350 DirectorySubEntryList Myst3Engine::listFilesMatching(const Common::String &room, uint32 index, uint16 face,
01351                                                      DirectorySubEntry::ResourceType type) {
01352     Common::String archiveRoom = room;
01353     if (archiveRoom == "") {
01354         archiveRoom = _db->getRoomName(_state->getLocationRoom(), _state->getLocationAge());
01355     }
01356 
01357     for (uint i = 0; i < _archivesCommon.size(); i++) {
01358         DirectorySubEntryList list = _archivesCommon[i]->listFilesMatching(archiveRoom, index, face, type);
01359         if (!list.empty()) {
01360             return list;
01361         }
01362     }
01363 
01364     return _archiveNode->listFilesMatching(archiveRoom, index, face, type);
01365 }
01366 
01367 Graphics::Surface *Myst3Engine::loadTexture(uint16 id) {
01368     const DirectorySubEntry *desc = getFileDescription("GLOB", id, 0, DirectorySubEntry::kRawData);
01369 
01370     if (!desc)
01371         error("Texture %d does not exist", id);
01372 
01373     Common::MemoryReadStream *data = desc->getData();
01374 
01375     uint32 magic = data->readUint32LE();
01376     if (magic != MKTAG('.', 'T', 'E', 'X'))
01377         error("Wrong texture format %d", id);
01378 
01379     data->readUint32LE(); // unk 1
01380     uint32 width = data->readUint32LE();
01381     uint32 height = data->readUint32LE();
01382     data->readUint32LE(); // unk 2
01383     data->readUint32LE(); // unk 3
01384 
01385 #ifdef SCUMM_BIG_ENDIAN
01386     Graphics::PixelFormat onDiskFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 24, 16, 8);
01387 #else
01388     Graphics::PixelFormat onDiskFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0);
01389 #endif
01390 
01391     Graphics::Surface *s = new Graphics::Surface();
01392     s->create(width, height, onDiskFormat);
01393 
01394     data->read(s->getPixels(), height * s->pitch);
01395     delete data;
01396 
01397     s->convertToInPlace(Texture::getRGBAPixelFormat());
01398 
01399     return s;
01400 }
01401 
01402 Graphics::Surface *Myst3Engine::decodeJpeg(const DirectorySubEntry *jpegDesc) {
01403     Common::MemoryReadStream *jpegStream = jpegDesc->getData();
01404 
01405     Image::JPEGDecoder jpeg;
01406     if (!jpeg.loadStream(*jpegStream))
01407         error("Could not decode Myst III JPEG");
01408     delete jpegStream;
01409 
01410     const Graphics::Surface *bitmap = jpeg.getSurface();
01411     assert(bitmap->format == Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
01412 
01413     // Convert the surface to RGBA if needed.
01414     // RGBA being the order of the bytes in memory, not in a 32 bits word.
01415     Graphics::PixelFormat rgbaFormat = Texture::getRGBAPixelFormat();
01416     Graphics::Surface *rgbaSurface = new Graphics::Surface();
01417 
01418 #ifdef SCUMM_BIG_ENDIAN
01419     assert(rgbaFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
01420     rgbaSurface->copyFrom(*bitmap);
01421     rgbaSurface->format = rgbaFormat;
01422 #else
01423     assert(rgbaFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
01424 
01425     rgbaSurface->create(bitmap->w, bitmap->h, rgbaFormat);
01426 
01427     // Use SWAP_BYTES_32 as an optimization. This path is hot.
01428     for (uint y = 0; y < bitmap->h; y++) {
01429         const uint32 *srcRow = (const uint32 *) bitmap->getBasePtr(0, y);
01430         uint32 *dstRow = (uint32 *) rgbaSurface->getBasePtr(0, y);
01431         for (uint x = 0; x < bitmap->w; x++) {
01432             *dstRow++ = SWAP_BYTES_32(*srcRow++);
01433         }
01434     }
01435 #endif
01436 
01437     return rgbaSurface;
01438 }
01439 
01440 int16 Myst3Engine::openDialog(uint16 id) {
01441     Dialog *dialog;
01442 
01443     if (getPlatform() == Common::kPlatformXbox) {
01444         dialog = new GamepadDialog(this, id);
01445     } else {
01446         dialog = new ButtonsDialog(this, id);
01447     }
01448 
01449     _drawables.push_back(dialog);
01450 
01451     int16 result = -2;
01452 
01453     while (result == -2 && !shouldQuit()) {
01454         result = dialog->update();
01455         drawFrame();
01456     }
01457 
01458     _drawables.pop_back();
01459 
01460     delete dialog;
01461 
01462     return result;
01463 }
01464 
01465 void Myst3Engine::dragSymbol(uint16 var, uint16 id) {
01466     DragItem drag(this, id);
01467 
01468     _drawables.push_back(&drag);
01469 
01470     _cursor->changeCursor(2);
01471     _state->setVar(var, -1);
01472 
01473     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
01474 
01475     while (inputValidatePressed() && !shouldQuit()) {
01476         processInput(false);
01477 
01478         HotSpot *hovered = getHoveredHotspot(nodeData, var);
01479         drag.setFrame(hovered ? 2 : 1);
01480 
01481         drawFrame();
01482     }
01483 
01484     _state->setVar(var, 1);
01485     _drawables.pop_back();
01486 
01487     HotSpot *hovered = getHoveredHotspot(nodeData, var);
01488     if (hovered) {
01489         _cursor->setVisible(false);
01490         _scriptEngine->run(&hovered->script);
01491         _cursor->setVisible(true);
01492     }
01493 }
01494 
01495 void Myst3Engine::dragItem(uint16 statusVar, uint16 movie, uint16 frame, uint16 hoverFrame, uint16 itemVar) {
01496     DragItem drag(this, movie);
01497 
01498     _drawables.push_back(&drag);
01499 
01500     _cursor->changeCursor(2);
01501     _state->setVar(statusVar, 0);
01502     _state->setVar(itemVar, 1);
01503 
01504     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
01505 
01506     while (inputValidatePressed() && !shouldQuit()) {
01507         processInput(false);
01508 
01509         HotSpot *hovered = getHoveredHotspot(nodeData, itemVar);
01510         drag.setFrame(hovered ? hoverFrame : frame);
01511 
01512         drawFrame();
01513     }
01514 
01515     _drawables.pop_back();
01516 
01517     HotSpot *hovered = getHoveredHotspot(nodeData, itemVar);
01518     if (hovered) {
01519         _cursor->setVisible(false);
01520         _scriptEngine->run(&hovered->script);
01521         _cursor->setVisible(true);
01522     } else {
01523         _state->setVar(statusVar, 1);
01524         _state->setVar(itemVar, 0);
01525     }
01526 }
01527 
01528 bool Myst3Engine::canSaveGameStateCurrently() {
01529     bool inMenuWithNoGameLoaded = _state->getLocationRoom() == kRoomMenu && _state->getMenuSavedAge() == 0;
01530     return canLoadGameStateCurrently() && !inMenuWithNoGameLoaded && _cursor->isVisible();
01531 }
01532 
01533 bool Myst3Engine::canLoadGameStateCurrently() {
01534     // Loading from the GMM is only possible when the game is interactive
01535     // This is to prevent loading from inner loops. Loading while
01536     // in an inner loop can cause the exit condition to never happen,
01537     // or can unload required resources.
01538     return _interactive;
01539 }
01540 
01541 void Myst3Engine::tryAutoSaving() {
01542     if (!canSaveGameStateCurrently()) {
01543         return; // Can't save right now, try again on the next frame
01544     }
01545 
01546     _lastSaveTime = _system->getMillis();
01547 
01548     // Get a thumbnail of the game screen
01549     if (!_menu->isOpen())
01550         _menu->generateSaveThumbnail();
01551 
01552     Common::Error result = saveGameState(0, "Autosave");
01553     if (result.getCode() != Common::kNoError) {
01554         warning("Unable to autosave: %s.", result.getDesc().c_str());
01555     }
01556 }
01557 
01558 Common::Error Myst3Engine::loadGameState(int slot) {
01559     Common::StringArray filenames = Saves::list(_saveFileMan, getPlatform());
01560     return loadGameState(filenames[slot], kTransitionNone);
01561 }
01562 
01563 Common::Error Myst3Engine::loadGameState(Common::String fileName, TransitionType transition) {
01564     Common::InSaveFile *saveFile = _saveFileMan->openForLoading(fileName);
01565     if (!saveFile) {
01566         return _saveFileMan->getError();
01567     }
01568 
01569     if (!_state->load(saveFile)) {
01570         delete saveFile;
01571         return Common::kUnknownError;
01572     }
01573     delete saveFile;
01574 
01575     _inventory->loadFromState();
01576 
01577     // Leave the load menu
01578     _state->setViewType(kMenu);
01579     _state->setLocationNextAge(_state->getMenuSavedAge());
01580     _state->setLocationNextRoom(_state->getMenuSavedRoom());
01581     _state->setLocationNextNode(_state->getMenuSavedNode());
01582     _state->setMenuSavedAge(0);
01583     _state->setMenuSavedRoom(0);
01584     _state->setMenuSavedNode(0);
01585     _sound->resetSoundVars();
01586     _sound->stopMusic(15);
01587     _state->setSoundScriptsSuspended(0);
01588     _sound->playEffect(696, 60);
01589 
01590     goToNode(0, transition);
01591     return Common::kNoError;
01592 }
01593 
01594 static bool isValidSaveFileChar(char c) {
01595     // Limit it to letters, digits, and a few other characters that should be safe
01596     return Common::isAlnum(c) || c == ' ' || c == '_' || c == '+' || c == '-' || c == '.';
01597 }
01598 
01599 static bool isValidSaveFileName(const Common::String &desc) {
01600     for (uint32 i = 0; i < desc.size(); i++)
01601         if (!isValidSaveFileChar(desc[i]))
01602             return false;
01603 
01604     return true;
01605 }
01606 
01607 Common::Error Myst3Engine::saveGameState(int slot, const Common::String &desc) {
01608     assert(!desc.empty());
01609 
01610     if (!isValidSaveFileName(desc)) {
01611         return Common::Error(Common::kCreatingFileFailed, _("Invalid file name for saving"));
01612     }
01613 
01614     // Strip extension
01615     Common::String saveName = desc;
01616     if (desc.hasSuffixIgnoreCase(".M3S") || desc.hasSuffixIgnoreCase(".M3X")) {
01617         saveName.erase(desc.size() - 4, desc.size());
01618     }
01619 
01620     // Try to use an already generated thumbnail
01621     const Graphics::Surface *thumbnail = _menu->borrowSaveThumbnail();
01622     if (!thumbnail) {
01623         return Common::Error(Common::kUnknownError, "No thumbnail");
01624     }
01625 
01626     Common::String fileName = Saves::buildName(saveName.c_str(), getPlatform());
01627     Common::ScopedPtr<Common::OutSaveFile> save(_saveFileMan->openForSaving(fileName));
01628     if (!save) {
01629         return _saveFileMan->getError();
01630     }
01631 
01632     // Save the state and the thumbnail
01633     if (!_state->save(save.get(), saveName, thumbnail)) {
01634         return Common::kUnknownError;
01635     }
01636 
01637     return Common::kNoError;
01638 }
01639 
01640 void Myst3Engine::animateDirectionChange(float targetPitch, float targetHeading, uint16 scriptTicks) {
01641     float startPitch = _state->getLookAtPitch();
01642     float startHeading = _state->getLookAtHeading();
01643 
01644     if (startPitch == targetPitch && startHeading == targetHeading)
01645         return; // Fast path
01646 
01647     float pitchDistance = targetPitch - startPitch;
01648     float headingDistance = targetHeading - startHeading;
01649 
01650     // Make sure to use the shortest direction
01651     while (ABS(headingDistance) > 180) {
01652         if (headingDistance > 0) {
01653             headingDistance -= 360;
01654         } else {
01655             headingDistance += 360;
01656         }
01657     }
01658 
01659     // Compute animation duration in frames
01660     float numTicks;
01661     if (scriptTicks) {
01662         numTicks = scriptTicks;
01663     } else {
01664         numTicks = sqrt(pitchDistance * pitchDistance + headingDistance * headingDistance)
01665                 * 30.0f / _state->getCameraMoveSpeed();
01666 
01667         if (numTicks > 0.0f)
01668             numTicks += 10.0f;
01669     }
01670 
01671     uint startTick = _state->getTickCount();
01672 
01673     // Draw animation
01674     if (numTicks != 0.0f) {
01675         while (1) {
01676             uint elapsedTicks = _state->getTickCount() - startTick;
01677             if (elapsedTicks >= numTicks || shouldQuit())
01678                 break;
01679 
01680             float step;
01681             if (numTicks >= 15) {
01682                 // Fast then slow movement
01683                 if (elapsedTicks > numTicks / 2.0f)
01684                     step = 1.0f - (numTicks - elapsedTicks) * (numTicks - elapsedTicks)
01685                                 / (numTicks / 2.0f * numTicks / 2.0f) / 2.0f;
01686                 else
01687                     step = elapsedTicks * elapsedTicks / (numTicks / 2.0f * numTicks / 2.0f) / 2.0f;
01688 
01689             } else {
01690                 // Constant speed movement
01691                 step = elapsedTicks / numTicks;
01692             }
01693 
01694             float nextPitch = startPitch + pitchDistance * step;
01695             float nextHeading = startHeading + headingDistance * step;
01696 
01697             _state->lookAt(nextPitch, nextHeading);
01698             drawFrame();
01699         }
01700     }
01701 
01702     _state->lookAt(targetPitch, targetHeading);
01703     drawFrame();
01704 }
01705 
01706 void Myst3Engine::getMovieLookAt(uint16 id, bool start, float &pitch, float &heading) {
01707     const DirectorySubEntry *desc = getFileDescription("", id, 0, DirectorySubEntry::kMovie);
01708 
01709     if (!desc)
01710         desc = getFileDescription("", id, 0, DirectorySubEntry::kMultitrackMovie);
01711 
01712     if (!desc)
01713         error("Movie %d does not exist", id);
01714 
01715     Math::Vector3d v;
01716     if (start)
01717         v = desc->getVideoData().v1;
01718     else
01719         v = desc->getVideoData().v2;
01720 
01721     Math::Vector2d horizontalProjection(v.x(), v.z());
01722     horizontalProjection.normalize();
01723 
01724     pitch = 90 - Math::Angle::arcCosine(v.y()).getDegrees();
01725     heading = Math::Angle::arcCosine(horizontalProjection.getY()).getDegrees();
01726 
01727     if (horizontalProjection.getX() < 0.0) {
01728         heading = 360 - heading;
01729     }
01730 }
01731 
01732 void Myst3Engine::playMovieGoToNode(uint16 movie, uint16 node) {
01733     uint16 room = _state->getLocationNextRoom();
01734     uint16 age = _state->getLocationNextAge();
01735 
01736     if (_state->getLocationNextNode()) {
01737         node = _state->getLocationNextNode();
01738     }
01739 
01740     if (_state->getViewType() == kCube && !_state->getCameraSkipAnimation()) {
01741         float startPitch, startHeading;
01742         getMovieLookAt(movie, true, startPitch, startHeading);
01743         animateDirectionChange(startPitch, startHeading, 0);
01744     }
01745     _state->setCameraSkipAnimation(0);
01746 
01747     loadNode(node, room, age);
01748 
01749     playSimpleMovie(movie, true, true);
01750 
01751     _state->setLocationNextNode(0);
01752     _state->setLocationNextRoom(0);
01753     _state->setLocationNextAge(0);
01754 
01755     if (_state->getViewType() == kCube) {
01756         float endPitch, endHeading;
01757         getMovieLookAt(movie, false, endPitch, endHeading);
01758         _state->lookAt(endPitch, endHeading);
01759     }
01760 
01761     setupTransition();
01762 }
01763 
01764 void Myst3Engine::playMovieFullFrame(uint16 movie) {
01765     if (_state->getViewType() == kCube && !_state->getCameraSkipAnimation()) {
01766         float startPitch, startHeading;
01767         getMovieLookAt(movie, true, startPitch, startHeading);
01768         animateDirectionChange(startPitch, startHeading, 0);
01769     }
01770     _state->setCameraSkipAnimation(0);
01771 
01772     playSimpleMovie(movie, true, false);
01773 
01774     if (_state->getViewType() == kCube) {
01775         float endPitch, endHeading;
01776         getMovieLookAt(movie, false, endPitch, endHeading);
01777         _state->lookAt(endPitch, endHeading);
01778     }
01779 
01780     setupTransition();
01781 }
01782 
01783 bool Myst3Engine::inputValidatePressed() {
01784     return _inputEnterPressed ||
01785             _inputSpacePressed ||
01786             getEventManager()->getButtonState() & Common::EventManager::LBUTTON;
01787 }
01788 
01789 bool Myst3Engine::inputEscapePressed() {
01790     return _inputEscapePressed;
01791 }
01792 
01793 bool Myst3Engine::inputSpacePressed() {
01794     return _inputSpacePressed;
01795 }
01796 
01797 bool Myst3Engine::inputTilePressed() {
01798     return _inputTildePressed;
01799 }
01800 
01801 void Myst3Engine::addSunSpot(uint16 pitch, uint16 heading, uint16 intensity,
01802         uint16 color, uint16 var, bool varControlledIntensity, uint16 radius) {
01803 
01804     SunSpot *s = new SunSpot();
01805 
01806     s->pitch = pitch;
01807     s->heading = heading;
01808     s->intensity = intensity * 2.55;
01809     s->color = (color & 0xF) | 16
01810             * ((color & 0xF) | 16
01811             * (((color >> 4) & 0xF) | 16
01812             * (((color >> 4) & 0xF) | 16
01813             * (((color >> 8) & 0xF) | 16
01814             * (((color >> 8) & 0xF))))));
01815     s->var = var;
01816     s->variableIntensity = varControlledIntensity;
01817     s->radius = radius;
01818 
01819     _sunspots.push_back(s);
01820 }
01821 
01822 SunSpot Myst3Engine::computeSunspotsIntensity(float pitch, float heading) {
01823     SunSpot result;
01824     result.intensity = -1;
01825     result.color = 0;
01826     result.radius = 0;
01827 
01828     for (uint i = 0; i < _sunspots.size(); i++) {
01829         SunSpot *s = _sunspots[i];
01830 
01831         uint32 value = _state->getVar(s->var);
01832 
01833         // Skip disabled items
01834         if (value == 0) continue;
01835 
01836         float distance = _scene->distanceToZone(s->heading, s->pitch, s->radius, heading, pitch);
01837 
01838         if (distance > result.radius) {
01839             result.radius = distance;
01840             result.color = s->color;
01841             result.intensity = s->intensity;
01842             result.variableIntensity = s->variableIntensity;
01843 
01844             if (result.variableIntensity) {
01845                 result.radius = value * distance / 100;
01846             }
01847         }
01848     }
01849 
01850     return result;
01851 }
01852 
01853 void Myst3Engine::settingsInitDefaults() {
01854     int defaultLanguage = _db->getGameLanguageCode();
01855 
01856     int defaultTextLanguage;
01857     if (getGameLocalizationType() == kLocMulti6)
01858         defaultTextLanguage = defaultLanguage;
01859     else
01860         defaultTextLanguage = getGameLanguage() != Common::EN_ANY;
01861 
01862     ConfMan.registerDefault("overall_volume", Audio::Mixer::kMaxMixerVolume);
01863     ConfMan.registerDefault("music_volume", Audio::Mixer::kMaxMixerVolume / 2);
01864     ConfMan.registerDefault("music_frequency", 75);
01865     ConfMan.registerDefault("audio_language", defaultLanguage);
01866     ConfMan.registerDefault("text_language", defaultTextLanguage);
01867     ConfMan.registerDefault("water_effects", true);
01868     ConfMan.registerDefault("transition_speed", 50);
01869     ConfMan.registerDefault("mouse_speed", 50);
01870     ConfMan.registerDefault("mouse_inverted", false);
01871     ConfMan.registerDefault("zip_mode", false);
01872     ConfMan.registerDefault("subtitles", false);
01873     ConfMan.registerDefault("vibrations", true); // Xbox specific
01874 }
01875 
01876 void Myst3Engine::settingsLoadToVars() {
01877     _state->setWaterEffects(ConfMan.getBool("water_effects"));
01878     _state->setTransitionSpeed(ConfMan.getInt("transition_speed"));
01879     _state->setMouseSpeed(ConfMan.getInt("mouse_speed"));
01880     _state->setZipModeEnabled(ConfMan.getBool("zip_mode"));
01881     _state->setSubtitlesEnabled(ConfMan.getBool("subtitles"));
01882 
01883     if (getPlatform() != Common::kPlatformXbox) {
01884         _state->setOverallVolume(CLIP<uint>(ConfMan.getInt("overall_volume") * 100 / 256, 0, 100));
01885         _state->setMusicVolume(CLIP<uint>(ConfMan.getInt("music_volume") * 100 / 256, 0, 100));
01886         _state->setMusicFrequency(ConfMan.getInt("music_frequency"));
01887         _state->setLanguageAudio(ConfMan.getInt("audio_language"));
01888         _state->setLanguageText(ConfMan.getInt("text_language"));
01889     } else {
01890         _state->setVibrationEnabled(ConfMan.getBool("vibrations"));
01891     }
01892 }
01893 
01894 void Myst3Engine::settingsApplyFromVars() {
01895     int32 oldTextLanguage = ConfMan.getInt("text_language");
01896 
01897     ConfMan.setInt("transition_speed", _state->getTransitionSpeed());
01898     ConfMan.setInt("mouse_speed", _state->getMouseSpeed());
01899     ConfMan.setBool("zip_mode", _state->getZipModeEnabled());
01900     ConfMan.setBool("subtitles", _state->getSubtitlesEnabled());
01901 
01902     if (getPlatform() != Common::kPlatformXbox) {
01903         ConfMan.setInt("overall_volume", _state->getOverallVolume() * 256 / 100);
01904         ConfMan.setInt("music_volume", _state->getMusicVolume() * 256 / 100);
01905         ConfMan.setInt("music_frequency", _state->getMusicFrequency());
01906         ConfMan.setInt("audio_language", _state->getLanguageAudio());
01907         ConfMan.setInt("text_language", _state->getLanguageText());
01908         ConfMan.setBool("water_effects", _state->getWaterEffects());
01909 
01910         // The language changed, reload the correct archives
01911         if (_state->getLanguageText() != oldTextLanguage) {
01912             closeArchives();
01913             openArchives();
01914         }
01915     } else {
01916         ConfMan.setBool("vibrations", _state->getVibrationEnabled());
01917     }
01918 
01919     // Mouse speed may have changed, refresh it
01920     _scene->updateMouseSpeed();
01921 
01922     syncSoundSettings();
01923 }
01924 
01925 void Myst3Engine::syncSoundSettings() {
01926     Engine::syncSoundSettings();
01927 
01928     uint soundOverall = ConfMan.getInt("overall_volume");
01929     uint soundVolumeMusic = ConfMan.getInt("music_volume");
01930 
01931     _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundOverall);
01932     _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic * soundOverall / 256);
01933 }
01934 
01935 bool Myst3Engine::isWideScreenModEnabled() const {
01936     return ConfMan.getBool("widescreen_mod");
01937 }
01938 
01939 void Myst3Engine::pauseEngineIntern(bool pause) {
01940     Engine::pauseEngineIntern(pause);
01941 
01942     if (!_state || !_cursor) {
01943         // This method may be called before the engine is fully initialized
01944         return;
01945     }
01946 
01947     for (uint i = 0; i < _movies.size(); i++) {
01948         _movies[i]->pause(pause);
01949     }
01950 
01951     _state->pauseEngine(pause);
01952 
01953     // Grab a game screen thumbnail in case we need one when writing a save file
01954     if (pause && !_menu->isOpen()) {
01955         // Opening the menu generates a save thumbnail so we only generate it if the menu is not open
01956         _menu->generateSaveThumbnail();
01957     }
01958 
01959     // Unlock the mouse so that the cursor is visible when the GMM opens
01960     if (_state->getViewType() == kCube && _cursor->isPositionLocked()) {
01961         _system->lockMouse(!pause);
01962     }
01963 
01964     // The user may have moved the mouse or resized the screen while the engine was paused
01965     if (!pause) {
01966         _gfx->computeScreenViewport();
01967         _cursor->updatePosition(_eventMan->getMousePos());
01968         _inventory->reflow();
01969     }
01970 }
01971 
01972 } // end of namespace Myst3


Generated on Sat Mar 16 2019 05:01:48 for ResidualVM by doxygen 1.7.1
curved edge   curved edge