KYRA: (EOB) - Start implementing EOB1 party transfer (not working yet)

(ScummVM specific solution which allows the selection of save files of all configured EOB1 targets)
This commit is contained in:
athrxx 2011-12-13 22:04:06 +01:00 committed by Johannes Schickel
parent 53e1bf21ec
commit c0e782fd0b
16 changed files with 297 additions and 47 deletions

View file

@ -26,8 +26,13 @@
#include "kyra/resource.h"
#include "kyra/sound_intern.h"
#include "common/savefile.h"
#include "common/str-array.h"
namespace Kyra {
// Character Generator
class CharacterGenerator {
public:
CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen);
@ -191,7 +196,7 @@ bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes) {
loop = false;
} else {
_activeBox = inputFlag;
inputFlag = 43;
inputFlag = _vm->_keyMap[Common::KEYCODE_RETURN];
}
}
@ -1426,13 +1431,157 @@ const int16 CharacterGenerator::_raceModifiers[] = {
0, 0, 0, 0, 1, -1, 0, 1, -1, 0, 0, 0, -1, 0, 0, 1, 0, 0
};
// Transfer Party
class TransferPartyWiz {
public:
TransferPartyWiz(EoBCoreEngine *vm, Screen_EoB *screen);
~TransferPartyWiz();
bool start();
private:
bool selectAndLoadTransferFile();
Common::String transferFileDialogue();
void convertStats();
EoBCoreEngine *_vm;
Screen_EoB *_screen;
};
TransferPartyWiz::TransferPartyWiz(EoBCoreEngine *vm, Screen_EoB *screen) : _vm(vm), _screen(screen) {
}
TransferPartyWiz::~TransferPartyWiz() {
}
bool TransferPartyWiz::start() {
_screen->copyPage(0, 12);
if (!selectAndLoadTransferFile())
return false;
convertStats();
return true;
}
bool TransferPartyWiz::selectAndLoadTransferFile() {
for (int numLoops = 1; numLoops; numLoops--) {
_screen->copyPage(12, 0);
_vm->_savegameFilename = transferFileDialogue();
if (_vm->_savegameFilename.empty()) {
if (_vm->_gui->confirmDialogue2(15, 68, 1))
numLoops++;
}
}
if (_vm->_savegameFilename.equals(_vm->_saveLoadStrings[1]))
return false;
if (_vm->loadGameState(-1).getCode() != Common::kNoError)
return false;
return true;
}
Common::String TransferPartyWiz::transferFileDialogue() {
Common::StringArray saveFileList = _vm->_saveFileMan->listSavefiles("*.*");
Common::StringArray targets;
Common::String tfile;
KyraEngine_v1::SaveHeader header;
Common::InSaveFile *in;
for (Common::StringArray::iterator i = saveFileList.begin(); i != saveFileList.end(); ++i) {
if (!(in = _vm->_saveFileMan->openForLoading(*i)))
continue;
if (KyraEngine_v1::readSaveHeader(in, false, header)) {
delete in;
continue;
}
delete in;
if (header.gameID != GI_EOB1)
continue;
i->insertChar('\0', i->size() - 4);
Common::StringArray::iterator ii = targets.begin();
for (; ii != targets.end(); ++ii) {
if (!i->compareToIgnoreCase(*ii))
break;
}
if (ii == targets.end())
targets.push_back(*i);
}
if (targets.begin() == targets.end())
return tfile;
Common::String target = _vm->_gui->transferTargetMenu(targets);
_screen->copyPage(12, 0);
if (target.equals(_vm->_saveLoadStrings[1]))
return target;
tfile = target + ".fin";
in = _vm->_saveFileMan->openForLoading(tfile);
if (in) {
delete in;
if (_vm->_gui->confirmDialogue2(15, -2, 1))
return tfile;
}
_screen->copyPage(12, 0);
tfile = _vm->_gui->transferFileMenu(target);
_screen->copyPage(12, 0);
return tfile;
}
void TransferPartyWiz::convertStats() {
for (int i = 0; i < 6; i++) {
EoBCharacter *c = &_vm->_characters[i];
uint32 aflags = 0;
for (int ii = 0; ii < 25; ii++) {
if (c->mageSpellsAvailableFlags & (1 << ii)) {
int8 f = (int8)_vm->_transferConvertTable[i + 1] - 1;
if (f != -1)
aflags |= (1 << f);
}
}
c->mageSpellsAvailableFlags = aflags;
c->flags &= 1;
c->hitPointsCur = c->hitPointsMax;
c->food = 100;
for (int ii = 0; ii < 3; ii++) {
int t = _vm->getCharacterClassType(c->cClass, ii);
if (t == -1)
continue;
if (c->experience[ii] < _vm->_transferExpTable[t])
c->experience[ii] = _vm->_transferExpTable[t];
}
}
}
// Start functions
bool EoBCoreEngine::startCharacterGeneration() {
return CharacterGenerator(this, _screen).start(_characters, &_faceShapes);
}
bool EoBCoreEngine::transferParty() {
return false;
bool EoBCoreEngine::startPartyTransfer() {
return TransferPartyWiz(this, _screen).start();
}
} // End of namespace Kyra

