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

menu.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 "engines/myst3/cursor.h"
00024 #include "engines/myst3/database.h"
00025 #include "engines/myst3/inventory.h"
00026 #include "engines/myst3/menu.h"
00027 #include "engines/myst3/myst3.h"
00028 #include "engines/myst3/node.h"
00029 #include "engines/myst3/scene.h"
00030 #include "engines/myst3/sound.h"
00031 #include "engines/myst3/state.h"
00032 
00033 #include "common/events.h"
00034 
00035 #include "graphics/colormasks.h"
00036 
00037 #include "gui/message.h"
00038 
00039 namespace Myst3 {
00040 
00041 Dialog::Dialog(Myst3Engine *vm, uint id):
00042     _vm(vm),
00043     _texture(0) {
00044     // Draw on the whole screen
00045     _isConstrainedToWindow = false;
00046     _scaled = !_vm->isWideScreenModEnabled();
00047 
00048     const DirectorySubEntry *countDesc = _vm->getFileDescription("DLGI", id, 0, DirectorySubEntry::kNumMetadata);
00049     const DirectorySubEntry *movieDesc = _vm->getFileDescription("DLOG", id, 0, DirectorySubEntry::kDialogMovie);
00050     if (!movieDesc) {
00051         movieDesc = _vm->getFileDescription("DLOG", id, 0, DirectorySubEntry::kStillMovie);
00052     }
00053 
00054     if (!movieDesc || !countDesc)
00055         error("Unable to load dialog %d", id);
00056 
00057     // Retrieve button count
00058     _buttonCount = countDesc->getMiscData(0);
00059     assert(_buttonCount <= 3);
00060 
00061     // Load the movie
00062     Common::MemoryReadStream *movieStream = movieDesc->getData();
00063     _bink.setDefaultHighColorFormat(Texture::getRGBAPixelFormat());
00064     _bink.loadStream(movieStream);
00065     _bink.start();
00066 
00067     const Graphics::Surface *frame = _bink.decodeNextFrame();
00068     _texture = _vm->_gfx->createTexture(frame);
00069 
00070     _vm->_sound->playEffect(699, 10);
00071 }
00072 
00073 Dialog::~Dialog() {
00074     _vm->_gfx->freeTexture(_texture);
00075 }
00076 
00077 void Dialog::draw() {
00078     Common::Rect textureRect = Common::Rect(_texture->width, _texture->height);
00079     _vm->_gfx->drawTexturedRect2D(getPosition(), textureRect, _texture);
00080 }
00081 
00082 Common::Rect Dialog::getPosition() const {
00083     Common::Rect viewport;
00084     if (_scaled) {
00085         viewport = Common::Rect(Renderer::kOriginalWidth, Renderer::kOriginalHeight);
00086     } else {
00087         viewport = _vm->_gfx->viewport();
00088     }
00089 
00090     Common::Rect screenRect = Common::Rect(_texture->width, _texture->height);
00091     screenRect.translate((viewport.width() - _texture->width) / 2,
00092             (viewport.height() - _texture->height) / 2);
00093     return screenRect;
00094 }
00095 
00096 ButtonsDialog::ButtonsDialog(Myst3Engine *vm, uint id):
00097     Dialog(vm, id),
00098     _frameToDisplay(0),
00099     _previousframe(0) {
00100 
00101     loadButtons();
00102 }
00103 
00104 ButtonsDialog::~ButtonsDialog() {
00105 }
00106 
00107 void ButtonsDialog::loadButtons() {
00108     const DirectorySubEntry *buttonsDesc = _vm->getFileDescription("DLGB", 1000, 0, DirectorySubEntry::kNumMetadata);
00109 
00110     if (!buttonsDesc)
00111         error("Unable to load dialog buttons description");
00112 
00113     for (uint i = 0; i < 3; i++) {
00114         uint32 left = buttonsDesc->getMiscData(i * 4);
00115         uint32 top = buttonsDesc->getMiscData(i * 4 + 1);
00116         uint32 width = buttonsDesc->getMiscData(i * 4 + 2);
00117         uint32 height = buttonsDesc->getMiscData(i * 4 + 3);
00118         _buttons[i] = Common::Rect(width, height);
00119         _buttons[i].translate(left, top);
00120     }
00121 }
00122 
00123 void ButtonsDialog::draw() {
00124     if (_frameToDisplay != _previousframe) {
00125         _bink.seekToFrame(_frameToDisplay);
00126 
00127         const Graphics::Surface *frame = _bink.decodeNextFrame();
00128         _texture->update(frame);
00129 
00130         _previousframe = _frameToDisplay;
00131     }
00132 
00133     Dialog::draw();
00134 }
00135 
00136 int16 ButtonsDialog::update() {
00137     // Process events
00138     Common::Event event;
00139     while (_vm->getEventManager()->pollEvent(event)) {
00140         _vm->processEventForKeyboardState(event);
00141 
00142         if (event.type == Common::EVENT_MOUSEMOVE) {
00143             // Compute local mouse coordinates
00144             _vm->_cursor->updatePosition(event.mouse);
00145             Common::Point localMouse = getRelativeMousePosition();
00146 
00147             // No hovered button
00148             _frameToDisplay = 0;
00149 
00150             // Display the frame corresponding to the hovered button
00151             for (uint i = 0; i < _buttonCount; i++) {
00152                 if (_buttons[i].contains(localMouse)) {
00153                     _frameToDisplay = i + 1;
00154                     break;
00155                 }
00156             }
00157         } else if (event.type == Common::EVENT_LBUTTONDOWN) {
00158             if (_frameToDisplay > 0) {
00159                 return _frameToDisplay;
00160             } else {
00161                 _vm->_sound->playEffect(697, 5);
00162             }
00163         } else if (event.type == Common::EVENT_KEYDOWN) {
00164             switch (event.kbd.keycode) {
00165             case Common::KEYCODE_ESCAPE:
00166                 return _buttonCount;
00167             default:
00168                 break;
00169             }
00170         }
00171     }
00172 
00173     return -2;
00174 }
00175 
00176 Common::Point ButtonsDialog::getRelativeMousePosition() const {
00177     Common::Rect position = getPosition();
00178     Common::Point localMouse =_vm->_cursor->getPosition(_scaled);
00179     localMouse.x -= position.left;
00180     localMouse.y -= position.top;
00181     return localMouse;
00182 }
00183 
00184 GamepadDialog::GamepadDialog(Myst3Engine *vm, uint id):
00185     Dialog(vm, id) {
00186 }
00187 
00188 GamepadDialog::~GamepadDialog() {
00189 }
00190 
00191 int16 GamepadDialog::update() {
00192     // Process events
00193     Common::Event event;
00194     while (_vm->getEventManager()->pollEvent(event)) {
00195         _vm->processEventForKeyboardState(event);
00196         _vm->processEventForGamepad(event);
00197 
00198         if (event.type == Common::EVENT_MOUSEMOVE) {
00199             // Compute local mouse coordinates
00200             _vm->_cursor->updatePosition(event.mouse);
00201         } else if (event.type == Common::EVENT_KEYDOWN) {
00202             switch (event.kbd.keycode) {
00203             case Common::KEYCODE_RETURN:
00204             case Common::KEYCODE_KP_ENTER:
00205                 return 1;
00206             case Common::KEYCODE_ESCAPE:
00207                 if (_buttonCount == 2) {
00208                     return 2;
00209                 } else {
00210                     return 1;
00211                 }
00212             default:
00213                 break;
00214             }
00215         }
00216     }
00217 
00218     return -2;
00219 }
00220 
00221 Menu::Menu(Myst3Engine *vm) :
00222         _vm(vm),
00223         _saveLoadSpotItem(0) {
00224 }
00225 
00226 Menu::~Menu() {
00227 }
00228 
00229 void Menu::updateMainMenu(uint16 action) {
00230     switch (action) {
00231     case 1: {
00232             // New game
00233             int16 choice = dialogConfirmValue();
00234 
00235             // If a game is playing, ask if wants to save
00236             if (_vm->_state->getMenuSavedAge() != 0) {
00237                 choice = _vm->openDialog(dialogIdFromType(kConfirmNewGame));
00238             }
00239 
00240             if (choice == dialogSaveValue()) {
00241                 // Go to save screen
00242                 _vm->_state->setMenuSaveBack(1);
00243                 _vm->_state->setMenuSaveAction(6);
00244                 goToNode(kNodeMenuSaveGame);
00245             } else if (choice == dialogConfirmValue()) {
00246                 // New game
00247                 goToNode(kNodeMenuNewGame);
00248             }
00249         }
00250         break;
00251     case 2: {
00252             // Load game
00253             int16 choice = dialogConfirmValue();
00254 
00255             // If a game is playing, ask if wants to save
00256             if (_vm->_state->getMenuSavedAge() != 0) {
00257                 choice = _vm->openDialog(dialogIdFromType(kConfirmLoadGame));
00258             }
00259 
00260             if (choice == dialogSaveValue()) {
00261                 // Go to save screen
00262                 _vm->_state->setMenuSaveBack(1);
00263                 _vm->_state->setMenuSaveAction(3);
00264                 goToNode(kNodeMenuSaveGame);
00265             } else if (choice == dialogConfirmValue()) {
00266                 // Load game screen
00267                 _vm->_state->setMenuLoadBack(1);
00268                 goToNode(kNodeMenuLoadGame);
00269             }
00270         }
00271         break;
00272     case 3:
00273         // Go to save screen
00274         _vm->_state->setMenuSaveBack(1);
00275         _vm->_state->setMenuSaveAction(1);
00276         goToNode(kNodeMenuSaveGame);
00277         break;
00278     case 4:
00279         // Settings
00280         _vm->_state->setMenuOptionsBack(1);
00281         _vm->runScriptsFromNode(599, 0, 0);
00282         break;
00283     case 5: {
00284             // Asked to quit
00285             int16 choice = dialogConfirmValue();
00286 
00287             // If a game is playing, ask if wants to save
00288             if (_vm->_state->getMenuSavedAge() != 0) {
00289                 choice = _vm->openDialog(dialogIdFromType(kConfirmQuit));
00290             }
00291 
00292             if (choice == dialogSaveValue()) {
00293                 // Go to save screen
00294                 _vm->_state->setMenuSaveBack(1);
00295                 _vm->_state->setMenuSaveAction(5);
00296                 goToNode(kNodeMenuSaveGame);
00297             } else if (choice == dialogConfirmValue()) {
00298                 // Quit
00299                 _vm->quitGame();
00300             }
00301         }
00302         break;
00303     default:
00304         warning("Menu action %d is not implemented", action);
00305         break;
00306     }
00307 }
00308 
00309 Graphics::Surface *Menu::captureThumbnail() {
00310     Graphics::Surface *big = _vm->_gfx->getScreenshot();
00311     Graphics::Surface *thumbnail = createThumbnail(big);
00312     big->free();
00313     delete big;
00314 
00315     return thumbnail;
00316 }
00317 
00318 void Menu::goToNode(uint16 node) {
00319     if (_vm->_state->getMenuSavedAge() == 0 && _vm->_state->getLocationRoom() != kRoomMenu) {
00320         // Entering menu, save current location ...
00321         _vm->_state->setMenuSavedAge(_vm->_state->getLocationAge());
00322         _vm->_state->setMenuSavedRoom(_vm->_state->getLocationRoom());
00323         _vm->_state->setMenuSavedNode(_vm->_state->getLocationNode());
00324 
00325         // ... and capture the screen
00326         Graphics::Surface *thumbnail = captureThumbnail();
00327         _saveThumbnail.reset(thumbnail);
00328 
00329         // Reset some sound variables
00330         if (_vm->_state->getLocationAge() == 6 && _vm->_state->getSoundEdannaUnk587() == 1 && _vm->_state->getSoundEdannaUnk1031()) {
00331             _vm->_state->setSoundEdannaUnk587(0);
00332         }
00333         if (_vm->_state->getLocationAge() == 10 && _vm->_state->getSoundAmateriaUnk627() == 1 && _vm->_state->getSoundAmateriaUnk930()){
00334             _vm->_state->setSoundAmateriaUnk627(0);
00335         }
00336         if (_vm->_state->getLocationAge() == 7 && _vm->_state->getSoundVoltaicUnk540() == 1 && _vm->_state->getSoundVoltaicUnk1146()) {
00337             _vm->_state->setSoundVoltaicUnk540(0);
00338         }
00339 
00340         _vm->_sound->stopMusic(60);
00341         _vm->_state->setSoundScriptsSuspended(1);
00342     }
00343 
00344     if (_vm->_state->hasVarMenuEscapePressed()) {
00345         // This variable does not exist in the Xbox version
00346         _vm->_state->setMenuEscapePressed(0);
00347     }
00348 
00349     _vm->_state->setLocationNextAge(9);
00350     _vm->_state->setLocationNextRoom(kRoomMenu);
00351     _vm->goToNode(node, kTransitionNone);
00352 }
00353 
00354 uint Menu::dialogIdFromType(DialogType type) {
00355     struct {
00356         DialogType type;
00357         uint id;
00358         uint idXbox;
00359     } mapping[] = {
00360             { kConfirmNewGame,        1080, 1010 },
00361             { kConfirmLoadGame,       1060, 1003 },
00362             { kConfirmOverwrite,      1040, 1004 },
00363             { kConfirmEraseSavedGame, 1020, 0 },
00364             { kErrorEraseSavedGame,   1050, 0 },
00365             { kConfirmQuit,           1070, 0 }
00366     };
00367 
00368     uint id = 0;
00369 
00370     for (uint i = 0; i < ARRAYSIZE(mapping); i++) {
00371         if (mapping[i].type == type) {
00372             if (_vm->getPlatform() == Common::kPlatformXbox) {
00373                 id = mapping[i].idXbox;
00374             } else {
00375                 id = mapping[i].id;
00376             }
00377         }
00378     }
00379 
00380     if (id == 0) {
00381         error("No id for dialog %d", type);
00382     }
00383 
00384     return id;
00385 }
00386 
00387 uint16 Menu::dialogConfirmValue() {
00388     if (_vm->getPlatform() == Common::kPlatformXbox) {
00389         return 1;
00390     }
00391 
00392     return 2;
00393 }
00394 
00395 uint16 Menu::dialogSaveValue() {
00396     if (_vm->getPlatform() == Common::kPlatformXbox) {
00397         return 999; // No save value
00398     }
00399 
00400     return 1;
00401 }
00402 
00403 Common::String Menu::getAgeLabel(GameState *gameState) {
00404     uint32 age = 0;
00405     uint32 room = gameState->getLocationRoom();
00406     if (room == kRoomMenu)
00407         age = gameState->getMenuSavedAge();
00408     else
00409         age = gameState->getLocationAge();
00410 
00411     // Look for the age name
00412     const DirectorySubEntry *desc = _vm->getFileDescription("AGES", 1000, 0, DirectorySubEntry::kTextMetadata);
00413 
00414     if (!desc)
00415         error("Unable to load age descriptions.");
00416 
00417     Common::String label = desc->getTextData(_vm->_db->getAgeLabelId(age));
00418     label.toUppercase();
00419 
00420     return label;
00421 }
00422 
00423 Graphics::Surface *Menu::createThumbnail(Graphics::Surface *big) {
00424     assert(big->format == Texture::getRGBAPixelFormat());
00425 
00426     Graphics::Surface *small = new Graphics::Surface();
00427     small->create(GameState::kThumbnailWidth, GameState::kThumbnailHeight, Texture::getRGBAPixelFormat());
00428 
00429     // The portion of the screenshot to keep
00430     Common::Rect frame = _vm->_scene->getPosition();
00431     Graphics::Surface frameSurface = big->getSubArea(frame);
00432 
00433     uint32 *dst = (uint32 *)small->getPixels();
00434     for (uint i = 0; i < small->h; i++) {
00435         for (uint j = 0; j < small->w; j++) {
00436             uint32 srcX = frameSurface.w * j / small->w;
00437             uint32 srcY = frameSurface.h * i / small->h;
00438             uint32 *src = (uint32 *)frameSurface.getBasePtr(srcX, srcY);
00439 
00440             // Copy RGBA pixel
00441             *dst++ = *src;
00442         }
00443     }
00444 
00445     return small;
00446 }
00447 
00448 void Menu::setSaveLoadSpotItem(uint16 id, SpotItemFace *spotItem) {
00449     if (id == 1) {
00450         _saveLoadSpotItem = spotItem;
00451     }
00452 }
00453 
00454 bool Menu::isOpen() const {
00455     return _vm->_state->getLocationAge() == 9 && _vm->_state->getLocationRoom() == kRoomMenu;
00456 }
00457 
00458 void Menu::generateSaveThumbnail() {
00459     _saveThumbnail.reset(captureThumbnail());
00460 }
00461 
00462 Graphics::Surface *Menu::borrowSaveThumbnail() {
00463     return _saveThumbnail.get();
00464 }
00465 
00466 PagingMenu::PagingMenu(Myst3Engine *vm) :
00467         Menu(vm),
00468         _saveDrawCaret(false),
00469         _saveCaretCounter(0) {
00470 }
00471 
00472 PagingMenu::~PagingMenu() {
00473 }
00474 
00475 void PagingMenu::saveLoadAction(uint16 action, uint16 item) {
00476     switch (action) {
00477     case 0:
00478         loadMenuOpen();
00479         break;
00480     case 1:
00481         loadMenuSelect(item);
00482         break;
00483     case 2:
00484         loadMenuLoad();
00485         break;
00486     case 3:
00487         saveMenuOpen();
00488         break;
00489     case 4:
00490         saveMenuSelect(item);
00491         break;
00492     case 5:
00493         saveMenuSave();
00494         break;
00495     case 6:
00496         loadMenuChangePage();
00497         break;
00498     case 7:
00499         saveMenuChangePage();
00500         break;
00501     case 8:
00502         saveLoadErase();
00503         break;
00504     default:
00505         warning("Save load menu action %d for item %d is not implemented", action, item);
00506     }
00507 }
00508 
00509 void PagingMenu::loadMenuOpen() {
00510     _saveLoadFiles = Saves::list(_vm->getSaveFileManager(), _vm->getPlatform());
00511 
00512     _vm->_state->setMenuSaveLoadCurrentPage(0);
00513     saveLoadUpdateVars();
00514 }
00515 
00516 void PagingMenu::saveLoadUpdateVars() {
00517     int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
00518 
00519     // Go back one page if the last element of the last page was removed
00520     if (page && (7 * page > (int)_saveLoadFiles.size() - 1))
00521         page--;
00522     _vm->_state->setMenuSaveLoadCurrentPage(page);
00523 
00524     // Set up pagination
00525     bool canGoLeft = (_saveLoadFiles.size() > 7) && page;
00526     bool canGoRight = (_saveLoadFiles.size() > 7) && (7 * (page + 1) < (int)_saveLoadFiles.size());
00527 
00528     _vm->_state->setMenuSaveLoadPageLeft(canGoLeft);
00529     _vm->_state->setMenuSaveLoadPageRight(canGoRight);
00530     _vm->_state->setMenuSaveLoadSelectedItem(-1);
00531 
00532     // Enable items
00533     uint16 itemsOnPage = _saveLoadFiles.size() % 7;
00534 
00535     if (itemsOnPage == 0 && _saveLoadFiles.size() != 0)
00536         itemsOnPage = 7;
00537     if (canGoRight)
00538         itemsOnPage = 7;
00539 
00540     for (uint i = 0; i < 7; i++)
00541         _vm->_state->setVar(1354 + i, i < itemsOnPage);
00542 }
00543 
00544 void PagingMenu::loadMenuSelect(uint16 item) {
00545     // Selecting twice the same item loads it
00546     if (item == _vm->_state->getMenuSaveLoadSelectedItem()) {
00547         loadMenuLoad();
00548         return;
00549     }
00550 
00551     _vm->_state->setMenuSaveLoadSelectedItem(item);
00552     int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
00553 
00554     uint16 index = page * 7 + item;
00555 
00556     assert(index < _saveLoadFiles.size());
00557     Common::String filename = _saveLoadFiles[index];
00558     Common::InSaveFile *saveFile = _vm->getSaveFileManager()->openForLoading(filename);
00559     if (!saveFile) {
00560         warning("Unable to open save '%s'", filename.c_str());
00561         return;
00562     }
00563 
00564     // Extract the age to load from the savegame
00565     GameState gameState = GameState(_vm->getPlatform(), _vm->_db);
00566     gameState.load(saveFile);
00567 
00568     // Update the age name
00569     _saveLoadAgeName = getAgeLabel(&gameState);
00570 
00571     // Update the save thumbnail
00572     if (_saveLoadSpotItem) {
00573         Graphics::Surface *thumbnail = GameState::readThumbnail(saveFile);
00574         _saveLoadSpotItem->updateData(thumbnail);
00575         thumbnail->free();
00576         delete thumbnail;
00577     }
00578 
00579     delete saveFile;
00580 }
00581 
00582 void PagingMenu::loadMenuLoad() {
00583     uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
00584     int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
00585 
00586     uint16 index = page * 7 + item;
00587     assert(index < _saveLoadFiles.size());
00588 
00589     Common::Error loadError = _vm->loadGameState(_saveLoadFiles[index], kTransitionFade);
00590     if (loadError.getCode() != Common::kNoError) {
00591         GUI::MessageDialog dialog(loadError.getDesc());
00592         dialog.runModal();
00593     }
00594 }
00595 
00596 void PagingMenu::saveMenuOpen() {
00597     _saveLoadFiles = Saves::list(_vm->getSaveFileManager(), _vm->getPlatform());
00598 
00599     _saveLoadAgeName = getAgeLabel(_vm->_state);
00600     _saveCaretCounter = kCaretSpeed;
00601 
00602     _vm->_state->setMenuSaveLoadCurrentPage(0);
00603     saveLoadUpdateVars();
00604 
00605     // Update the thumbnail to display
00606     if (_saveLoadSpotItem && _saveThumbnail)
00607         _saveLoadSpotItem->updateData(_saveThumbnail.get());
00608 }
00609 
00610 void PagingMenu::saveMenuSelect(uint16 item) {
00611     _vm->_state->setMenuSaveLoadSelectedItem(item);
00612 
00613     if (item != 7) {
00614         int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
00615 
00616         uint16 index = page * 7 + item;
00617 
00618         assert(index < _saveLoadFiles.size());
00619         _saveName = _saveLoadFiles[index];
00620     }
00621 }
00622 
00623 void PagingMenu::saveMenuChangePage() {
00624     saveLoadUpdateVars();
00625     _vm->_state->setMenuSaveLoadSelectedItem(7);
00626 }
00627 
00628 void PagingMenu::saveMenuSave() {
00629     if (_saveName.empty())
00630         return;
00631 
00632     Common::String fileName = _saveName;
00633     if (!fileName.hasSuffixIgnoreCase(".M3S"))
00634         fileName += ".M3S";
00635 
00636     // Check if file exists
00637     bool fileExists = false;
00638     for (uint i = 0; i < _saveLoadFiles.size(); i++) {
00639         if (_saveLoadFiles[i].equalsIgnoreCase(fileName)) {
00640             fileExists = true;
00641             break;
00642         }
00643     }
00644 
00645     // Ask the user if he wants to overwrite the existing save
00646     if (fileExists && _vm->openDialog(dialogIdFromType(kConfirmOverwrite)) != 1)
00647         return;
00648 
00649     Common::Error saveError = _vm->saveGameState(_saveName, _saveThumbnail.get());
00650     if (saveError.getCode() != Common::kNoError) {
00651         GUI::MessageDialog dialog(saveError.getDesc());
00652         dialog.runModal();
00653     }
00654 
00655     // Do next action
00656     _vm->_state->setMenuNextAction(_vm->_state->getMenuSaveAction());
00657     _vm->runScriptsFromNode(88);
00658 }
00659 
00660 void PagingMenu::saveLoadErase() {
00661     uint16 node = _vm->_state->getLocationNode();
00662     uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
00663     int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
00664 
00665     uint16 index = page * 7 + item;
00666     assert(index < _saveLoadFiles.size());
00667 
00668     // Confirm dialog
00669     if (_vm->openDialog(dialogIdFromType(kConfirmEraseSavedGame)) != 1)
00670         return;
00671 
00672     // Delete the file
00673     if (!_vm->getSaveFileManager()->removeSavefile(_saveLoadFiles[index]))
00674         _vm->openDialog(dialogIdFromType(kErrorEraseSavedGame)); // Error dialog
00675 
00676     _saveLoadFiles = Saves::list(_vm->getSaveFileManager(), _vm->getPlatform());
00677 
00678     saveLoadUpdateVars();
00679 
00680     // Load menu specific
00681     if (node == kNodeMenuLoadGame && _saveLoadSpotItem) {
00682         _saveLoadSpotItem->clear();
00683         _saveLoadAgeName.clear();
00684     }
00685 
00686     // Save menu specific
00687     if (node == kNodeMenuSaveGame)
00688         _vm->_state->setMenuSaveLoadSelectedItem(7);
00689 }
00690 
00691 void PagingMenu::draw() {
00692     uint16 node = _vm->_state->getLocationNode();
00693     uint16 room = _vm->_state->getLocationRoom();
00694     uint16 age = _vm->_state->getLocationAge();
00695 
00696     if (room != kRoomMenu || !(node == kNodeMenuLoadGame || node == kNodeMenuSaveGame))
00697         return;
00698 
00699     int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
00700     NodePtr nodeData = _vm->_db->getNodeData(node, room, age);
00701 
00702     for (uint i = 0; i < 7; i++) {
00703         uint itemToDisplay = page * 7 + i;
00704 
00705         if (itemToDisplay >= _saveLoadFiles.size())
00706             break;
00707 
00708         PolarRect rect = nodeData->hotspots[i + 1].rects[0];
00709 
00710         Common::String display = prepareSaveNameForDisplay(_saveLoadFiles[itemToDisplay]);
00711         _vm->_gfx->draw2DText(display, Common::Point(rect.centerPitch, rect.centerHeading));
00712     }
00713 
00714     if (!_saveLoadAgeName.empty()) {
00715         PolarRect rect = nodeData->hotspots[8].rects[0];
00716         _vm->_gfx->draw2DText(_saveLoadAgeName, Common::Point(rect.centerPitch, rect.centerHeading));
00717     }
00718 
00719     // Save screen specific
00720     if (node == kNodeMenuSaveGame) {
00721         uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
00722         Common::String display = prepareSaveNameForDisplay(_saveName);
00723 
00724         if (item == 7) {
00725             _saveCaretCounter--;
00726             if (_saveCaretCounter < 0) {
00727                 _saveCaretCounter = kCaretSpeed;
00728                 _saveDrawCaret = !_saveDrawCaret;
00729             }
00730 
00731             if (_saveDrawCaret) {
00732                 display += '|';
00733             }
00734         }
00735 
00736         PolarRect rect = nodeData->hotspots[9].rects[0];
00737         _vm->_gfx->draw2DText(display, Common::Point(rect.centerPitch, rect.centerHeading));
00738     }
00739 }
00740 
00741 bool PagingMenu::handleInput(const Common::KeyState &e) {
00742     uint16 node = _vm->_state->getLocationNode();
00743     uint16 room = _vm->_state->getLocationRoom();
00744     uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
00745 
00746     if (room != kRoomMenu || node != kNodeMenuSaveGame || item != 7)
00747         return false;
00748 
00749     Common::String display = prepareSaveNameForDisplay(_saveName);
00750 
00751     if (e.keycode == Common::KEYCODE_BACKSPACE
00752             || e.keycode == Common::KEYCODE_DELETE) {
00753         display.deleteLastChar();
00754         _saveName = display;
00755         return true;
00756     } else if (e.keycode == Common::KEYCODE_RETURN
00757             || e.keycode == Common::KEYCODE_KP_ENTER) {
00758         saveMenuSave();
00759         return true;
00760     }
00761 
00762     if (((e.ascii >= 'a' && e.ascii <= 'z')
00763             || (e.ascii >= 'A' && e.ascii <= 'Z')
00764             || (e.ascii >= '0' && e.ascii <= '9')
00765             || e.ascii == ' ')
00766             && (display.size() < 17)) {
00767         display += e.ascii;
00768         display.toUppercase();
00769         _saveName = display;
00770 
00771         return true;
00772     }
00773 
00774     return false;
00775 }
00776 
00777 void PagingMenu::loadMenuChangePage() {
00778     saveLoadUpdateVars();
00779 }
00780 
00781 Common::String PagingMenu::prepareSaveNameForDisplay(const Common::String &name) {
00782     Common::String display = name;
00783     display.toUppercase();
00784     if (display.hasSuffixIgnoreCase(".M3S")) {
00785         display.deleteLastChar();
00786         display.deleteLastChar();
00787         display.deleteLastChar();
00788         display.deleteLastChar();
00789     }
00790 
00791     while (display.size() > 17)
00792         display.deleteLastChar();
00793 
00794     return display;
00795 }
00796 
00797 AlbumMenu::AlbumMenu(Myst3Engine *vm) :
00798         Menu(vm) {
00799 }
00800 
00801 AlbumMenu::~AlbumMenu() {
00802 }
00803 
00804 void AlbumMenu::draw() {
00805     uint16 node = _vm->_state->getLocationNode();
00806     uint16 room = _vm->_state->getLocationRoom();
00807 
00808     // Load and save menus only
00809     if (room != kRoomMenu || !(node == kNodeMenuLoadGame || node == kNodeMenuSaveGame))
00810         return;
00811 
00812     if (!_saveLoadAgeName.empty()) {
00813         Common::Point p(184 - (13 * _saveLoadAgeName.size()) / 2, 305);
00814         _vm->_gfx->draw2DText(_saveLoadAgeName, p);
00815     }
00816 
00817     if (!_saveLoadTime.empty()) {
00818         Common::Point p(184 - (13 * _saveLoadTime.size()) / 2, 323);
00819         _vm->_gfx->draw2DText(_saveLoadTime, p);
00820     }
00821 }
00822 
00823 bool AlbumMenu::handleInput(const Common::KeyState &e) {
00824     return false;
00825 }
00826 
00827 void AlbumMenu::saveLoadAction(uint16 action, uint16 item) {
00828     switch (action) {
00829     case 0:
00830         loadMenuOpen();
00831         break;
00832     case 1:
00833         loadMenuSelect();
00834         break;
00835     case 2:
00836         loadMenuLoad();
00837         break;
00838     case 3:
00839         saveMenuOpen();
00840         break;
00841     case 4:
00842         saveMenuSave();
00843         break;
00844     case 5:
00845         setSavesAvailable();
00846         break;
00847     default:
00848         warning("Save load menu action %d for item %d is not implemented", action, item);
00849     }
00850 }
00851 
00852 Common::String AlbumMenu::getSaveNameTemplate() {
00853     const DirectorySubEntry *saveNameDesc = _vm->getFileDescription("SAVE", 1000, 0, DirectorySubEntry::kTextMetadata);
00854     return saveNameDesc->getTextData(0); // "EXILE Saved Game %d"
00855 }
00856 
00857 Common::HashMap<int, Common::String> AlbumMenu::listSaveFiles() {
00858     Common::StringArray saveFiles = _vm->getSaveFileManager()->listSavefiles("*.m3x");
00859     Common::String fileNameTemplate = Common::String::format("%s.m3x", getSaveNameTemplate().c_str());
00860 
00861     Common::HashMap<int, Common::String> filteredSaveFiles;
00862     for (uint i = 0; i < 10; i++) {
00863         Common::String fileName = Common::String::format(fileNameTemplate.c_str(), i);
00864 
00865         for (uint j = 0; j < saveFiles.size(); j++) {
00866             if (saveFiles[j].equalsIgnoreCase(fileName)) {
00867                 filteredSaveFiles.setVal(i, saveFiles[j]);
00868                 break;
00869             }
00870         }
00871     }
00872 
00873     return filteredSaveFiles;
00874 }
00875 
00876 void AlbumMenu::loadSaves() {
00877     Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
00878     for (uint i = 0; i < 10; i++) {
00879         // Skip empty slots
00880         if (!saveFiles.contains(i)) continue;
00881 
00882         // Open save
00883         Common::InSaveFile *saveFile = _vm->getSaveFileManager()->openForLoading(saveFiles[i]);
00884         if (!saveFile) {
00885             warning("Failed to open save %s for reading.", saveFiles[i].c_str());
00886             continue;
00887         }
00888 
00889         // Read state data
00890         Common::Serializer s = Common::Serializer(saveFile, 0);
00891         GameState::StateData data;
00892         data.syncWithSaveGame(s);
00893 
00894         if (_albumSpotItems.contains(i)) {
00895             // Read and resize the thumbnail
00896             Graphics::Surface *saveThumb = GameState::readThumbnail(saveFile);
00897             Graphics::Surface *miniThumb = GameState::resizeThumbnail(saveThumb, kAlbumThumbnailWidth, kAlbumThumbnailHeight);
00898             saveThumb->free();
00899             delete saveThumb;
00900 
00901             SpotItemFace *spotItem = _albumSpotItems.getVal(i);
00902             spotItem->updateData(miniThumb);
00903 
00904             miniThumb->free();
00905             delete miniThumb;
00906         }
00907 
00908         delete saveFile;
00909     }
00910 }
00911 
00912 void AlbumMenu::loadMenuOpen() {
00913     _saveLoadAgeName = "";
00914     _saveLoadTime = "";
00915 
00916     loadSaves();
00917 }
00918 
00919 void AlbumMenu::loadMenuSelect() {
00920     uint16 node = _vm->_state->getLocationNode();
00921     uint16 room = _vm->_state->getLocationRoom();
00922 
00923     // Details are only updated on the load menu
00924     if (room != kRoomMenu || node != kNodeMenuLoadGame)
00925         return;
00926 
00927     int32 selectedSave = _vm->_state->getMenuSelectedSave();
00928     Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
00929 
00930     if (!saveFiles.contains(selectedSave)) {
00931         // No save in the selected slot
00932         _saveLoadAgeName = "";
00933         _saveLoadTime = "";
00934         _saveLoadSpotItem->initBlack(GameState::kThumbnailWidth, GameState::kThumbnailHeight);
00935         return;
00936     }
00937 
00938     Common::String filename = saveFiles[selectedSave];
00939     Common::InSaveFile *saveFile = _vm->getSaveFileManager()->openForLoading(filename);
00940     if (!saveFile) {
00941         warning("Unable to open save '%s'", filename.c_str());
00942         return;
00943     }
00944 
00945     // Extract the age to load from the savegame
00946     GameState *gameState = new GameState(_vm->getPlatform(), _vm->_db);
00947     gameState->load(saveFile);
00948 
00949     // Update the age name and save date
00950     _saveLoadAgeName = getAgeLabel(gameState);
00951     _saveLoadTime = gameState->formatSaveTime();
00952 
00953     // Update the save thumbnail
00954     if (_saveLoadSpotItem) {
00955         Graphics::Surface *thumbnail = GameState::readThumbnail(saveFile);
00956         _saveLoadSpotItem->updateData(thumbnail);
00957         thumbnail->free();
00958         delete thumbnail;
00959     }
00960 
00961     delete gameState;
00962 }
00963 
00964 void AlbumMenu::loadMenuLoad() {
00965     int32 selectedSave = _vm->_state->getMenuSelectedSave();
00966     Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
00967 
00968     if (!saveFiles.contains(selectedSave)) {
00969         return; // No save to load, do nothing
00970     }
00971 
00972     Common::Error loadError = _vm->loadGameState(saveFiles[selectedSave], kTransitionFade);
00973     if (loadError.getCode() != Common::kNoError) {
00974         GUI::MessageDialog dialog(loadError.getDesc());
00975         dialog.runModal();
00976     }
00977 }
00978 
00979 void AlbumMenu::saveMenuOpen() {
00980     loadSaves();
00981 
00982     _saveLoadAgeName = getAgeLabel(_vm->_state);
00983     _saveLoadTime = "";
00984 
00985     // Update the thumbnail to display
00986     if (_saveLoadSpotItem && _saveThumbnail)
00987         _saveLoadSpotItem->updateData(_saveThumbnail.get());
00988 }
00989 
00990 void AlbumMenu::saveMenuSave() {
00991     int32 selectedSave = _vm->_state->getMenuSelectedSave();
00992 
00993     Common::String saveNameTemplate = getSaveNameTemplate();
00994     Common::String saveName = Common::String::format(saveNameTemplate.c_str(), selectedSave);
00995 
00996     // Ask the user if he wants to overwrite the existing save
00997     Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
00998     if (saveFiles.contains(selectedSave) && _vm->openDialog(dialogIdFromType(kConfirmOverwrite)) != 1)
00999         return;
01000 
01001     Common::Error saveError = _vm->saveGameState(saveName, _saveThumbnail.get());
01002     if (saveError.getCode() != Common::kNoError) {
01003         GUI::MessageDialog dialog(saveError.getDesc());
01004         dialog.runModal();
01005     }
01006 
01007     // Do next action
01008     _vm->_state->setMenuNextAction(_vm->_state->getMenuSaveAction());
01009     _vm->runScriptsFromNode(88);
01010 }
01011 
01012 void AlbumMenu::setSavesAvailable() {
01013     Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
01014     _vm->_state->setMenuSavesAvailable(!saveFiles.empty());
01015 }
01016 
01017 void AlbumMenu::setSaveLoadSpotItem(uint16 id, SpotItemFace *spotItem) {
01018     if (id % 100 == 2) {
01019         _albumSpotItems.setVal(id / 100, spotItem);
01020     } else {
01021         Menu::setSaveLoadSpotItem(id, spotItem);
01022     }
01023 }
01024 
01025 } // End of namespace Myst3


Generated on Sat Apr 4 2020 05:00:32 for ResidualVM by doxygen 1.7.1
curved edge   curved edge