mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-08 10:51:11 +00:00
ULTIMA4: Move saving code into Savegame class
This also merges the serparate save files the original had for the party, creatures, and dungeon information.
This commit is contained in:
parent
30fa38e336
commit
21355706b4
@ -22,12 +22,115 @@
|
||||
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/filesys/io.h"
|
||||
#include "ultima/ultima4/game/object.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/game/object.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
void SaveGame::save(Common::WriteStream *stream) {
|
||||
Common::Serializer ser(nullptr, stream);
|
||||
|
||||
if (g_context->_location->_prev) {
|
||||
_x = g_context->_location->_coords.x;
|
||||
_y = g_context->_location->_coords.y;
|
||||
_dngLevel = g_context->_location->_coords.z;
|
||||
_dngX = g_context->_location->_prev->_coords.x;
|
||||
_dngY = g_context->_location->_prev->_coords.y;
|
||||
} else {
|
||||
_x = g_context->_location->_coords.x;
|
||||
_y = g_context->_location->_coords.y;
|
||||
_dngLevel = g_context->_location->_coords.z;
|
||||
}
|
||||
|
||||
_location = g_context->_location->_map->_id;
|
||||
// _orientation = (Direction)(g_ultima->_saveGame->_orientation - DIR_WEST);
|
||||
|
||||
synchronize(ser);
|
||||
|
||||
/*
|
||||
* Save monsters
|
||||
*/
|
||||
|
||||
// fix creature animations. This was done for compatibility with u4dos,
|
||||
// so may be redundant now
|
||||
g_context->_location->_map->resetObjectAnimations();
|
||||
g_context->_location->_map->fillMonsterTable();
|
||||
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_map->_monsterTable, ser);
|
||||
|
||||
/**
|
||||
* Write dungeon info
|
||||
*/
|
||||
if (g_context->_location->_context & CTX_DUNGEON) {
|
||||
unsigned int x, y, z;
|
||||
|
||||
typedef Std::map<const Creature *, int, Std::PointerHash> DngCreatureIdMap;
|
||||
static DngCreatureIdMap id_map;
|
||||
|
||||
/**
|
||||
* Map creatures to u4dos dungeon creature Ids
|
||||
*/
|
||||
if (id_map.size() == 0) {
|
||||
id_map[creatureMgr->getById(RAT_ID)] = 1;
|
||||
id_map[creatureMgr->getById(BAT_ID)] = 2;
|
||||
id_map[creatureMgr->getById(GIANT_SPIDER_ID)] = 3;
|
||||
id_map[creatureMgr->getById(GHOST_ID)] = 4;
|
||||
id_map[creatureMgr->getById(SLIME_ID)] = 5;
|
||||
id_map[creatureMgr->getById(TROLL_ID)] = 6;
|
||||
id_map[creatureMgr->getById(GREMLIN_ID)] = 7;
|
||||
id_map[creatureMgr->getById(MIMIC_ID)] = 8;
|
||||
id_map[creatureMgr->getById(REAPER_ID)] = 9;
|
||||
id_map[creatureMgr->getById(INSECT_SWARM_ID)] = 10;
|
||||
id_map[creatureMgr->getById(GAZER_ID)] = 11;
|
||||
id_map[creatureMgr->getById(PHANTOM_ID)] = 12;
|
||||
id_map[creatureMgr->getById(ORC_ID)] = 13;
|
||||
id_map[creatureMgr->getById(SKELETON_ID)] = 14;
|
||||
id_map[creatureMgr->getById(ROGUE_ID)] = 15;
|
||||
}
|
||||
|
||||
for (z = 0; z < g_context->_location->_map->_levels; z++) {
|
||||
for (y = 0; y < g_context->_location->_map->_height; y++) {
|
||||
for (x = 0; x < g_context->_location->_map->_width; x++) {
|
||||
unsigned char tile = g_context->_location->_map->translateToRawTileIndex(*g_context->_location->_map->getTileFromData(MapCoords(x, y, z)));
|
||||
Object *obj = g_context->_location->_map->objectAt(MapCoords(x, y, z));
|
||||
|
||||
/**
|
||||
* Add the creature to the tile
|
||||
*/
|
||||
if (obj && obj->getType() == Object::CREATURE) {
|
||||
const Creature *m = dynamic_cast<Creature *>(obj);
|
||||
DngCreatureIdMap::iterator m_id = id_map.find(m);
|
||||
if (m_id != id_map.end())
|
||||
tile |= m_id->_value;
|
||||
}
|
||||
|
||||
// Write the tile
|
||||
stream->writeByte(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write out monsters
|
||||
*/
|
||||
|
||||
// fix creature animations so they are compatible with u4dos.
|
||||
// This may be redundant now for ScummVM
|
||||
g_context->_location->_prev->_map->resetObjectAnimations();
|
||||
g_context->_location->_prev->_map->fillMonsterTable(); /* fill the monster table so we can save it */
|
||||
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_prev->_map->_monsterTable, ser);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveGame::load(Common::SeekableReadStream *stream) {
|
||||
|
||||
}
|
||||
|
||||
void SaveGame::synchronize(Common::Serializer &s) {
|
||||
int i;
|
||||
|
||||
@ -180,6 +283,12 @@ void SaveGamePlayerRecord::init() {
|
||||
|
||||
void SaveGameMonsterRecord::synchronize(SaveGameMonsterRecord *monsterTable, Common::Serializer &s) {
|
||||
int i;
|
||||
const uint32 IDENT = MKTAG('M', 'O', 'N', 'S');
|
||||
uint32 val = IDENT;
|
||||
|
||||
s.syncAsUint32BE(val);
|
||||
if (s.isLoading() && val != IDENT)
|
||||
error("Invalid savegame");
|
||||
|
||||
if (s.isSaving() && !monsterTable) {
|
||||
int dataSize = MONSTERTABLE_SIZE * 8;
|
||||
|
@ -225,9 +225,26 @@ struct SaveGameMonsterRecord {
|
||||
* Represents the on-disk contents of PARTY.SAV.
|
||||
*/
|
||||
struct SaveGame {
|
||||
void synchronize(Common::Serializer &s);
|
||||
/**
|
||||
* Initialize a new savegame structure
|
||||
*/
|
||||
void init(const SaveGamePlayerRecord *avatarInfo);
|
||||
|
||||
/**
|
||||
* Load an entire savegame, including monsters
|
||||
*/
|
||||
void save(Common::WriteStream *stream);
|
||||
|
||||
/**
|
||||
* Save an entire savegame, including monsters
|
||||
*/
|
||||
void load(Common::SeekableReadStream *stream);
|
||||
|
||||
/**
|
||||
* Synchronizes data for the savegame structure
|
||||
*/
|
||||
void synchronize(Common::Serializer &s);
|
||||
|
||||
unsigned int _unknown1;
|
||||
unsigned int _moves;
|
||||
SaveGamePlayerRecord _players[8];
|
||||
|
@ -85,7 +85,6 @@ void gameInnHandler(void);
|
||||
void gameLostEighth(Virtue virtue);
|
||||
void gamePartyStarving(void);
|
||||
time_t gameTimeSinceLastCommand(void);
|
||||
int gameSave(void);
|
||||
|
||||
/* spell functions */
|
||||
void gameCastSpell(unsigned int spell, int caster, int param);
|
||||
@ -333,142 +332,6 @@ void GameController::init() {
|
||||
TRACE(gameDbg, "gameInit() completed successfully.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the game state into party.sav and creatures.sav.
|
||||
*/
|
||||
int gameSave() {
|
||||
Common::OutSaveFile *saveGameFile, *monstersFile, *dngMapFile;
|
||||
SaveGame save = *g_ultima->_saveGame;
|
||||
|
||||
/*************************************************/
|
||||
/* Make sure the savegame struct is accurate now */
|
||||
|
||||
if (g_context->_location->_prev) {
|
||||
save._x = g_context->_location->_coords.x;
|
||||
save._y = g_context->_location->_coords.y;
|
||||
save._dngLevel = g_context->_location->_coords.z;
|
||||
save._dngX = g_context->_location->_prev->_coords.x;
|
||||
save._dngY = g_context->_location->_prev->_coords.y;
|
||||
} else {
|
||||
save._x = g_context->_location->_coords.x;
|
||||
save._y = g_context->_location->_coords.y;
|
||||
save._dngLevel = g_context->_location->_coords.z;
|
||||
save._dngX = g_ultima->_saveGame->_dngX;
|
||||
save._dngY = g_ultima->_saveGame->_dngY;
|
||||
}
|
||||
save._location = g_context->_location->_map->_id;
|
||||
save._orientation = (Direction)(g_ultima->_saveGame->_orientation - DIR_WEST);
|
||||
|
||||
/* Done making sure the savegame struct is accurate */
|
||||
/****************************************************/
|
||||
|
||||
saveGameFile = g_system->getSavefileManager()->openForSaving(PARTY_SAV_BASE_FILENAME);
|
||||
if (!saveGameFile) {
|
||||
screenMessage("Error opening " PARTY_SAV_BASE_FILENAME "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Common::Serializer ser(nullptr, saveGameFile);
|
||||
save.synchronize(ser);
|
||||
delete saveGameFile;
|
||||
|
||||
monstersFile = g_system->getSavefileManager()->openForSaving(MONSTERS_SAV_BASE_FILENAME);
|
||||
if (!monstersFile) {
|
||||
screenMessage("Error opening %s\n", MONSTERS_SAV_BASE_FILENAME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fix creature animations so they are compatible with u4dos */
|
||||
g_context->_location->_map->resetObjectAnimations();
|
||||
g_context->_location->_map->fillMonsterTable(); /* fill the monster table so we can save it */
|
||||
|
||||
Common::Serializer ser2(nullptr, monstersFile);
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_map->_monsterTable, ser2);
|
||||
delete monstersFile;
|
||||
|
||||
/**
|
||||
* Write dungeon info
|
||||
*/
|
||||
if (g_context->_location->_context & CTX_DUNGEON) {
|
||||
unsigned int x, y, z;
|
||||
|
||||
typedef Std::map<const Creature *, int, Std::PointerHash> DngCreatureIdMap;
|
||||
static DngCreatureIdMap id_map;
|
||||
|
||||
/**
|
||||
* Map creatures to u4dos dungeon creature Ids
|
||||
*/
|
||||
if (id_map.size() == 0) {
|
||||
id_map[creatureMgr->getById(RAT_ID)] = 1;
|
||||
id_map[creatureMgr->getById(BAT_ID)] = 2;
|
||||
id_map[creatureMgr->getById(GIANT_SPIDER_ID)] = 3;
|
||||
id_map[creatureMgr->getById(GHOST_ID)] = 4;
|
||||
id_map[creatureMgr->getById(SLIME_ID)] = 5;
|
||||
id_map[creatureMgr->getById(TROLL_ID)] = 6;
|
||||
id_map[creatureMgr->getById(GREMLIN_ID)] = 7;
|
||||
id_map[creatureMgr->getById(MIMIC_ID)] = 8;
|
||||
id_map[creatureMgr->getById(REAPER_ID)] = 9;
|
||||
id_map[creatureMgr->getById(INSECT_SWARM_ID)] = 10;
|
||||
id_map[creatureMgr->getById(GAZER_ID)] = 11;
|
||||
id_map[creatureMgr->getById(PHANTOM_ID)] = 12;
|
||||
id_map[creatureMgr->getById(ORC_ID)] = 13;
|
||||
id_map[creatureMgr->getById(SKELETON_ID)] = 14;
|
||||
id_map[creatureMgr->getById(ROGUE_ID)] = 15;
|
||||
}
|
||||
|
||||
dngMapFile = g_system->getSavefileManager()->openForSaving("dngmap.sav");
|
||||
if (!dngMapFile) {
|
||||
screenMessage("Error opening dngmap.sav\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (z = 0; z < g_context->_location->_map->_levels; z++) {
|
||||
for (y = 0; y < g_context->_location->_map->_height; y++) {
|
||||
for (x = 0; x < g_context->_location->_map->_width; x++) {
|
||||
unsigned char tile = g_context->_location->_map->translateToRawTileIndex(*g_context->_location->_map->getTileFromData(MapCoords(x, y, z)));
|
||||
Object *obj = g_context->_location->_map->objectAt(MapCoords(x, y, z));
|
||||
|
||||
/**
|
||||
* Add the creature to the tile
|
||||
*/
|
||||
if (obj && obj->getType() == Object::CREATURE) {
|
||||
const Creature *m = dynamic_cast<Creature *>(obj);
|
||||
DngCreatureIdMap::iterator m_id = id_map.find(m);
|
||||
if (m_id != id_map.end())
|
||||
tile |= m_id->_value;
|
||||
}
|
||||
|
||||
// Write the tile
|
||||
dngMapFile->writeByte(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete dngMapFile;
|
||||
|
||||
/**
|
||||
* Write out monsters
|
||||
*/
|
||||
|
||||
monstersFile = g_system->getSavefileManager()->openForSaving(
|
||||
OUTMONST_SAV_BASE_FILENAME);
|
||||
if (!monstersFile) {
|
||||
screenMessage("Error opening %s\n", OUTMONST_SAV_BASE_FILENAME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fix creature animations so they are compatible with u4dos */
|
||||
g_context->_location->_prev->_map->resetObjectAnimations();
|
||||
g_context->_location->_prev->_map->fillMonsterTable(); /* fill the monster table so we can save it */
|
||||
|
||||
Common::Serializer ser3(nullptr, monstersFile);
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_prev->_map->_monsterTable, ser3);
|
||||
delete monstersFile;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the view mode.
|
||||
*/
|
||||
@ -1171,9 +1034,11 @@ bool GameController::keyPressed(int key) {
|
||||
case 'q':
|
||||
screenMessage("Quit & Save...\n%d moves\n", g_ultima->_saveGame->_moves);
|
||||
if (g_context->_location->_context & CTX_CAN_SAVE_GAME) {
|
||||
gameSave();
|
||||
screenMessage("Press Alt-x to quit\n");
|
||||
} else screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
|
||||
if (g_ultima->saveGameDialog())
|
||||
screenMessage("Press Alt-x to quit\n");
|
||||
} else {
|
||||
screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
@ -746,18 +746,13 @@ void IntroController::finishInitiateGame(const Common::String &nameBuffer, SexTy
|
||||
// ask questions that determine character class
|
||||
startQuestions();
|
||||
|
||||
// write out save game an segue into game
|
||||
SaveGame saveGame;
|
||||
// Setup savegame fields. The original wrote out multiple files and
|
||||
// then loaded them up once the game starts. Now we're simply setting
|
||||
// up the savegame fields and letting the game read from them later,
|
||||
// as if a savegame had been loaded
|
||||
SaveGame &saveGame = *g_ultima->_saveGame;
|
||||
SaveGamePlayerRecord avatar;
|
||||
|
||||
Common::OutSaveFile *saveGameFile = g_system->getSavefileManager()->openForSaving(PARTY_SAV_BASE_FILENAME);
|
||||
if (!saveGameFile) {
|
||||
_questionArea.disableCursor();
|
||||
_errorMessage = "Unable to create save game!";
|
||||
updateScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
avatar.init();
|
||||
strcpy(avatar.name, nameBuffer.c_str());
|
||||
avatar._sex = sex;
|
||||
@ -770,18 +765,6 @@ void IntroController::finishInitiateGame(const Common::String &nameBuffer, SexTy
|
||||
saveGame._reagents[REAG_GARLIC] = 4;
|
||||
saveGame._torches = 2;
|
||||
|
||||
Common::Serializer ser(nullptr, saveGameFile);
|
||||
saveGame.synchronize(ser);
|
||||
|
||||
saveGameFile->finalize();
|
||||
delete saveGameFile;
|
||||
|
||||
saveGameFile = g_system->getSavefileManager()->openForSaving(MONSTERS_SAV_BASE_FILENAME);
|
||||
if (saveGameFile) {
|
||||
Common::Serializer ser2(nullptr, saveGameFile);
|
||||
SaveGameMonsterRecord::synchronize(nullptr, ser2);
|
||||
delete saveGameFile;
|
||||
}
|
||||
_justInitiatedNewGame = true;
|
||||
|
||||
// show the text thats segues into the main game
|
||||
|
Loading…
Reference in New Issue
Block a user