Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
/* 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 "common/config-manager.h"
#include "common/file.h"
#include "common/translation.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/games/start_crusader_process.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gumps/main_menu_process.h"
#include "ultima/ultima8/gumps/cru_credits_gump.h"
#include "ultima/ultima8/gumps/cru_demo_gump.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/gfx/xform_blend.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/actors/npc_dat.h"
#include "common/memstream.h"
#include "gui/message.h"
namespace Ultima {
namespace Ultima8 {
CruGame::CruGame() : Game(), _skipIntroMovie(false) {
}
CruGame::~CruGame() {
}
static bool loadPalette(const char *path, PaletteManager::PalIndex index) {
Common::File pf;
if (!pf.open(path)) {
warning("Unable to load %s", path);
return false;
}
Common::MemoryReadStream xfds(CruXFormPal, 1024);
PaletteManager::get_instance()->load(index, pf, xfds);
return true;
}
bool CruGame::loadFiles() {
// Load palette
debug(1, "Load Palettes");
if (!loadPalette("static/gamepal.pal", PaletteManager::Pal_Game))
return false;
// This one is not always present and only needed for the credits,
// let it fail if needed.
loadPalette("static/cred.pal", PaletteManager::Pal_Cred);
if (!loadPalette("static/diff.pal", PaletteManager::Pal_Diff))
return false;
if (!loadPalette("static/misc.pal", PaletteManager::Pal_Misc))
return false;
if (!loadPalette("static/misc2.pal", PaletteManager::Pal_Misc2))
return false;
// We don't use his one at the moment, ok to fail.
loadPalette("static/star.pal", PaletteManager::Pal_Star);
debug(1, "Load GameData");
GameData::get_instance()->loadRemorseData();
return true;
}
bool CruGame::startGame() {
// NOTE: assumes the entire engine has been reset!
debug(1, "Starting new Crusader: No Remorse 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);
Actor *actor = ItemFactory::createActor(1, 0, 0, Item::FLG_IN_NPC_LIST,
1, 1, Item::EXT_PERMANENT_NPC, false);
if (!actor)
error("Couldn't create MainActor");
const NPCDat *npcData = GameData::get_instance()->getNPCDataForShape(1);
actor->setStr(75);
actor->setHP(npcData->getMaxHp());
actor->setInt(5000); // max mana (energy) is 2x intelligence, or 10000.
actor->setMana(2500);
ObjectManager::get_instance()->assignActorObjId(actor, 1);
actor->setLocation(0, 0, 0); // Map 1 (mission 1)
// Some useful points to warp into for testing No Remorse
//actor->setLocation(60716, 59400, 16); // Map 1 (mission 1)
//actor->setLocation(42493, 26621, 16); // Map 2 (mission 1 / level 4)
//actor->setLocation(34302, 32254, 16); // Map 3 (mission 2)
//actor->setLocation(34813, 33789, 16); // Map 4
//actor->setLocation(37373, 30205, 16); // Map 5
//actor->setLocation(37373, 30205, 16); // Map 6
//actor->setLocation(35070, 26142, 96); // Map 7
//actor->setLocation(29693, 32253, 0); // Map 8 - unfinished area?
//actor->setLocation(2046, 2046, 0); // Map 9
//actor->setLocation(14845, 6141, 0); // Map 22 - debugging map
//actor->setLocation(34302, 32254, 16); // Map 40 (Rebel base)
World::get_instance()->switchMap(0);
return true;
}
bool CruGame::startInitialUsecode(int saveSlot) {
if (saveSlot >= 0 && ConfMan.getBool("skip_intro"))
_skipIntroMovie = true;
Process* proc = new StartCrusaderProcess(saveSlot);
Kernel::get_instance()->addProcess(proc);
return true;
}
static ProcId playMovie(const char *movieID, bool fade, bool noScale) {
MovieGump *gump = MovieGump::CruMovieViewer(movieID, 640, 480, nullptr, nullptr, 0);
if (!gump) {
debug(1, "RemorseGame::playIntro: movie %s not found.", movieID);
return 0;
}
gump->CreateNotifier();
return gump->GetNotifyProcess()->getPid();
}
ProcId CruGame::playIntroMovie(bool fade) {
if (_skipIntroMovie)
return 0;
const char *name = (GAME_IS_REMORSE ? "T01" : "origin");
ProcId pid = playMovie(name, fade, true);
if (!pid) {
GUI::MessageDialogWithURL dialog(_("Crusader intro movie file missing - check that the FLICS and SOUND directories have been copied from the CD. More instructions are on the wiki: https://wiki.scummvm.org/index.php?title=Crusader:_No_Remorse."), "https://wiki.scummvm.org/index.php?title=Crusader:_No_Remorse");
dialog.runModal();
}
return pid;
}
ProcId CruGame::playIntroMovie2(bool fade) {
if (_skipIntroMovie)
return 0;
const char *name = (GAME_IS_REMORSE ? "T02" : "ANIM01");
return playMovie(name, fade, false);
}
ProcId CruGame::playEndgameMovie(bool fade) {
return playMovie("O01", fade, false);
}
void CruGame::playDemoScreen() {
Process *menuproc = new MainMenuProcess();
Kernel::get_instance()->addProcess(menuproc);
const char *bmp_filename = "static/buyme.dat";
auto *bmprs = new Common::File();
if (!bmprs->open(bmp_filename)) {
warning("RemorseGame::playDemoScreen: error opening demo background: %s", bmp_filename);
delete bmprs;
return;
}
Gump *gump = new CruDemoGump(bmprs);
gump->InitGump(0);
gump->CreateNotifier();
Process *notifyproc = gump->GetNotifyProcess();
if (notifyproc) {
menuproc->waitFor(notifyproc);
}
}
ProcId CruGame::playCreditsNoMenu() {
const char *txt_filename = "static/credits.dat";
const char *bmp_filename = "static/cred.dat";
auto *txtrs = new Common::File();
auto *bmprs = new Common::File();
if (!txtrs->open(txt_filename)) {
warning("RemorseGame::playCredits: error opening credits text: %s", txt_filename);
delete txtrs;
delete bmprs;
return 0;
}
if (!bmprs->open(bmp_filename)) {
warning("RemorseGame::playCredits: error opening credits background: %s", bmp_filename);
delete txtrs;
delete bmprs;
return 0;
}
Gump *creditsgump = new CruCreditsGump(txtrs, bmprs);
creditsgump->InitGump(nullptr);
creditsgump->CreateNotifier();
Process *notifyproc = creditsgump->GetNotifyProcess();
return notifyproc->getPid();
}
void CruGame::playCredits() {
Process *menuproc = new MainMenuProcess();
Kernel::get_instance()->addProcess(menuproc);
ProcId creditsnotify = playCreditsNoMenu();
if (creditsnotify) {
menuproc->waitFor(creditsnotify);
}
}
void CruGame::writeSaveInfo(Common::WriteStream *ws) {
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,69 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_CRUGAME_H
#define ULTIMA8_GAMES_CRUGAME_H
#include "ultima/ultima8/games/game.h"
namespace Ultima {
namespace Ultima8 {
class CruGame : public Game {
public:
CruGame();
~CruGame() override;
//! load/init game's data files
bool loadFiles() override;
//! initialize new game
bool startGame() override;
//! start initial usecode
bool startInitialUsecode(int saveSlot = -1) override;
//! write game-specific savegame info (avatar stats, equipment, ...)
void writeSaveInfo(Common::WriteStream *ws) override;
ProcId playIntroMovie(bool fade) override;
ProcId playIntroMovie2(bool fade);
ProcId playEndgameMovie(bool fade) override;
void playCredits() override;
void playQuotes() override { }; // no quotes for Crusader
void playDemoScreen() override;
/** Play credits but without showing a menu at the end - just finish. */
ProcId playCreditsNoMenu();
void setSkipIntroMovie() {
_skipIntroMovie = true;
}
private:
/** Whether this game should skip the intro movie on startup */
bool _skipIntroMovie;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,92 @@
/* 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 "common/config-manager.h"
#include "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/games/u8_game.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/main_menu_process.h"
namespace Ultima {
namespace Ultima8 {
Game *Game::_game = nullptr;
Game::Game() {
_game = this;
}
Game::~Game() {
assert(_game == this);
_game = nullptr;
}
// static
Game *Game::createGame(const GameInfo *info) {
switch (info->_type) {
case GameInfo::GAME_U8:
return new U8Game();
case GameInfo::GAME_REMORSE:
case GameInfo::GAME_REGRET:
return new CruGame();
default:
error("createGame: invalid game tyoe");
}
return nullptr;
}
uint32 Game::I_playEndgame(const uint8 *args, unsigned int /*argsize*/) {
ConfMan.setBool("endgame", true);
ConfMan.setBool("quotes", true);
ConfMan.flushToDisk();
PaletteManager *palman = PaletteManager::get_instance();
palman->untransformPalette(PaletteManager::Pal_Game);
Process *menuproc = new MainMenuProcess();
Kernel::get_instance()->addProcess(menuproc);
ProcId moviepid = Game::get_instance()->playEndgameMovie(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
menuproc->waitFor(movieproc);
}
return 0;
}
uint32 Game::I_playCredits(const uint8 */*args*/, unsigned int /*argsize*/) {
Game::get_instance()->playCredits();
return 0;
}
uint32 Game::I_playDemoScreen(const uint8 */*args*/, unsigned int /*argsize*/) {
Game::get_instance()->playDemoScreen();
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,71 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_GAME_H
#define ULTIMA8_GAMES_GAME_H
#include "ultima/ultima8/games/game_info.h"
#include "ultima/ultima8/usecode/intrinsics.h"
namespace Ultima {
namespace Ultima8 {
class Game {
public:
Game();
virtual ~Game();
static Game *get_instance() {
return _game;
}
//! load/init game's data files
virtual bool loadFiles() = 0;
//! initialize new game
virtual bool startGame() = 0;
//! start initial usecode
virtual bool startInitialUsecode(int saveSlot = -1) = 0;
//! write game-specific savegame info (avatar stats, equipment, ...)
virtual void writeSaveInfo(Common::WriteStream *ws) = 0;
virtual ProcId playIntroMovie(bool fade) = 0;
virtual ProcId playEndgameMovie(bool fade) = 0;
virtual void playCredits() = 0;
virtual void playQuotes() = 0;
virtual void playDemoScreen() = 0;
static Game *createGame(const GameInfo *info);
INTRINSIC(I_playEndgame);
INTRINSIC(I_playCredits);
INTRINSIC(I_playDemoScreen);
protected:
static Game *_game;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,680 @@
/* 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 "common/config-manager.h"
#include "common/file.h"
#include "ultima/ultima8/misc/common_types.h"
#include "ultima/ultima8/misc/util.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/usecode/usecode_flex.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/fonts/font_shape_archive.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/world/map_glob.h"
#include "ultima/ultima8/world/fire_type_table.h"
#include "ultima/ultima8/world/actors/npc_dat.h"
#include "ultima/ultima8/world/actors/combat_dat.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/wpn_ovlay_dat.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/games/game_info.h"
#include "ultima/ultima8/gumps/weasel_dat.h"
#include "ultima/ultima8/conf/config_file_manager.h"
#include "ultima/ultima8/convert/crusader/convert_shape_crusader.h"
#include "ultima/ultima8/audio/music_flex.h"
#include "ultima/ultima8/audio/speech_flex.h"
namespace Ultima {
namespace Ultima8 {
GameData *GameData::_gameData = nullptr;
GameData::GameData(GameInfo *gameInfo)
: _fixed(nullptr), _mainShapes(nullptr), _mainUsecode(nullptr), _globs(),
_fonts(nullptr), _gumps(nullptr), _mouse(nullptr), _music(nullptr),
_weaponOverlay(nullptr), _soundFlex(nullptr), _gameInfo(gameInfo) {
debug(1, "Creating GameData...");
_gameData = this;
_speech.resize(1024);
}
GameData::~GameData() {
debug(1, "Destroying GameData...");
delete _fixed;
_fixed = nullptr;
delete _mainShapes;
_mainShapes = nullptr;
delete _mainUsecode;
_mainUsecode = nullptr;
for (unsigned int i = 0; i < _globs.size(); ++i)
delete _globs[i];
_globs.clear();
delete _fonts;
_fonts = nullptr;
delete _gumps;
_gumps = nullptr;
delete _mouse;
_mouse = nullptr;
delete _music;
_music = nullptr;
delete _weaponOverlay;
_weaponOverlay = nullptr;
delete _soundFlex;
_soundFlex = nullptr;
for (unsigned int i = 0; i < _npcTable.size(); ++i)
delete _npcTable[i];
_npcTable.clear();
_gameData = nullptr;
for (unsigned int i = 0; i < _speech.size(); ++i) {
SpeechFlex **s = _speech[i];
if (s) delete *s;
delete s;
}
_speech.clear();
}
MapGlob *GameData::getGlob(uint32 glob) const {
if (glob < _globs.size())
return _globs[glob];
else
return nullptr;
}
ShapeArchive *GameData::getShapeFlex(uint16 flexId) const {
switch (flexId) {
case MAINSHAPES:
return _mainShapes;
case GUMPS:
return _gumps;
default:
break;
};
return nullptr;
}
Shape *GameData::getShape(FrameID f) const {
ShapeArchive *sf = getShapeFlex(f._flexId);
if (!sf)
return nullptr;
Shape *shape = sf->getShape(f._shapeNum);
return shape;
}
const ShapeFrame *GameData::getFrame(FrameID f) const {
const Shape *shape = getShape(f);
if (!shape)
return nullptr;
const ShapeFrame *frame = shape->getFrame(f._frameNum);
return frame;
}
void GameData::loadTranslation() {
ConfigFileManager *config = ConfigFileManager::get_instance();
Common::Path translationfile;
if (_gameInfo->_type == GameInfo::GAME_U8) {
switch (_gameInfo->_language) {
case GameInfo::GAMELANG_ENGLISH:
// using "translation" to enable bug fixes
translationfile = "u8english.ini";
break;
case GameInfo::GAMELANG_FRENCH:
translationfile = "u8french.ini";
break;
case GameInfo::GAMELANG_GERMAN:
translationfile = "u8german.ini";
break;
case GameInfo::GAMELANG_SPANISH:
translationfile = "u8spanish.ini";
break;
case GameInfo::GAMELANG_JAPANESE:
translationfile = "u8japanese.ini";
break;
default:
warning("Unknown language.");
break;
}
}
if (!translationfile.empty()) {
debug(1, "Loading translation: %s", translationfile.toString().c_str());
config->readConfigFile(translationfile, "language");
}
}
Std::string GameData::translate(const Std::string &text) {
// TODO: maybe cache these lookups? config calls may be expensive
ConfigFileManager *config = ConfigFileManager::get_instance();
Std::string trans;
if (config->get("language", "text", text, trans)) {
return trans;
}
return text;
}
FrameID GameData::translate(FrameID f) {
// TODO: maybe cache these lookups? config calls may be expensive
// TODO: add any non-gump shapes when applicable
// TODO: allow translations to be in another shapeflex
ConfigFileManager *config = ConfigFileManager::get_instance();
Std::string category = "language";
Std::string section;
switch (f._flexId) {
case GUMPS:
section = "gumps";
break;
default:
return f;
}
char buf[100];
Common::sprintf_s(buf, "%d,%d", f._shapeNum, f._frameNum);
Std::string key = buf;
Std::string trans;
if (!config->get(category, section, key, trans)) {
return f;
}
FrameID t;
t._flexId = f._flexId;
int n = sscanf(trans.c_str(), "%u,%u", &t._shapeNum, &t._frameNum);
if (n != 2) {
warning("Invalid shape translation: %s", trans.c_str());
return f;
}
return t;
}
void GameData::loadU8Data() {
auto *fd = new Common::File();
if (!fd->open("static/fixed.dat"))
error("Unable to load static/fixed.dat");
_fixed = new RawArchive(fd);
char langletter = _gameInfo->getLanguageUsecodeLetter();
if (!langletter)
error("Unknown language. Unable to open usecode");
Std::string filename = "usecode/";
filename += langletter;
filename += "usecode.flx";
auto *uds = new Common::File();
if (!uds->open(Common::Path(filename)))
error("Unable to load %s", filename.c_str());
_mainUsecode = new UsecodeFlex(uds);
// Load main shapes
debug(1, "Load Shapes");
auto *sf = new Common::File();
if (!(sf->open("static/u8shapes.flx") || sf->open("static/u8shapes.cmp")))
error("Unable to load static/u8shapes.flx or static/u8shapes.cmp");
_mainShapes = new MainShapeArchive(sf, MAINSHAPES,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
// Load weapon, armour info
ConfigFileManager *config = ConfigFileManager::get_instance();
config->readConfigFile("u8weapons.ini", "weapons");
config->readConfigFile("u8armour.ini", "armour");
config->readConfigFile("u8monsters.ini", "monsters");
config->readConfigFile("u8game.ini", "game");
// Load typeflags
auto *tfs = new Common::File();
if (!tfs->open("static/typeflag.dat"))
error("Unable to load static/typeflag.dat");
_mainShapes->loadTypeFlags(tfs);
delete tfs;
// Load animdat
auto *af = new Common::File();
if (!af->open("static/anim.dat"))
error("Unable to load static/anim.dat");
_mainShapes->loadAnimDat(af);
delete af;
// Load weapon overlay data
auto *wod = new Common::File();
if (!wod->open("static/wpnovlay.dat"))
error("Unable to load static/wpnovlay.dat");
RawArchive *overlayflex = new RawArchive(wod);
_weaponOverlay = new WpnOvlayDat();
_weaponOverlay->load(overlayflex);
delete overlayflex;
// Load _globs
auto *gds = new Common::File();
if (!gds->open("static/glob.flx"))
error("Unable to load static/glob.flx");
RawArchive *globflex = new RawArchive(gds);
_globs.clear();
_globs.resize(globflex->getCount());
for (unsigned int i = 0; i < globflex->getCount(); ++i) {
MapGlob *glob = 0;
Common::SeekableReadStream *globrs = globflex->get_datasource(i);
if (globrs && globrs->size()) {
glob = new MapGlob();
glob->read(globrs);
}
delete globrs;
_globs[i] = glob;
}
delete globflex;
// Load fonts
auto *fds = new Common::File();
if (!fds->open("static/u8fonts.flx"))
error("Unable to load static/u8fonts.flx");
_fonts = new FontShapeArchive(fds, OTHER,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
_fonts->setHVLeads();
// Load \mouse
auto *msds = new Common::File();
if (!msds->open("static/u8mouse.shp"))
error("Unable to load static/u8mouse.shp");
_mouse = new Shape(msds, 0);
_mouse->setPalette(PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
delete msds;
auto *gumpds = new Common::File();
if (!gumpds->open("static/u8gumps.flx"))
error("Unable to load static/u8gumps.flx");
_gumps = new GumpShapeArchive(gumpds, GUMPS,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
auto *gumpageds = new Common::File();
if (!gumpageds->open("static/gumpage.dat"))
error("Unable to load static/gumpage.dat");
_gumps->loadGumpage(gumpageds);
delete gumpageds;
auto *mf = new Common::File();
if (!mf->open("sound/music.flx"))
error("Unable to load sound/music.flx");
_music = new MusicFlex(mf);
auto *sndflx = new Common::File();
if (!sndflx->open("sound/sound.flx"))
error("Unable to load sound/sound.flx");
_soundFlex = new SoundFlex(sndflx);
loadTranslation();
}
void GameData::setupFontOverrides() {
setupTTFOverrides("game", false);
if (_gameInfo->_language == GameInfo::GAMELANG_JAPANESE)
setupJPOverrides();
}
void GameData::setupJPOverrides() {
ConfigFileManager *config = ConfigFileManager::get_instance();
FontManager *fontmanager = FontManager::get_instance();
KeyMap jpkeyvals;
jpkeyvals = config->listKeyValues("language", "jpfonts");
for (const auto &i : jpkeyvals) {
int fontnum = atoi(i._key.c_str());
const Std::string &fontdesc = i._value;
Std::vector<Std::string> vals;
SplitString(fontdesc, ',', vals);
if (vals.size() != 2) {
warning("Invalid jpfont override: %s", fontdesc.c_str());
continue;
}
unsigned int jpfontnum = atoi(vals[0].c_str());
uint32 col32 = strtol(vals[1].c_str(), 0, 0);
if (!fontmanager->addJPOverride(fontnum, jpfontnum, col32)) {
warning("failed to setup jpfont override for font %d", fontnum);
}
}
setupTTFOverrides("language", true);
}
void GameData::setupTTFOverrides(const char *category, bool SJIS) {
ConfigFileManager *config = ConfigFileManager::get_instance();
FontManager *fontmanager = FontManager::get_instance();
KeyMap ttfkeyvals;
bool overridefonts = ConfMan.getBool("font_override");
if (!overridefonts) return;
ttfkeyvals = config->listKeyValues(category, "fontoverride");
for (const auto &i : ttfkeyvals) {
int fontnum = atoi(i._key.c_str());
const Std::string &fontdesc = i._value;
Std::vector<Std::string> vals;
SplitString(fontdesc, ',', vals);
if (vals.size() != 4) {
warning("Invalid ttf override: %s", fontdesc.c_str());
continue;
}
const Common::Path filename(vals[0]);
int pointsize = atoi(vals[1].c_str());
uint32 col32 = strtol(vals[2].c_str(), 0, 0);
int border = atoi(vals[3].c_str());
if (!fontmanager->addTTFOverride(fontnum, filename, pointsize,
col32, border, SJIS)) {
warning("failed to setup ttf override for font %d", fontnum);
}
}
}
SpeechFlex *GameData::getSpeechFlex(uint32 shapeNum) {
if (shapeNum >= _speech.size())
return nullptr;
SpeechFlex **s = _speech[shapeNum];
if (s)
return *s;
char langletter = _gameInfo->getLanguageFileLetter();
if (!langletter) {
warning("GameData::getSpeechFlex: Unknown language.");
return nullptr;
}
Common::Path path(Common::String::format("sound/%c%i.flx", langletter, shapeNum));
auto *sflx = new Common::File();
if (!sflx->open(path)) {
delete sflx;
return nullptr;
}
s = new SpeechFlex *;
*s = new SpeechFlex(sflx);
_speech[shapeNum] = s;
return *s;
}
const NPCDat *GameData::getNPCData(uint16 entry) const {
if (entry < _npcTable.size()) {
return _npcTable[entry];
}
return nullptr;
}
const NPCDat *GameData::getNPCDataForShape(uint16 shapeno) const {
for (const auto *npcdat : _npcTable) {
if (npcdat->getShapeNo() == shapeno)
return npcdat;
}
return nullptr;
}
const CombatDat *GameData::getCombatDat(uint16 entry) const {
if (entry < _combatData.size()) {
return _combatData[entry];
}
return nullptr;
}
const WeaselDat *GameData::getWeaselDat(uint16 entry) const {
if (entry < _weaselData.size()) {
return _weaselData[entry];
}
return nullptr;
}
const FireType *GameData::getFireType(uint16 type) const {
return FireTypeTable::get(type);
}
void GameData::loadRemorseData() {
auto *fd = new Common::File();
if (!fd->open("static/fixed.dat"))
error("Unable to load static/fixed.dat");
_fixed = new RawArchive(fd);
char langletter = _gameInfo->getLanguageUsecodeLetter();
if (!langletter)
error("Unknown language. Unable to open usecode");
Std::string filename = "usecode/";
filename += langletter;
filename += "usecode.flx";
auto *uds = new Common::File();
if (!uds->open(filename.c_str()))
error("Unable to load %s", filename.c_str());
_mainUsecode = new UsecodeFlex(uds);
// Load main shapes
debug(1, "Load Shapes");
auto *sf = new Common::File();
if (!sf->open("static/shapes.flx"))
error("Unable to load static/shapes.flx");
_mainShapes = new MainShapeArchive(sf, MAINSHAPES,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game),
&CrusaderShapeFormat);
ConfigFileManager *config = ConfigFileManager::get_instance();
// Load weapon, armour info
if (_gameInfo->_type == GameInfo::GAME_REMORSE)
config->readConfigFile("remorseweapons.ini", "weapons");
else
config->readConfigFile("regretweapons.ini", "weapons");
config->readConfigFile("remorsegame.ini", "game");
// Load typeflags
auto *tfs = new Common::File();
if (!tfs->open("static/typeflag.dat"))
error("Unable to load static/typeflag.dat");
_mainShapes->loadTypeFlags(tfs);
delete tfs;
// Load animdat
auto *af = new Common::File();
if (!af->open("static/anim.dat"))
error("Unable to load static/anim.dat");
_mainShapes->loadAnimDat(af);
delete af;
// Load weapon overlay data
auto *wod = new Common::File();
if (!wod->open("static/wpnovlay.dat"))
error("Unable to load static/wpnovlay.dat");
RawArchive *overlayflex = new RawArchive(wod);
_weaponOverlay = new WpnOvlayDat();
_weaponOverlay->load(overlayflex);
delete overlayflex;
// Load globs
auto *gds = new Common::File();
if (!gds->open("static/glob.flx"))
error("Unable to load static/glob.flx");
RawArchive *globflex = new RawArchive(gds);
_globs.clear();
_globs.resize(globflex->getCount());
for (unsigned int i = 0; i < globflex->getCount(); ++i) {
MapGlob *glob = 0;
Common::SeekableReadStream *globrs = globflex->get_datasource(i);
if (globrs && globrs->size()) {
glob = new MapGlob();
glob->read(globrs);
}
delete globrs;
_globs[i] = glob;
}
delete globflex;
// Load fonts
auto *fds = new Common::File();
if (!fds->open("static/fonts.flx"))
error("Unable to load static/fonts.flx");
_fonts = new FontShapeArchive(fds, OTHER,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
_fonts->setHVLeads();
// Load mouse
auto *msds = new Common::File();
if (!msds->open("static/mouse.shp"))
error("Unable to load static/mouse.shp");
_mouse = new Shape(msds, 0);
_mouse->setPalette(PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
delete msds;
auto *gumpds = new Common::File();
if (!gumpds->open("static/gumps.flx"))
error("Unable to load static/gumps.flx");
_gumps = new GumpShapeArchive(gumpds, GUMPS,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game));
auto *dtableds = new Common::File();
if (!dtableds->open("static/dtable.flx"))
error("Unable to load static/dtable.flx");
RawArchive *dtableflex = new RawArchive(dtableds);
_npcTable = NPCDat::load(dtableflex);
delete dtableflex;
auto *damageds = new Common::File();
if (!damageds->open("static/damage.flx"))
error("Unable to load static/damage.flx");
RawArchive *damageflex = new RawArchive(damageds);
if (damageflex->getCount() != 1)
error("static/damage.flx appears corrupted");
_mainShapes->loadDamageDat(damageflex->get_datasource(0));
delete damageflex;
auto *combatds = new Common::File();
if (!combatds->open("static/combat.dat"))
error("Unable to load static/combat.dat");
RawArchive *combatflex = new RawArchive(combatds);
_combatData.clear();
_combatData.resize(combatflex->getCount());
for (uint32 i = 0; i < combatflex->getCount(); i++) {
Common::SeekableReadStream *combatflexrs = combatflex->get_datasource(i);
if (combatflexrs && combatflexrs->size() > 20) {
_combatData[i] = new CombatDat(*combatflexrs);
}
delete combatflexrs;
}
delete combatflex;
auto *stuffds = new Common::File();
if (!stuffds->open("static/stuff.dat"))
error("Unable to load static/stuff.dat");
// Weasel shop data.
// 14 blocks of 323 bytes, references like W01 and I07
// (weapon and inventory)
while (!stuffds->eos()) {
WeaselDat *data = new WeaselDat(stuffds);
_weaselData.push_back(data);
}
delete stuffds;
auto *xformpalds = new Common::File();
if (!xformpalds->open("static/xformpal.dat"))
error("Unable to load static/xformpal.dat");
RawArchive *xformpalflex = new RawArchive(xformpalds);
// TODO: What's in this flex?
// Object 1: 32 bytes
// Object 2: 2304 bytes - presumably data for 3 palettes == 768 * 3
// almost no low numbers (so not raw palette data, would be missing black..)
delete xformpalflex;
// Note: No MusicFlex for Remorse, as the music is all in different AMF files.
// The remorse_music_process will load them.
auto *sndflx = new Common::File();
if (!sndflx->open("sound/sound.flx"))
error("Unable to load sound/sound.flx");
_soundFlex = new SoundFlex(sndflx);
loadTranslation();
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,146 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_GAMEDATA_H
#define ULTIMA8_GAMES_GAMEDATA_H
#include "ultima/shared/std/containers.h"
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gfx/frame_id.h"
namespace Ultima {
namespace Ultima8 {
class RawArchive;
class MainShapeArchive;
class FontShapeArchive;
class GumpShapeArchive;
class ShapeArchive;
class Usecode;
class MapGlob;
class Shape;
class MusicFlex;
class WpnOvlayDat;
class NPCDat;
class CombatDat;
class FireType;
class ShapeFrame;
class WeaselDat;
class SoundFlex;
class SpeechFlex;
struct GameInfo;
class GameData {
public:
GameData(GameInfo *gameinfo);
~GameData();
static GameData *get_instance() {
return _gameData;
}
void loadU8Data();
void loadRemorseData();
void setupFontOverrides();
Usecode *getMainUsecode() const {
return _mainUsecode;
}
MainShapeArchive *getMainShapes() const {
return _mainShapes;
}
RawArchive *getFixed() const {
return _fixed;
}
MapGlob *getGlob(uint32 glob) const;
FontShapeArchive *getFonts() const {
return _fonts;
}
GumpShapeArchive *getGumps() const {
return _gumps;
}
Shape *getMouse() const {
return _mouse;
}
MusicFlex *getMusic() const {
return _music;
}
WpnOvlayDat *getWeaponOverlay() const {
return _weaponOverlay;
}
SoundFlex *getSoundFlex() const {
return _soundFlex;
}
SpeechFlex *getSpeechFlex(uint32 shapenum);
ShapeArchive *getShapeFlex(uint16 flexId) const;
Shape *getShape(FrameID frameid) const;
const ShapeFrame *getFrame(FrameID frameid) const;
const NPCDat *getNPCData(uint16 entry) const;
const NPCDat *getNPCDataForShape(uint16 shapeno) const;
const CombatDat *getCombatDat(uint16 entry) const;
const FireType *getFireType(uint16 type) const;
const WeaselDat *getWeaselDat(uint16 level) const;
Std::string translate(const Std::string &text);
FrameID translate(FrameID frame);
enum ShapeFlexId {
OTHER = 0,
MAINSHAPES = 1,
GUMPS = 2
};
private:
void loadTranslation();
void setupTTFOverrides(const char *category, bool SJIS);
void setupJPOverrides();
RawArchive *_fixed;
MainShapeArchive *_mainShapes;
Usecode *_mainUsecode;
Std::vector<MapGlob *> _globs;
FontShapeArchive *_fonts;
GumpShapeArchive *_gumps;
Shape *_mouse;
MusicFlex *_music;
WpnOvlayDat *_weaponOverlay;
Std::vector<NPCDat *> _npcTable;
Std::vector<CombatDat *> _combatData;
Std::vector<WeaselDat *> _weaselData;
SoundFlex *_soundFlex;
Std::vector<SpeechFlex **> _speech;
GameInfo *_gameInfo;
static GameData *_gameData;
};
#define _TL_(x) (GameData::get_instance()->translate(x))
#define _TL_SHP_(x) (GameData::get_instance()->translate(x))
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,225 @@
/* 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/ultima8/misc/debugger.h"
#include "ultima/ultima8/games/game_info.h"
#include "ultima/ultima8/misc/util.h"
namespace Ultima {
namespace Ultima8 {
struct GameTypeDesc {
const char *shortname;
const char *longname;
};
struct GameLangDesc {
char letter;
char usecodeletter;
const char *name;
};
// Keep order the same as the GameType enum!
static const GameTypeDesc gametypes[] = {
{ "", "" },
{ "ultima8", "Ultima VIII: Pagan" },
{ "remorse", "Crusader: No Remorse" },
{ "regret", "Crusader: No Regret" },
{ "pentmenu", "Pentagram Menu" },
{ 0, 0 }
};
// Keep order the same as the GameLanguage enum!
static const GameLangDesc gamelangs[] = {
{ 0, 0, "unknown" },
{ 'e', 'e', "English" },
{ 'f', 'f', "French" },
{ 'g', 'g', "German" },
{ 'e', 'e', "Spanish" },
{ 'e', 'j', "Japanese" },
{ '\0', '\0', 0 }
};
GameInfo::GameInfo() : _type(GAME_UNKNOWN), version(0), _language(GAMELANG_UNKNOWN),
_ucOffVariant(GAME_UC_DEFAULT) {
for (int i = 0; i < 16; ++i)
_md5[i] = 0;
}
char GameInfo::getLanguageFileLetter() const {
switch (_type) {
case GAME_U8: {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
return gamelangs[l].letter;
}
case GAME_REMORSE:
case GAME_REGRET:
return 'e';
default:
return 0;
}
}
char GameInfo::getLanguageUsecodeLetter() const {
switch (_type) {
case GAME_U8: {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
return gamelangs[l].usecodeletter;
}
case GAME_REMORSE:
case GAME_REGRET:
return 'e';
default:
return 0;
}
}
Std::string GameInfo::getLanguage() const {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
return gamelangs[l].name;
}
Std::string GameInfo::getGameTitle() const {
unsigned int t = static_cast<unsigned int>(_type);
assert(t < (sizeof(gametypes) / sizeof(gametypes[0])) - 1);
return gametypes[t].longname;
}
Std::string GameInfo::getPrintableVersion() const {
char buf[32];
Common::sprintf_s(buf, "%d.%02d", version / 100, version % 100);
return buf;
}
Std::string GameInfo::getPrintDetails() const {
Std::string ret;
Std::string title = getGameTitle();
if (title == "") title = "Unknown";
ret = title + ", ";
Std::string lang = getLanguage();
if (lang == "") lang = "Unknown";
ret += lang;
ret += ", version ";
ret += getPrintableVersion();
ret += ", md5 ";
ret += getPrintableMD5();
return ret;
}
Std::string GameInfo::getPrintableMD5() const {
Std::string ret;
char buf[33];
for (int i = 0; i < 16; ++i) {
Common::sprintf_s(buf + 2 * i, 3, "%02x", _md5[i]);
}
ret = buf;
return ret;
}
bool GameInfo::match(GameInfo &other, bool ignoreMD5) const {
if (_type != other._type) return false;
if (_language != other._language) return false;
if (ignoreMD5) return true;
// NOTE: Version and MD5 hash are not currently set
if (version != other.version) return false;
return (memcmp(_md5, other._md5, 16) == 0);
}
void GameInfo::save(Common::WriteStream *ws) {
unsigned int l = static_cast<unsigned int>(_language);
assert(l < (sizeof(gamelangs) / sizeof(gamelangs[0])) - 1);
unsigned int t = static_cast<unsigned int>(_type);
assert(t < (sizeof(gametypes) / sizeof(gametypes[0])) - 1);
Std::string game = gametypes[t].shortname;
Std::string lang = gamelangs[l].name;
char buf[16];
Common::sprintf_s(buf, "%d", version);
Std::string ver = buf;
Std::string md5Str = getPrintableMD5();
Std::string d = game + "," + lang + "," + ver + "," + md5Str + "\n";
ws->write(d.c_str(), d.size());
}
bool GameInfo::load(Common::SeekableReadStream *rs, uint32 ver) {
Std::string s;
Std::vector<Std::string> parts;
s = rs->readLine();
SplitString(s, ',', parts);
if (parts.size() != 4) return false;
int i = 0;
while (gametypes[i].shortname) {
if (parts[0] == gametypes[i].shortname) {
_type = static_cast<GameType>(i);
break;
}
i++;
}
if (!gametypes[i].shortname) return false;
i = 0;
while (gamelangs[i].name) {
if (parts[1] == gamelangs[i].name) {
_language = static_cast<GameLanguage>(i);
break;
}
i++;
}
if (!gamelangs[i].name) return false;
this->version = strtol(parts[2].c_str(), 0, 0);
for (i = 0; i < 16; ++i) {
char buf[3];
buf[0] = parts[3][2 * i];
buf[1] = parts[3][2 * i + 1];
buf[2] = 0;
long x = strtol(buf, 0, 16);
_md5[i] = static_cast<uint8>(x);
}
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,90 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_GAMEINFO_H
#define ULTIMA8_GAMES_GAMEINFO_H
#include "ultima/shared/std/string.h"
namespace Ultima {
namespace Ultima8 {
class IDataSource;
//! GameInfo contains detailed information about the game
struct GameInfo {
GameInfo();
Std::string _name;
enum GameType {
GAME_UNKNOWN = 0,
GAME_U8,
GAME_REMORSE,
GAME_REGRET
} _type;
// Usecode coff variant
enum GameUsecodeOffsetVariant {
GAME_UC_DEFAULT, // Most versions of most games
GAME_UC_ORIG, // Original (pre-patch) CD versions of Crusader games
GAME_UC_DEMO, // Crusader: No Remorse or No Regret Demos
GAME_UC_REM_ES, // Crusader: No Remorse Spanish
GAME_UC_REM_FR, // Crusader: No Remorse French
GAME_UC_REM_JA, // Crusader: No Remorse Japanese
GAME_UC_REG_DE // Crusader: No Regret German
} _ucOffVariant;
//! version number, encoded as 100*major + minor
//! so, 2.12 becomes 212
//! 0 = unknown
int version;
enum GameLanguage {
GAMELANG_UNKNOWN = 0,
GAMELANG_ENGLISH,
GAMELANG_FRENCH,
GAMELANG_GERMAN,
GAMELANG_SPANISH,
GAMELANG_JAPANESE
} _language;
uint8 _md5[16];
char getLanguageFileLetter() const;
char getLanguageUsecodeLetter() const;
Std::string getLanguage() const;
Std::string getGameTitle() const;
Std::string getPrintableVersion() const;
Std::string getPrintDetails() const;
Std::string getPrintableMD5() const;
bool match(GameInfo &other, bool ignoreMD5 = false) const;
void save(Common::WriteStream *ws);
bool load(Common::SeekableReadStream *rs, uint32 /* version */);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,162 @@
/* 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/ultima8/games/start_crusader_process.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/difficulty_gump.h"
#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/cru_pickup_area_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/actors/teleport_to_egg_process.h"
#include "ultima/ultima8/gfx/palette_fader_process.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(StartCrusaderProcess)
StartCrusaderProcess::StartCrusaderProcess(int saveSlot) : Process(),
_initStage(PlayFirstMovie), _saveSlot(saveSlot) {
_flags |= PROC_PREVENT_SAVE;
}
void StartCrusaderProcess::run() {
if (_initStage == PlayFirstMovie) {
_initStage = PlaySecondMovie;
ProcId moviepid = Game::get_instance()->playIntroMovie(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
waitFor(movieproc);
}
return;
} else if (_initStage == PlaySecondMovie) {
_initStage = ShowDifficultyMenu;
CruGame *game = dynamic_cast<CruGame *>(Game::get_instance());
assert(game);
ProcId moviepid = game->playIntroMovie2(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
waitFor(movieproc);
}
return;
}
// Try to load the save game, if succeeded this process will terminate
if (_saveSlot >= 0) {
Common::Error loadError = Ultima8Engine::get_instance()->loadGameState(_saveSlot);
if (loadError.getCode() != Common::kNoError) {
Ultima8Engine::get_instance()->setError(loadError);
fail();
return;
}
terminate();
return;
}
if (_initStage == ShowDifficultyMenu) {
DifficultyGump *gump = new DifficultyGump();
_initStage = StartGame;
gump->InitGump(nullptr, true);
return;
}
Gump *statusGump = new CruStatusGump(true);
statusGump->InitGump(nullptr, false);
Gump *cruPickupAreaGump = new CruPickupAreaGump(true);
cruPickupAreaGump->InitGump(nullptr, false);
MainActor *avatar = getMainActor();
int mapnum = avatar->getMapNum();
// These items are the same in Regret and Remorse
Item *datalink = ItemFactory::createItem(0x4d4, 0, 0, 0, 0, mapnum, 0, true);
avatar->addItemCru(datalink, false);
Item *smiley = ItemFactory::createItem(0x598, 0, 0, 0, 0, mapnum, 0, true);
smiley->moveToContainer(avatar);
avatar->setShieldType(1);
#if 0
// Give the avatar *all the weapons and ammo*.. (handy for testing)
Ultima8Engine::get_instance()->setCheatMode(true);
static const uint32 wpnshapes[] = {
// Weapons
0x032E, 0x032F, 0x0330, 0x038C, 0x0332, 0x0333, 0x0334,
0x038E, 0x0388, 0x038A, 0x038D, 0x038B, 0x0386,
// Ammo
0x033D, 0x033E, 0x033F, 0x0340, 0x0341,
// No Regret Weapons
0x5F6, 0x5F5, 0x198,
// No Regret Ammo
0x615, 0x614
};
for (int i = 0; i < ARRAYSIZE(wpnshapes); i++) {
for (int j = 0; j < 5; j++) {
Item *wpn = ItemFactory::createItem(wpnshapes[i], 0, 0, 0, 0, mapnum, 0, true);
avatar->addItemCru(wpn, false);
}
}
#endif
avatar->teleport(1, 0x1e);
// The first level 0x1e teleporter in No Remorse goes straight to another
// teleport, so undo the flag that normally stops that.
avatar->setJustTeleported(false);
if (GAME_IS_REGRET) {
avatar->setInCombat(0);
avatar->setDir(dir_south);
avatar->setActorFlag(Actor::ACT_WEAPONREADY);
}
Process *fader = new PaletteFaderProcess(TEX32_PACK_RGBA(0xFF, 0xFF, 0xFF, 0x00), true, 0x7FFF, 60, false);
Kernel::get_instance()->addProcess(fader);
Ultima8Engine::get_instance()->setAvatarInStasis(false);
terminate();
}
void StartCrusaderProcess::saveData(Common::WriteStream *ws) {
warning("Attempted save of process with prevent save flag");
Process::saveData(ws);
}
bool StartCrusaderProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,60 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_STARTCRUSADERPROCESS_H
#define ULTIMA8_GAMES_STARTCRUSADERPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Item;
class StartCrusaderProcess : public Process {
public:
enum CruInitStage {
PlayFirstMovie,
PlaySecondMovie,
ShowDifficultyMenu,
StartGame
};
protected:
CruInitStage _initStage;
int _saveSlot;
public:
StartCrusaderProcess(int saveSlot);
ENABLE_RUNTIME_CLASSTYPE()
void run() override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,130 @@
/* 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/ultima8/games/start_u8_process.h"
#include "ultima/ultima8/games/game.h"
#include "ultima/ultima8/world/loop_script.h"
#include "ultima/ultima8/usecode/uc_list.h"
#include "ultima/ultima8/world/current_map.h"
#include "ultima/ultima8/world/egg.h"
#include "ultima/ultima8/world/camera_process.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/gumps/menu_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/gfx/palette_fader_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(StartU8Process)
StartU8Process::StartU8Process(int saveSlot) : Process(),
_init(false), _saveSlot(saveSlot), _skipStart(saveSlot >= 0) {
_flags |= PROC_PREVENT_SAVE;
}
void StartU8Process::run() {
if (!_skipStart && !_init) {
_init = true;
ProcId moviepid = Game::get_instance()->playIntroMovie(false);
Process *movieproc = Kernel::get_instance()->getProcess(moviepid);
if (movieproc) {
waitFor(movieproc);
return;
}
}
// Try to load the save game, if succeeded this process will terminate
if (_saveSlot >= 0) {
Common::Error loadError = Ultima8Engine::get_instance()->loadGameState(_saveSlot);
if (loadError.getCode() != Common::kNoError) {
Ultima8Engine::get_instance()->setError(loadError);
fail();
return;
}
PaletteFaderProcess::I_fadeFromBlack(0, 0);
terminate();
return;
}
CurrentMap *currentmap = World::get_instance()->getCurrentMap();
UCList uclist(2);
if (!_skipStart) {
LOOPSCRIPT(script, LS_AND(LS_SHAPE_EQUAL1(73), LS_Q_EQUAL(36)));
currentmap->areaSearch(&uclist, script, sizeof(script),
0, 256, false, 16188, 7500);
if (uclist.getSize() < 1) {
warning("Unable to find FIRST egg");
return;
}
uint16 objid = uclist.getuint16(0);
Egg *egg = dynamic_cast<Egg *>(getObject(objid));
Point3 pt = egg->getLocation();
// Center on egg
CameraProcess::SetCameraProcess(new CameraProcess(pt));
egg->hatch();
}
// Music Egg
// Item 2145 (class Item, shape 562, 0, (11551,2079,48) q:52, m:0, n:0, f:2000, ef:2)
uclist.free();
LOOPSCRIPT(musicscript, LS_SHAPE_EQUAL1(562));
currentmap->areaSearch(&uclist, musicscript, sizeof(musicscript),
0, 256, false, 11551, 2079);
if (uclist.getSize() < 1) {
warning("Unable to find MUSIC egg");
} else {
ObjId objid = uclist.getuint16(0);
Item *musicEgg = getItem(objid);
musicEgg->callUsecodeEvent_cachein();
}
if (!_skipStart)
MenuGump::inputName();
else
Ultima8Engine::get_instance()->setAvatarInStasis(false);
terminate();
}
void StartU8Process::saveData(Common::WriteStream *ws) {
warning("Attempted save of process with prevent save flag");
Process::saveData(ws);
}
bool StartU8Process::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,53 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_STARTU8PROCESS_H
#define ULTIMA8_GAMES_STARTU8PROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Item;
class StartU8Process : public Process {
protected:
bool _init;
bool _skipStart;
int _saveSlot;
public:
StartU8Process(int saveSlot = -1);
ENABLE_RUNTIME_CLASSTYPE()
void run() override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,233 @@
/* 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/ultima8/misc/common_types.h"
#include "ultima/ultima8/games/treasure_loader.h"
#include "ultima/ultima8/conf/config_file_manager.h"
#include "ultima/ultima8/misc/util.h"
namespace Ultima {
namespace Ultima8 {
TreasureLoader::TreasureLoader() {
}
TreasureLoader::~TreasureLoader() {
}
void TreasureLoader::loadDefaults() {
ConfigFileManager *config = ConfigFileManager::get_instance();
KeyMap lootkeyvals;
// load default treasure types
lootkeyvals = config->listKeyValues("game", "treasure");
for (const auto &i : lootkeyvals) {
TreasureInfo ti;
bool ok = internalParse(i._value, ti, true);
if (ok) {
_defaultTreasure[i._key] = ti;
} else {
warning("Failed to parse treasure type '%s': %s", i._key.c_str(), i._value.c_str());
}
}
}
bool TreasureLoader::parse(const Std::string &desc,
Std::vector<TreasureInfo> &treasure) const {
treasure.clear();
Std::vector<Std::string> tr;
SplitString(desc, ';', tr);
TreasureInfo ti;
for (unsigned int i = 0; i < tr.size(); ++i) {
if (internalParse(tr[i], ti, false)) {
treasure.push_back(ti);
} else {
return false;
}
}
return true;
}
bool TreasureLoader::internalParse(const Std::string &desc, TreasureInfo &ti,
bool loadingDefault) const {
ti.clear();
bool loadedDefault = false;
Std::vector<Common::Pair<Std::string, Std::string> > kv;
SplitStringKV(desc, ' ', kv);
for (unsigned int i = 0; i < kv.size(); ++i) {
const Std::string &key = kv[i].first;
Std::string val = kv[i].second;
if (key == "shape") {
if (!parseUInt32Vector(val, ti._shapes)) {
warning("Failed to parse treasure shape list '%s'", val.c_str());
return false;
}
// validate the shapes are > 0 and < max shape
for (unsigned int j = 0; j < ti._shapes.size(); j++) {
if (ti._shapes[j] <= 0 || ti._shapes[j] > 65535) {
warning("Invalid treasure shape in list '%s'", val.c_str());
return false;
}
}
} else if (key == "frame") {
if (!parseUInt32Vector(val, ti._frames)) {
warning("Failed to parse treasure frame list '%s'", val.c_str());
return false;
}
// validate the frames are < max frame (0 frame is valid)
for (unsigned int j = 0; j < ti._frames.size(); j++) {
if (ti._frames[j] > 65535) {
warning("Invalid treasure frame in list '%s'", val.c_str());
return false;
}
}
} else if (key == "count") {
if (!parseUIntRange(val, ti._minCount, ti._maxCount)) {
int x;
if (!parseInt(val, x) || x <= 0) {
warning("Invalid treasure count '%s'", val.c_str());
return false;
}
ti._minCount = ti._maxCount = x;
}
} else if (key == "chance") {
if (!parseDouble(val, ti._chance) || ti._chance <= 0) {
warning("Invalid treasure chance '%s'", val.c_str());
return false;
}
} else if (key == "map") {
if (val.size() > 1 && val[0] == '!')
val[0] = '-'; // HACK: invert map for 'not this map'
if (!parseInt(val, ti._map))
return false;
} else if (key == "special" && loadingDefault) {
ti._special = val;
} else if (key == "type" && !loadingDefault) {
if (loadedDefault)
return false;
TreasureMap::const_iterator iter;
iter = _defaultTreasure.find(val);
if (iter != _defaultTreasure.end())
ti = iter->_value;
else
return false;
loadedDefault = true;
} else if (key == "mult" && !loadingDefault) {
if (!loadedDefault) {
warning("Need defaults before applying multiplier in treasure data '%s'", val.c_str());
return false;
}
unsigned int minmult, maxmult;
if (!parseUIntRange(val, minmult, maxmult)) {
int x;
if (!parseInt(val, x) || x <= 0) {
warning("Invalid treasure multiplier '%s'", val.c_str());
return false;
}
minmult = maxmult = x;
}
ti._minCount *= minmult;
ti._maxCount *= maxmult;
} else {
warning("Unknown key parsing treasure '%s'", key.c_str());
return false;
}
}
return true;
}
bool TreasureLoader::parseUInt32Vector(const Std::string &val_,
Std::vector<uint32> &vec) const {
Std::string val = val_;
vec.clear();
if (val.empty())
return false;
while (!val.empty()) {
Std::string::size_type pos = val.find(',');
const Std::string item = val.substr(0, pos);
Std::string::size_type itempos = val.find('-');
if (itempos != Std::string::npos) {
unsigned int min, max;
if (!parseUIntRange(item, min, max))
return false;
for (unsigned int i = min; i <= max; ++i)
vec.push_back(i);
} else {
int x;
if (!parseInt(item, x) || x < 0)
return false;
vec.push_back(x);
}
if (pos != Std::string::npos) pos++;
val.erase(0, pos);
}
return true;
}
bool TreasureLoader::parseUIntRange(const Std::string &val,
unsigned int &min, unsigned int &max) const {
Std::string::size_type pos = val.find('-');
if (pos == 0 || pos == Std::string::npos || pos + 1 >= val.size())
return false;
int t1 = 0;
int t2 = 0;
bool ok = true;
ok = ok && parseInt(val.substr(0, pos), t1);
ok = ok && parseInt(val.substr(pos + 1), t2);
ok = ok && (t1 <= t2 && t1 >= 0 && t2 >= 0);
if (ok) {
min = t1;
max = t2;
}
return ok;
}
bool TreasureLoader::parseDouble(const Std::string &val, double &d) const {
if (val.empty())
return false;
// TODO: error checking
d = atof(val.c_str());
return true;
}
bool TreasureLoader::parseInt(const Std::string &val, int &i) const {
if (val.empty())
return false;
// TODO: error checking
i = strtol(val.c_str(), 0, 0);
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,58 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_TREASURELOADER_H
#define ULTIMA8_GAMES_TREASURELOADER_H
#include "ultima/ultima8/world/actors/treasure_info.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima8 {
typedef Common::HashMap<Std::string, TreasureInfo, Common::IgnoreCase_Hash> TreasureMap;
class TreasureLoader {
public:
TreasureLoader();
~TreasureLoader();
//! load defaults from 'game' ini section
void loadDefaults();
//! parse treasure string into vector of TreasureInfo objects
bool parse(const Std::string &, Std::vector<TreasureInfo> &treasure) const;
private:
TreasureMap _defaultTreasure;
bool internalParse(const Std::string &desc, TreasureInfo &ti, bool loadingDefault) const;
bool parseUInt32Vector(const Std::string &val, Std::vector<uint32> &vec) const;
bool parseUIntRange(const Std::string &val, unsigned int &min, unsigned int &max) const;
bool parseDouble(const Std::string &val, double &d) const;
bool parseInt(const Std::string &val, int &i) const;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,316 @@
/* 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 "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<uint8>(avname.size());
ws->writeByte(namelength);
for (unsigned int i = 0; i < namelength; ++i)
ws->writeByte(static_cast<uint8>(avname[i]));
Point3 pt = av->getLocation();
ws->writeUint16LE(av->getMapNum());
ws->writeUint32LE(static_cast<uint32>(pt.x));
ws->writeUint32LE(static_cast<uint32>(pt.y));
ws->writeUint32LE(static_cast<uint32>(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

View File

@@ -0,0 +1,60 @@
/* 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/>.
*
*/
#ifndef ULTIMA8_GAMES_U8GAME_H
#define ULTIMA8_GAMES_U8GAME_H
#include "ultima/ultima8/games/game.h"
namespace Ultima {
namespace Ultima8 {
class U8Game: public Game {
public:
U8Game();
~U8Game() override;
//! load/init game's data files
bool loadFiles() override;
//! initialize new game
bool startGame() override;
//! start initial usecode
bool startInitialUsecode(int saveSlot) override;
//! write game-specific savegame info (avatar stats, equipment, ...)
void writeSaveInfo(Common::WriteStream *ws) override;
ProcId playIntroMovie(bool fade) override;
ProcId playEndgameMovie(bool fade) override;
void playCredits() override;
void playQuotes() override;
void playDemoScreen() override { }; // no demo for U8
protected:
Std::string getCreditText(Common::SeekableReadStream *rs);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif