588 lines
14 KiB
C++
588 lines
14 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/nuvie/core/nuvie_defs.h"
|
|
#include "ultima/nuvie/misc/u6_misc.h"
|
|
#include "ultima/nuvie/files/nuvie_io.h"
|
|
#include "ultima/nuvie/files/nuvie_io_file.h"
|
|
#include "ultima/nuvie/files/u6_lzw.h"
|
|
#include "ultima/nuvie/gui/gui.h"
|
|
#include "ultima/nuvie/gui/widgets/console.h"
|
|
#include "ultima/nuvie/save/save_game.h"
|
|
#include "ultima/nuvie/conf/configuration.h"
|
|
#include "ultima/nuvie/core/game.h"
|
|
#include "ultima/nuvie/core/obj_manager.h"
|
|
#include "ultima/nuvie/save/obj_list.h"
|
|
#include "ultima/nuvie/actors/actor_manager.h"
|
|
#include "ultima/nuvie/core/egg_manager.h"
|
|
#include "ultima/nuvie/actors/actor.h"
|
|
#include "ultima/nuvie/views/view_manager.h"
|
|
#include "ultima/nuvie/gui/widgets/map_window.h"
|
|
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
|
|
#include "ultima/nuvie/core/party.h"
|
|
#include "ultima/nuvie/core/player.h"
|
|
#include "ultima/nuvie/portraits/portrait.h"
|
|
#include "ultima/nuvie/core/game_clock.h"
|
|
#include "ultima/nuvie/gui/widgets/command_bar.h"
|
|
#include "ultima/nuvie/core/weather.h"
|
|
#include "ultima/nuvie/script/script.h"
|
|
#include "ultima/nuvie/core/events.h"
|
|
#include "ultima/nuvie/nuvie.h"
|
|
#include "common/system.h"
|
|
#include "common/savefile.h"
|
|
#include "common/translation.h"
|
|
#include "gui/browser.h"
|
|
|
|
namespace Ultima {
|
|
namespace Nuvie {
|
|
|
|
#define OBJLIST_FILENAME "savegame/objlist"
|
|
#define OBJBLK_FILENAME "savegame/objblkxx"
|
|
#define GAME_ID(GT) ((GT == GAME_SAVAGE_EMPIRE) ? MKTAG16('S', 'E') : \
|
|
((GT == GAME_MARTIAN_DREAMS) ? MKTAG16('M', 'D') : MKTAG16('U', '6')))
|
|
|
|
SaveGame::SaveGame(Configuration *cfg) : config(cfg) {
|
|
// We don't need ObjManager here as there will be nothing to clean at this stage
|
|
init(nullptr);
|
|
}
|
|
|
|
SaveGame::~SaveGame() {
|
|
objlist.close();
|
|
}
|
|
|
|
void SaveGame::init(ObjManager *obj_manager) {
|
|
if (objlist.get_size() > 0)
|
|
objlist.close();
|
|
|
|
if (obj_manager)
|
|
obj_manager->clean();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
bool SaveGame::load_new() {
|
|
Common::Path filename;
|
|
U6Lzw lzw;
|
|
NuvieIOBuffer buf;
|
|
uint32 decomp_size;
|
|
uint8 i;
|
|
uint32 pos;
|
|
|
|
ObjManager *obj_manager = Game::get_game()->get_obj_manager();
|
|
|
|
init(obj_manager);
|
|
|
|
// Load surface chunks
|
|
config_get_path(config, "lzobjblk", filename);
|
|
unsigned char *data = lzw.decompress_file(filename, decomp_size);
|
|
if (!data)
|
|
return false;
|
|
|
|
buf.open(data, decomp_size, NUVIE_BUF_NOCOPY);
|
|
|
|
for (i = 0; i < 64; i++)
|
|
obj_manager->load_super_chunk(&buf, 0, i);
|
|
|
|
buf.close();
|
|
free(data);
|
|
|
|
// Load dungeon chunks
|
|
config_get_path(config, "lzdngblk", filename);
|
|
data = lzw.decompress_file(filename, decomp_size);
|
|
if (!data)
|
|
return false;
|
|
|
|
buf.open(data, decomp_size, NUVIE_BUF_NOCOPY);
|
|
|
|
for (i = 0; i < 5; i++)
|
|
obj_manager->load_super_chunk(&buf, i, 0);
|
|
|
|
pos = buf.position();
|
|
buf.close();
|
|
|
|
// load objlist
|
|
objlist.open(&data[pos], decomp_size - pos, NUVIE_BUF_COPY);
|
|
|
|
update_objlist_for_new_game();
|
|
|
|
load_objlist();
|
|
|
|
Actor *player = Game::get_game()->get_player()->get_actor();
|
|
Game::get_game()->get_egg_manager()->spawn_eggs(player->get_x(), player->get_y(), player->get_z(), true);
|
|
|
|
free(data);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveGame::load_original() {
|
|
Std::string objblk_filename;
|
|
Common::Path path, objlist_filename;
|
|
char x, y;
|
|
uint16 len;
|
|
NuvieIOFileRead objlist_file;
|
|
|
|
NuvieIOFileRead *objblk_file = new NuvieIOFileRead();
|
|
ObjManager *obj_manager = Game::get_game()->get_obj_manager();
|
|
|
|
init(obj_manager);
|
|
|
|
objblk_filename = OBJBLK_FILENAME;
|
|
len = objblk_filename.size();
|
|
|
|
uint8 i = 0;
|
|
|
|
for (y = 'a'; y < 'i'; y++) {
|
|
for (x = 'a'; x < 'i'; x++) {
|
|
objblk_filename[len - 1] = y;
|
|
objblk_filename[len - 2] = x;
|
|
ConsoleAddInfo("Loading file: %s", objblk_filename.c_str());
|
|
config_get_path(config, objblk_filename, path);
|
|
if (objblk_file->open(path) == false) {
|
|
delete objblk_file;
|
|
return false;
|
|
}
|
|
|
|
if (obj_manager->load_super_chunk(objblk_file, 0, i) == false) {
|
|
delete objblk_file;
|
|
return false;
|
|
}
|
|
i++;
|
|
objblk_file->close();
|
|
}
|
|
}
|
|
|
|
objblk_filename[len - 1] = 'i';
|
|
|
|
for (i = 0, x = 'a'; x < 'f'; x++, i++) { // Load dungeons
|
|
objblk_filename[len - 2] = x;
|
|
config_get_path(config, objblk_filename, path);
|
|
objblk_file->open(path);
|
|
|
|
if (obj_manager->load_super_chunk(objblk_file, i, 0) == false) {
|
|
delete objblk_file;
|
|
return false;
|
|
}
|
|
|
|
objblk_file->close();
|
|
}
|
|
|
|
delete objblk_file;
|
|
|
|
//print_egg_list();
|
|
config_get_path(config, OBJLIST_FILENAME, objlist_filename);
|
|
if (objlist_file.open(objlist_filename) == false)
|
|
return false;
|
|
|
|
unsigned char *data = objlist_file.readAll();
|
|
|
|
objlist.open(data, objlist_file.get_size(), NUVIE_BUF_COPY);
|
|
free(data);
|
|
|
|
load_objlist();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveGame::transfer_character() {
|
|
::GUI::BrowserDialog dialog(_("Transfer Character"), true);
|
|
if (!dialog.runModal())
|
|
return false;
|
|
|
|
Common::FSNode folder = dialog.getResult();
|
|
|
|
// TODO: Load in character data from given folder and start new game
|
|
GUIErrorMessage(Common::String::format("Load party file from folder - %s", folder.getPath().toString(Common::Path::kNativeSeparator).c_str()));
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SaveGame::load_objlist() {
|
|
Game *game;
|
|
GameClock *clock;
|
|
ActorManager *actor_manager;
|
|
ObjManager *obj_manager;
|
|
ViewManager *view_manager;
|
|
MapWindow *map_window;
|
|
MsgScroll *scroll;
|
|
CommandBar *command_bar;
|
|
Player *player;
|
|
Party *party;
|
|
Portrait *portrait;
|
|
Weather *weather;
|
|
uint16 px, py;
|
|
uint8 pz;
|
|
|
|
game = Game::get_game();
|
|
|
|
clock = game->get_clock();
|
|
actor_manager = game->get_actor_manager();
|
|
obj_manager = game->get_obj_manager();
|
|
scroll = game->get_scroll();
|
|
map_window = game->get_map_window();
|
|
command_bar = game->get_command_bar();
|
|
|
|
player = game->get_player();
|
|
party = game->get_party();
|
|
portrait = game->get_portrait();
|
|
view_manager = game->get_view_manager();
|
|
weather = game->get_weather();
|
|
|
|
portrait->load(&objlist); // load avatar portrait number.
|
|
|
|
clock->load(&objlist);
|
|
game->set_ethereal(false); // needs to go before actor_manager->load(&objlist);
|
|
actor_manager->load(&objlist);
|
|
|
|
party->load(&objlist);
|
|
player->load(&objlist);
|
|
|
|
weather->load(&objlist);
|
|
|
|
command_bar->set_combat_mode(party->is_in_combat_mode()); // update CommandBar
|
|
command_bar->load(&objlist);
|
|
|
|
view_manager->reload();
|
|
|
|
game->get_script()->call_load_game(&objlist);
|
|
game->get_event()->set_control_cheat(false);
|
|
|
|
player->get_location(&px, &py, &pz);
|
|
obj_manager->update(px, py, pz); // spawn eggs.
|
|
|
|
map_window->centerMapOnActor(player->get_actor());
|
|
|
|
scroll->display_string("\nGame Loaded\n\n");
|
|
|
|
scroll->init(player->get_name());
|
|
|
|
scroll->display_prompt();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveGame::check_version(NuvieIOFileRead *loadfile, uint16 gameType) {
|
|
uint16 version, gt;
|
|
|
|
loadfile->seekStart();
|
|
version = loadfile->read2();
|
|
gt = loadfile->read2();
|
|
|
|
if (version != SAVE_VERSION) {
|
|
DEBUG(0, LEVEL_ERROR, "Incompatible savegame version. Savegame version '%d', current system version '%d'\n", version, SAVE_VERSION);
|
|
return false;
|
|
}
|
|
|
|
if (gt != gameType) {
|
|
DEBUG(0, LEVEL_ERROR, "Incorrect game type\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveGame::load(const Common::String &filename) {
|
|
uint8 i;
|
|
uint32 bytes_read;
|
|
NuvieIOFileRead loadFile;
|
|
GameId gameType = g_engine->getGameId();
|
|
ObjManager *obj_manager = Game::get_game()->get_obj_manager();
|
|
|
|
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(filename);
|
|
if (!saveFile || !loadFile.open(saveFile))
|
|
return false;
|
|
|
|
ConsoleAddInfo("Loading Game: %s", filename.c_str());
|
|
DEBUG(0, LEVEL_NOTIFICATION, "Loading Game: %s\n", filename.c_str());
|
|
|
|
if (!check_version(&loadFile, GAME_ID(gameType))) {
|
|
DEBUG(0, LEVEL_NOTIFICATION, "version incorrect\n");
|
|
return false;
|
|
}
|
|
|
|
init(obj_manager); // needs to come after checking for failure
|
|
|
|
// Load actor inventories
|
|
obj_manager->load_super_chunk(&loadFile, 0, 0);
|
|
|
|
// Load eggs
|
|
obj_manager->load_super_chunk(&loadFile, 0, 0);
|
|
|
|
// Load surface objects
|
|
for (i = 0; i < 64; i++) {
|
|
ConsoleAddInfo("Loading super chunk %d of 64", i + 1);
|
|
obj_manager->load_super_chunk(&loadFile, 0, i);
|
|
}
|
|
|
|
// Load dungeon objects
|
|
for (i = 0; i < 5; i++) {
|
|
obj_manager->load_super_chunk(&loadFile, i + 1, 0);
|
|
}
|
|
|
|
uint32 objlist_size = loadFile.read4();
|
|
unsigned char *data = loadFile.readBuf(objlist_size, &bytes_read);
|
|
|
|
objlist.open(data, objlist_size, NUVIE_BUF_COPY);
|
|
|
|
free(data);
|
|
loadFile.close();
|
|
|
|
load_objlist();
|
|
|
|
delete saveFile;
|
|
return true;
|
|
}
|
|
|
|
bool SaveGame::save(const Common::String &filename, const Common::String &save_description, bool isAutosave) {
|
|
NuvieIOFileWrite saveFile;
|
|
GameId gameType = g_engine->getGameId();
|
|
ObjManager *obj_manager = Game::get_game()->get_obj_manager();
|
|
|
|
bool newgame;
|
|
config->value("config/newgame", newgame, false);
|
|
if (newgame) {
|
|
config->set("config/newgame", false);
|
|
config->write();
|
|
}
|
|
|
|
saveFile.open(filename, isAutosave);
|
|
|
|
saveFile.write2(SAVE_VERSION);
|
|
saveFile.write2(GAME_ID(gameType));
|
|
|
|
obj_manager->save_inventories(&saveFile);
|
|
|
|
obj_manager->save_eggs(&saveFile);
|
|
|
|
// Save surface objects
|
|
for (uint8 i = 0; i < 64; i++)
|
|
obj_manager->save_super_chunk(&saveFile, 0, i);
|
|
|
|
// Save dungeon objects
|
|
for (uint8 i = 0; i < 5; i++)
|
|
obj_manager->save_super_chunk(&saveFile, i + 1, 0);
|
|
|
|
save_objlist();
|
|
|
|
saveFile.write4(objlist.get_size());
|
|
saveFile.writeBuf(objlist.get_raw_data(), objlist.get_size());
|
|
|
|
saveFile.writeDesc(save_description);
|
|
saveFile.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SaveGame::save_objlist() {
|
|
Game *game;
|
|
GameClock *clock;
|
|
ActorManager *actor_manager;
|
|
Player *player;
|
|
Party *party;
|
|
Weather *weather;
|
|
|
|
game = Game::get_game();
|
|
|
|
clock = game->get_clock();
|
|
actor_manager = game->get_actor_manager();
|
|
|
|
player = game->get_player();
|
|
party = game->get_party();
|
|
weather = game->get_weather();
|
|
|
|
clock->save(&objlist);
|
|
actor_manager->save(&objlist);
|
|
|
|
player->save(&objlist);
|
|
party->save(&objlist);
|
|
|
|
weather->save(&objlist);
|
|
game->get_command_bar()->save(&objlist);
|
|
|
|
game->get_script()->call_save_game(&objlist);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SaveGame::update_objlist_for_new_game() {
|
|
nuvie_game_t type = Game::get_game()->get_game_type();
|
|
if (type == NUVIE_GAME_SE) {
|
|
update_objlist_for_new_game_se();
|
|
} else if (type == NUVIE_GAME_MD) {
|
|
update_objlist_for_new_game_md();
|
|
} else {
|
|
update_objlist_for_new_game_u6();
|
|
}
|
|
|
|
}
|
|
|
|
void SaveGame::update_objlist_for_new_game_u6() {
|
|
Std::string name = "";
|
|
|
|
config->value("config/newgamedata/name", name, "Avatar");
|
|
objlist.seek(0xf00);
|
|
int len = name.size();
|
|
if (len > 13)
|
|
len = 13;
|
|
|
|
objlist.writeBuf((const unsigned char *)name.c_str(), len + 1);
|
|
|
|
objlist.seek(0x1c71); // gender
|
|
|
|
int gender;
|
|
config->value("config/newgamedata/gender", gender, 0);
|
|
|
|
objlist.write1(gender == 1 ? 0 : 1);
|
|
|
|
int portrait;
|
|
config->value("config/newgamedata/portrait", portrait, 0);
|
|
|
|
objlist.write1((uint8)(gender * 6 + portrait) + 1);
|
|
|
|
int stat;
|
|
config->value("config/newgamedata/str", stat, 0xf);
|
|
objlist.seek(0x901);
|
|
objlist.write1(stat);
|
|
|
|
config->value("config/newgamedata/dex", stat, 0xf);
|
|
objlist.seek(0xa01);
|
|
objlist.write1(stat);
|
|
|
|
int intelligence;
|
|
config->value("config/newgamedata/int", intelligence, 0xf);
|
|
objlist.seek(0xb01);
|
|
objlist.write1(intelligence);
|
|
|
|
config->value("config/newgamedata/exp", stat, 0x172);
|
|
objlist.seek(0xc02);
|
|
objlist.write2(stat); // experience
|
|
|
|
config->value("config/newgamedata/magic", stat, intelligence * 2);
|
|
objlist.seek(0x13f2);
|
|
objlist.write1(stat); // magic
|
|
|
|
config->value("config/newgamedata/level", stat, 3);
|
|
objlist.seek(0xff2);
|
|
objlist.write1(stat); //level
|
|
}
|
|
|
|
void SaveGame::update_objlist_for_new_game_se() {
|
|
Std::string name = "";
|
|
|
|
config->value("config/newgamedata/name", name, "Avatar");
|
|
objlist.seek(0xf00);
|
|
int len = name.size();
|
|
if (len > 13)
|
|
len = 13;
|
|
|
|
objlist.writeBuf((const unsigned char *)name.c_str(), len + 1);
|
|
|
|
int str, dex, intelligence;
|
|
config->value("config/newgamedata/str", str, 0xf);
|
|
objlist.seek(0x901);
|
|
objlist.write1(str);
|
|
|
|
config->value("config/newgamedata/dex", dex, 0xf);
|
|
objlist.seek(0xa01);
|
|
objlist.write1(dex);
|
|
|
|
config->value("config/newgamedata/int", intelligence, 0xf);
|
|
objlist.seek(0xb01);
|
|
objlist.write1(intelligence);
|
|
|
|
objlist.seek(0xe01);
|
|
objlist.write1(str * 2 + intelligence); //hp
|
|
|
|
objlist.seek(0x14f2);
|
|
objlist.write1(dex); // movement points
|
|
}
|
|
|
|
void SaveGame::update_objlist_for_new_game_md() {
|
|
Std::string name = "";
|
|
|
|
int gender;
|
|
config->value("config/newgamedata/gender", gender, 0);
|
|
|
|
config->value("config/newgamedata/name", name, "Avatar");
|
|
objlist.seek(0xf00);
|
|
int len = name.size();
|
|
if (len > 13)
|
|
len = 13;
|
|
|
|
objlist.writeBuf((const unsigned char *)name.c_str(), len + 1);
|
|
|
|
int str, dex, intelligence;
|
|
config->value("config/newgamedata/str", str, 0xf);
|
|
objlist.seek(0x901);
|
|
objlist.write1(str);
|
|
|
|
config->value("config/newgamedata/dex", dex, 0xf);
|
|
objlist.seek(0xa01);
|
|
objlist.write1(dex);
|
|
|
|
config->value("config/newgamedata/int", intelligence, 0xf);
|
|
objlist.seek(0xb01);
|
|
objlist.write1(intelligence);
|
|
|
|
objlist.seek(0xc02);
|
|
objlist.write2(600); // experience
|
|
|
|
objlist.seek(0xe01);
|
|
objlist.write1(str * 2 + (4 * 24)); //hp = strength * 2 + level * 24
|
|
|
|
objlist.seek(0xff2);
|
|
objlist.write1(4); // level
|
|
|
|
// FIXME: movement points where are they?
|
|
objlist.seek(0x14f2);
|
|
objlist.write1(dex); // movement points
|
|
|
|
objlist.seek(0x15f2);
|
|
objlist.write1(20); // movement points
|
|
|
|
objlist.seek(OBJLIST_OFFSET_MD_GENDER);
|
|
objlist.write1(gender);
|
|
|
|
// Read in avatar's obj_n + frame_n data
|
|
objlist.seek(0x403);
|
|
uint8 b2 = objlist.read1();
|
|
uint16 obj_n = gender == 0 ? 343 : 344;
|
|
uint8 frame_n = (b2 & 0xfc) >> 2;
|
|
|
|
// Write out adjusted avatar gender obj_n + frame_n
|
|
objlist.seek(0x402);
|
|
objlist.write1(obj_n & 0xff);
|
|
b2 = obj_n >> 8;
|
|
b2 += frame_n << 2;
|
|
objlist.write1(b2);
|
|
|
|
// Also write to the old obj_n + frame_n data.
|
|
objlist.seek(0x16f3);
|
|
objlist.write1(obj_n & 0xff);
|
|
b2 = obj_n >> 8;
|
|
b2 += frame_n << 2;
|
|
objlist.write1(b2);
|
|
}
|
|
|
|
} // End of namespace Nuvie
|
|
} // End of namespace Ultima
|