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


Generated on Sat Mar 23 2019 05:01:50 for ResidualVM by doxygen 1.7.1
curved edge   curved edge