/* 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 .
*
*/
#include "common/config-manager.h"
#include "common/file.h"
#include "common/translation.h"
#include "gui/error.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/games/u8_game.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/fade_to_modal_process.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/xform_blend.h"
#include "ultima/ultima8/filesys/u8_save_file.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gumps/credits_gump.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/games/start_u8_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "common/memstream.h"
namespace Ultima {
namespace Ultima8 {
U8Game::U8Game() : Game() {
// Set some defaults for gameplay-related settings
ConfMan.registerDefault("endgame", false);
ConfMan.registerDefault("quotes", false);
ConfMan.registerDefault("footsteps", true);
ConfMan.registerDefault("targetedjump", true);
ConfMan.registerDefault("subtitles", true);
ConfMan.registerDefault("speech_mute", false);
const GameInfo *info = Ultima8Engine::get_instance()->getGameInfo();
if (info->_language == GameInfo::GAMELANG_JAPANESE) {
ConfMan.registerDefault("talkspeed", 24);
} else {
ConfMan.registerDefault("talkspeed", 60);
}
}
U8Game::~U8Game() {
}
bool U8Game::loadFiles() {
// Load palette
debug(1, "Load Palette");
Common::File pf;
if (!pf.open("static/u8pal.pal")) {
warning("Unable to load static/u8pal.pal.");
return false;
}
pf.seek(4); // seek past header
Common::MemoryReadStream xfds(U8XFormPal, 1024);
PaletteManager::get_instance()->load(PaletteManager::Pal_Game, pf, xfds);
debug(1, "Load GameData");
GameData::get_instance()->loadU8Data();
return true;
}
bool U8Game::startGame() {
// NOTE: assumes the entire engine has been reset!
debug(1, "Starting new Ultima 8 game.");
ObjectManager *objman = ObjectManager::get_instance();
// reserve a number of objids just in case we'll need them sometime
for (uint16 i = 384; i < 512; ++i)
objman->reserveObjId(i);
// Reserved for the Guardian Bark hack
objman->reserveObjId(kGuardianId);
auto *savers = new Common::File();
if (!savers->open("savegame/u8save.000")) {
Common::U32String errmsg = _(
"Missing Required File\n\n"
"Starting a game requires SAVEGAME/U8SAVE.000\n"
"from an original installation.\n\n"
"Please check you have copied all the files correctly.");
::GUI::displayErrorDialog(errmsg);
delete savers;
error("Unable to load savegame/u8save.000");
return false;
}
U8SaveFile *u8save = new U8SaveFile(savers);
Common::SeekableReadStream *nfd = u8save->createReadStreamForMember("NONFIXED.DAT");
if (!nfd) {
warning("Unable to load savegame/u8save.000/NONFIXED.DAT.");
return false;
}
World::get_instance()->loadNonFixed(nfd); // deletes nfd
Common::SeekableReadStream *icd = u8save->createReadStreamForMember("ITEMCACH.DAT");
if (!icd) {
warning("Unable to load savegame/u8save.000/ITEMCACH.DAT.");
return false;
}
Common::SeekableReadStream *npcd = u8save->createReadStreamForMember("NPCDATA.DAT");
if (!npcd) {
warning("Unable to load savegame/u8save.000/NPCDATA.DAT.");
delete icd;
return false;
}
World::get_instance()->loadItemCachNPCData(icd, npcd); // deletes icd, npcd
delete u8save;
MainActor *av = getMainActor();
assert(av);
av->setName("Avatar"); // default name
// avatar needs a backpack ... CONSTANTs and all that
Item *backpack = ItemFactory::createItem(529, 0, 0, 0, 0, 0, 0, true);
backpack->moveToContainer(av);
World::get_instance()->switchMap(av->getMapNum());
Ultima8Engine::get_instance()->setAvatarInStasis(true);
return true;
}
bool U8Game::startInitialUsecode(int saveSlot) {
Process *proc = new StartU8Process(saveSlot);
Kernel::get_instance()->addProcess(proc);
return true;
}
ProcId U8Game::playIntroMovie(bool fade) {
const GameInfo *gameinfo = Ultima8Engine::get_instance()->getGameInfo();
char langletter = gameinfo->getLanguageFileLetter();
if (!langletter) {
warning("U8Game::playIntro: Unknown language.");
return 0;
}
Common::String filename = "static/";
filename += langletter;
filename += "intro.skf";
auto *skf = new Common::File();
if (!skf->open(filename.c_str())) {
debug(1, "U8Game::playIntro: movie not found.");
delete skf;
return 0;
}
return MovieGump::U8MovieViewer(skf, fade, true, true);
}
ProcId U8Game::playEndgameMovie(bool fade) {
static const Common::Path filename = "static/endgame.skf";
auto *skf = new Common::File();
if (!skf->open(filename)) {
debug(1, "U8Game::playEndgame: movie not found.");
delete skf;
return 0;
}
return MovieGump::U8MovieViewer(skf, fade, false, true);
}
void U8Game::playCredits() {
const GameInfo *gameinfo = Ultima8Engine::get_instance()->getGameInfo();
char langletter = gameinfo->getLanguageFileLetter();
if (!langletter) {
warning("U8Game::playCredits: Unknown language.");
return;
}
Common::String filename = "static/";
filename += langletter;
filename += "credits.dat";
auto *rs = new Common::File();
if (!rs->open(filename.c_str())) {
warning("U8Game::playCredits: error opening credits file: %s", filename.c_str());
delete rs;
return;
}
Std::string text = getCreditText(rs);
delete rs;
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) musicproc->playMusic(51); // CONSTANT!
CreditsGump *gump = new CreditsGump(text);
gump->SetFlagWhenFinished("quotes");
FadeToModalProcess *p = new FadeToModalProcess(gump);
Kernel::get_instance()->addProcess(p);
}
void U8Game::playQuotes() {
static const Common::Path filename = "static/quotes.dat";
auto *rs = new Common::File();
if (!rs->open(filename)) {
warning("U8Game::playQuotes: error opening quotes file: %s", filename.toString().c_str());
delete rs;
return;
}
const Std::string text = getCreditText(rs);
delete rs;
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) musicproc->playMusic(113); // CONSTANT!
CreditsGump *gump = new CreditsGump(text, 80);
FadeToModalProcess *p = new FadeToModalProcess(gump);
Kernel::get_instance()->addProcess(p);
}
void U8Game::writeSaveInfo(Common::WriteStream *ws) {
MainActor *av = getMainActor();
const Std::string &avname = av->getName();
const uint8 namelength = static_cast(avname.size());
ws->writeByte(namelength);
for (unsigned int i = 0; i < namelength; ++i)
ws->writeByte(static_cast(avname[i]));
Point3 pt = av->getLocation();
ws->writeUint16LE(av->getMapNum());
ws->writeUint32LE(static_cast(pt.x));
ws->writeUint32LE(static_cast(pt.y));
ws->writeUint32LE(static_cast(pt.z));
ws->writeUint16LE(av->getStr());
ws->writeUint16LE(av->getInt());
ws->writeUint16LE(av->getDex());
ws->writeUint16LE(av->getHP());
ws->writeUint16LE(av->getMaxHP());
ws->writeUint16LE(av->getMana());
ws->writeUint16LE(av->getMaxMana());
ws->writeUint16LE(av->getArmourClass());
ws->writeUint16LE(av->getTotalWeight());
for (unsigned int i = 1; i <= 6; i++) {
uint16 objid = av->getEquip(i);
Item *item = getItem(objid);
if (item) {
ws->writeUint32LE(item->getShape());
ws->writeUint32LE(item->getFrame());
} else {
ws->writeUint32LE(0);
ws->writeUint32LE(0);
}
}
}
Std::string U8Game::getCreditText(Common::SeekableReadStream *rs) {
unsigned int size = rs->size();
Std::string text(size, ' ');
for (unsigned int i = 0; i < size; ++i) {
uint8 c = rs->readByte();
int x;
switch (i) {
case 0:
case 1:
x = 0;
break;
case 2:
x = 0xE1;
break;
default:
x = 0x20 * (i + 1) + (i >> 1);
x += (i % 0x40) * ((i & 0xC0) >> 6) * 0x40;
break;
}
char d = (c ^ x) & 0xFF;
if (d == 0) d = '\n';
text[i] = d;
}
return text;
}
} // End of namespace Ultima8
} // End of namespace Ultima