View file

@ -167,6 +167,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
_expObjectAnimTbl1Size = _expObjectAnimTbl2Size = _expObjectAnimTbl3Size = _wllFlagPresetSize = _scriptTimersCount = _buttonList1Size = _buttonList2Size =
_buttonList3Size = _buttonList4Size = _buttonList5Size = _buttonList6Size = _buttonList7Size = _buttonList8Size = 0;
_inventorySlotsY = _mnDef = 0;
_transferStringsScummVM = 0;
_buttonDefs = 0;
_npcPreset = 0;
_chargenStatStrings = _chargenRaceSexStrings = _chargenClassStrings = _chargenAlignmentStrings = _pryDoorStrings = _warningStrings = _ripItemStrings =
@ -463,7 +464,7 @@ Common::Error EoBCoreEngine::go() {
startupNew();
} else if (action == -3) {
// transfer party
repeatLoop = transferParty();
repeatLoop = startPartyTransfer();
if (repeatLoop && !shouldQuit())
startupNew();
}

View file

@ -245,6 +245,7 @@ friend class GUI_EoB;
friend class EoBInfProcessor;
friend class DarkmoonSequenceHelper;
friend class CharacterGenerator;
friend class TransferPartyWiz;
public:
EoBCoreEngine(OSystem *system, const GameFlags &flags);
virtual ~EoBCoreEngine();
@ -318,7 +319,7 @@ protected:
// Character generation / party transfer
bool startCharacterGeneration();
bool transferParty();
bool startPartyTransfer();
uint8 **_faceShapes;
@ -1125,6 +1126,7 @@ protected:
const char *_menuOkString;
const char *const *_menuStringsTransfer;
const char *const *_transferStringsScummVM;
const char *const *_menuStringsSpec;
const char *const *_menuStringsSpellNo;
const char *const *_menuYesNoStrings;

View file

