MACVENTURE: Add basic game save/load feature

This commit is contained in:
Borja Lorente 2016-07-16 19:57:36 +02:00
parent 734b453c86
commit a06f291280
9 changed files with 144 additions and 48 deletions

View file

@ -43,7 +43,7 @@ PrebuiltDialog prebuiltDialogs[kPrebuiltDialogCount] = {
{/* kSaveAsDialog */
Common::Rect(0, 146, 456, 254),
{
{kDEButton, "YES", kDANone, Common::Point(24, 68), 120, 22},
{kDEButton, "YES", kDASaveAs, Common::Point(24, 68), 120, 22},
{kDEButton, "NO", kDACloseDialog, Common::Point(168, 68), 120, 22},
{kDEButton, "CANCEL", kDACloseDialog, Common::Point(312, 68), 120, 22},
{kDEPlainText, "Save As...", kDANone, Common::Point(100, 10), 340, 38},
@ -92,6 +92,10 @@ void Dialog::handleDialogAction(DialogElement *trigger, DialogAction action) {
_gui->setTextInput(_userInput);
_gui->closeDialog();
break;
case kDASaveAs:
_gui->saveInto(0);
_gui->closeDialog();
break;
}
}
@ -169,11 +173,6 @@ DialogElement::DialogElement(Dialog *dialog, Common::String title, DialogAction
}
bool DialogElement::processEvent(MacVenture::Dialog *dialog, Common::Event event) {
// HACK for test, please delete me
Common::Point mouse = event.mouse;
dialog->localize(mouse);
if (_bounds.contains(mouse)) debug(1, "Hovering over: %s", _text.c_str());
return doProcessEvent(dialog, event);
}

View file

@ -38,7 +38,8 @@ class DialogElement;
enum DialogAction {
kDANone,
kDACloseDialog,
kDASubmit
kDASubmit,
kDASaveAs
};
enum PrebuiltDialogs {

View file

@ -335,8 +335,8 @@ WindowReference Gui::createInventoryWindow(ObjID objRef) {
newWindow->setDimensions(newData.bounds);
newWindow->setCallback(inventoryWindowCallback, this);
//loadBorder(newWindow, "border_no_scroll_inac.bmp", false);
//loadBorder(newWindow, "border_no_scroll_act.bmp", true);
loadBorder(newWindow, "border_no_scroll_inac.bmp", false);
loadBorder(newWindow, "border_no_scroll_act.bmp", true);
_inventoryWindows.push_back(newWindow);
debug("Create new inventory window. Reference: %d", newData.refcon);
@ -686,13 +686,12 @@ void Gui::drawWindowTitle(WindowReference target, Graphics::ManagedSurface * sur
}
void Gui::drawDraggedObject() {
if (_draggedObj.id != 0) {
if (_draggedObj.id != 0 &&
_engine->isObjVisible(_draggedObj.id)) {
ensureAssetLoaded(_draggedObj.id);
ImageAsset *asset = _assets[_draggedObj.id];
_draggedSurface.create(asset->getWidth(), asset->getHeight(), _screen.format);
_screen.copyRectToSurface(_draggedSurface, _draggedObj.pos.x, _draggedObj.pos.y,
Common::Rect(asset->getWidth() - 1, asset->getHeight() - 1));
asset->blitInto(&_draggedSurface, 0, 0, kBlitBIC);
@ -820,6 +819,14 @@ void Gui::getTextFromUser() {
_engine->clickToContinue();
}
void Gui::loadGame(int slot) {
_engine->loadGameState(slot);
}
void Gui::saveInto(int slot) {
_engine->saveGameState(slot, "desc");
_engine->preparedToRun();
}
void Gui::moveDraggedObject(Common::Point target) {
Common::Point newPos = target + _draggedObj.mouseOffset;
@ -857,11 +864,13 @@ WindowReference Gui::findWindowAtPoint(Common::Point point) {
Graphics::MacWindow *win;
for (it = _windowData->begin(); it != _windowData->end(); it++) {
win = findWindow(it->refcon);
if (win && win->getDimensions().contains(point) && it->refcon != kDiplomaWindow) { //HACK, diploma should be cosnidered
if (win && it->refcon != kDiplomaWindow) { //HACK, diploma should be cosnidered
if (win->getDimensions().contains(point)) {
if (win->isActive())
return it->refcon;
}
}
}
return kNoWindow;
}
@ -869,6 +878,7 @@ Common::Point Gui::getWindowSurfacePos(WindowReference reference) {
const WindowData &data = getWindowData(reference);
BorderBounds border = borderBounds(data.type);
Graphics::MacWindow *win = findWindow(reference);
if (!win) return Common::Point(0, 0);
return Common::Point(win->getDimensions().left + border.leftOffset, win->getDimensions().top + border.topOffset);
}
@ -976,9 +986,9 @@ void Gui::selectDraggable(ObjID child, WindowReference origin, Common::Point cli
}
void Gui::handleDragRelease(Common::Point pos, bool shiftPressed, bool isDoubleClick) {
if (_draggedObj.id != 0) {
WindowReference destinationWindow = findWindowAtPoint(pos);
if (destinationWindow == kNoWindow) return;
if (_draggedObj.id != 0) {
if (_draggedObj.hasMoved) {
ObjID destObject = getWindowData(destinationWindow).objRef;
pos -= (_draggedObj.startPos - _draggedObj.mouseOffset);
@ -1027,9 +1037,11 @@ void Gui::handleMenuAction(MenuAction action) {
break;
case MacVenture::kMenuActionOpen:
debug("MacVenture Menu Action: Open");
loadGame(0);
break;
case MacVenture::kMenuActionSave:
debug("MacVenture Menu Action: Save");
saveInto(0);
break;
case MacVenture::kMenuActionSaveAs:
debug("MacVenture Menu Action: Save As");
@ -1139,8 +1151,8 @@ bool Gui::tryCloseWindow(WindowReference winID) {
Common::Point Gui::getObjMeasures(ObjID obj) {
ensureAssetLoaded(obj);
int w = MAX(0, (int)_assets[obj]->getWidth());
int h = MAX(0, (int)_assets[obj]->getHeight());
int w = _assets[obj]->getWidth();
int h = _assets[obj]->getHeight();
return Common::Point(w, h);
}
@ -1296,7 +1308,7 @@ void Gui::processCursorTick() {
void Gui::handleSingleClick(Common::Point pos) {
debug("Single Click");
// HACK
// HACK THERE HAS TO BE A MORE ELEGANT WAY
if (_dialog) return;
handleDragRelease(pos, false, false);
}

View file

@ -247,10 +247,14 @@ public:
void printText(const Common::String &text);
//Dialog interactions
void getTextFromUser();
void setTextInput(Common::String str);
void closeDialog();
void loadGame(int slot);
void saveInto(int slot);
// Ugly switches
BorderBounds borderBounds(MVWindowType type);

View file

@ -387,11 +387,11 @@ bool ImageAsset::isRectInside(Common::Rect rect) {
}
uint ImageAsset::getWidth() {
return _bitWidth;
return MAX(0, (int)_bitWidth);
}
uint ImageAsset::getHeight() {
return _bitHeight;
return MAX(0, (int)_bitHeight);
}
void ImageAsset::blitDirect(Graphics::ManagedSurface * target, uint32 ox, uint32 oy, const Common::Array<byte>& data) {

View file

@ -82,7 +82,6 @@ Common::Error MacVentureEngine::run() {
// Additional setup.
debug("MacVentureEngine::init");
_resourceManager = new Common::MacResManager();
if (!_resourceManager->open(getGameFileName()))
error("Could not open %s as a resource fork", getGameFileName());
@ -113,7 +112,6 @@ Common::Error MacVentureEngine::run() {
_destObject = 0;
_prepared = true;
//if !savegame
_cmdReady = true;
_selectedControl = kStartOrResume;
ObjID playerParent = _world->getObjAttr(1, kAttrParentObject);
@ -162,6 +160,42 @@ Common::Error MacVentureEngine::run() {
return Common::kNoError;
}
Common::Error MacVentureEngine::loadGameState(int slot) {
Common::InSaveFile *file = getSaveFileManager()->openForLoading("Shadowgate.1");
_world->loadGameFrom(file);
reset();
return Common::kNoError;
}
Common::Error MacVentureEngine::saveGameState(int slot, const Common::String &desc) {
Common::SaveFileManager *manager = getSaveFileManager();
// HACK Get a real name!
Common::OutSaveFile *file = manager->openForSaving("Shadowgate.1");
_world->saveGameInto(file);
delete file;
}
void MacVentureEngine::reset() {
resetInternals();
resetGui();
}
void MacVentureEngine::resetInternals() {
_scriptEngine->reset();
_currentSelection.clear();
_selectedObjs.clear();
_objQueue.clear();
_textQueue.clear();
}
void MacVentureEngine::resetGui() {
_gui->updateWindowInfo(kMainGameWindow, getParent(1), _world->getChildren(getParent(1), true));
// HACK! should update all inventories
_gui->updateWindowInfo(kInventoryStart, 1, _world->getChildren(1, true));
_gui->updateWindowInfo(kExitsWindow, getParent(1), _world->getChildren(getParent(1), true));
}
void MacVentureEngine::requestQuit() {
// TODO: Display save game dialog and such
_gameState = kGameStateQuitting;
@ -391,6 +425,20 @@ Common::String MacVentureEngine::getUserInput() {
return _userInput;
}
Common::String MacVentureEngine::getStartGameFileName() {
Common::SeekableReadStream *res;
res = _resourceManager->getResource(MKTAG('S', 'T', 'R', ' '), kStartGameFilenameID);
if (!res)
return "";
byte length = res->readByte();
char *fileName = new char[length + 1];
res->read(fileName, length);
fileName[length] = '\0';
return Common::String(fileName, length);
}
const GlobalSettings& MacVentureEngine::getGlobalSettings() const {
return _globalSettings;
}

View file

@ -24,10 +24,13 @@
#define MACVENTURE_H
#include "engines/engine.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "common/debug.h"
#include "common/random.h"
#include "common/macresman.h"
#include "common/huffman.h"
#include "common/savefile.h"
#include "gui/debugger.h"
@ -41,6 +44,8 @@ struct ADGameDescription;
namespace MacVenture {
class SaveFileManager;
class Console;
class World;
class ScriptEngine;
@ -158,6 +163,12 @@ public:
~MacVentureEngine();
virtual Common::Error run();
virtual Common::Error loadGameState(int slot);
virtual Common::Error saveGameState(int slot, const Common::String &desc);
void reset();
void resetInternals();
void resetGui();
void requestQuit();
void requestUnpause();
@ -189,7 +200,9 @@ public:
void setTextInput(Common::String content);
Common::String getUserInput();
// Data retrieval
Common::String getStartGameFileName();
bool isPaused();
bool needsClickToContinue();
Common::String getCommandsPausedString() const;

View file

@ -9,7 +9,7 @@ World::World(MacVentureEngine *engine, Common::MacResManager *resMan) {
_resourceManager = resMan;
_engine = engine;
if (!loadStartGameFileName())
if ((_startGameFileName = _engine->getStartGameFileName()) == "")
error("Could not load initial game configuration");
Common::File saveGameFile;
@ -150,9 +150,11 @@ void World::updateObj(ObjID objID) {
}
void World::captureChildren(ObjID objID) {
warning("Capture children unimplemented!");
}
void World::releaseChildren(ObjID objID) {
warning("Release children unimplemented!");
}
Common::String World::getText(ObjID objID, ObjID source, ObjID target) {
@ -175,23 +177,8 @@ bool World::intersects(ObjID objID, Common::Rect rect) {
return _engine->getObjBounds(objID).intersects(rect);
}
bool World::loadStartGameFileName() {
Common::SeekableReadStream *res;
res = _resourceManager->getResource(MKTAG('S', 'T', 'R', ' '), kStartGameFilenameID);
if (!res)
return false;
byte length = res->readByte();
char *fileName = new char[length + 1];
res->read(fileName, length);
fileName[length] = '\0';
_startGameFileName = Common::String(fileName, length);
return true;
}
void World::calculateObjectRelations() {
_relations.clear();
ObjID val, next;
uint32 numObjs = _engine->getGlobalSettings().numObjects;
const AttributeGroup &parents = *_saveGame->getGroup(0);
@ -228,6 +215,16 @@ void World::setParent(ObjID child, ObjID newParent) {
_relations[oldNdx] = child;
}
void World::loadGameFrom(Common::InSaveFile *file) {
if (_saveGame) delete _saveGame;
_saveGame = new SaveGame(_engine, file);
calculateObjectRelations();
}
void World::saveGameInto(Common::OutSaveFile *file) {
_saveGame->saveInto(file);
}
// SaveGame
SaveGame::SaveGame(MacVentureEngine *engine, Common::SeekableReadStream *res) {
_groups = Common::Array<AttributeGroup>();
@ -263,14 +260,34 @@ void SaveGame::setGlobal(uint32 attrID, Attribute value) {
_globals[attrID] = value;
}
const Common::Array<uint16>& MacVenture::SaveGame::getGlobals() {
const Common::Array<uint16>& SaveGame::getGlobals() {
return _globals;
}
const Common::String & MacVenture::SaveGame::getText() {
const Common::String & SaveGame::getText() {
return _text;
}
void SaveGame::saveInto(Common::OutSaveFile *file) {
warning("Saving the game not yet tested!");
// Save attibutes
Common::Array<AttributeGroup>::const_iterator itg;
for(itg = _groups.begin(); itg != _groups.end(); itg++) {
Common::Array<Attribute>::const_iterator ita;
for (ita = itg->begin(); ita != itg->end(); ita++) {
file->writeUint16BE((*ita));
}
}
// Save globals
Common::Array<uint16>::const_iterator global;
for (global = _globals.begin(); global != _globals.end(); global++) {
file->writeUint16BE((*global));
}
// Save text
_text = "Hello";
file->write(_text.c_str(), _text.size());
}
void SaveGame::loadGroups(MacVentureEngine *engine, Common::SeekableReadStream * res) {
GlobalSettings settings = engine->getGlobalSettings();
for (int i = 0; i < settings.numGroups; ++i) {

View file

@ -74,6 +74,8 @@ public:
const AttributeGroup *getGroup(uint32 groupID);
const Common::String &getText();
void saveInto(Common::OutSaveFile *file);
private:
void loadGroups(MacVentureEngine *engine, Common::SeekableReadStream *res);
void loadGlobals(MacVentureEngine *engine, Common::SeekableReadStream *res);
@ -90,7 +92,6 @@ public:
World(MacVentureEngine *engine, Common::MacResManager *resMan);
~World();
void setObjAttr(ObjID objID, uint32 attrID, Attribute value);
void setGlobal(uint32 attrID, Attribute value);
void updateObj(ObjID objID);
@ -107,11 +108,13 @@ public:
Common::Array<ObjID> getFamily(ObjID objID, bool recursive);
Common::Array<ObjID> getChildren(ObjID objID, bool recursive);
void loadGameFrom(Common::InSaveFile *file);
void saveGameInto(Common::OutSaveFile *file);
private:
bool isObjDraggable(ObjID objID);
bool intersects(ObjID objID, Common::Rect rect);
bool loadStartGameFileName();
void calculateObjectRelations();
void setParent(ObjID child, ObjID newParent);
@ -132,4 +135,3 @@ private:
} // End of namespace MacVenture
#endif