From 379a8cbbe031c0c637029946b4cebea1e3e72bd5 Mon Sep 17 00:00:00 2001 From: johndoe123 Date: Wed, 23 Jan 2013 11:24:17 +0000 Subject: [PATCH] NEVERHOOD: Add support for loading/saving games with the game's own menu - Add LoadGameMenu class --- engines/neverhood/gamemodule.cpp | 31 ++-- engines/neverhood/gamemodule.h | 6 +- engines/neverhood/menumodule.cpp | 233 +++++++++++++++++++++++++++---- engines/neverhood/menumodule.h | 39 ++++-- engines/neverhood/neverhood.cpp | 2 +- engines/neverhood/neverhood.h | 2 + engines/neverhood/saveload.cpp | 2 +- 7 files changed, 260 insertions(+), 55 deletions(-) diff --git a/engines/neverhood/gamemodule.cpp b/engines/neverhood/gamemodule.cpp index fa8a6d5d293..48462a2282f 100644 --- a/engines/neverhood/gamemodule.cpp +++ b/engines/neverhood/gamemodule.cpp @@ -75,7 +75,7 @@ enum { GameModule::GameModule(NeverhoodEngine *vm) : Module(vm, NULL), _moduleNum(-1), _prevChildObject(NULL), _prevModuleNum(-1), - _restartGameRequested(false), _mainMenuRequested(false), _gameWasLoaded(false) { + _restoreGameRequested(false), _restartGameRequested(false), _mainMenuRequested(false), _gameWasLoaded(false) { // Other initializations moved to actual engine class _vm->_soundMan->playSoundThree(0x002D0031, 0x8861079); @@ -464,13 +464,8 @@ void GameModule::startup() { #endif } -void GameModule::restoreGame() { - delete _childObject; - delete _prevChildObject; - _childObject = NULL; - _prevChildObject = NULL; - _prevModuleNum = 0; - createModuleByHash(getGlobalVar(V_MODULE_NAME)); +void GameModule::requestRestoreGame() { + _restoreGameRequested = true; } void GameModule::requestRestartGame(bool requestMainMenu) { @@ -478,11 +473,27 @@ void GameModule::requestRestartGame(bool requestMainMenu) { _mainMenuRequested = requestMainMenu; } -void GameModule::checkMainMenu() { +void GameModule::redrawPrevChildObject() { + if (_prevChildObject) { + _prevChildObject->draw(); + _vm->_screen->update(); + } +} + +void GameModule::checkRequests() { if (_restartGameRequested) { _restartGameRequested = false; _vm->_gameVars->clear(); - restoreGame(); + requestRestoreGame(); + } + if (_restoreGameRequested) { + _restoreGameRequested = false; + delete _childObject; + delete _prevChildObject; + _childObject = NULL; + _prevChildObject = NULL; + _prevModuleNum = 0; + createModuleByHash(getGlobalVar(V_MODULE_NAME)); } if (_mainMenuRequested) openMainMenu(); diff --git a/engines/neverhood/gamemodule.h b/engines/neverhood/gamemodule.h index b212a1905c8..04fc780b021 100644 --- a/engines/neverhood/gamemodule.h +++ b/engines/neverhood/gamemodule.h @@ -33,9 +33,10 @@ public: GameModule(NeverhoodEngine *vm); virtual ~GameModule(); void startup(); - void restoreGame(); + void requestRestoreGame(); void requestRestartGame(bool requestMainMenu); - void checkMainMenu(); + void redrawPrevChildObject(); + void checkRequests(); void handleMouseMove(int16 x, int16 y); void handleMouseDown(int16 x, int16 y); void handleMouseUp(int16 x, int16 y); @@ -58,6 +59,7 @@ protected: Entity *_prevChildObject; int _prevModuleNum; bool _gameWasLoaded; + bool _restoreGameRequested; bool _restartGameRequested; bool _mainMenuRequested; bool _someFlag1; diff --git a/engines/neverhood/menumodule.cpp b/engines/neverhood/menumodule.cpp index b86fc7095ac..396d8cfb30b 100644 --- a/engines/neverhood/menumodule.cpp +++ b/engines/neverhood/menumodule.cpp @@ -23,13 +23,16 @@ #include "neverhood/menumodule.h" #include "neverhood/gamemodule.h" +#include "engines/savestate.h" + namespace Neverhood { enum { MAIN_MENU = 0, CREDITS_SCENE = 1, MAKING_OF = 2, - SAVE_GAME_MENU = 3 + LOAD_GAME_MENU = 3, + SAVE_GAME_MENU = 4 }; enum { @@ -77,10 +80,14 @@ MenuModule::~MenuModule() { _vm->_screen->setPaletteData(_savedPaletteData); } +void MenuModule::setLoadgameInfo(uint slot) { + _savegameSlot = slot; + debug("LOADGAME: slot = %d", slot); +} + void MenuModule::setSavegameInfo(const Common::String &description, uint slot, bool newSavegame) { _savegameDescription = description; - _savegameSlot = slot; - _newSavegame = newSavegame; + _savegameSlot = newSavegame ? _savegameList->size() : slot; debug("SAVEGAME: description = [%s]; slot = %d; new = %d", description.c_str(), slot, newSavegame); } @@ -96,6 +103,9 @@ void MenuModule::createScene(int sceneNum, int which) { case MAKING_OF: createSmackerScene(kMakingOfSmackerFileHashList, false, true, true); break; + case LOAD_GAME_MENU: + createLoadGameMenu(); + break; case SAVE_GAME_MENU: createSaveGameMenu(); break; @@ -108,14 +118,13 @@ void MenuModule::updateScene() { if (!updateChild()) { switch (_sceneNum) { case MAIN_MENU: - // TODO switch (_moduleResult) { case kMainMenuRestartGame: _vm->_gameModule->requestRestartGame(false); leaveModule(0); break; case kMainMenuLoadGame: - // TODO createLoadGameMenu(); + createScene(LOAD_GAME_MENU, -1); break; case kMainMenuSaveGame: createScene(SAVE_GAME_MENU, -1); @@ -149,8 +158,11 @@ void MenuModule::updateScene() { case MAKING_OF: createScene(MAIN_MENU, -1); break; + case LOAD_GAME_MENU: + handleLoadGameMenuAction(_moduleResult != 1); + break; case SAVE_GAME_MENU: - handleSaveGameMenuAction(_moduleResult); + handleSaveGameMenuAction(_moduleResult != 1); break; default: break; @@ -163,27 +175,71 @@ uint32 MenuModule::handleMessage(int messageNum, const MessageParam ¶m, Enti return Module::handleMessage(messageNum, param, sender);; } +void MenuModule::createLoadGameMenu() { + _savegameSlot = -1; + _savegameList = new Common::StringArray(); + loadSavegameList(); + _childObject = new LoadGameMenu(_vm, this, _savegameList); +} + void MenuModule::createSaveGameMenu() { - // TODO Load actual savegames list :) - _savegameList = new StringArray(); - _savegameList->push_back(Common::String("Annoying scene")); - _savegameList->push_back(Common::String("Stuff happens")); - for (uint i = 0; i < 33; ++i) - _savegameList->push_back(Common::String::format("Game %d", i)); + _savegameSlot = -1; + _savegameList = new Common::StringArray(); + loadSavegameList(); _childObject = new SaveGameMenu(_vm, this, _savegameList); } -void MenuModule::handleSaveGameMenuAction(int action) { - if (action != 0) { - createScene(MAIN_MENU, -1); - } else { - // TODO Actual saving later 0048A62E - createScene(MAIN_MENU, -1); +void MenuModule::handleLoadGameMenuAction(bool doLoad) { + createScene(MAIN_MENU, -1); + if (doLoad && _savegameSlot >= 0) { + _vm->loadGameState(_savegameSlot); + leaveModule(0); } delete _savegameList; _savegameList = NULL; } +void MenuModule::handleSaveGameMenuAction(bool doSave) { + createScene(MAIN_MENU, -1); + if (doSave && _savegameSlot >= 0) { + // Restore the scene palette and background so that the correct thumbnail is saved + byte *menuPaletteData = _vm->_screen->getPaletteData(); + _vm->_screen->setPaletteData(_savedPaletteData); + _vm->_gameModule->redrawPrevChildObject(); + _vm->saveGameState(_savegameSlot, _savegameDescription); + _vm->_screen->setPaletteData(menuPaletteData); + leaveModule(0); + } + delete _savegameList; + _savegameList = NULL; +} + +void MenuModule::loadSavegameList() { + + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Neverhood::NeverhoodEngine::SaveHeader header; + Common::String pattern = _vm->getTargetName(); + pattern += ".???"; + + Common::StringArray filenames; + filenames = saveFileMan->listSavefiles(pattern.c_str()); + Common::sort(filenames.begin(), filenames.end()); + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) { + int slotNum = atoi(file->c_str() + file->size() - 3); + if (slotNum >= 0 && slotNum <= 999) { + Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); + if (in) { + if (Neverhood::NeverhoodEngine::readSaveHeader(in, false, header) == Neverhood::NeverhoodEngine::kRSHENoError) + _savegameList->push_back(header.description); + delete in; + } + } + } + +} + MenuButton::MenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex, uint32 fileHash, const NRect &collisionBounds) : StaticSprite(vm, 900), _parentScene(parentScene), _buttonIndex(buttonIndex), _countdown(0) { @@ -393,7 +449,6 @@ Widget::Widget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene void Widget::onClick() { _parentScene->setCurrWidget(this); - // TODO? _parentScene->onClick(_itemID, 0); } void Widget::setPosition(int16 x, int16 y) { @@ -494,7 +549,7 @@ TextEditWidget::TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 item : Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority), _maxStringLength(maxStringLength), _fontSurface(fontSurface), _fileHash(fileHash), _rect(rect), _cursorSurface(NULL), _cursorTicks(0), _cursorPos(0), _cursorFileHash(0), _cursorWidth(0), _cursorHeight(0), - _modified(false) { + _modified(false), _readOnly(false) { _maxVisibleChars = (_rect.x2 - _rect.x1) / _fontSurface->getCharWidth(); _cursorPos = 0; @@ -543,17 +598,19 @@ void TextEditWidget::addSprite() { cursorSpriteResource.load(_cursorFileHash, true); _cursorSurface = new BaseSurface(_vm, 0, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height); _cursorSurface->drawSpriteResourceEx(cursorSpriteResource, false, false, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height); - _cursorSurface->setVisible(true); + _cursorSurface->setVisible(!_readOnly); refresh(); } void TextEditWidget::enterWidget() { - _cursorSurface->setVisible(true); + if (!_readOnly) + _cursorSurface->setVisible(true); refresh(); } void TextEditWidget::exitWidget() { - _cursorSurface->setVisible(false); + if (!_readOnly) + _cursorSurface->setVisible(false); refresh(); } @@ -568,7 +625,7 @@ void TextEditWidget::drawCursor() { NDrawRect sourceRect(0, 0, _cursorWidth, _cursorHeight); _surface->copyFrom(_cursorSurface->getSurface(), _rect.x1 + _cursorPos * _fontSurface->getCharWidth(), _rect.y1 + (_rect.y2 - _cursorHeight - _rect.y1 + 1) / 2, sourceRect); - } else + } else if (!_readOnly) _cursorSurface->setVisible(false); } @@ -628,10 +685,11 @@ void TextEditWidget::handleKeyDown(Common::KeyCode keyCode) { } break; default: + doRefresh = false; break; } if (doRefresh) { - _cursorSurface->setVisible(true); + _cursorSurface->setVisible(!_readOnly); _cursorTicks = 0; refresh(); } @@ -645,7 +703,7 @@ void TextEditWidget::refresh() { void TextEditWidget::update() { Widget::update(); - if (_parentScene->getCurrWidget() == this && _cursorTicks++ == 10) { + if (!_readOnly && _parentScene->getCurrWidget() == this && _cursorTicks++ == 10) { _cursorSurface->setVisible(!_cursorSurface->getVisible()); refresh(); _cursorTicks = 0; @@ -667,7 +725,7 @@ uint32 TextEditWidget::handleMessage(int messageNum, const MessageParam ¶m, SavegameListBox::SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, int baseObjectPriority, int baseSurfacePriority, - StringArray *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect) + Common::StringArray *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect) : Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority), _savegameList(savegameList), _fontSurface(fontSurface), _bgFileHash(bgFileHash), _rect(rect), _maxStringLength(0), _firstVisibleItem(0), _lastVisibleItem(0), _currIndex(0) { @@ -687,7 +745,6 @@ void SavegameListBox::onClick() { _currIndex = newIndex; refresh(); _parentScene->setCurrWidget(this); - debug("_currIndex = %d", _currIndex); _parentScene->handleEvent(_itemID, 5); } } @@ -707,7 +764,7 @@ void SavegameListBox::addSprite() { } void SavegameListBox::buildItems() { - StringArray &savegameList = *_savegameList; + Common::StringArray &savegameList = *_savegameList; int16 itemX = _rect.x1, itemY = 0; for (uint i = 0; i < savegameList.size(); ++i) { const byte *string = (const byte*)savegameList[i].c_str(); @@ -770,7 +827,7 @@ void SavegameListBox::pageDown() { } } -SaveGameMenu::SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, StringArray *savegameList) +SaveGameMenu::SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, Common::StringArray *savegameList) : WidgetScene(vm, parentModule), _savegameList(savegameList) { static const uint32 kSaveGameMenuButtonFileHashes[] = { @@ -819,7 +876,6 @@ SaveGameMenu::SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, StringArra addCollisionSprite(menuButton); } - SetUpdateHandler(&Scene::update); SetMessageHandler(&SaveGameMenu::handleMessage); } @@ -885,4 +941,119 @@ uint32 SaveGameMenu::handleMessage(int messageNum, const MessageParam ¶m, En return 0; } +LoadGameMenu::LoadGameMenu(NeverhoodEngine *vm, Module *parentModule, Common::StringArray *savegameList) + : WidgetScene(vm, parentModule), _savegameList(savegameList) { + + static const uint32 kLoadGameMenuButtonFileHashes[] = { + 0x100B2091, + 0x84822B03, + 0x20E22087, + 0x04040107, + 0x04820122, + 0x24423047 + }; + + static const NRect kLoadGameMenuButtonCollisionBounds[] = { + NRect( 44, 115, 108, 147), + NRect( 52, 396, 112, 426), + NRect(188, 116, 245, 196), + NRect(189, 209, 239, 269), + NRect(187, 301, 233, 349), + NRect(182, 358, 241, 433) + }; + + static const NRect kListBoxRect(0, 0, 320, 271); + static const NRect kTextEditRect(0, 0, 320, 17); + static const NRect kMouseRect(263, 48, 583, 65); + + _fontSurface = new FontSurface(_vm, calcHash("bgLoadTinyAlphabet"), 32, 7, 32, 11, 17); + + setBackground(0x98620234); + setPalette(0x98620234); + insertScreenMouse(0x2023098E, &kMouseRect); + insertStaticSprite(0x0BC600A3, 200); + insertStaticSprite(0x0F960021, 200); + + _listBox = new SavegameListBox(_vm, 263, 142, 69/*ItemID*/, this, 1000, 1000, + _savegameList, _fontSurface, 0x04040409, kListBoxRect); + _listBox->addSprite(); + + _textEditWidget = new TextEditWidget(_vm, 263, 48, 70/*ItemID*/, this, 1000, 1000, 29, + _fontSurface, 0x10924C03, kTextEditRect); + _textEditWidget->setCursor(0x18032303, 2, 13); + _textEditWidget->setReadOnly(true); + _textEditWidget->addSprite(); + setCurrWidget(_textEditWidget); + + for (uint buttonIndex = 0; buttonIndex < 6; ++buttonIndex) { + Sprite *menuButton = insertSprite(this, buttonIndex, + kLoadGameMenuButtonFileHashes[buttonIndex], kLoadGameMenuButtonCollisionBounds[buttonIndex]); + addCollisionSprite(menuButton); + } + + SetUpdateHandler(&Scene::update); + SetMessageHandler(&LoadGameMenu::handleMessage); +} + +LoadGameMenu::~LoadGameMenu() { + delete _fontSurface; +} + +void LoadGameMenu::handleEvent(int16 itemID, int eventType) { + if (itemID == 69 && eventType == 5) { + uint currIndex = _listBox->getCurrIndex(); + _textEditWidget->setString((*_savegameList)[currIndex]); + setCurrWidget(_textEditWidget); + } +} + +uint32 LoadGameMenu::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) { + Scene::handleMessage(messageNum, param, sender); + switch (messageNum) { +#if 0 + case 0x000A: + sendMessage(_textEditWidget, 0x000A, param.asInteger()); + setCurrWidget(_textEditWidget); + break; +#endif + case 0x000B: + if (param.asInteger() == Common::KEYCODE_RETURN) { + ((MenuModule*)_parentModule)->setLoadgameInfo(_listBox->getCurrIndex()); + leaveScene(0); + } else if (param.asInteger() == Common::KEYCODE_ESCAPE) { + leaveScene(1); + }/* else { + sendMessage(_textEditWidget, 0x000B, param.asInteger()); + setCurrWidget(_textEditWidget); + }*/ + break; + case 0x2000: + // Handle menu button click + switch (param.asInteger()) { + case 0: + // TODO Same handling as Return, merge + ((MenuModule*)_parentModule)->setLoadgameInfo(_listBox->getCurrIndex()); + leaveScene(0); + break; + case 1: + leaveScene(1); + break; + case 2: + _listBox->pageUp(); + break; + case 3: + _listBox->scrollUp(); + break; + case 4: + _listBox->scrollDown(); + break; + case 5: + _listBox->pageDown(); + break; + } + break; + } + return 0; +} + } // End of namespace Neverhood diff --git a/engines/neverhood/menumodule.h b/engines/neverhood/menumodule.h index 51c32aa878e..083679571b4 100644 --- a/engines/neverhood/menumodule.h +++ b/engines/neverhood/menumodule.h @@ -24,31 +24,33 @@ #define NEVERHOOD_MENUMODULE_H #include "common/str.h" +#include "common/str-array.h" #include "neverhood/neverhood.h" #include "neverhood/module.h" #include "neverhood/scene.h" namespace Neverhood { -typedef Common::Array StringArray; - class MenuModule : public Module { public: MenuModule(NeverhoodEngine *vm, Module *parentModule, int which); virtual ~MenuModule(); + void setLoadgameInfo(uint slot); void setSavegameInfo(const Common::String &description, uint slot, bool newSavegame); protected: int _sceneNum; byte *_savedPaletteData; - StringArray *_savegameList; + Common::StringArray *_savegameList; Common::String _savegameDescription; - uint _savegameSlot; - bool _newSavegame; + int _savegameSlot; void createScene(int sceneNum, int which); void updateScene(); uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); + void createLoadGameMenu(); void createSaveGameMenu(); - void handleSaveGameMenuAction(int action); + void handleLoadGameMenuAction(bool doLoad); + void handleSaveGameMenuAction(bool doSave); + void loadSavegameList(); }; class MenuButton : public StaticSprite { @@ -158,6 +160,7 @@ public: void handleAsciiKey(char ch); void handleKeyDown(Common::KeyCode keyCode); void refresh(); + void setReadOnly(bool value) { _readOnly = value; } bool isModified() const { return _modified; } protected: NRect _rect; @@ -173,6 +176,7 @@ protected: uint32 _cursorFileHash; int16 _cursorWidth, _cursorHeight; bool _modified; + bool _readOnly; void update(); uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); }; @@ -181,7 +185,7 @@ class SavegameListBox : public Widget { public: SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, int baseObjectPriority, int baseSurfacePriority, - StringArray *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect); + Common::StringArray *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect); virtual void onClick(); virtual void addSprite(); void buildItems(); @@ -199,7 +203,7 @@ protected: Common::Array _textLabelItems; int _firstVisibleItem; int _lastVisibleItem; - StringArray *_savegameList; + Common::StringArray *_savegameList; FontSurface *_fontSurface; uint _currIndex; int _maxVisibleItemsCount; @@ -207,11 +211,26 @@ protected: class SaveGameMenu : public WidgetScene { public: - SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, StringArray *savegameList); + SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, Common::StringArray *savegameList); ~SaveGameMenu(); virtual void handleEvent(int16 itemID, int eventType); protected: - StringArray *_savegameList; + Common::StringArray *_savegameList; + FontSurface *_fontSurface; + SavegameListBox *_listBox; + TextEditWidget *_textEditWidget; + Common::String _savegameDescription; + void update(); + uint32 handleMessage(int messageNum, const MessageParam ¶m, Entity *sender); +}; + +class LoadGameMenu : public WidgetScene { +public: + LoadGameMenu(NeverhoodEngine *vm, Module *parentModule, Common::StringArray *savegameList); + ~LoadGameMenu(); + virtual void handleEvent(int16 itemID, int eventType); +protected: + Common::StringArray *_savegameList; FontSurface *_fontSurface; SavegameListBox *_listBox; TextEditWidget *_textEditWidget; diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp index bfba0b9b951..7b068bdf46d 100644 --- a/engines/neverhood/neverhood.cpp +++ b/engines/neverhood/neverhood.cpp @@ -159,7 +159,7 @@ void NeverhoodEngine::mainLoop() { } } if (_system->getMillis() >= nextFrameTime) { - _gameModule->checkMainMenu(); + _gameModule->checkRequests(); _gameModule->handleUpdate(); _gameModule->draw(); _screen->update(); diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h index 3653127f918..14c283bc120 100644 --- a/engines/neverhood/neverhood.h +++ b/engines/neverhood/neverhood.h @@ -28,6 +28,7 @@ #include "common/keyboard.h" #include "common/random.h" #include "common/savefile.h" +#include "common/str-array.h" #include "common/system.h" #include "audio/mixer.h" #include "engines/engine.h" @@ -72,6 +73,7 @@ public: Common::Platform getPlatform() const; bool hasFeature(EngineFeature f) const; bool isDemo() const; + Common::String getTargetName() { return _targetName; }; Common::RandomSource *_rnd; diff --git a/engines/neverhood/saveload.cpp b/engines/neverhood/saveload.cpp index e1ef95477b5..2f97adeeee8 100644 --- a/engines/neverhood/saveload.cpp +++ b/engines/neverhood/saveload.cpp @@ -125,7 +125,7 @@ void NeverhoodEngine::loadgame(const char *filename) { _gameState.sceneNum = _gameVars->getGlobalVar(V_CURRENT_SCENE); _gameState.which = _gameVars->getGlobalVar(V_CURRENT_SCENE_WHICH); - _gameModule->restoreGame(); + _gameModule->requestRestoreGame(); delete in;