@ -46,8 +46,8 @@ GUI::~GUI() {
}
}
void GUI::updateSaveFileList(bool excludeQuickSaves) {
Common::String pattern = _vm->_targetName + ".???";
void GUI::updateSaveFileList(Common::String targetName, bool excludeQuickSaves) {
Common::String pattern = targetName + ".???";
Common::StringArray saveFileList = _vm->_saveFileMan->listSavefiles(pattern);
_saveSlots.clear();
@ -93,8 +93,8 @@ int GUI::getNextSavegameSlot() {
return 0;
}
void GUI::updateSaveSlotsList() {
if (!_saveSlotsListUpdateNeeded)
void GUI::updateSaveSlotsList(Common::String targetName, bool force) {
if (!_saveSlotsListUpdateNeeded && !force)
return;
_saveSlotsListUpdateNeeded = false;
@ -105,7 +105,7 @@ void GUI::updateSaveSlotsList() {
delete[] _savegameList;
}
updateSaveFileList(true);
updateSaveFileList(targetName, true);
int numSaves = _savegameListSize = _saveSlots.size();
bool allowEmptySlots = (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2);
@ -120,7 +120,7 @@ void GUI::updateSaveSlotsList() {
memset(_savegameList, 0, _savegameListSize * sizeof(char*));
for (int i = 0; i < numSaves; i++) {
in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i]), header);
in = _vm->openSaveForReading(_vm->getSavegameFilename(targetName, _saveSlots[i]).c_str(), header, targetName == _vm->_targetName);
char **listEntry = &_savegameList[allowEmptySlots? _saveSlots[i] : i];
if (in) {
*listEntry = new char[header.description.size() + 1];

View file

@ -118,9 +118,9 @@ protected:
// Since ScummVM's savegame indices aren't, we re-index them.
// The integers stored in _saveSlots are ScummVM savegame indices.
Common::Array<int> _saveSlots;
void updateSaveFileList(bool excludeQuickSaves = false);
void updateSaveFileList(Common::String targetName, bool excludeQuickSaves = false);
int getNextSavegameSlot();
void updateSaveSlotsList();
void updateSaveSlotsList(Common::String targetName, bool force = false);
virtual void sortSaveSlots();

View file

@ -2220,6 +2220,7 @@ bool GUI_EoB::runLoadMenu(int x, int y) {
_screen->modifyScreenDim(11, dm->sx + (x >> 3), dm->sy + y, dm->w, dm->h);
for (bool runLoop = true; runLoop && !_vm->shouldQuit(); ) {
updateSaveSlotsList(_vm->_targetName);
int slot = selectSaveSlotDialogue(x, y, 1);
if (slot > 5) {
runLoop = result = false;
@ -2495,6 +2496,66 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
return _keyPressed.keycode == Common::KEYCODE_ESCAPE ? -1 : len;
}
Common::String GUI_EoB::transferTargetMenu(Common::Array<Common::String> &targets) {
_savegameListSize = targets.size();
if (_savegameList) {
for (int i = 0; i < _savegameListSize; i++)
delete[] _savegameList[i];
delete[] _savegameList;
}
_savegameList = new char*[_savegameListSize];
memset(_savegameList, 0, _savegameListSize * sizeof(char*));
Common::StringArray::iterator ii = targets.begin();
for (int i = 0; i < _savegameListSize; ++i) {
_savegameList[i] = new char[(*ii).size() + 1];
strcpy(_savegameList[i], (*ii++).c_str());
}
const ScreenDim *dm = _screen->getScreenDim(11);
int xo = dm->sx;
int yo = dm->sy;
_screen->modifyScreenDim(11, dm->sx + 9, dm->sy + 14, dm->w, dm->h);
int slot = 0;
do {
slot = selectSaveSlotDialogue(72, 14, 2);
if (slot == 6)
break;
} while (_saveSlotIdTemp[slot] == -1);
_screen->copyRegion(72, 14, 72, 14, 176, 144, 12, 0, Screen::CR_NO_P_CHECK);
_screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
return (slot < 6) ? _savegameList[_savegameOffset + slot] : _vm->_saveLoadStrings[1];
}
Common::String GUI_EoB::transferFileMenu(Common::String &target) {
updateSaveSlotsList(target, true);
_saveSlotsListUpdateNeeded = true;
const ScreenDim *dm = _screen->getScreenDim(11);
int xo = dm->sx;
int yo = dm->sy;
_screen->modifyScreenDim(11, dm->sx + 9, dm->sy + 14, dm->w, dm->h);
int slot = 0;
do {
slot = selectSaveSlotDialogue(72, 14, 4);
if (slot < 6) {
if (_saveSlotIdTemp[slot] == -1)
messageDialogue(11, 65, 6);
else {
_screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
return _vm->getSavegameFilename(target, _saveSlotIdTemp[slot]);
}
}
} while (_saveSlotIdTemp[slot] == -1);
_screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
return _vm->_saveLoadStrings[1];
}
void GUI_EoB::createScreenThumbnail(Graphics::Surface &dst) {
uint8 *screenPal = new uint8[768];
_screen->getRealPalette(0, screenPal);
@ -2528,6 +2589,7 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
_screen->modifyScreenDim(11, dm->sx + (x >> 3), dm->sy + y, dm->w, dm->h);
for (bool runLoop = true; runLoop && !_vm->shouldQuit(); ) {
updateSaveSlotsList(_vm->_targetName);
int slot = selectSaveSlotDialogue(x, y, 0);
if (slot > 5) {
runLoop = result = false;
@ -2575,16 +2637,14 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
}
int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
assert (id < 2);
_saveSlotX = _saveSlotY = 0;
_screen->setCurPage(2);
updateSaveSlotsList();
_savegameOffset = 0;
drawMenuButtonBox(0, 0, 176, 144, false, false);
_screen->printShadedText(_vm->_saveLoadStrings[2 + id], 52, 5, 15, 0);
const char *title = (id < 2) ? _vm->_saveLoadStrings[2 + id] : _vm->_transferStringsScummVM[id - 1];
_screen->printShadedText(title, 52, 5, 15, 0);
_screen->copyRegion(0, 0, x, y, 176, 144, 2, 0, Screen::CR_NO_P_CHECK);
_screen->setCurPage(0);
@ -3806,8 +3866,10 @@ const char *GUI_EoB::getMenuString(int id) {
if (id >= 69)
return _vm->_menuStringsTransfer[id - 69];
else if (id >= 67)
return _vm->_menuStringsDefeat[id - 67];
else if (id == 68)
return _vm->_transferStringsScummVM[0];
else if (id == 67)
return _vm->_menuStringsDefeat[0];
else if (id == 66)
return _vm->_errorSlotEmptyString;
else if (id == 65)
@ -3846,6 +3908,8 @@ const char *GUI_EoB::getMenuString(int id) {
return _vm->_menuStringsSaveLoad[id - 9];
else if (id >= 1)
return _vm->_menuStringsMain[id - 1];
else if (id < 0)
return _vm->_transferStringsScummVM[-id];
return empty;
}

View file

@ -69,6 +69,10 @@ public:
int getTextInput(char *dest, int x, int y, int destMaxLen, int textColor1, int textColor2, int cursorColor);
// Transfer party
Common::String transferTargetMenu(Common::Array<Common::String> &targets);
Common::String transferFileMenu(Common::String &target);
// utilities for thumbnail creation
void createScreenThumbnail(Graphics::Surface &dst);

View file

@ -1148,7 +1148,7 @@ int GUI_HoF::sliderHandler(Button *caller) {
}
int GUI_HoF::loadMenu(Button *caller) {
updateSaveFileList();
updateSaveFileList(_vm->_targetName);
if (!_vm->_menuDirectlyToLoad) {
updateMenuButton(caller);

View file

@ -596,7 +596,7 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) {
}
int GUI_LoK::saveGameMenu(Button *button) {
updateSaveFileList();
updateSaveFileList(_vm->_targetName);
updateMenuButton(button);
_menu[2].item[5].enabled = true;
@ -636,7 +636,7 @@ int GUI_LoK::saveGameMenu(Button *button) {
}
int GUI_LoK::loadGameMenu(Button *button) {
updateSaveFileList();
updateSaveFileList(_vm->_targetName);
if (_vm->_menuDirectlyToLoad) {
_menu[2].item[5].enabled = false;

View file

@ -2228,7 +2228,7 @@ int GUI_LoL::runMenu(Menu &menu) {
_vm->_mouseX = _vm->_mouseY = 0;
if (_currentMenu == &_loadMenu || _currentMenu == &_saveMenu || _currentMenu == &_deleteMenu) {
updateSaveSlotsList();
updateSaveSlotsList(_vm->_targetName);
setupSaveMenuSlots(*_currentMenu, 4);
}

View file

@ -1277,7 +1277,7 @@ int GUI_MR::optionsButton(Button *button) {
}
int GUI_MR::loadMenu(Button *caller) {
updateSaveFileList();
updateSaveFileList(_vm->_targetName);
if (!_vm->_menuDirectlyToLoad) {
updateMenuButton(caller);

View file

@ -594,7 +594,7 @@ int GUI_v2::cancelLoadMenu(Button *caller) {
}
int GUI_v2::saveMenu(Button *caller) {
updateSaveFileList();
updateSaveFileList(_vm->_targetName);
updateMenuButton(caller);
@ -690,7 +690,7 @@ int GUI_v2::cancelSaveMenu(Button *caller) {
}
int GUI_v2::deleteMenu(Button *caller) {
updateSaveFileList();
updateSaveFileList(_vm->_targetName);
updateMenuButton(caller);
if (_saveSlots.size() < 2) {

View file

@ -424,7 +424,7 @@ protected:
Common::Error saveGameState(int slot, const Common::String &desc) { return saveGameStateIntern(slot, desc.c_str(), 0); }
virtual Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0;
Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header);
Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header, bool checkID = true);
Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const;
// TODO: Consider moving this to Screen

View file

@ -122,7 +122,7 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::Seekab
return ((in->err() || in->eos()) ? kRSHEIoError : kRSHENoError);
}
Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) {
Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header, bool checkID) {
Common::SeekableReadStream *in = 0;
if (!(in = _saveFileMan->openForLoading(filename)))
return 0;
@ -142,7 +142,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena
if (!header.originalSave) {
if (!header.oldHeader) {
if (header.gameID != _flags.gameID) {
if (header.gameID != _flags.gameID && checkID) {
warning("Trying to load game state from other game (save game: %u, running game: %u)", header.gameID, _flags.gameID);
delete in;
return 0;

View file

@ -32,23 +32,19 @@
namespace Kyra {
Common::Error EoBCoreEngine::loadGameState(int slot) {
const char *fileName = 0;
if (slot == -1) {
_savegameFilename = /*_targetName + */Common::String("eob.fin");
fileName = _savegameFilename.c_str();
} else {
fileName = getSavegameFilename(slot);
}
// Special slot id -1 for EOB1 party transfer
const char *fileName = (slot == -1) ? _savegameFilename.c_str() : getSavegameFilename(slot);
SaveHeader header;
Common::InSaveFile *saveFile = openSaveForReading(fileName, header);
Common::InSaveFile *saveFile = openSaveForReading(fileName, header, (slot != -1));
if (!saveFile)
return Common::Error(Common::kReadingFailed);
Common::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !header.originalSave, DisposeAfterUse::YES);
_loading = true;
_screen->fadeToBlack(10);
if (slot != -1)
_screen->fadeToBlack(10);
for (int i = 0; i < 6; i++) {
EoBCharacter *c = &_characters[i];
@ -77,6 +73,8 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
c->cClass = in.readByte();
c->alignment = in.readByte();
c->portrait = in.readSByte();
if (slot == -1 && c->portrait < 0)
c->portrait += 43;
c->food = in.readByte();
in.read(c->level, 3);
for (int ii = 0; ii < 3; ii++)
@ -120,6 +118,10 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
}
_screen->_curPage = 0;
// No more data required for party transfer
if (slot == -1)
return Common::kNoError;
_currentLevel = in.readByte();
_currentSub = in.readSByte();
_currentBlock = in.readUint16BE();
@ -303,6 +305,7 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
Common::String saveNameTmp;
const char *fileName = 0;
// Special slot id -1 to create final save for party transfer
if (slot == -1) {
_savegameFilename = _targetName + Common::String(".fin");
fileName = _savegameFilename.c_str();

View file

@ -473,15 +473,8 @@ void EoBCoreEngine::initStaticResource() {
0
};
static const char *errorSlotNoNameString[3] = {
" You must specify\r a name for your\r save game!",
" Spielstände müssen\r einen Namen haben!",
0
};
_saveLoadStrings = saveLoadStrings[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)];
_errorSlotEmptyString = errorSlotEmptyString[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)];
_errorSlotNoNameString = errorSlotNoNameString[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)];
_menuOkString = "OK";
}
@ -1107,6 +1100,14 @@ void EoBEngine::initStaticResource() {
p->tuResist = (int8)*ps++;
p->dmgModifierEvade = *ps++;
}
static const char *errorSlotNoNameString[3] = {
" You must specify\r a name for your\r save game!",
" Spielstaende mues-\r sen einen Namen\r haben!",
0
};
_errorSlotNoNameString = errorSlotNoNameString[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)];
}
void EoBEngine::initSpells() {
@ -1241,6 +1242,32 @@ void DarkMoonEngine::initStaticResource() {
_wallOfForceDsNumW = _staticres->loadRawData(kEoB2WallOfForceNumW, temp);
_wallOfForceDsNumH = _staticres->loadRawData(kEoB2WallOfForceNumH, temp);
_wallOfForceShpId = _staticres->loadRawData(kEoB2WallOfForceShpId, temp);
static const char *errorSlotNoNameString[3] = {
" You must specify\r a name for your\r save game!",
" Spielst[nde m]ssen\r einen Namen haben!",
0
};
_errorSlotNoNameString = errorSlotNoNameString[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)];
// ScummVM specific
static const char *transferStringsScummVM[3][4] = {
{ "\r We cannot find any EOB save game\r file. Please make sure that the\r save game file with the party\r you wish to transfer is located\r in your ScummVM save game\r directory. If you have set up\r multiple save directories you\r have to copy the EOB save file\r into your EOB II save directory.\r Do you wish to try again?",
"Game ID",
"\r It seems that you have already\r defeated Xanathar here. Do you\r wish to transfer the party that\r finished the game? If not, you\r will be able to select a save\r game from the save game\r dialogue.",
"Select File"
},
{ "\r Kein EOB-Spielstand zu finden.\r Bitte Spielstandsdatei mit der\r zu ]bernehmenden Gruppe in das\r ScummVM Spielstands-Verzeichnis\r kopieren. Bei mehreren Spiel-\r stands-Verzeichnissen bitte\r den EOB-Spielstand in das\r EOB II-Spielstands-Verzeichnis\r kopieren. Nochmal versuchen?",
"Game ID",
"\r Wie es scheint, wurde Xanathar\r hier bereits besiegt. Soll die\r Gruppe, mit der das Spiel be-\r endet wurde, ]bernommen werden?\r Falls nicht, kann ein Spielstand\r aus der Spielstandsliste gew[hlt\r werden.",
"Spiel W[hlen"
},
{ 0, 0, 0, 0
}
};
_transferStringsScummVM = transferStringsScummVM[(_flags.lang == Common::EN_ANY) ? 0 : ((_flags.lang == Common::DE_DEU) ? 1 : 2)];
}
void DarkMoonEngine::initSpells() {