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     if (!_node) {
00876         return; // The main init script does not load a node
00877     }
00878 
00879     // The effects can only be created after running the node init scripts
00880     _node->initEffects();
00881     _shakeEffect = ShakeEffect::create(this);
00882     _rotationEffect = RotationEffect::create(this);
00883 
00884     // WORKAROUND: In Narayan, the scripts in node NACH 9 test on var 39
00885     // without first reinitializing it leading to Saavedro not always giving
00886     // Releeshan to the player when he is trapped between both shields.
00887     if (nodeID == 9 && roomID == kRoomNarayan)
00888         _state->setVar(39, 0);
00889 }
00890 
00891 void Myst3Engine::unloadNode() {
00892     if (!_node)
00893         return;
00894 
00895     // Delete all movies
00896     removeMovie(0);
00897 
00898     // Remove all sunspots
00899     for (uint i = 0; i < _sunspots.size(); i++)
00900         delete _sunspots[i];
00901 
00902     _sunspots.clear();
00903 
00904     // Clean up the effects
00905     delete _shakeEffect;
00906     _shakeEffect = nullptr;
00907     _state->setShakeEffectAmpl(0);
00908     delete _rotationEffect;
00909     _rotationEffect = nullptr;
00910 
00911     delete _node;
00912     _node = nullptr;
00913 }
00914 
00915 void Myst3Engine::runNodeInitScripts() {
00916     NodePtr nodeData = _db->getNodeData(
00917             _state->getLocationNode(),
00918             _state->getLocationRoom(),
00919             _state->getLocationAge());
00920 
00921     NodePtr nodeDataInit = _db->getNodeData(
00922             32765,
00923             _state->getLocationRoom(),
00924             _state->getLocationAge());
00925 
00926     if (nodeDataInit)
00927         runScriptsFromNode(32765);
00928 
00929     if (!nodeData)
00930         error("Node %d unknown in the database", _state->getLocationNode());
00931 
00932     for (uint j = 0; j < nodeData->scripts.size(); j++) {
00933         if (_state->evaluate(nodeData->scripts[j].condition)) {
00934             _scriptEngine->run(&nodeData->scripts[j].script);
00935         }
00936     }
00937 
00938     // Mark the node as a reachable zip destination
00939     _state->markNodeAsVisited(
00940             _state->getLocationNode(),
00941             _state->getLocationRoom(),
00942             _state->getLocationAge());
00943 }
00944 
00945 void Myst3Engine::runNodeBackgroundScripts() {
00946     NodePtr nodeDataRoom = _db->getNodeData(32765, _state->getLocationRoom(), _state->getLocationAge());
00947 
00948     if (nodeDataRoom) {
00949         for (uint j = 0; j < nodeDataRoom->hotspots.size(); j++) {
00950             if (nodeDataRoom->hotspots[j].condition == -1) {
00951                 if (!_scriptEngine->run(&nodeDataRoom->hotspots[j].script))
00952                     break;
00953             }
00954         }
00955     }
00956 
00957     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
00958 
00959     for (uint j = 0; j < nodeData->hotspots.size(); j++) {
00960         if (nodeData->hotspots[j].condition == -1) {
00961             if (!_scriptEngine->run(&nodeData->hotspots[j].script))
00962                 break;
00963         }
00964     }
00965 }
00966 
00967 void Myst3Engine::loadNodeCubeFaces(uint16 nodeID) {
00968     _state->setViewType(kCube);
00969 
00970     _cursor->lockPosition(true);
00971     updateCursor();
00972 
00973     _node = new NodeCube(this, nodeID);
00974 }
00975 
00976 void Myst3Engine::loadNodeFrame(uint16 nodeID) {
00977     _state->setViewType(kFrame);
00978 
00979     _cursor->lockPosition(false);
00980     updateCursor();
00981 
00982     _node = new NodeFrame(this, nodeID);
00983 }
00984 
00985 void Myst3Engine::loadNodeMenu(uint16 nodeID) {
00986     _state->setViewType(kMenu);
00987 
00988     _cursor->lockPosition(false);
00989     updateCursor();
00990 
00991     _node = new NodeFrame(this, nodeID);
00992 }
00993 
00994 void Myst3Engine::runScriptsFromNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
00995     if (roomID == 0)
00996         roomID = _state->getLocationRoom();
00997 
00998     if (ageID == 0)
00999         ageID = _state->getLocationAge();
01000 
01001     NodePtr nodeData = _db->getNodeData(nodeID, roomID, ageID);
01002 
01003     for (uint j = 0; j < nodeData->scripts.size(); j++) {
01004         if (_state->evaluate(nodeData->scripts[j].condition)) {
01005             if (!_scriptEngine->run(&nodeData->scripts[j].script))
01006                 break;
01007         }
01008     }
01009 }
01010 
01011 void Myst3Engine::runBackgroundSoundScriptsFromNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
01012     if (_state->getSoundScriptsSuspended())
01013         return;
01014 
01015     if (roomID == 0)
01016         roomID = _state->getLocationRoom();
01017 
01018     if (ageID == 0)
01019         ageID = _state->getLocationAge();
01020 
01021     NodePtr nodeData = _db->getNodeData(nodeID, roomID, ageID);
01022 
01023     if (!nodeData) return;
01024 
01025     if (_backgroundSoundScriptLastRoomId != roomID || _backgroundSoundScriptLastAgeId != ageID) {
01026         bool sameScript;
01027         if (   _backgroundSoundScriptLastRoomId != 0 && roomID != 0
01028             && _backgroundSoundScriptLastAgeId  != 0 && ageID  != 0) {
01029             sameScript = _db->areRoomsScriptsEqual(_backgroundSoundScriptLastRoomId, _backgroundSoundScriptLastAgeId,
01030                                                    roomID, ageID, kScriptTypeBackgroundSound);
01031         } else {
01032             sameScript = false;
01033         }
01034 
01035         // Stop previous music when the music script changes
01036         if (!sameScript
01037             && _backgroundSoundScriptLastRoomId != kRoomMenu
01038             && _backgroundSoundScriptLastRoomId != kRoomJournals
01039             && roomID != kRoomMenu
01040             && roomID != kRoomJournals) {
01041 
01042             _sound->stopMusic(_state->getSoundScriptFadeOutDelay());
01043 
01044             if (!nodeData->backgroundSoundScripts.empty()) {
01045                 _state->setSoundScriptsPaused(1);
01046                 _state->setSoundScriptsTimer(0);
01047             }
01048         }
01049 
01050         _backgroundSoundScriptLastRoomId = roomID;
01051         _backgroundSoundScriptLastAgeId  = ageID;
01052     }
01053 
01054     for (uint j = 0; j < nodeData->backgroundSoundScripts.size(); j++) {
01055         if (_state->evaluate(nodeData->backgroundSoundScripts[j].condition)) {
01056             if (!_scriptEngine->run(&nodeData->backgroundSoundScripts[j].script))
01057                 break;
01058         }
01059     }
01060 }
01061 
01062 void Myst3Engine::runAmbientScripts(uint32 node) {
01063     uint32 room = _ambient->_scriptRoom;
01064     uint32 age = _ambient->_scriptAge;
01065 
01066     if (room == 0)
01067         room = _state->getLocationRoom();
01068 
01069     if (age == 0)
01070         age = _state->getLocationAge();
01071 
01072     NodePtr nodeData = _db->getNodeData(node, room, age);
01073 
01074     if (!nodeData) return;
01075 
01076     for (uint j = 0; j < nodeData->soundScripts.size(); j++)
01077         if (_state->evaluate(nodeData->soundScripts[j].condition))
01078             _scriptEngine->run(&nodeData->soundScripts[j].script);
01079 }
01080 
01081 void Myst3Engine::loadMovie(uint16 id, uint16 condition, bool resetCond, bool loop) {
01082     ScriptedMovie *movie;
01083 
01084     if (!_state->getMovieUseBackground()) {
01085         movie = new ScriptedMovie(this, id);
01086     } else {
01087         movie = new ProjectorMovie(this, id, _projectorBackground);
01088         _projectorBackground = 0;
01089         _state->setMovieUseBackground(0);
01090     }
01091 
01092     movie->setCondition(condition);
01093     movie->setDisableWhenComplete(resetCond);
01094     movie->setLoop(loop);
01095 
01096     if (_state->getMovieScriptDriven()) {
01097         movie->setScriptDriven(_state->getMovieScriptDriven());
01098         _state->setMovieScriptDriven(0);
01099     }
01100 
01101     if (_state->getMovieStartFrameVar()) {
01102         movie->setStartFrameVar(_state->getMovieStartFrameVar());
01103         _state->setMovieStartFrameVar(0);
01104     }
01105 
01106     if (_state->getMovieEndFrameVar()) {
01107         movie->setEndFrameVar(_state->getMovieEndFrameVar());
01108         _state->setMovieEndFrameVar(0);
01109     }
01110 
01111     if (_state->getMovieStartFrame()) {
01112         movie->setStartFrame(_state->getMovieStartFrame());
01113         _state->setMovieStartFrame(0);
01114     }
01115 
01116     if (_state->getMovieEndFrame()) {
01117         movie->setEndFrame(_state->getMovieEndFrame());
01118         _state->setMovieEndFrame(0);
01119     }
01120 
01121     if (_state->getMovieNextFrameGetVar()) {
01122         movie->setNextFrameReadVar(_state->getMovieNextFrameGetVar());
01123         _state->setMovieNextFrameGetVar(0);
01124     }
01125 
01126     if (_state->getMovieNextFrameSetVar()) {
01127         movie->setNextFrameWriteVar(_state->getMovieNextFrameSetVar());
01128         _state->setMovieNextFrameSetVar(0);
01129     }
01130 
01131     if (_state->getMoviePlayingVar()) {
01132         movie->setPlayingVar(_state->getMoviePlayingVar());
01133         _state->setMoviePlayingVar(0);
01134     }
01135 
01136     if (_state->getMovieOverridePosition()) {
01137         movie->setPosU(_state->getMovieOverridePosU());
01138         movie->setPosV(_state->getMovieOverridePosV());
01139         _state->setMovieOverridePosition(0);
01140     }
01141 
01142     if (_state->getMovieUVar()) {
01143         movie->setPosUVar(_state->getMovieUVar());
01144         _state->setMovieUVar(0);
01145     }
01146 
01147     if (_state->getMovieVVar()) {
01148         movie->setPosVVar(_state->getMovieVVar());
01149         _state->setMovieVVar(0);
01150     }
01151 
01152     if (_state->getMovieOverrideCondition()) {
01153         movie->setCondition(_state->getMovieOverrideCondition());
01154         _state->setMovieOverrideCondition(0);
01155     }
01156 
01157     if (_state->getMovieConditionBit()) {
01158         movie->setConditionBit(_state->getMovieConditionBit());
01159         _state->setMovieConditionBit(0);
01160     }
01161 
01162     if (_state->getMovieForce2d()) {
01163         movie->setForce2d(_state->getMovieForce2d());
01164         _state->setMovieForce2d(0);
01165     }
01166 
01167     if (_state->getMovieVolume1()) {
01168         movie->setVolume(_state->getMovieVolume1());
01169         _state->setMovieVolume1(0);
01170     } else {
01171         movie->setVolume(_state->getMovieVolume2());
01172     }
01173 
01174     if (_state->getMovieVolumeVar()) {
01175         movie->setVolumeVar(_state->getMovieVolumeVar());
01176         _state->setMovieVolumeVar(0);
01177     }
01178 
01179     if (_state->getMovieSoundHeading()) {
01180         movie->setSoundHeading(_state->getMovieSoundHeading());
01181         _state->setMovieSoundHeading(0);
01182     }
01183 
01184     if (_state->getMoviePanningStrenght()) {
01185         movie->setSoundAttenuation(_state->getMoviePanningStrenght());
01186         _state->setMoviePanningStrenght(0);
01187     }
01188 
01189     if (_state->getMovieAdditiveBlending()) {
01190         movie->setAdditiveBlending(true);
01191         _state->setMovieAdditiveBlending(0);
01192     }
01193 
01194     if (_state->getMovieTransparency()) {
01195         movie->setTransparency(_state->getMovieTransparency());
01196         _state->setMovieTransparency(0);
01197     } else {
01198         movie->setTransparency(100);
01199     }
01200 
01201     if (_state->getMovieTransparencyVar()) {
01202         movie->setTransparencyVar(_state->getMovieTransparencyVar());
01203         _state->setMovieTransparencyVar(0);
01204     }
01205 
01206     _movies.push_back(movie);
01207 }
01208 
01209 void Myst3Engine::playSimpleMovie(uint16 id, bool fullframe, bool refreshAmbientSounds) {
01210     SimpleMovie movie(this, id);
01211 
01212     if (!movie.isVideoLoaded()) {
01213         // The video was not loaded and it was optional, just do nothing
01214         return;
01215     }
01216 
01217     if (_state->getMovieSynchronized()) {
01218         movie.setSynchronized(_state->getMovieSynchronized());
01219         _state->setMovieSynchronized(0);
01220     }
01221 
01222     if (_state->getMovieStartFrame()) {
01223         movie.setStartFrame(_state->getMovieStartFrame());
01224         _state->setMovieStartFrame(0);
01225     }
01226 
01227     if (_state->getMovieEndFrame()) {
01228         movie.setEndFrame(_state->getMovieEndFrame());
01229         _state->setMovieEndFrame(0);
01230     }
01231 
01232     if (_state->getMovieVolume1()) {
01233         movie.setVolume(_state->getMovieVolume1());
01234         _state->setMovieVolume1(0);
01235     } else {
01236         movie.setVolume(_state->getMovieVolume2());
01237     }
01238 
01239     if (fullframe) {
01240         movie.setForce2d(_state->getViewType() == kCube);
01241         movie.setForceOpaque(true);
01242         movie.setPosU(0);
01243         movie.setPosV(_state->getViewType() == kMenu ? Renderer::kTopBorderHeight : 0);
01244     }
01245 
01246     movie.play();
01247 
01248     if (refreshAmbientSounds) {
01249         movie.refreshAmbientSounds();
01250     }
01251 
01252     _drawables.push_back(&movie);
01253 
01254     while (!shouldQuit() && !movie.endOfVideo()) {
01255         movie.update();
01256 
01257         // Process events
01258         processInput(false);
01259 
01260         // Handle skipping
01261         if (_inputSpacePressed || _inputEscapePressed) {
01262             // Consume the escape key press so the menu does not open
01263             _inputEscapePressedNotConsumed = false;
01264             break;
01265         }
01266 
01267         drawFrame();
01268     }
01269 
01270     _drawables.pop_back();
01271 
01272     // Reset the movie script so that the next movie will not try to run them
01273     // when the user has skipped this one before the script is triggered.
01274     _state->setMovieScriptStartFrame(0);
01275     _state->setMovieScript(0);
01276     _state->setMovieAmbiantScriptStartFrame(0);
01277     _state->setMovieAmbiantScript(0);
01278 }
01279 
01280 void Myst3Engine::removeMovie(uint16 id) {
01281     if (id == 0) {
01282         for (uint i = 0; i < _movies.size(); i++)
01283             delete _movies[i];
01284 
01285         _movies.clear();
01286         return;
01287     } else {
01288         for (uint i = 0; i < _movies.size(); i++)
01289             if (_movies[i]->getId() == id) {
01290                 delete _movies[i];
01291                 _movies.remove_at(i);
01292                 break;
01293             }
01294     }
01295 }
01296 
01297 void Myst3Engine::setMovieLooping(uint16 id, bool loop) {
01298     for (uint i = 0; i < _movies.size(); i++) {
01299         if (_movies[i]->getId() == id) {
01300             // Enable or disable looping
01301             _movies[i]->setLoop(loop);
01302             _movies[i]->setDisableWhenComplete(!loop);
01303             break;
01304         }
01305     }
01306 }
01307 
01308 void Myst3Engine::addSpotItem(uint16 id, int16 condition, bool fade) {
01309     assert(_node);
01310 
01311     _node->loadSpotItem(id, condition, fade);
01312 }
01313 
01314 SpotItemFace *Myst3Engine::addMenuSpotItem(uint16 id, int16 condition, const Common::Rect &rect) {
01315     assert(_node);
01316 
01317     SpotItemFace *face = _node->loadMenuSpotItem(condition, rect);
01318 
01319     _menu->setSaveLoadSpotItem(id, face);
01320 
01321     return face;
01322 }
01323 
01324 void Myst3Engine::loadNodeSubtitles(uint32 id) {
01325     assert(_node);
01326 
01327     _node->loadSubtitles(id);
01328 }
01329 
01330 const DirectorySubEntry *Myst3Engine::getFileDescription(const Common::String &room, uint32 index, uint16 face,
01331                                                          DirectorySubEntry::ResourceType type) {
01332     Common::String archiveRoom = room;
01333     if (archiveRoom == "") {
01334         archiveRoom = _db->getRoomName(_state->getLocationRoom(), _state->getLocationAge());
01335     }
01336 
01337     const DirectorySubEntry *desc = 0;
01338 
01339     // Search common archives
01340     uint i = 0;
01341     while (!desc && i < _archivesCommon.size()) {
01342         desc = _archivesCommon[i]->getDescription(archiveRoom, index, face, type);
01343         i++;
01344     }
01345 
01346     // Search currently loaded node archive
01347     if (!desc && _archiveNode)
01348         desc = _archiveNode->getDescription(archiveRoom, index, face, type);
01349 
01350     return desc;
01351 }
01352 
01353 DirectorySubEntryList Myst3Engine::listFilesMatching(const Common::String &room, uint32 index, uint16 face,
01354                                                      DirectorySubEntry::ResourceType type) {
01355     Common::String archiveRoom = room;
01356     if (archiveRoom == "") {
01357         archiveRoom = _db->getRoomName(_state->getLocationRoom(), _state->getLocationAge());
01358     }
01359 
01360     for (uint i = 0; i < _archivesCommon.size(); i++) {
01361         DirectorySubEntryList list = _archivesCommon[i]->listFilesMatching(archiveRoom, index, face, type);
01362         if (!list.empty()) {
01363             return list;
01364         }
01365     }
01366 
01367     return _archiveNode->listFilesMatching(archiveRoom, index, face, type);
01368 }
01369 
01370 Graphics::Surface *Myst3Engine::loadTexture(uint16 id) {
01371     const DirectorySubEntry *desc = getFileDescription("GLOB", id, 0, DirectorySubEntry::kRawData);
01372 
01373     if (!desc)
01374         error("Texture %d does not exist", id);
01375 
01376     Common::MemoryReadStream *data = desc->getData();
01377 
01378     uint32 magic = data->readUint32LE();
01379     if (magic != MKTAG('.', 'T', 'E', 'X'))
01380         error("Wrong texture format %d", id);
01381 
01382     data->readUint32LE(); // unk 1
01383     uint32 width = data->readUint32LE();
01384     uint32 height = data->readUint32LE();
01385     data->readUint32LE(); // unk 2
01386     data->readUint32LE(); // unk 3
01387 
01388 #ifdef SCUMM_BIG_ENDIAN
01389     Graphics::PixelFormat onDiskFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 24, 16, 8);
01390 #else
01391     Graphics::PixelFormat onDiskFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0);
01392 #endif
01393 
01394     Graphics::Surface *s = new Graphics::Surface();
01395     s->create(width, height, onDiskFormat);
01396 
01397     data->read(s->getPixels(), height * s->pitch);
01398     delete data;
01399 
01400     s->convertToInPlace(Texture::getRGBAPixelFormat());
01401 
01402     return s;
01403 }
01404 
01405 Graphics::Surface *Myst3Engine::decodeJpeg(const DirectorySubEntry *jpegDesc) {
01406     Common::MemoryReadStream *jpegStream = jpegDesc->getData();
01407 
01408     Image::JPEGDecoder jpeg;
01409     if (!jpeg.loadStream(*jpegStream))
01410         error("Could not decode Myst III JPEG");
01411     delete jpegStream;
01412 
01413     const Graphics::Surface *bitmap = jpeg.getSurface();
01414     assert(bitmap->format == Graphics::PixelFormat(4, 8, 8, 8, 0, 24, 16, 8, 0));
01415 
01416     // Convert the surface to RGBA if needed.
01417     // RGBA being the order of the bytes in memory, not in a 32 bits word.
01418     Graphics::PixelFormat rgbaFormat = Texture::getRGBAPixelFormat();
01419     Graphics::Surface *rgbaSurface = new Graphics::Surface();
01420 
01421 #ifdef SCUMM_BIG_ENDIAN
01422     assert(rgbaFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
01423     rgbaSurface->copyFrom(*bitmap);
01424     rgbaSurface->format = rgbaFormat;
01425 #else
01426     assert(rgbaFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
01427 
01428     rgbaSurface->create(bitmap->w, bitmap->h, rgbaFormat);
01429 
01430     // Use SWAP_BYTES_32 as an optimization. This path is hot.
01431     for (uint y = 0; y < bitmap->h; y++) {
01432         const uint32 *srcRow = (const uint32 *) bitmap->getBasePtr(0, y);
01433         uint32 *dstRow = (uint32 *) rgbaSurface->getBasePtr(0, y);
01434         for (uint x = 0; x < bitmap->w; x++) {
01435             *dstRow++ = SWAP_BYTES_32(*srcRow++);
01436         }
01437     }
01438 #endif
01439 
01440     return rgbaSurface;
01441 }
01442 
01443 int16 Myst3Engine::openDialog(uint16 id) {
01444     Dialog *dialog;
01445 
01446     if (getPlatform() == Common::kPlatformXbox) {
01447         dialog = new GamepadDialog(this, id);
01448     } else {
01449         dialog = new ButtonsDialog(this, id);
01450     }
01451 
01452     _drawables.push_back(dialog);
01453 
01454     int16 result = -2;
01455 
01456     while (result == -2 && !shouldQuit()) {
01457         result = dialog->update();
01458         drawFrame();
01459     }
01460 
01461     _drawables.pop_back();
01462 
01463     delete dialog;
01464 
01465     return result;
01466 }
01467 
01468 void Myst3Engine::dragSymbol(uint16 var, uint16 id) {
01469     DragItem drag(this, id);
01470 
01471     _drawables.push_back(&drag);
01472 
01473     _cursor->changeCursor(2);
01474     _state->setVar(var, -1);
01475 
01476     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
01477 
01478     while (inputValidatePressed() && !shouldQuit()) {
01479         processInput(false);
01480 
01481         HotSpot *hovered = getHoveredHotspot(nodeData, var);
01482         drag.setFrame(hovered ? 2 : 1);
01483 
01484         drawFrame();
01485     }
01486 
01487     _state->setVar(var, 1);
01488     _drawables.pop_back();
01489 
01490     HotSpot *hovered = getHoveredHotspot(nodeData, var);
01491     if (hovered) {
01492         _cursor->setVisible(false);
01493         _scriptEngine->run(&hovered->script);
01494         _cursor->setVisible(true);
01495     }
01496 }
01497 
01498 void Myst3Engine::dragItem(uint16 statusVar, uint16 movie, uint16 frame, uint16 hoverFrame, uint16 itemVar) {
01499     DragItem drag(this, movie);
01500 
01501     _drawables.push_back(&drag);
01502 
01503     _cursor->changeCursor(2);
01504     _state->setVar(statusVar, 0);
01505     _state->setVar(itemVar, 1);
01506 
01507     NodePtr nodeData = _db->getNodeData(_state->getLocationNode(), _state->getLocationRoom(), _state->getLocationAge());
01508 
01509     while (inputValidatePressed() && !shouldQuit()) {
01510         processInput(false);
01511 
01512         HotSpot *hovered = getHoveredHotspot(nodeData, itemVar);
01513         drag.setFrame(hovered ? hoverFrame : frame);
01514 
01515         drawFrame();
01516     }
01517 
01518     _drawables.pop_back();
01519 
01520     HotSpot *hovered = getHoveredHotspot(nodeData, itemVar);
01521     if (hovered) {
01522         _cursor->setVisible(false);
01523         _scriptEngine->run(&hovered->script);
01524         _cursor->setVisible(true);
01525     } else {
01526         _state->setVar(statusVar, 1);
01527         _state->setVar(itemVar, 0);
01528     }
01529 }
01530 
01531 bool Myst3Engine::canSaveGameStateCurrently() {
01532     bool inMenuWithNoGameLoaded = _state->getLocationRoom() == kRoomMenu && _state->getMenuSavedAge() == 0;
01533     return canLoadGameStateCurrently() && !inMenuWithNoGameLoaded && _cursor->isVisible();
01534 }
01535 
01536 bool Myst3Engine::canLoadGameStateCurrently() {
01537     // Loading from the GMM is only possible when the game is interactive
01538     // This is to prevent loading from inner loops. Loading while
01539     // in an inner loop can cause the exit condition to never happen,
01540     // or can unload required resources.
01541     return _interactive;
01542 }
01543 
01544 void Myst3Engine::tryAutoSaving() {
01545     if (!canSaveGameStateCurrently()) {
01546         return; // Can't save right now, try again on the next frame
01547     }
01548 
01549     _lastSaveTime = _system->getMillis();
01550 
01551     // Get a thumbnail of the game screen
01552     if (!_menu->isOpen())
01553         _menu->generateSaveThumbnail();
01554 
01555     Common::Error result = saveGameState(0, "Autosave");
01556     if (result.getCode() != Common::kNoError) {
01557         warning("Unable to autosave: %s.", result.getDesc().c_str());
01558     }
01559 }
01560 
01561 Common::Error Myst3Engine::loadGameState(int slot) {
01562     Common::StringArray filenames = Saves::list(_saveFileMan, getPlatform());
01563     return loadGameState(filenames[slot], kTransitionNone);
01564 }
01565 
01566 Common::Error Myst3Engine::loadGameState(Common::String fileName, TransitionType transition) {
01567     Common::InSaveFile *saveFile = _saveFileMan->openForLoading(fileName);
01568     if (!saveFile) {
01569         return _saveFileMan->getError();
01570     }
01571 
01572     if (!_state->load(saveFile)) {
01573         delete saveFile;
01574         return Common::kUnknownError;
01575     }
01576     delete saveFile;
01577 
01578     _inventory->loadFromState();
01579 
01580     // Leave the load menu
01581     _state->setViewType(kMenu);
01582     _state->setLocationNextAge(_state->getMenuSavedAge());
01583     _state->setLocationNextRoom(_state->getMenuSavedRoom());
01584     _state->setLocationNextNode(_state->getMenuSavedNode());
01585     _state->setMenuSavedAge(0);
01586     _state->setMenuSavedRoom(0);
01587     _state->setMenuSavedNode(0);
01588     _sound->resetSoundVars();
01589     _sound->stopMusic(15);
01590     _state->setSoundScriptsSuspended(0);
01591     _sound->playEffect(696, 60);
01592 
01593     goToNode(0, transition);
01594     return Common::kNoError;
01595 }
01596 
01597 static bool isValidSaveFileChar(char c) {
01598     // Limit it to letters, digits, and a few other characters that should be safe
01599     return Common::isAlnum(c) || c == ' ' || c == '_' || c == '+' || c == '-' || c == '.';
01600 }
01601 
01602 static bool isValidSaveFileName(const Common::String &desc) {
01603     for (uint32 i = 0; i < desc.size(); i++)
01604         if (!isValidSaveFileChar(desc[i]))
01605             return false;
01606 
01607     return true;
01608 }
01609 
01610 Common::Error Myst3Engine::saveGameState(int slot, const Common::String &desc) {
01611     assert(!desc.empty());
01612 
01613     if (!isValidSaveFileName(desc)) {
01614         return Common::Error(Common::kCreatingFileFailed, _("Invalid file name for saving"));
01615     }
01616 
01617     // Strip extension
01618     Common::String saveName = desc;
01619     if (desc.hasSuffixIgnoreCase(".M3S") || desc.hasSuffixIgnoreCase(".M3X")) {
01620         saveName.erase(desc.size() - 4, desc.size());
01621     }
01622 
01623     // Try to use an already generated thumbnail
01624     const Graphics::Surface *thumbnail = _menu->borrowSaveThumbnail();
01625     if (!thumbnail) {
01626         return Common::Error(Common::kUnknownError, "No thumbnail");
01627     }
01628 
01629     Common::String fileName = Saves::buildName(saveName.c_str(), getPlatform());
01630     Common::ScopedPtr<Common::OutSaveFile> save(_saveFileMan->openForSaving(fileName));
01631     if (!save) {
01632         return _saveFileMan->getError();
01633     }
01634 
01635     // Save the state and the thumbnail
01636     if (!_state->save(save.get(), saveName, thumbnail)) {
01637         return Common::kUnknownError;
01638     }
01639 
01640     return Common::kNoError;
01641 }
01642 
01643 void Myst3Engine::animateDirectionChange(float targetPitch, float targetHeading, uint16 scriptTicks) {
01644     float startPitch = _state->getLookAtPitch();
01645     float startHeading = _state->getLookAtHeading();
01646 
01647     if (startPitch == targetPitch && startHeading == targetHeading)
01648         return; // Fast path
01649 
01650     float pitchDistance = targetPitch - startPitch;
01651     float headingDistance = targetHeading - startHeading;
01652 
01653     // Make sure to use the shortest direction
01654     while (ABS(headingDistance) > 180) {
01655         if (headingDistance > 0) {
01656             headingDistance -= 360;
01657         } else {
01658             headingDistance += 360;
01659         }
01660     }
01661 
01662     // Compute animation duration in frames
01663     float numTicks;
01664     if (scriptTicks) {
01665         numTicks = scriptTicks;
01666     } else {
01667         numTicks = sqrt(pitchDistance * pitchDistance + headingDistance * headingDistance)
01668                 * 30.0f / _state->getCameraMoveSpeed();
01669 
01670         if (numTicks > 0.0f)
01671             numTicks += 10.0f;
01672     }
01673 
01674     uint startTick = _state->getTickCount();
01675 
01676     // Draw animation
01677     if (numTicks != 0.0f) {
01678         while (1) {
01679             uint elapsedTicks = _state->getTickCount() - startTick;
01680             if (elapsedTicks >= numTicks || shouldQuit())
01681                 break;
01682 
01683             float step;
01684             if (numTicks >= 15) {
01685                 // Fast then slow movement
01686                 if (elapsedTicks > numTicks / 2.0f)
01687                     step = 1.0f - (numTicks - elapsedTicks) * (numTicks - elapsedTicks)
01688                                 / (numTicks / 2.0f * numTicks / 2.0f) / 2.0f;
01689                 else
01690                     step = elapsedTicks * elapsedTicks / (numTicks / 2.0f * numTicks / 2.0f) / 2.0f;
01691 
01692             } else {
01693                 // Constant speed movement
01694                 step = elapsedTicks / numTicks;
01695             }
01696 
01697             float nextPitch = startPitch + pitchDistance * step;
01698             float nextHeading = startHeading + headingDistance * step;
01699 
01700             _state->lookAt(nextPitch, nextHeading);
01701             drawFrame();
01702         }
01703     }
01704 
01705     _state->lookAt(targetPitch, targetHeading);
01706     drawFrame();
01707 }
01708 
01709 void Myst3Engine::getMovieLookAt(uint16 id, bool start, float &pitch, float &heading) {
01710     const DirectorySubEntry *desc = getFileDescription("", id, 0, DirectorySubEntry::kMovie);
01711 
01712     if (!desc)
01713         desc = getFileDescription("", id, 0, DirectorySubEntry::kMultitrackMovie);
01714 
01715     if (!desc)
01716         error("Movie %d does not exist", id);
01717 
01718     Math::Vector3d v;
01719     if (start)
01720         v = desc->getVideoData().v1;
01721     else
01722         v = desc->getVideoData().v2;
01723 
01724     Math::Vector2d horizontalProjection(v.x(), v.z());
01725     horizontalProjection.normalize();
01726 
01727     pitch = 90 - Math::Angle::arcCosine(v.y()).getDegrees();
01728     heading = Math::Angle::arcCosine(horizontalProjection.getY()).getDegrees();
01729 
01730     if (horizontalProjection.getX() < 0.0) {
01731         heading = 360 - heading;
01732     }
01733 }
01734 
01735 void Myst3Engine::playMovieGoToNode(uint16 movie, uint16 node) {
01736     uint16 room = _state->getLocationNextRoom();
01737     uint16 age = _state->getLocationNextAge();
01738 
01739     if (_state->getLocationNextNode()) {
01740         node = _state->getLocationNextNode();
01741     }
01742 
01743     if (_state->getViewType() == kCube && !_state->getCameraSkipAnimation()) {
01744         float startPitch, startHeading;
01745         getMovieLookAt(movie, true, startPitch, startHeading);
01746         animateDirectionChange(startPitch, startHeading, 0);
01747     }
01748     _state->setCameraSkipAnimation(0);
01749 
01750     loadNode(node, room, age);
01751 
01752     playSimpleMovie(movie, true, true);
01753 
01754     _state->setLocationNextNode(0);
01755     _state->setLocationNextRoom(0);
01756     _state->setLocationNextAge(0);
01757 
01758     if (_state->getViewType() == kCube) {
01759         float endPitch, endHeading;
01760         getMovieLookAt(movie, false, endPitch, endHeading);
01761         _state->lookAt(endPitch, endHeading);
01762     }
01763 
01764     setupTransition();
01765 }
01766 
01767 void Myst3Engine::playMovieFullFrame(uint16 movie) {
01768     if (_state->getViewType() == kCube && !_state->getCameraSkipAnimation()) {
01769         float startPitch, startHeading;
01770         getMovieLookAt(movie, true, startPitch, startHeading);
01771         animateDirectionChange(startPitch, startHeading, 0);
01772     }
01773     _state->setCameraSkipAnimation(0);
01774 
01775     playSimpleMovie(movie, true, false);
01776 
01777     if (_state->getViewType() == kCube) {
01778         float endPitch, endHeading;
01779         getMovieLookAt(movie, false, endPitch, endHeading);
01780         _state->lookAt(endPitch, endHeading);
01781     }
01782 
01783     setupTransition();
01784 }
01785 
01786 bool Myst3Engine::inputValidatePressed() {
01787     return _inputEnterPressed ||
01788             _inputSpacePressed ||
01789             getEventManager()->getButtonState() & Common::EventManager::LBUTTON;
01790 }
01791 
01792 bool Myst3Engine::inputEscapePressed() {
01793     return _inputEscapePressed;
01794 }
01795 
01796 bool Myst3Engine::inputSpacePressed() {
01797     return _inputSpacePressed;
01798 }
01799 
01800 bool Myst3Engine::inputTilePressed() {
01801     return _inputTildePressed;
01802 }
01803 
01804 void Myst3Engine::addSunSpot(uint16 pitch, uint16 heading, uint16 intensity,
01805         uint16 color, uint16 var, bool varControlledIntensity, uint16 radius) {
01806 
01807     SunSpot *s = new SunSpot();
01808 
01809     s->pitch = pitch;
01810     s->heading = heading;
01811     s->intensity = intensity * 2.55;
01812     s->color = (color & 0xF) | 16
01813             * ((color & 0xF) | 16
01814             * (((color >> 4) & 0xF) | 16
01815             * (((color >> 4) & 0xF) | 16
01816             * (((color >> 8) & 0xF) | 16
01817             * (((color >> 8) & 0xF))))));
01818     s->var = var;
01819     s->variableIntensity = varControlledIntensity;
01820     s->radius = radius;
01821 
01822     _sunspots.push_back(s);
01823 }
01824 
01825 SunSpot Myst3Engine::computeSunspotsIntensity(float pitch, float heading) {
01826     SunSpot result;
01827     result.intensity = -1;
01828     result.color = 0;
01829     result.radius = 0;
01830 
01831     for (uint i = 0; i < _sunspots.size(); i++) {
01832         SunSpot *s = _sunspots[i];
01833 
01834         uint32 value = _state->getVar(s->var);
01835 
01836         // Skip disabled items
01837         if (value == 0) continue;
01838 
01839         float distance = _scene->distanceToZone(s->heading, s->pitch, s->radius, heading, pitch);
01840 
01841         if (distance > result.radius) {
01842             result.radius = distance;
01843             result.color = s->color;
01844             result.intensity = s->intensity;
01845             result.variableIntensity = s->variableIntensity;
01846 
01847             if (result.variableIntensity) {
01848                 result.radius = value * distance / 100;
01849             }
01850         }
01851     }
01852 
01853     return result;
01854 }
01855 
01856 void Myst3Engine::settingsInitDefaults() {
01857     int defaultLanguage = _db->getGameLanguageCode();
01858 
01859     int defaultTextLanguage;
01860     if (getGameLocalizationType() == kLocMulti6)
01861         defaultTextLanguage = defaultLanguage;
01862     else
01863         defaultTextLanguage = getGameLanguage() != Common::EN_ANY;
01864 
01865     ConfMan.registerDefault("overall_volume", Audio::Mixer::kMaxMixerVolume);
01866     ConfMan.registerDefault("music_volume", Audio::Mixer::kMaxMixerVolume / 2);
01867     ConfMan.registerDefault("music_frequency", 75);
01868     ConfMan.registerDefault("audio_language", defaultLanguage);
01869     ConfMan.registerDefault("text_language", defaultTextLanguage);
01870     ConfMan.registerDefault("water_effects", true);
01871     ConfMan.registerDefault("transition_speed", 50);
01872     ConfMan.registerDefault("mouse_speed", 50);
01873     ConfMan.registerDefault("mouse_inverted", false);
01874     ConfMan.registerDefault("zip_mode", false);
01875     ConfMan.registerDefault("subtitles", false);
01876     ConfMan.registerDefault("vibrations", true); // Xbox specific
01877 }
01878 
01879 void Myst3Engine::settingsLoadToVars() {
01880     _state->setWaterEffects(ConfMan.getBool("water_effects"));
01881     _state->setTransitionSpeed(ConfMan.getInt("transition_speed"));
01882     _state->setMouseSpeed(ConfMan.getInt("mouse_speed"));
01883     _state->setZipModeEnabled(ConfMan.getBool("zip_mode"));
01884     _state->setSubtitlesEnabled(ConfMan.getBool("subtitles"));
01885 
01886     if (getPlatform() != Common::kPlatformXbox) {
01887         _state->setOverallVolume(CLIP<uint>(ConfMan.getInt("overall_volume") * 100 / 256, 0, 100));
01888         _state->setMusicVolume(CLIP<uint>(ConfMan.getInt("music_volume") * 100 / 256, 0, 100));
01889         _state->setMusicFrequency(ConfMan.getInt("music_frequency"));
01890         _state->setLanguageAudio(ConfMan.getInt("audio_language"));
01891         _state->setLanguageText(ConfMan.getInt("text_language"));
01892     } else {
01893         _state->setVibrationEnabled(ConfMan.getBool("vibrations"));
01894     }
01895 }
01896 
01897 void Myst3Engine::settingsApplyFromVars() {
01898     int32 oldTextLanguage = ConfMan.getInt("text_language");
01899 
01900     ConfMan.setInt("transition_speed", _state->getTransitionSpeed());
01901     ConfMan.setInt("mouse_speed", _state->getMouseSpeed());
01902     ConfMan.setBool("zip_mode", _state->getZipModeEnabled());
01903     ConfMan.setBool("subtitles", _state->getSubtitlesEnabled());
01904 
01905     if (getPlatform() != Common::kPlatformXbox) {
01906         ConfMan.setInt("overall_volume", _state->getOverallVolume() * 256 / 100);
01907         ConfMan.setInt("music_volume", _state->getMusicVolume() * 256 / 100);
01908         ConfMan.setInt("music_frequency", _state->getMusicFrequency());
01909         ConfMan.setInt("audio_language", _state->getLanguageAudio());
01910         ConfMan.setInt("text_language", _state->getLanguageText());
01911         ConfMan.setBool("water_effects", _state->getWaterEffects());
01912 
01913         // The language changed, reload the correct archives
01914         if (_state->getLanguageText() != oldTextLanguage) {
01915             closeArchives();
01916             openArchives();
01917         }
01918     } else {
01919         ConfMan.setBool("vibrations", _state->getVibrationEnabled());
01920     }
01921 
01922     // Mouse speed may have changed, refresh it
01923     _scene->updateMouseSpeed();
01924 
01925     syncSoundSettings();
01926 }
01927 
01928 void Myst3Engine::syncSoundSettings() {
01929     Engine::syncSoundSettings();
01930 
01931     uint soundOverall = ConfMan.getInt("overall_volume");
01932     uint soundVolumeMusic = ConfMan.getInt("music_volume");
01933 
01934     _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundOverall);
01935     _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic * soundOverall / 256);
01936 }
01937 
01938 bool Myst3Engine::isWideScreenModEnabled() const {
01939     return ConfMan.getBool("widescreen_mod");
01940 }
01941 
01942 void Myst3Engine::pauseEngineIntern(bool pause) {
01943     Engine::pauseEngineIntern(pause);
01944 
01945     if (!_state || !_cursor) {
01946         // This method may be called before the engine is fully initialized
01947         return;
01948     }
01949 
01950     for (uint i = 0; i < _movies.size(); i++) {
01951         _movies[i]->pause(pause);
01952     }
01953 
01954     _state->pauseEngine(pause);
01955 
01956     // Grab a game screen thumbnail in case we need one when writing a save file
01957     if (pause && !_menu->isOpen()) {
01958         // Opening the menu generates a save thumbnail so we only generate it if the menu is not open
01959         _menu->generateSaveThumbnail();
01960     }
01961 
01962     // Unlock the mouse so that the cursor is visible when the GMM opens
01963     if (_state->getViewType() == kCube && _cursor->isPositionLocked()) {
01964         _system->lockMouse(!pause);
01965     }
01966 
01967     // The user may have moved the mouse or resized the screen while the engine was paused
01968     if (!pause) {
01969         _gfx->computeScreenViewport();
01970         _cursor->updatePosition(_eventMan->getMousePos());
01971         _inventory->reflow();
01972     }
01973 }
01974 
01975 } // end of namespace Myst3


Generated on Sat Apr 20 2019 05:03:16 for ResidualVM by doxygen 1.7.1
curved edge   curved edge