mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 01:39:57 +00:00
35d89e25a6
No behavior changes. Not all rooms were given useful names, but the most used were. More useful names for the more obscure rooms can be added later.
1004 lines
26 KiB
C++
1004 lines
26 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "engines/myst3/cursor.h"
|
|
#include "engines/myst3/database.h"
|
|
#include "engines/myst3/inventory.h"
|
|
#include "engines/myst3/menu.h"
|
|
#include "engines/myst3/myst3.h"
|
|
#include "engines/myst3/node.h"
|
|
#include "engines/myst3/scene.h"
|
|
#include "engines/myst3/sound.h"
|
|
#include "engines/myst3/state.h"
|
|
|
|
#include "common/events.h"
|
|
|
|
#include "graphics/colormasks.h"
|
|
|
|
namespace Myst3 {
|
|
|
|
Dialog::Dialog(Myst3Engine *vm, uint id):
|
|
_vm(vm),
|
|
_texture(0) {
|
|
// Draw on the whole screen
|
|
_isConstrainedToWindow = false;
|
|
_scaled = !_vm->isWideScreenModEnabled();
|
|
|
|
const DirectorySubEntry *countDesc = _vm->getFileDescription("DLGI", id, 0, DirectorySubEntry::kNumMetadata);
|
|
const DirectorySubEntry *movieDesc = _vm->getFileDescription("DLOG", id, 0, DirectorySubEntry::kDialogMovie);
|
|
if (!movieDesc) {
|
|
movieDesc = _vm->getFileDescription("DLOG", id, 0, DirectorySubEntry::kStillMovie);
|
|
}
|
|
|
|
if (!movieDesc || !countDesc)
|
|
error("Unable to load dialog %d", id);
|
|
|
|
// Retrieve button count
|
|
_buttonCount = countDesc->getMiscData(0);
|
|
assert(_buttonCount <= 3);
|
|
|
|
// Load the movie
|
|
Common::MemoryReadStream *movieStream = movieDesc->getData();
|
|
_bink.setDefaultHighColorFormat(Texture::getRGBAPixelFormat());
|
|
_bink.loadStream(movieStream);
|
|
_bink.start();
|
|
|
|
const Graphics::Surface *frame = _bink.decodeNextFrame();
|
|
_texture = _vm->_gfx->createTexture(frame);
|
|
|
|
_vm->_sound->playEffect(699, 10);
|
|
}
|
|
|
|
Dialog::~Dialog() {
|
|
_vm->_gfx->freeTexture(_texture);
|
|
}
|
|
|
|
void Dialog::draw() {
|
|
Common::Rect textureRect = Common::Rect(_texture->width, _texture->height);
|
|
_vm->_gfx->drawTexturedRect2D(getPosition(), textureRect, _texture);
|
|
}
|
|
|
|
Common::Rect Dialog::getPosition() const {
|
|
Common::Rect viewport;
|
|
if (_scaled) {
|
|
viewport = Common::Rect(Renderer::kOriginalWidth, Renderer::kOriginalHeight);
|
|
} else {
|
|
viewport = _vm->_gfx->viewport();
|
|
}
|
|
|
|
Common::Rect screenRect = Common::Rect(_texture->width, _texture->height);
|
|
screenRect.translate((viewport.width() - _texture->width) / 2,
|
|
(viewport.height() - _texture->height) / 2);
|
|
return screenRect;
|
|
}
|
|
|
|
ButtonsDialog::ButtonsDialog(Myst3Engine *vm, uint id):
|
|
Dialog(vm, id),
|
|
_frameToDisplay(0),
|
|
_previousframe(0) {
|
|
|
|
loadButtons();
|
|
}
|
|
|
|
ButtonsDialog::~ButtonsDialog() {
|
|
}
|
|
|
|
void ButtonsDialog::loadButtons() {
|
|
const DirectorySubEntry *buttonsDesc = _vm->getFileDescription("DLGB", 1000, 0, DirectorySubEntry::kNumMetadata);
|
|
|
|
if (!buttonsDesc)
|
|
error("Unable to load dialog buttons description");
|
|
|
|
for (uint i = 0; i < 3; i++) {
|
|
uint32 left = buttonsDesc->getMiscData(i * 4);
|
|
uint32 top = buttonsDesc->getMiscData(i * 4 + 1);
|
|
uint32 width = buttonsDesc->getMiscData(i * 4 + 2);
|
|
uint32 height = buttonsDesc->getMiscData(i * 4 + 3);
|
|
_buttons[i] = Common::Rect(width, height);
|
|
_buttons[i].translate(left, top);
|
|
}
|
|
}
|
|
|
|
void ButtonsDialog::draw() {
|
|
if (_frameToDisplay != _previousframe) {
|
|
_bink.seekToFrame(_frameToDisplay);
|
|
|
|
const Graphics::Surface *frame = _bink.decodeNextFrame();
|
|
_texture->update(frame);
|
|
|
|
_previousframe = _frameToDisplay;
|
|
}
|
|
|
|
Dialog::draw();
|
|
}
|
|
|
|
int16 ButtonsDialog::update() {
|
|
// Process events
|
|
Common::Event event;
|
|
while (_vm->getEventManager()->pollEvent(event)) {
|
|
if (event.type == Common::EVENT_MOUSEMOVE) {
|
|
// Compute local mouse coordinates
|
|
_vm->_cursor->updatePosition(event.mouse);
|
|
Common::Point localMouse = getRelativeMousePosition();
|
|
|
|
// No hovered button
|
|
_frameToDisplay = 0;
|
|
|
|
// Display the frame corresponding to the hovered button
|
|
for (uint i = 0; i < _buttonCount; i++) {
|
|
if (_buttons[i].contains(localMouse)) {
|
|
_frameToDisplay = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
} else if (event.type == Common::EVENT_LBUTTONDOWN) {
|
|
if (_frameToDisplay > 0) {
|
|
return _frameToDisplay;
|
|
} else {
|
|
_vm->_sound->playEffect(697, 5);
|
|
}
|
|
} else if (event.type == Common::EVENT_KEYDOWN) {
|
|
switch (event.kbd.keycode) {
|
|
case Common::KEYCODE_ESCAPE:
|
|
return _buttonCount;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -2;
|
|
}
|
|
|
|
Common::Point ButtonsDialog::getRelativeMousePosition() const {
|
|
Common::Rect position = getPosition();
|
|
Common::Point localMouse =_vm->_cursor->getPosition(_scaled);
|
|
localMouse.x -= position.left;
|
|
localMouse.y -= position.top;
|
|
return localMouse;
|
|
}
|
|
|
|
GamepadDialog::GamepadDialog(Myst3Engine *vm, uint id):
|
|
Dialog(vm, id) {
|
|
}
|
|
|
|
GamepadDialog::~GamepadDialog() {
|
|
}
|
|
|
|
int16 GamepadDialog::update() {
|
|
// Process events
|
|
Common::Event event;
|
|
while (_vm->getEventManager()->pollEvent(event)) {
|
|
if (event.type == Common::EVENT_MOUSEMOVE) {
|
|
// Compute local mouse coordinates
|
|
_vm->_cursor->updatePosition(event.mouse);
|
|
} else if (event.type == Common::EVENT_KEYDOWN) {
|
|
switch (event.kbd.keycode) {
|
|
case Common::KEYCODE_RETURN:
|
|
case Common::KEYCODE_KP_ENTER:
|
|
return 1;
|
|
case Common::KEYCODE_ESCAPE:
|
|
if (_buttonCount == 2) {
|
|
return 2;
|
|
} else {
|
|
return 1;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -2;
|
|
}
|
|
|
|
Menu::Menu(Myst3Engine *vm) :
|
|
_vm(vm),
|
|
_saveLoadSpotItem(0) {
|
|
}
|
|
|
|
Menu::~Menu() {
|
|
}
|
|
|
|
void Menu::updateMainMenu(uint16 action) {
|
|
switch (action) {
|
|
case 1: {
|
|
// New game
|
|
int16 choice = dialogConfirmValue();
|
|
|
|
// If a game is playing, ask if wants to save
|
|
if (_vm->_state->getMenuSavedAge() != 0) {
|
|
choice = _vm->openDialog(dialogIdFromType(kConfirmNewGame));
|
|
}
|
|
|
|
if (choice == dialogSaveValue()) {
|
|
// Go to save screen
|
|
_vm->_state->setMenuSaveBack(1);
|
|
_vm->_state->setMenuSaveAction(6);
|
|
goToNode(300);
|
|
} else if (choice == dialogConfirmValue()) {
|
|
// New game
|
|
goToNode(98);
|
|
}
|
|
}
|
|
break;
|
|
case 2: {
|
|
// Load game
|
|
int16 choice = dialogConfirmValue();
|
|
|
|
// If a game is playing, ask if wants to save
|
|
if (_vm->_state->getMenuSavedAge() != 0) {
|
|
choice = _vm->openDialog(dialogIdFromType(kConfirmLoadGame));
|
|
}
|
|
|
|
if (choice == dialogSaveValue()) {
|
|
// Go to save screen
|
|
_vm->_state->setMenuSaveBack(1);
|
|
_vm->_state->setMenuSaveAction(3);
|
|
goToNode(300);
|
|
} else if (choice == dialogConfirmValue()) {
|
|
// Load game screen
|
|
_vm->_state->setMenuLoadBack(1);
|
|
goToNode(200);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
// Go to save screen
|
|
_vm->_state->setMenuSaveBack(1);
|
|
_vm->_state->setMenuSaveAction(1);
|
|
goToNode(300);
|
|
break;
|
|
case 4:
|
|
// Settings
|
|
_vm->_state->setMenuOptionsBack(1);
|
|
_vm->runScriptsFromNode(599, 0, 0);
|
|
break;
|
|
case 5: {
|
|
// Asked to quit
|
|
int16 choice = dialogConfirmValue();
|
|
|
|
// If a game is playing, ask if wants to save
|
|
if (_vm->_state->getMenuSavedAge() != 0) {
|
|
choice = _vm->openDialog(dialogIdFromType(kConfirmQuit));
|
|
}
|
|
|
|
if (choice == dialogSaveValue()) {
|
|
// Go to save screen
|
|
_vm->_state->setMenuSaveBack(1);
|
|
_vm->_state->setMenuSaveAction(5);
|
|
goToNode(300);
|
|
} else if (choice == dialogConfirmValue()) {
|
|
// Quit
|
|
_vm->quitGame();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
warning("Menu action %d is not implemented", action);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Graphics::Surface *Menu::captureThumbnail() {
|
|
Graphics::Surface *big = _vm->_gfx->getScreenshot();
|
|
Graphics::Surface *thumbnail = createThumbnail(big);
|
|
big->free();
|
|
delete big;
|
|
|
|
return thumbnail;
|
|
}
|
|
|
|
void Menu::goToNode(uint16 node) {
|
|
if (_vm->_state->getMenuSavedAge() == 0 && _vm->_state->getLocationRoom() != kRoomMenu) {
|
|
// Entering menu, save current location ...
|
|
_vm->_state->setMenuSavedAge(_vm->_state->getLocationAge());
|
|
_vm->_state->setMenuSavedRoom(_vm->_state->getLocationRoom());
|
|
_vm->_state->setMenuSavedNode(_vm->_state->getLocationNode());
|
|
|
|
// ... and capture the screen
|
|
Graphics::Surface *thumbnail = captureThumbnail();
|
|
_saveThumbnail.reset(thumbnail);
|
|
|
|
// Reset some sound variables
|
|
if (_vm->_state->getLocationAge() == 6 && _vm->_state->getSoundEdannaUnk587() == 1 && _vm->_state->getSoundEdannaUnk1031()) {
|
|
_vm->_state->setSoundEdannaUnk587(0);
|
|
}
|
|
if (_vm->_state->getLocationAge() == 10 && _vm->_state->getSoundAmateriaUnk627() == 1 && _vm->_state->getSoundAmateriaUnk930()){
|
|
_vm->_state->setSoundAmateriaUnk627(0);
|
|
}
|
|
if (_vm->_state->getLocationAge() == 7 && _vm->_state->getSoundVoltaicUnk540() == 1 && _vm->_state->getSoundVoltaicUnk1146()) {
|
|
_vm->_state->setSoundVoltaicUnk540(0);
|
|
}
|
|
|
|
_vm->_sound->stopMusic(60);
|
|
_vm->_state->setSoundScriptsSuspended(1);
|
|
}
|
|
|
|
if (_vm->_state->hasVarMenuEscapePressed()) {
|
|
// This variable does not exist in the Xbox version
|
|
_vm->_state->setMenuEscapePressed(0);
|
|
}
|
|
|
|
_vm->_state->setLocationNextAge(9);
|
|
_vm->_state->setLocationNextRoom(kRoomMenu);
|
|
_vm->goToNode(node, kTransitionNone);
|
|
}
|
|
|
|
uint Menu::dialogIdFromType(DialogType type) {
|
|
struct {
|
|
DialogType type;
|
|
uint id;
|
|
uint idXbox;
|
|
} mapping[] = {
|
|
{ kConfirmNewGame, 1080, 1010 },
|
|
{ kConfirmLoadGame, 1060, 1003 },
|
|
{ kConfirmOverwrite, 1040, 1004 },
|
|
{ kConfirmEraseSavedGame, 1020, 0 },
|
|
{ kErrorEraseSavedGame, 1050, 0 },
|
|
{ kConfirmQuit, 1070, 0 }
|
|
};
|
|
|
|
uint id = 0;
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(mapping); i++) {
|
|
if (mapping[i].type == type) {
|
|
if (_vm->getPlatform() == Common::kPlatformXbox) {
|
|
id = mapping[i].idXbox;
|
|
} else {
|
|
id = mapping[i].id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (id == 0) {
|
|
error("No id for dialog %d", type);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
uint16 Menu::dialogConfirmValue() {
|
|
if (_vm->getPlatform() == Common::kPlatformXbox) {
|
|
return 1;
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
uint16 Menu::dialogSaveValue() {
|
|
if (_vm->getPlatform() == Common::kPlatformXbox) {
|
|
return 999; // No save value
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Common::String Menu::getAgeLabel(GameState *gameState) {
|
|
uint32 age = 0;
|
|
uint32 room = gameState->getLocationRoom();
|
|
if (room == kRoomMenu)
|
|
age = gameState->getMenuSavedAge();
|
|
else
|
|
age = gameState->getLocationAge();
|
|
|
|
// Look for the age name
|
|
const DirectorySubEntry *desc = _vm->getFileDescription("AGES", 1000, 0, DirectorySubEntry::kTextMetadata);
|
|
|
|
if (!desc)
|
|
error("Unable to load age descriptions.");
|
|
|
|
Common::String label = desc->getTextData(_vm->_db->getAgeLabelId(age));
|
|
label.toUppercase();
|
|
|
|
return label;
|
|
}
|
|
|
|
Graphics::Surface *Menu::createThumbnail(Graphics::Surface *big) {
|
|
assert(big->format == Texture::getRGBAPixelFormat());
|
|
|
|
Graphics::Surface *small = new Graphics::Surface();
|
|
small->create(GameState::kThumbnailWidth, GameState::kThumbnailHeight, Texture::getRGBAPixelFormat());
|
|
|
|
// The portion of the screenshot to keep
|
|
Common::Rect frame = _vm->_scene->getPosition();
|
|
Graphics::Surface frameSurface = big->getSubArea(frame);
|
|
|
|
uint32 *dst = (uint32 *)small->getPixels();
|
|
for (uint i = 0; i < small->h; i++) {
|
|
for (uint j = 0; j < small->w; j++) {
|
|
uint32 srcX = frameSurface.w * j / small->w;
|
|
uint32 srcY = frameSurface.h * i / small->h;
|
|
uint32 *src = (uint32 *)frameSurface.getBasePtr(srcX, srcY);
|
|
|
|
// Copy RGBA pixel
|
|
*dst++ = *src;
|
|
}
|
|
}
|
|
|
|
return small;
|
|
}
|
|
|
|
void Menu::setSaveLoadSpotItem(uint16 id, SpotItemFace *spotItem) {
|
|
if (id == 1) {
|
|
_saveLoadSpotItem = spotItem;
|
|
}
|
|
}
|
|
|
|
bool Menu::isOpen() const {
|
|
return _vm->_state->getLocationAge() == 9 && _vm->_state->getLocationRoom() == kRoomMenu;
|
|
}
|
|
|
|
void Menu::generateSaveThumbnail() {
|
|
_saveThumbnail.reset(captureThumbnail());
|
|
}
|
|
|
|
Graphics::Surface *Menu::borrowSaveThumbnail() {
|
|
return _saveThumbnail.get();
|
|
}
|
|
|
|
PagingMenu::PagingMenu(Myst3Engine *vm) :
|
|
Menu(vm),
|
|
_saveDrawCaret(false),
|
|
_saveCaretCounter(0) {
|
|
}
|
|
|
|
PagingMenu::~PagingMenu() {
|
|
}
|
|
|
|
void PagingMenu::saveLoadAction(uint16 action, uint16 item) {
|
|
switch (action) {
|
|
case 0:
|
|
loadMenuOpen();
|
|
break;
|
|
case 1:
|
|
loadMenuSelect(item);
|
|
break;
|
|
case 2:
|
|
loadMenuLoad();
|
|
break;
|
|
case 3:
|
|
saveMenuOpen();
|
|
break;
|
|
case 4:
|
|
saveMenuSelect(item);
|
|
break;
|
|
case 5:
|
|
saveMenuSave();
|
|
break;
|
|
case 6:
|
|
loadMenuChangePage();
|
|
break;
|
|
case 7:
|
|
saveMenuChangePage();
|
|
break;
|
|
case 8:
|
|
saveLoadErase();
|
|
break;
|
|
default:
|
|
warning("Save load menu action %d for item %d is not implemented", action, item);
|
|
}
|
|
}
|
|
|
|
void PagingMenu::loadMenuOpen() {
|
|
_saveLoadFiles = Saves::list(_vm->getSaveFileManager(), _vm->getPlatform());
|
|
|
|
_vm->_state->setMenuSaveLoadCurrentPage(0);
|
|
saveLoadUpdateVars();
|
|
}
|
|
|
|
void PagingMenu::saveLoadUpdateVars() {
|
|
int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
|
|
|
|
// Go back one page if the last element of the last page was removed
|
|
if (page && (7 * page > (int)_saveLoadFiles.size() - 1))
|
|
page--;
|
|
_vm->_state->setMenuSaveLoadCurrentPage(page);
|
|
|
|
// Set up pagination
|
|
bool canGoLeft = (_saveLoadFiles.size() > 7) && page;
|
|
bool canGoRight = (_saveLoadFiles.size() > 7) && (7 * (page + 1) < (int)_saveLoadFiles.size());
|
|
|
|
_vm->_state->setMenuSaveLoadPageLeft(canGoLeft);
|
|
_vm->_state->setMenuSaveLoadPageRight(canGoRight);
|
|
_vm->_state->setMenuSaveLoadSelectedItem(-1);
|
|
|
|
// Enable items
|
|
uint16 itemsOnPage = _saveLoadFiles.size() % 7;
|
|
|
|
if (itemsOnPage == 0 && _saveLoadFiles.size() != 0)
|
|
itemsOnPage = 7;
|
|
if (canGoRight)
|
|
itemsOnPage = 7;
|
|
|
|
for (uint i = 0; i < 7; i++)
|
|
_vm->_state->setVar(1354 + i, i < itemsOnPage);
|
|
}
|
|
|
|
void PagingMenu::loadMenuSelect(uint16 item) {
|
|
// Selecting twice the same item loads it
|
|
if (item == _vm->_state->getMenuSaveLoadSelectedItem()) {
|
|
loadMenuLoad();
|
|
return;
|
|
}
|
|
|
|
_vm->_state->setMenuSaveLoadSelectedItem(item);
|
|
int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
|
|
|
|
uint16 index = page * 7 + item;
|
|
|
|
assert(index < _saveLoadFiles.size());
|
|
Common::String filename = _saveLoadFiles[index];
|
|
Common::InSaveFile *saveFile = _vm->getSaveFileManager()->openForLoading(filename);
|
|
if (!saveFile) {
|
|
warning("Unable to open save '%s'", filename.c_str());
|
|
return;
|
|
}
|
|
|
|
// Extract the age to load from the savegame
|
|
GameState gameState = GameState(_vm->getPlatform(), _vm->_db);
|
|
gameState.load(saveFile);
|
|
|
|
// Update the age name
|
|
_saveLoadAgeName = getAgeLabel(&gameState);
|
|
|
|
// Update the save thumbnail
|
|
if (_saveLoadSpotItem) {
|
|
Graphics::Surface *thumbnail = GameState::readThumbnail(saveFile);
|
|
_saveLoadSpotItem->updateData(thumbnail);
|
|
thumbnail->free();
|
|
delete thumbnail;
|
|
}
|
|
|
|
delete saveFile;
|
|
}
|
|
|
|
void PagingMenu::loadMenuLoad() {
|
|
uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
|
|
int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
|
|
|
|
uint16 index = page * 7 + item;
|
|
assert(index < _saveLoadFiles.size());
|
|
|
|
_vm->loadGameState(_saveLoadFiles[index], kTransitionFade);
|
|
}
|
|
|
|
void PagingMenu::saveMenuOpen() {
|
|
_saveLoadFiles = Saves::list(_vm->getSaveFileManager(), _vm->getPlatform());
|
|
|
|
_saveLoadAgeName = getAgeLabel(_vm->_state);
|
|
_saveCaretCounter = kCaretSpeed;
|
|
|
|
_vm->_state->setMenuSaveLoadCurrentPage(0);
|
|
saveLoadUpdateVars();
|
|
|
|
// Update the thumbnail to display
|
|
if (_saveLoadSpotItem && _saveThumbnail)
|
|
_saveLoadSpotItem->updateData(_saveThumbnail.get());
|
|
}
|
|
|
|
void PagingMenu::saveMenuSelect(uint16 item) {
|
|
_vm->_state->setMenuSaveLoadSelectedItem(item);
|
|
|
|
if (item != 7) {
|
|
int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
|
|
|
|
uint16 index = page * 7 + item;
|
|
|
|
assert(index < _saveLoadFiles.size());
|
|
_saveName = _saveLoadFiles[index];
|
|
}
|
|
}
|
|
|
|
void PagingMenu::saveMenuChangePage() {
|
|
saveLoadUpdateVars();
|
|
_vm->_state->setMenuSaveLoadSelectedItem(7);
|
|
}
|
|
|
|
void PagingMenu::saveMenuSave() {
|
|
if (_saveName.empty())
|
|
return;
|
|
|
|
Common::String fileName = _saveName;
|
|
if (!fileName.hasSuffixIgnoreCase(".M3S"))
|
|
fileName += ".M3S";
|
|
|
|
// Check if file exists
|
|
bool fileExists = false;
|
|
for (uint i = 0; i < _saveLoadFiles.size(); i++) {
|
|
if (_saveLoadFiles[i].equalsIgnoreCase(fileName)) {
|
|
fileExists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Ask the user if he wants to overwrite the existing save
|
|
if (fileExists && _vm->openDialog(dialogIdFromType(kConfirmOverwrite)) != 1)
|
|
return;
|
|
|
|
// Save the state and the thumbnail
|
|
Common::OutSaveFile *save = _vm->getSaveFileManager()->openForSaving(fileName);
|
|
_vm->_state->save(save, _saveName, _saveThumbnail.get());
|
|
delete save;
|
|
|
|
// Do next action
|
|
_vm->_state->setMenuNextAction(_vm->_state->getMenuSaveAction());
|
|
_vm->runScriptsFromNode(88);
|
|
}
|
|
|
|
void PagingMenu::saveLoadErase() {
|
|
uint16 node = _vm->_state->getLocationNode();
|
|
uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
|
|
int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
|
|
|
|
uint16 index = page * 7 + item;
|
|
assert(index < _saveLoadFiles.size());
|
|
|
|
// Confirm dialog
|
|
if (_vm->openDialog(dialogIdFromType(kConfirmEraseSavedGame)) != 1)
|
|
return;
|
|
|
|
// Delete the file
|
|
if (!_vm->getSaveFileManager()->removeSavefile(_saveLoadFiles[index]))
|
|
_vm->openDialog(dialogIdFromType(kErrorEraseSavedGame)); // Error dialog
|
|
|
|
_saveLoadFiles = Saves::list(_vm->getSaveFileManager(), _vm->getPlatform());
|
|
|
|
saveLoadUpdateVars();
|
|
|
|
// Load menu specific
|
|
if (node == 200 && _saveLoadSpotItem) {
|
|
_saveLoadSpotItem->clear();
|
|
_saveLoadAgeName.clear();
|
|
}
|
|
|
|
// Save menu specific
|
|
if (node == 300)
|
|
_vm->_state->setMenuSaveLoadSelectedItem(7);
|
|
}
|
|
|
|
void PagingMenu::draw() {
|
|
uint16 node = _vm->_state->getLocationNode();
|
|
uint16 room = _vm->_state->getLocationRoom();
|
|
uint16 age = _vm->_state->getLocationAge();
|
|
|
|
if (room != kRoomMenu || !(node == 200 || node == 300))
|
|
return;
|
|
|
|
int16 page = _vm->_state->getMenuSaveLoadCurrentPage();
|
|
NodePtr nodeData = _vm->_db->getNodeData(node, room, age);
|
|
|
|
for (uint i = 0; i < 7; i++) {
|
|
uint itemToDisplay = page * 7 + i;
|
|
|
|
if (itemToDisplay >= _saveLoadFiles.size())
|
|
break;
|
|
|
|
PolarRect rect = nodeData->hotspots[i + 1].rects[0];
|
|
|
|
Common::String display = prepareSaveNameForDisplay(_saveLoadFiles[itemToDisplay]);
|
|
_vm->_gfx->draw2DText(display, Common::Point(rect.centerPitch, rect.centerHeading));
|
|
}
|
|
|
|
if (!_saveLoadAgeName.empty()) {
|
|
PolarRect rect = nodeData->hotspots[8].rects[0];
|
|
_vm->_gfx->draw2DText(_saveLoadAgeName, Common::Point(rect.centerPitch, rect.centerHeading));
|
|
}
|
|
|
|
// Save screen specific
|
|
if (node == 300) {
|
|
uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
|
|
Common::String display = prepareSaveNameForDisplay(_saveName);
|
|
|
|
if (item == 7) {
|
|
_saveCaretCounter--;
|
|
if (_saveCaretCounter < 0) {
|
|
_saveCaretCounter = kCaretSpeed;
|
|
_saveDrawCaret = !_saveDrawCaret;
|
|
}
|
|
|
|
if (_saveDrawCaret) {
|
|
display += '|';
|
|
}
|
|
}
|
|
|
|
PolarRect rect = nodeData->hotspots[9].rects[0];
|
|
_vm->_gfx->draw2DText(display, Common::Point(rect.centerPitch, rect.centerHeading));
|
|
}
|
|
}
|
|
|
|
bool PagingMenu::handleInput(const Common::KeyState &e) {
|
|
uint16 node = _vm->_state->getLocationNode();
|
|
uint16 room = _vm->_state->getLocationRoom();
|
|
uint16 item = _vm->_state->getMenuSaveLoadSelectedItem();
|
|
|
|
if (room != kRoomMenu || node != 300 || item != 7)
|
|
return false;
|
|
|
|
Common::String display = prepareSaveNameForDisplay(_saveName);
|
|
|
|
if (e.keycode == Common::KEYCODE_BACKSPACE
|
|
|| e.keycode == Common::KEYCODE_DELETE) {
|
|
display.deleteLastChar();
|
|
_saveName = display;
|
|
return true;
|
|
} else if (e.keycode == Common::KEYCODE_RETURN
|
|
|| e.keycode == Common::KEYCODE_KP_ENTER) {
|
|
saveMenuSave();
|
|
return true;
|
|
}
|
|
|
|
if (((e.ascii >= 'a' && e.ascii <= 'z')
|
|
|| (e.ascii >= 'A' && e.ascii <= 'Z')
|
|
|| (e.ascii >= '0' && e.ascii <= '9')
|
|
|| e.ascii == ' ')
|
|
&& (display.size() < 17)) {
|
|
display += e.ascii;
|
|
display.toUppercase();
|
|
_saveName = display;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void PagingMenu::loadMenuChangePage() {
|
|
saveLoadUpdateVars();
|
|
}
|
|
|
|
Common::String PagingMenu::prepareSaveNameForDisplay(const Common::String &name) {
|
|
Common::String display = name;
|
|
display.toUppercase();
|
|
if (display.hasSuffixIgnoreCase(".M3S")) {
|
|
display.deleteLastChar();
|
|
display.deleteLastChar();
|
|
display.deleteLastChar();
|
|
display.deleteLastChar();
|
|
}
|
|
|
|
while (display.size() > 17)
|
|
display.deleteLastChar();
|
|
|
|
return display;
|
|
}
|
|
|
|
AlbumMenu::AlbumMenu(Myst3Engine *vm) :
|
|
Menu(vm) {
|
|
}
|
|
|
|
AlbumMenu::~AlbumMenu() {
|
|
}
|
|
|
|
void AlbumMenu::draw() {
|
|
uint16 node = _vm->_state->getLocationNode();
|
|
uint16 room = _vm->_state->getLocationRoom();
|
|
|
|
// Load and save menus only
|
|
if (room != kRoomMenu || !(node == 200 || node == 300))
|
|
return;
|
|
|
|
if (!_saveLoadAgeName.empty()) {
|
|
Common::Point p(184 - (13 * _saveLoadAgeName.size()) / 2, 305);
|
|
_vm->_gfx->draw2DText(_saveLoadAgeName, p);
|
|
}
|
|
|
|
if (!_saveLoadTime.empty()) {
|
|
Common::Point p(184 - (13 * _saveLoadTime.size()) / 2, 323);
|
|
_vm->_gfx->draw2DText(_saveLoadTime, p);
|
|
}
|
|
}
|
|
|
|
bool AlbumMenu::handleInput(const Common::KeyState &e) {
|
|
return false;
|
|
}
|
|
|
|
void AlbumMenu::saveLoadAction(uint16 action, uint16 item) {
|
|
switch (action) {
|
|
case 0:
|
|
loadMenuOpen();
|
|
break;
|
|
case 1:
|
|
loadMenuSelect();
|
|
break;
|
|
case 2:
|
|
loadMenuLoad();
|
|
break;
|
|
case 3:
|
|
saveMenuOpen();
|
|
break;
|
|
case 4:
|
|
saveMenuSave();
|
|
break;
|
|
case 5:
|
|
setSavesAvailable();
|
|
break;
|
|
default:
|
|
warning("Save load menu action %d for item %d is not implemented", action, item);
|
|
}
|
|
}
|
|
|
|
Common::String AlbumMenu::getSaveNameTemplate() {
|
|
const DirectorySubEntry *saveNameDesc = _vm->getFileDescription("SAVE", 1000, 0, DirectorySubEntry::kTextMetadata);
|
|
return saveNameDesc->getTextData(0); // "EXILE Saved Game %d"
|
|
}
|
|
|
|
Common::HashMap<int, Common::String> AlbumMenu::listSaveFiles() {
|
|
Common::StringArray saveFiles = _vm->getSaveFileManager()->listSavefiles("*.m3x");
|
|
Common::String fileNameTemplate = Common::String::format("%s.m3x", getSaveNameTemplate().c_str());
|
|
|
|
Common::HashMap<int, Common::String> filteredSaveFiles;
|
|
for (uint i = 0; i < 10; i++) {
|
|
Common::String fileName = Common::String::format(fileNameTemplate.c_str(), i);
|
|
|
|
for (uint j = 0; j < saveFiles.size(); j++) {
|
|
if (saveFiles[j].equalsIgnoreCase(fileName)) {
|
|
filteredSaveFiles.setVal(i, saveFiles[j]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return filteredSaveFiles;
|
|
}
|
|
|
|
void AlbumMenu::loadSaves() {
|
|
Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
|
|
for (uint i = 0; i < 10; i++) {
|
|
// Skip empty slots
|
|
if (!saveFiles.contains(i)) continue;
|
|
|
|
// Open save
|
|
Common::InSaveFile *saveFile = _vm->getSaveFileManager()->openForLoading(saveFiles[i]);
|
|
|
|
// Read state data
|
|
Common::Serializer s = Common::Serializer(saveFile, 0);
|
|
GameState::StateData data;
|
|
data.syncWithSaveGame(s);
|
|
|
|
if (_albumSpotItems.contains(i)) {
|
|
// Read and resize the thumbnail
|
|
Graphics::Surface *saveThumb = GameState::readThumbnail(saveFile);
|
|
Graphics::Surface *miniThumb = GameState::resizeThumbnail(saveThumb, kAlbumThumbnailWidth, kAlbumThumbnailHeight);
|
|
saveThumb->free();
|
|
delete saveThumb;
|
|
|
|
SpotItemFace *spotItem = _albumSpotItems.getVal(i);
|
|
spotItem->updateData(miniThumb);
|
|
|
|
miniThumb->free();
|
|
delete miniThumb;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AlbumMenu::loadMenuOpen() {
|
|
_saveLoadAgeName = "";
|
|
_saveLoadTime = "";
|
|
|
|
loadSaves();
|
|
}
|
|
|
|
void AlbumMenu::loadMenuSelect() {
|
|
uint16 node = _vm->_state->getLocationNode();
|
|
uint16 room = _vm->_state->getLocationRoom();
|
|
|
|
// Details are only updated on the load menu
|
|
if (room != kRoomMenu || node != 200)
|
|
return;
|
|
|
|
int32 selectedSave = _vm->_state->getMenuSelectedSave();
|
|
Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
|
|
|
|
if (!saveFiles.contains(selectedSave)) {
|
|
// No save in the selected slot
|
|
_saveLoadAgeName = "";
|
|
_saveLoadTime = "";
|
|
_saveLoadSpotItem->initBlack(GameState::kThumbnailWidth, GameState::kThumbnailHeight);
|
|
return;
|
|
}
|
|
|
|
Common::String filename = saveFiles[selectedSave];
|
|
Common::InSaveFile *saveFile = _vm->getSaveFileManager()->openForLoading(filename);
|
|
if (!saveFile) {
|
|
warning("Unable to open save '%s'", filename.c_str());
|
|
return;
|
|
}
|
|
|
|
// Extract the age to load from the savegame
|
|
GameState *gameState = new GameState(_vm->getPlatform(), _vm->_db);
|
|
gameState->load(saveFile);
|
|
|
|
// Update the age name and save date
|
|
_saveLoadAgeName = getAgeLabel(gameState);
|
|
_saveLoadTime = gameState->formatSaveTime();
|
|
|
|
// Update the save thumbnail
|
|
if (_saveLoadSpotItem) {
|
|
Graphics::Surface *thumbnail = GameState::readThumbnail(saveFile);
|
|
_saveLoadSpotItem->updateData(thumbnail);
|
|
thumbnail->free();
|
|
delete thumbnail;
|
|
}
|
|
|
|
delete gameState;
|
|
}
|
|
|
|
void AlbumMenu::loadMenuLoad() {
|
|
int32 selectedSave = _vm->_state->getMenuSelectedSave();
|
|
Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
|
|
|
|
if (!saveFiles.contains(selectedSave)) {
|
|
return; // No save to load, do nothing
|
|
}
|
|
|
|
_vm->loadGameState(saveFiles[selectedSave], kTransitionFade);
|
|
}
|
|
|
|
void AlbumMenu::saveMenuOpen() {
|
|
loadSaves();
|
|
|
|
_saveLoadAgeName = getAgeLabel(_vm->_state);
|
|
_saveLoadTime = "";
|
|
|
|
// Update the thumbnail to display
|
|
if (_saveLoadSpotItem && _saveThumbnail)
|
|
_saveLoadSpotItem->updateData(_saveThumbnail.get());
|
|
}
|
|
|
|
void AlbumMenu::saveMenuSave() {
|
|
int32 selectedSave = _vm->_state->getMenuSelectedSave();
|
|
|
|
Common::String saveNameTemplate = getSaveNameTemplate();
|
|
Common::String saveName = Common::String::format(saveNameTemplate.c_str(), selectedSave);
|
|
Common::String fileName = saveName + ".m3x";
|
|
|
|
// Ask the user if he wants to overwrite the existing save
|
|
Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
|
|
if (saveFiles.contains(selectedSave) && _vm->openDialog(dialogIdFromType(kConfirmOverwrite)) != 1)
|
|
return;
|
|
|
|
// Save the state and the thumbnail
|
|
Common::OutSaveFile *save = _vm->getSaveFileManager()->openForSaving(fileName);
|
|
_vm->_state->save(save, saveName, _saveThumbnail.get());
|
|
delete save;
|
|
|
|
// Do next action
|
|
_vm->_state->setMenuNextAction(_vm->_state->getMenuSaveAction());
|
|
_vm->runScriptsFromNode(88);
|
|
}
|
|
|
|
void AlbumMenu::setSavesAvailable() {
|
|
Common::HashMap<int, Common::String> saveFiles = listSaveFiles();
|
|
_vm->_state->setMenuSavesAvailable(!saveFiles.empty());
|
|
}
|
|
|
|
void AlbumMenu::setSaveLoadSpotItem(uint16 id, SpotItemFace *spotItem) {
|
|
if (id % 100 == 2) {
|
|
_albumSpotItems.setVal(id / 100, spotItem);
|
|
} else {
|
|
Menu::setSaveLoadSpotItem(id, spotItem);
|
|
}
|
|
}
|
|
|
|
} // End of namespace Myst3
|