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,756 @@
/* 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 "kyra/gui/debugger.h"
#include "kyra/engine/kyra_lok.h"
#include "kyra/engine/kyra_hof.h"
#include "kyra/engine/timer.h"
#include "kyra/resource/resource.h"
#include "kyra/engine/lol.h"
#include "kyra/engine/eobcommon.h"
#include "common/system.h"
#include "common/config-manager.h"
namespace Kyra {
Debugger::Debugger(KyraEngine_v1 *vm)
: ::GUI::Debugger(), _vm(vm) {
}
void Debugger::initialize() {
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
registerCmd("screen_debug_mode", WRAP_METHOD(Debugger, cmdSetScreenDebug));
registerCmd("load_palette", WRAP_METHOD(Debugger, cmdLoadPalette));
registerCmd("facings", WRAP_METHOD(Debugger, cmdShowFacings));
registerCmd("gamespeed", WRAP_METHOD(Debugger, cmdGameSpeed));
registerCmd("flags", WRAP_METHOD(Debugger, cmdListFlags));
registerCmd("toggleflag", WRAP_METHOD(Debugger, cmdToggleFlag));
registerCmd("queryflag", WRAP_METHOD(Debugger, cmdQueryFlag));
registerCmd("timers", WRAP_METHOD(Debugger, cmdListTimers));
registerCmd("settimercountdown", WRAP_METHOD(Debugger, cmdSetTimerCountdown));
}
bool Debugger::cmdSetScreenDebug(int argc, const char **argv) {
if (argc > 1) {
if (scumm_stricmp(argv[1], "enable") == 0)
_vm->screen()->enableScreenDebug(true);
else if (scumm_stricmp(argv[1], "disable") == 0)
_vm->screen()->enableScreenDebug(false);
else
debugPrintf("Use screen_debug_mode <enable/disable> to enable or disable it.\n");
} else {
debugPrintf("Screen debug mode is %s.\n", (_vm->screen()->queryScreenDebug() ? "enabled" : "disabled"));
debugPrintf("Use screen_debug_mode <enable/disable> to enable or disable it.\n");
}
return true;
}
bool Debugger::cmdLoadPalette(int argc, const char **argv) {
Palette palette(_vm->screen()->getPalette(0).getNumColors());
if (argc <= 1) {
debugPrintf("Use load_palette <file> [start_col] [end_col]\n");
return true;
}
if (_vm->game() != GI_KYRA1 && _vm->resource()->getFileSize(argv[1]) != 768) {
uint8 *buffer = new uint8[320 * 200 * sizeof(uint8)];
if (!buffer) {
debugPrintf("ERROR: Cannot allocate buffer for screen region!\n");
return true;
}
_vm->screen()->copyRegionToBuffer(5, 0, 0, 320, 200, buffer);
_vm->screen()->loadBitmap(argv[1], 5, 5, nullptr);
palette.copy(_vm->screen()->getCPagePtr(5), 0, 256);
_vm->screen()->copyBlockToPage(5, 0, 0, 320, 200, buffer);
delete[] buffer;
} else if (!_vm->screen()->loadPalette(argv[1], palette)) {
debugPrintf("ERROR: Palette '%s' not found!\n", argv[1]);
return true;
}
int startCol = 0;
int endCol = palette.getNumColors();
if (argc > 2)
startCol = MIN(palette.getNumColors(), MAX(0, atoi(argv[2])));
if (argc > 3)
endCol = MIN(palette.getNumColors(), MAX(0, atoi(argv[3])));
if (startCol > 0)
palette.copy(_vm->screen()->getPalette(0), 0, startCol);
if (endCol < palette.getNumColors())
palette.copy(_vm->screen()->getPalette(0), endCol);
_vm->screen()->setScreenPalette(palette);
_vm->screen()->updateScreen();
return true;
}
bool Debugger::cmdShowFacings(int argc, const char **argv) {
debugPrintf("Facing directions:\n");
debugPrintf("7 0 1\n");
debugPrintf(" \\ | / \n");
debugPrintf("6--*--2\n");
debugPrintf(" / | \\\n");
debugPrintf("5 4 3\n");
return true;
}
bool Debugger::cmdGameSpeed(int argc, const char **argv) {
if (argc == 2) {
int val = atoi(argv[1]);
if (val < 1 || val > 1000) {
debugPrintf("speed must lie between 1 and 1000 (default: 60)\n");
return true;
}
_vm->_tickLength = (uint8)(1000.0 / val);
} else {
debugPrintf("Syntax: gamespeed <value>\n");
}
return true;
}
bool Debugger::cmdListFlags(int argc, const char **argv) {
for (int i = 0, p = 0; i < (int)sizeof(_vm->_flagsTable) * 8; i++, ++p) {
debugPrintf("(%-3i): %-2i", i, _vm->queryGameFlag(i));
if (p == 5) {
debugPrintf("\n");
p -= 6;
}
}
debugPrintf("\n");
return true;
}
bool Debugger::cmdToggleFlag(int argc, const char **argv) {
if (argc > 1) {
uint flag = atoi(argv[1]);
if (_vm->queryGameFlag(flag))
_vm->resetGameFlag(flag);
else
_vm->setGameFlag(flag);
debugPrintf("Flag %i is now %i\n", flag, _vm->queryGameFlag(flag));
} else {
debugPrintf("Syntax: toggleflag <flag>\n");
}
return true;
}
bool Debugger::cmdQueryFlag(int argc, const char **argv) {
if (argc > 1) {
uint flag = atoi(argv[1]);
debugPrintf("Flag %i is %i\n", flag, _vm->queryGameFlag(flag));
} else {
debugPrintf("Syntax: queryflag <flag>\n");
}
return true;
}
bool Debugger::cmdListTimers(int argc, const char **argv) {
debugPrintf("Current time: %-8u\n", g_system->getMillis());
for (int i = 0; i < _vm->timer()->count(); i++)
debugPrintf("Timer %-2i: Active: %-3s Countdown: %-6i %-8u\n", i, _vm->timer()->isEnabled(i) ? "Yes" : "No", _vm->timer()->getDelay(i), _vm->timer()->getNextRun(i));
return true;
}
bool Debugger::cmdSetTimerCountdown(int argc, const char **argv) {
if (argc > 2) {
uint timer = atoi(argv[1]);
uint countdown = atoi(argv[2]);
_vm->timer()->setCountdown(timer, countdown);
debugPrintf("Timer %i now has countdown %i\n", timer, _vm->timer()->getDelay(timer));
} else {
debugPrintf("Syntax: settimercountdown <timer> <countdown>\n");
}
return true;
}
#pragma mark -
Debugger_LoK::Debugger_LoK(KyraEngine_LoK *vm)
: Debugger(vm), _vm(vm) {
}
void Debugger_LoK::initialize() {
registerCmd("enter", WRAP_METHOD(Debugger_LoK, cmdEnterRoom));
registerCmd("scenes", WRAP_METHOD(Debugger_LoK, cmdListScenes));
registerCmd("give", WRAP_METHOD(Debugger_LoK, cmdGiveItem));
registerCmd("birthstones", WRAP_METHOD(Debugger_LoK, cmdListBirthstones));
Debugger::initialize();
}
bool Debugger_LoK::cmdEnterRoom(int argc, const char **argv) {
uint direction = 0;
if (argc > 1) {
int room = atoi(argv[1]);
// game will crash if entering a non-existent room
if (room >= _vm->_roomTableSize) {
debugPrintf("room number must be any value between (including) 0 and %d\n", _vm->_roomTableSize - 1);
return true;
}
if (argc > 2) {
direction = atoi(argv[2]);
} else {
if (_vm->_roomTable[room].northExit != 0xFFFF)
direction = 3;
else if (_vm->_roomTable[room].eastExit != 0xFFFF)
direction = 4;
else if (_vm->_roomTable[room].southExit != 0xFFFF)
direction = 1;
else if (_vm->_roomTable[room].westExit != 0xFFFF)
direction = 2;
}
_vm->_system->hideOverlay();
_vm->_currentCharacter->facing = direction;
_vm->enterNewScene(room, _vm->_currentCharacter->facing, 0, 0, 1);
while (!_vm->_screen->isMouseVisible())
_vm->_screen->showMouse();
detach();
return false;
}
debugPrintf("Syntax: room <roomnum> <direction>\n");
return true;
}
bool Debugger_LoK::cmdListScenes(int argc, const char **argv) {
for (int i = 0; i < _vm->_roomTableSize; i++) {
debugPrintf("%-3i: %-10s", i, _vm->_roomFilenameTable[_vm->_roomTable[i].nameIndex]);
if (!(i % 8))
debugPrintf("\n");
}
debugPrintf("\n");
debugPrintf("Current room: %i\n", _vm->_currentRoom);
return true;
}
bool Debugger_LoK::cmdGiveItem(int argc, const char **argv) {
if (argc == 2) {
int item = atoi(argv[1]);
// Kyrandia 1 has only 108 items (-1 to 106), otherwise it will crash
if (item < -1 || item > 106) {
debugPrintf("'itemid' must be any value between (including) -1 and 106\n");
return true;
}
_vm->setMouseItem(item);
_vm->_itemInHand = item;
} else {
debugPrintf("Syntax: give <itemid>\n");
}
return true;
}
bool Debugger_LoK::cmdListBirthstones(int argc, const char **argv) {
debugPrintf("Needed birthstone gems:\n");
for (int i = 0; i < ARRAYSIZE(_vm->_birthstoneGemTable); ++i)
debugPrintf("%-3d '%s'\n", _vm->_birthstoneGemTable[i], _vm->_itemList[_vm->_birthstoneGemTable[i]]);
return true;
}
#pragma mark -
Debugger_v2::Debugger_v2(KyraEngine_v2 *vm) : Debugger(vm), _vm(vm) {
}
void Debugger_v2::initialize() {
registerCmd("character_info", WRAP_METHOD(Debugger_v2, cmdCharacterInfo));
registerCmd("enter", WRAP_METHOD(Debugger_v2, cmdEnterScene));
registerCmd("scenes", WRAP_METHOD(Debugger_v2, cmdListScenes));
registerCmd("scene_info", WRAP_METHOD(Debugger_v2, cmdSceneInfo));
registerCmd("scene_to_facing", WRAP_METHOD(Debugger_v2, cmdSceneToFacing));
registerCmd("give", WRAP_METHOD(Debugger_v2, cmdGiveItem));
Debugger::initialize();
}
bool Debugger_v2::cmdEnterScene(int argc, const char **argv) {
uint direction = 0;
if (argc > 1) {
int scene = atoi(argv[1]);
// game will crash if entering a non-existent scene
if (scene >= _vm->_sceneListSize) {
debugPrintf("scene number must be any value between (including) 0 and %d\n", _vm->_sceneListSize - 1);
return true;
}
if (argc > 2) {
direction = atoi(argv[2]);
} else {
if (_vm->_sceneList[scene].exit1 != 0xFFFF)
direction = 4;
else if (_vm->_sceneList[scene].exit2 != 0xFFFF)
direction = 6;
else if (_vm->_sceneList[scene].exit3 != 0xFFFF)
direction = 0;
else if (_vm->_sceneList[scene].exit4 != 0xFFFF)
direction = 2;
}
_vm->_system->hideOverlay();
_vm->_mainCharacter.facing = direction;
_vm->enterNewScene(scene, _vm->_mainCharacter.facing, 0, 0, 1);
while (!_vm->screen_v2()->isMouseVisible())
_vm->screen_v2()->showMouse();
detach();
return false;
}
debugPrintf("Syntax: %s <scenenum> <direction>\n", argv[0]);
return true;
}
bool Debugger_v2::cmdListScenes(int argc, const char **argv) {
int shown = 1;
for (int i = 0; i < _vm->_sceneListSize; ++i) {
if (_vm->_sceneList[i].filename1[0]) {
debugPrintf("%-2i: %-10s", i, _vm->_sceneList[i].filename1);
if (!(shown % 5))
debugPrintf("\n");
++shown;
}
}
debugPrintf("\n");
debugPrintf("Current scene: %i\n", _vm->_currentScene);
return true;
}
bool Debugger_v2::cmdSceneInfo(int argc, const char **argv) {
debugPrintf("Current scene: %d '%s'\n", _vm->_currentScene, _vm->_sceneList[_vm->_currentScene].filename1);
debugPrintf("\n");
debugPrintf("Exit information:\n");
debugPrintf("Exit1: leads to %d, position %dx%d\n", int16(_vm->_sceneExit1), _vm->_sceneEnterX1, _vm->_sceneEnterY1);
debugPrintf("Exit2: leads to %d, position %dx%d\n", int16(_vm->_sceneExit2), _vm->_sceneEnterX2, _vm->_sceneEnterY2);
debugPrintf("Exit3: leads to %d, position %dx%d\n", int16(_vm->_sceneExit3), _vm->_sceneEnterX3, _vm->_sceneEnterY3);
debugPrintf("Exit4: leads to %d, position %dx%d\n", int16(_vm->_sceneExit4), _vm->_sceneEnterX4, _vm->_sceneEnterY4);
debugPrintf("Special exit information:\n");
if (!_vm->_specialExitCount) {
debugPrintf("No special exits.\n");
} else {
debugPrintf("This scene has %d special exits.\n", _vm->_specialExitCount);
for (int i = 0; i < _vm->_specialExitCount; ++i) {
debugPrintf("SpecialExit%d: facing %d, position (x1/y1/x2/y2): %d/%d/%d/%d\n", i,
_vm->_specialExitTable[20 + i], _vm->_specialExitTable[0 + i], _vm->_specialExitTable[5 + i],
_vm->_specialExitTable[10 + i], _vm->_specialExitTable[15 + i]);
}
}
return true;
}
bool Debugger_v2::cmdCharacterInfo(int argc, const char **argv) {
debugPrintf("Main character is in scene: %d '%s'\n", _vm->_mainCharacter.sceneId, _vm->_sceneList[_vm->_mainCharacter.sceneId].filename1);
debugPrintf("Position: %dx%d\n", _vm->_mainCharacter.x1, _vm->_mainCharacter.y1);
debugPrintf("Facing: %d\n", _vm->_mainCharacter.facing);
debugPrintf("Inventory:\n");
for (int i = 0; i < 20; ++i) {
debugPrintf("%-2d ", int8(_vm->_mainCharacter.inventory[i]));
if (i == 9 || i == 19)
debugPrintf("\n");
}
return true;
}
bool Debugger_v2::cmdSceneToFacing(int argc, const char **argv) {
if (argc == 2) {
int facing = atoi(argv[1]);
int16 exit = -1;
switch (facing) {
case 0: case 1: case 7:
exit = _vm->_sceneList[_vm->_currentScene].exit1;
break;
case 6:
exit = _vm->_sceneList[_vm->_currentScene].exit2;
break;
case 3: case 4: case 5:
exit = _vm->_sceneList[_vm->_currentScene].exit3;
break;
case 2:
exit = _vm->_sceneList[_vm->_currentScene].exit4;
break;
default:
break;
}
debugPrintf("Exit to facing %d leads to room %d.\n", facing, exit);
} else {
debugPrintf("Usage: %s <facing>\n", argv[0]);
}
return true;
}
bool Debugger_v2::cmdGiveItem(int argc, const char **argv) {
if (argc == 2) {
int item = atoi(argv[1]);
if (item < -1 || item > _vm->engineDesc().maxItemId) {
debugPrintf("itemid must be any value between (including) -1 and %d\n", _vm->engineDesc().maxItemId);
return true;
}
_vm->setHandItem(item);
} else {
debugPrintf("Syntax: give <itemid>\n");
}
return true;
}
#pragma mark -
Debugger_HoF::Debugger_HoF(KyraEngine_HoF *vm) : Debugger_v2(vm), _vm(vm) {
}
void Debugger_HoF::initialize() {
registerCmd("pass_codes", WRAP_METHOD(Debugger_HoF, cmdPasscodes));
Debugger_v2::initialize();
}
bool Debugger_HoF::cmdPasscodes(int argc, const char **argv) {
if (argc == 2) {
int val = atoi(argv[1]);
if (val < 0 || val > 1) {
debugPrintf("value must be either 1 (on) or 0 (off)\n");
return true;
}
_vm->_dbgPass = val;
} else {
debugPrintf("Syntax: pass_codes <0/1>\n");
}
return true;
}
#pragma mark -
#ifdef ENABLE_LOL
Debugger_LoL::Debugger_LoL(LoLEngine *vm) : Debugger(vm), _vm(vm) {
}
#endif // ENABLE_LOL
#ifdef ENABLE_EOB
Debugger_EoB::Debugger_EoB(EoBCoreEngine *vm) : Debugger(vm), _vm(vm) {
}
void Debugger_EoB::initialize() {
registerCmd("import_savefile", WRAP_METHOD(Debugger_EoB, cmdImportSaveFile));
registerCmd("save_original", WRAP_METHOD(Debugger_EoB, cmdSaveOriginal));
registerCmd("list_monsters", WRAP_METHOD(Debugger_EoB, cmdListMonsters));
registerCmd("show_position", WRAP_METHOD(Debugger_EoB, cmdShowPosition));
registerCmd("set_position", WRAP_METHOD(Debugger_EoB, cmdSetPosition));
registerCmd("print_map", WRAP_METHOD(Debugger_EoB, cmdPrintMap));
registerCmd("open_door", WRAP_METHOD(Debugger_EoB, cmdOpenDoor));
registerCmd("close_door", WRAP_METHOD(Debugger_EoB, cmdCloseDoor));
registerCmd("list_flags", WRAP_METHOD(Debugger_EoB, cmdListFlags));
registerCmd("set_flag", WRAP_METHOD(Debugger_EoB, cmdSetFlag));
registerCmd("clear_flag", WRAP_METHOD(Debugger_EoB, cmdClearFlag));
}
bool Debugger_EoB::cmdImportSaveFile(int argc, const char **argv) {
if (!_vm->_allowImport) {
debugPrintf("This command only works from the main menu.\n");
return true;
}
if (argc == 3) {
int slot = atoi(argv[1]);
if (slot < -1 || slot > 989) {
debugPrintf("slot must be between (including) -1 and 989 \n");
return true;
}
debugPrintf(_vm->importOriginalSaveFile(slot, Common::Path(argv[2], Common::Path::kNativeSeparator)) ? "Success.\n" : "Failure.\n");
_vm->loadItemDefs();
} else {
debugPrintf("Syntax: import_savefile <dest slot> <source file>\n (Imports source save game file to dest slot.)\n import_savefile -1\n (Imports all original save game files found and puts them into the first available slots.)\n\n");
}
return true;
}
bool Debugger_EoB::cmdSaveOriginal(int argc, const char **argv) {
if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
debugPrintf("Command not supported for this version.\n");
return true;
}
if (!_vm->_runFlag) {
debugPrintf("This command doesn't work during intro or outro sequences,\nfrom the main menu or from the character generation.\n");
return true;
}
Common::Path dir = ConfMan.getPath("savepath");
Common::FSNode nd(dir);
if (!nd.isDirectory())
return false;
if (_vm->game() == GI_EOB1) {
if (argc == 1) {
if (_vm->saveAsOriginalSaveFile()) {
Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA.SAV"));
if (nf.isReadable())
debugPrintf("Saved to file: %s\n\n", nf.getPath().toString(Common::Path::kNativeSeparator).c_str());
else
debugPrintf("Failure.\n");
} else {
debugPrintf("Failure.\n");
}
} else {
debugPrintf("Syntax: save_original\n (Saves game in original file format to a file which can be used with the original game executable.)\n\n");
}
return true;
} else if (argc == 2) {
int slot = atoi(argv[1]);
if (slot < 0 || slot > 5) {
debugPrintf("Slot must be between (including) 0 and 5.\n");
} else if (_vm->saveAsOriginalSaveFile(slot)) {
Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA%d.SAV", slot));
if (nf.isReadable())
debugPrintf("Saved to file: %s\n\n", nf.getPath().toString(Common::Path::kNativeSeparator).c_str());
else
debugPrintf("Failure.\n");
} else {
debugPrintf("Failure.\n");
}
return true;
}
debugPrintf("Syntax: save_original <slot>\n (Saves game in original file format to a file which can be used with the original game executable.\n A save slot between 0 and 5 must be specified.)\n\n");
return true;
}
bool Debugger_EoB::cmdListMonsters(int, const char **) {
debugPrintf("\nCurrent level: %d\n----------------------\n\n", _vm->_currentLevel);
debugPrintf("Id Type Unit Block Position Direction Sub Level Mode Dst.block HP Flags\n--------------------------------------------------------------------------------------------------------------\n");
for (int i = 0; i < 30; i++) {
EoBMonsterInPlay *m = &_vm->_monsters[i];
debugPrintf("%.02d %.02d %.02d 0x%.04x %d %d %d %.02d 0x%.04x %.03d/%.03d 0x%.02x\n", i, m->type, m->unit, m->block, m->pos, m->dir, m->sub, m->mode, m->dest, m->hitPointsCur, m->hitPointsMax, m->flags);
}
debugPrintf("\n");
return true;
}
bool Debugger_EoB::cmdShowPosition(int, const char **) {
debugPrintf("\nCurrent level: %d\nCurrent Sub Level: %d\nCurrent block: %d (0x%.04x)\nNext block: %d (0x%.04x)\nCurrent direction: %d\n\n", _vm->_currentLevel, _vm->_currentSub, _vm->_currentBlock, _vm->_currentBlock, _vm->calcNewBlockPosition(_vm->_currentBlock, _vm->_currentDirection), _vm->calcNewBlockPosition(_vm->_currentBlock, _vm->_currentDirection), _vm->_currentDirection);
return true;
}
bool Debugger_EoB::cmdSetPosition(int argc, const char **argv) {
if (argc == 4) {
_vm->_currentBlock = atoi(argv[3]);
int sub = atoi(argv[2]);
int level = atoi(argv[1]);
int maxLevel = (_vm->game() == GI_EOB1) ? 12 : 16;
if (level < 1 || level > maxLevel) {
debugPrintf("<level> must be a value from 1 to %d.\n\n", maxLevel);
return true;
}
if (level != _vm->_currentLevel || sub != _vm->_currentSub) {
_vm->completeDoorOperations();
_vm->generateTempData();
_vm->txt()->removePageBreakFlag();
_vm->screen()->setScreenDim(7);
_vm->loadLevel(level, sub);
if (_vm->_dialogueField)
_vm->restoreAfterDialogueSequence();
}
_vm->moveParty(_vm->_currentBlock);
_vm->_sceneUpdateRequired = true;
_vm->gui_drawAllCharPortraitsWithStats();
debugPrintf("Success.\n\n");
} else {
debugPrintf("Syntax: set_position <level>, <sub level>, <block>\n");
debugPrintf(" (Warning: The sub level and block position parameters will not be checked. Invalid parameters may cause problems.)\n\n");
}
return true;
}
bool Debugger_EoB::cmdPrintMap(int, const char **) {
const uint8 illusion1 = _vm->gameFlags().gameID == GI_EOB1 ? 67 : 46;
const uint8 illusion2 = _vm->gameFlags().gameID == GI_EOB1 ? 64 : 46;
const uint8 plate1 = _vm->gameFlags().gameID == GI_EOB1 ? 28 : 35;
const uint8 plate2 = _vm->gameFlags().gameID == GI_EOB1 ? 28 : 36;
const uint8 hole = _vm->gameFlags().gameID == GI_EOB1 ? 27 : 38;
const uint8 stairsUp = 23;
const uint8 stairsDown = 24;
const uint8 types[] = { _vm->_teleporterWallId, illusion1, illusion2, stairsUp, stairsDown, hole, plate1, plate2 };
const uint8 signs[] = { 'T', 'i', 'i', 'U', 'D', 215, 'O', 'O', 'k' };
for (int i = 0; i < 1024; ++i) {
if (!(i % 0x20))
debugPrintf("\n");
LevelBlockProperty *bl = &_vm->_levelBlockProperties[i];
uint8 f = _vm->_wllWallFlags[bl->walls[0]] | _vm->_wllWallFlags[bl->walls[1]] | _vm->_wllWallFlags[bl->walls[2]] | _vm->_wllWallFlags[bl->walls[3]];
uint8 s = _vm->_specialWallTypes[bl->walls[0]] | _vm->_specialWallTypes[bl->walls[1]] | _vm->_specialWallTypes[bl->walls[2]] | _vm->_specialWallTypes[bl->walls[3]];
uint8 c = ' ';
if (s == 3 || s == 4)
c = '/';
else if (s == 2 || s == 8)
c = 176;
else if (f & 8)
c = 216;
else if (f & 1)
c = 35;
bool key = false;
for (int t = bl->drawObjects; t; ) {
const EoBItem *itm = &_vm->_items[t];
if (itm->type == 38)
key = true;
t = (itm->next != bl->drawObjects) ? itm->next : 0;
}
if (_vm->_currentBlock == i) {
c = 'X';
} else if (key) {
c = signs[8];
} else {
for (int ii = 0; ii < ARRAYSIZE(types); ++ii) {
if (bl->walls[0] == types[ii] || bl->walls[1] == types[ii] || bl->walls[2] == types[ii] || bl->walls[3] == types[ii]) {
c = signs[ii];
break;
}
}
}
debugPrintf("%c", c);
}
debugPrintf("\n\nParty Position: %c Door: %c Stairs Up/Down: %c/%c Plate: %c Hole: %c\nSwitch: %c Clickable Object: %c Illusion Wall: %c Teleporter: %c Key: %c\n\n", 'X', 216, signs[3], signs[4], signs[6], signs[5], '/', 176, signs[1], signs[0], signs[8]);
return true;
}
bool Debugger_EoB::cmdOpenDoor(int, const char **) {
uint16 block = _vm->calcNewBlockPosition(_vm->_currentBlock, _vm->_currentDirection);
uint8 v = _vm->_wllWallFlags[_vm->_levelBlockProperties[block].walls[0]] | _vm->_wllWallFlags[_vm->_levelBlockProperties[block].walls[1]];
int flg = (_vm->_flags.gameID == GI_EOB1) ? 1 : 0x10;
if (!(v & 8)) {
debugPrintf("Couldn't open any door. Make sure you're facing the door you wish to open and standing right in front of it.\n\n");
} else if (v & flg) {
debugPrintf("The door seems to be already open.\n\n");
} else {
_vm->openDoor(block);
debugPrintf("Trying to open door at block %d.\n\n", block);
}
return true;
}
bool Debugger_EoB::cmdCloseDoor(int, const char **) {
uint16 block = _vm->calcNewBlockPosition(_vm->_currentBlock, _vm->_currentDirection);
uint8 v = _vm->_wllWallFlags[_vm->_levelBlockProperties[block].walls[0]] | _vm->_wllWallFlags[_vm->_levelBlockProperties[block].walls[1]];
if (!(v & 8)) {
debugPrintf("Couldn't close any door. Make sure you're facing the door you wish to close and standing right in front of it.\n\n");
} else if ((_vm->_flags.gameID == GI_EOB1 && !(v & 1)) || (_vm->_flags.gameID == GI_EOB2 && (v & 0x20))) {
debugPrintf("The door seems to be already closed.\n\n");
} else {
_vm->closeDoor(block);
debugPrintf("Trying to close door at block %d.\n\n", block);
}
return true;
}
bool Debugger_EoB::cmdListFlags(int, const char **) {
debugPrintf("Flag Status\n----------------------\n\n");
for (int i = 0; i < 32; i++) {
uint32 flag = 1 << i;
debugPrintf("%.2d %s\n", i, _vm->checkScriptFlags(flag) ? "TRUE" : "FALSE");
}
debugPrintf("\n");
return true;
}
bool Debugger_EoB::cmdSetFlag(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Syntax: set_flag <flag>\n\n");
return true;
}
int flag = atoi(argv[1]);
if (flag < 0 || flag > 31) {
debugPrintf("<flag> must be a value from 0 to 31.\n\n");
} else {
_vm->setScriptFlags(1 << flag);
debugPrintf("Flag '%.2d' has been set.\n\n", flag);
}
return true;
}
bool Debugger_EoB::cmdClearFlag(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Syntax: clear_flag <flag>\n\n");
return true;
}
int flag = atoi(argv[1]);
if (flag < 0 || flag > 31) {
debugPrintf("<flag> must be a value from 0 to 31.\n\n");
} else {
_vm->clearScriptFlags(1 << flag);
debugPrintf("Flag '%.2d' has been cleared.\n\n", flag);
}
return true;
}
#endif // ENABLE_EOB
} // End of namespace Kyra

135
engines/kyra/gui/debugger.h Normal file
View File

@@ -0,0 +1,135 @@
/* 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 KYRA_DEBUGGER_H
#define KYRA_DEBUGGER_H
#include "gui/debugger.h"
namespace Kyra {
class KyraEngine_v1;
class KyraEngine_LoK;
class KyraEngine_v2;
class KyraEngine_HoF;
class Debugger : public ::GUI::Debugger {
public:
Debugger(KyraEngine_v1 *vm);
virtual void initialize();
protected:
KyraEngine_v1 *_vm;
bool cmdSetScreenDebug(int argc, const char **argv);
bool cmdLoadPalette(int argc, const char **argv);
bool cmdShowFacings(int argc, const char **argv);
bool cmdGameSpeed(int argc, const char **argv);
bool cmdListFlags(int argc, const char **argv);
bool cmdToggleFlag(int argc, const char **argv);
bool cmdQueryFlag(int argc, const char **argv);
bool cmdListTimers(int argc, const char **argv);
bool cmdSetTimerCountdown(int argc, const char **argv);
};
class Debugger_LoK : public Debugger {
public:
Debugger_LoK(KyraEngine_LoK *vm);
void initialize() override;
protected:
KyraEngine_LoK *_vm;
bool cmdEnterRoom(int argc, const char **argv);
bool cmdListScenes(int argc, const char **argv);
bool cmdGiveItem(int argc, const char **argv);
bool cmdListBirthstones(int argc, const char **argv);
};
class Debugger_v2 : public Debugger {
public:
Debugger_v2(KyraEngine_v2 *vm);
~Debugger_v2() override {}
void initialize() override;
protected:
KyraEngine_v2 *_vm;
bool cmdEnterScene(int argc, const char **argv);
bool cmdListScenes(int argc, const char **argv);
bool cmdSceneInfo(int argc, const char **argv);
bool cmdCharacterInfo(int argc, const char **argv);
bool cmdSceneToFacing(int argc, const char **argv);
bool cmdGiveItem(int argc, const char **argv);
};
class Debugger_HoF : public Debugger_v2 {
public:
Debugger_HoF(KyraEngine_HoF *vm);
void initialize() override;
protected:
KyraEngine_HoF *_vm;
bool cmdPasscodes(int argc, const char **argv);
};
#ifdef ENABLE_LOL
class LoLEngine;
class Debugger_LoL : public Debugger {
public:
Debugger_LoL(LoLEngine *vm);
protected:
LoLEngine *_vm;
};
#endif // ENABLE_LOL
#ifdef ENABLE_EOB
class EoBCoreEngine;
class Debugger_EoB : public Debugger {
public:
Debugger_EoB(EoBCoreEngine *vm);
void initialize() override;
protected:
EoBCoreEngine *_vm;
bool cmdImportSaveFile(int argc, const char **argv);
bool cmdSaveOriginal(int argc, const char **argv);
bool cmdListMonsters(int argc, const char **argv);
bool cmdShowPosition(int argc, const char **argv);
bool cmdSetPosition(int argc, const char **argv);
bool cmdPrintMap(int argc, const char **argv);
bool cmdOpenDoor(int argc, const char **argv);
bool cmdCloseDoor(int argc, const char **argv);
bool cmdListFlags(int argc, const char **argv);
bool cmdSetFlag(int argc, const char **argv);
bool cmdClearFlag(int argc, const char **argv);
};
#endif // ENABLE_EOB
} // End of namespace Kyra
#endif

137
engines/kyra/gui/gui.cpp Normal file
View File

@@ -0,0 +1,137 @@
/* 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 "kyra/gui/gui.h"
#include "kyra/kyra_v1.h"
#include "kyra/engine/util.h"
#include "common/savefile.h"
#include "common/system.h"
namespace Kyra {
GUI::GUI(KyraEngine_v1 *kyra) : _vm(kyra), _screen(kyra ? kyra->screen() : nullptr) {
_saveSlotsListUpdateNeeded = true;
_savegameListSize = 0;
_savegameList = nullptr;
}
GUI::~GUI() {
if (_savegameList) {
for (int i = 0; i < _savegameListSize; i++)
delete[] _savegameList[i];
delete[] _savegameList;
_savegameList = nullptr;
}
}
void GUI::updateSaveFileList(Common::String targetName, bool excludeQuickSaves) {
Common::String pattern = targetName + ".###";
Common::StringArray saveFileList = _vm->_saveFileMan->listSavefiles(pattern);
_saveSlots.clear();
for (Common::StringArray::const_iterator i = saveFileList.begin(); i != saveFileList.end(); ++i) {
// The last 3 digits of the filename correspond to the save slot.
const int slotNum = atoi(i->c_str() + i->size() - 3);
if (excludeQuickSaves && slotNum >= 990)
continue;
_saveSlots.push_back(slotNum);
}
if (_saveSlots.begin() == _saveSlots.end())
return;
sortSaveSlots();
}
void GUI::sortSaveSlots() {
Common::sort(_saveSlots.begin(), _saveSlots.end(), Common::Less<int>());
if (_saveSlots.size() > 2)
Common::sort(_saveSlots.begin() + 1, _saveSlots.end(), Common::Greater<int>());
}
int GUI::getNextSavegameSlot() {
Common::InSaveFile *in;
int start = _vm->game() == GI_LOL ? 0 : 1;
for (int i = start; i < 990; i++) {
if ((in = _vm->_saveFileMan->openForLoading(_vm->getSavegameFilename(i))))
delete in;
else
return i;
}
warning("Didn't save: Ran out of saveGame filenames");
return 0;
}
void GUI::updateSaveSlotsList(Common::String targetName, bool force) {
if (!_saveSlotsListUpdateNeeded && !force)
return;
_saveSlotsListUpdateNeeded = false;
if (_savegameList) {
for (int i = 0; i < _savegameListSize; i++)
delete[] _savegameList[i];
delete[] _savegameList;
}
updateSaveFileList(targetName, true);
int numSaves = _savegameListSize = _saveSlots.size();
bool allowEmptySlots = (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2);
if (_savegameListSize) {
if (allowEmptySlots)
_savegameListSize = 990;
KyraEngine_v1::SaveHeader header;
Common::InSaveFile *in;
_savegameList = new char*[_savegameListSize]();
for (int i = 0; i < numSaves; i++) {
in = _vm->openSaveForReading(_vm->getSavegameFilename(targetName, _saveSlots[i]).c_str(), header, targetName == _vm->_targetName);
char **listEntry = &_savegameList[allowEmptySlots ? _saveSlots[i] : i];
if (in) {
uint buffSize = header.description.size() + 1;
*listEntry = new char[buffSize];
Common::strlcpy(*listEntry, header.description.c_str(), buffSize);
// Ingame auto-generated Japanese EOB SegaCD savegame descriptions have a special 1-byte encoding that
// does not survive this conversion. And the rest of the characters in these descriptions do not require it.
if (!(_vm->gameFlags().platform == Common::kPlatformSegaCD && _vm->gameFlags().lang == Common::JA_JPN && Common::String(*listEntry).contains('\r')))
Util::convertString_GUItoKYRA(*listEntry, buffSize,
_vm->gameFlags().lang == Common::ZH_TWN ? Common::CodePage::kBig5 : Common::CodePage::kDos850);
delete in;
} else {
*listEntry = nullptr;
error("GUI::updateSavegameList(): Unexpected missing save file for slot: %d.", _saveSlots[i]);
}
}
} else {
_savegameList = nullptr;
}
}
} // End of namespace Kyra

135
engines/kyra/gui/gui.h Normal file
View File

@@ -0,0 +1,135 @@
/* 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 KYRA_GUI_H
#define KYRA_GUI_H
#include "kyra/kyra_v1.h"
#include "kyra/graphics/screen.h"
#include "common/ptr.h"
#include "common/array.h"
#include "common/func.h"
#include "graphics/surface.h"
namespace Kyra {
#define BUTTON_FUNCTOR(type, x, y) Button::Callback(new Common::Functor1Mem<Button *, int, type>(x, y))
struct Button {
typedef Common::Functor1<Button *, int> CallbackFunctor;
typedef Common::SharedPtr<CallbackFunctor> Callback;
Button() : nextButton(0), index(0), keyCode(0), keyCode2(0), data0Val1(0), data1Val1(0), data2Val1(0), data3Val1(0), flags(0),
data0ShapePtr(0), data1ShapePtr(0), data2ShapePtr(0), data0Callback(), data1Callback(), data2Callback(),
dimTableIndex(0), x(0), y(0), width(0), height(0), data0Val2(0), data0Val3(0), data1Val2(0), data1Val3(0),
data2Val2(0), data2Val3(0), data3Val2(0), data3Val3(0), flags2(0), mouseWheel(0), buttonCallback(), extButtonDef(0), arg(0) {}
Button *nextButton;
uint16 index;
uint16 keyCode;
uint16 keyCode2;
byte data0Val1;
byte data1Val1;
byte data2Val1;
byte data3Val1;
uint16 flags;
const uint8 *data0ShapePtr;
const uint8 *data1ShapePtr;
const uint8 *data2ShapePtr;
Callback data0Callback;
Callback data1Callback;
Callback data2Callback;
uint16 dimTableIndex;
int16 x, y;
uint16 width, height;
uint8 data0Val2;
uint8 data0Val3;
uint8 data1Val2;
uint8 data1Val3;
uint8 data2Val2;
uint8 data2Val3;
uint8 data3Val2;
uint8 data3Val3;
uint16 flags2;
int8 mouseWheel;
Callback buttonCallback;
const void *extButtonDef;
uint16 arg;
};
class Screen;
class TextDisplayer;
class GUI {
public:
GUI(KyraEngine_v1 *vm);
virtual ~GUI();
// button specific
virtual void processButton(Button *button) = 0;
virtual int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) = 0;
// utilities for thumbnail creation
virtual void createScreenThumbnail(Graphics::Surface &dst) = 0;
void notifyUpdateSaveSlotsList() { _saveSlotsListUpdateNeeded = true; }
protected:
KyraEngine_v1 *_vm;
Screen *_screen;
// The engine expects a list of contiguous savegame indices.
// 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(Common::String targetName, bool excludeQuickSaves = false);
int getNextSavegameSlot();
void updateSaveSlotsList(Common::String targetName, bool force = false);
virtual void sortSaveSlots();
char **_savegameList;
int _savegameListSize;
bool _saveSlotsListUpdateNeeded;
Common::KeyState _keyPressed;
};
} // End of namespace Kyra
#endif

4976
engines/kyra/gui/gui_eob.cpp Normal file

File diff suppressed because it is too large Load Diff

208
engines/kyra/gui/gui_eob.h Normal file
View File

@@ -0,0 +1,208 @@
/* 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/>.
*
*/
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
#ifndef KYRA_GUI_EOB_H
#define KYRA_GUI_EOB_H
#include "kyra/gui/gui.h"
#ifdef ENABLE_EOB
namespace Kyra {
struct EoBRect16 {
int16 x1;
int16 y1;
uint16 x2;
uint16 y2;
};
class DarkMoonEngine;
class Screen_EoB;
class GUI_EoB : public GUI {
friend class EoBCoreEngine;
friend class CharacterGenerator;
public:
GUI_EoB(EoBCoreEngine *vm);
~GUI_EoB() override;
void initStaticData() {}
// button specific
void processButton(Button *button) override;
int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) override;
// Non button based menu handling (main menu, character generation)
void simpleMenu_setup(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int itemOffset, int lineSpacing, int textColor, int highlightColor, int shadowColor);
int simpleMenu_process(int sd, const char *const *strings, void *b, int32 menuItemsMask, int itemOffset);
void simpleMenu_unselect(int sd, const char *const *strings, void *b, int32 menuItemsMask, int itemOffset);
// Button based menus (camp menu, load menu)
virtual void runCampMenu();
virtual bool runLoadMenu(int x, int y, bool fromMainMenu = false);
bool confirmDialogue2(int dim, int id, int deflt);
void messageDialog(int dim, int id, int buttonTextCol);
void messageDialog2(int dim, int id, int buttonTextCol);
void updateBoxFrameHighLight(int box);
int getTextInput(char *dest, int x, int y, int destMaxLen, int textColor1, int textColor2, int cursorColor);
// Transfer party
void transferWaitBox();
Common::String transferTargetMenu(Common::Array<Common::String> &targets);
bool transferFileMenu(Common::String &targetName, Common::String &selection);
// utilities for thumbnail creation
void createScreenThumbnail(Graphics::Surface &dst) override;
protected:
const char *getMenuString(int id);
Button *initMenu(int id);
void releaseButtons(Button *list);
virtual int mapPointToEntry(const Common::Point &p) const;
int8 *_numAssignedSpellsOfType;
char** _saveSlotStringsTemp;
int16 _saveSlotX;
int16 _saveSlotY;
int _menuCur;
int _clickableCharactersPage;
char _csjis[3];
Screen_EoB *_screen;
private:
int simpleMenu_getMenuItem(int index, int32 menuItemsMask, int itemOffset);
void simpleMenu_flashSelection(const char *str, int x, int y, int color1, int color2, int color3);
void simpleMenu_initMenuItemsMask(int menuId, int maxItem, int32 menuItemsMask, int unk);
void simpleMenu_printButton(int sd, int num, const char *title, bool isHighlight, bool isInitial);
Common::Point simpleMenu_getTextPoint(int num, int *col = nullptr);
int simpleMenu_getMouseItem(int sd);
bool runSaveMenu(int x, int y);
int selectSaveSlotDialog(int x, int y, int id);
virtual void drawSaveSlotDialog(int x, int y, int id);
void runMemorizePrayMenu(int charIndex, int spellType);
void scribeScrollDialogue();
bool restParty();
virtual void drawCampMenu() {}
virtual void initMemorizePrayMenu(int) {}
virtual void initScribeScrollMenu() {}
virtual void printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight);
virtual bool confirmDialogue(int id);
int selectCharacterDialogue(int id);
virtual void displayTextBox(int id, int textColor = 0xFF, bool wait = true);
virtual void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill);
void drawMenuButtonBox(int x, int y, int w, int h, bool clicked, bool noFill);
void drawTextBox(int dim, int id);
virtual void drawSaveSlotButton(int slot, int redrawBox, bool highlight);
virtual void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight);
virtual void updateOptionsStrings();
Button *linkButton(Button *list, Button *newbt);
void setupSaveMenuSlots();
virtual int getHighlightSlot();
void sortSaveSlots() override;
virtual void restParty_updateRestTime(int hours, bool init);
char **_menuStringsPrefsTemp;
int16 *_saveSlotIdTemp;
int _savegameOffset;
const int _numSlotsVisible;
EoBCoreEngine *_vm;
Button *_specialProcessButton;
Button *_backupButtonList;
uint16 _flagsMouseLeft;
uint16 _flagsMouseRight;
uint16 _flagsModifier;
uint16 _progress;
uint16 _prcButtonUnk3;
uint16 _cflag;
int _menuLineSpacing;
int _menuLastInFlags;
int _menuTextColor;
int _menuHighlightColor;
int _menuShadowColor;
int _menuLines[2];
int _menuColumnWidth[2];
int _menuColumnOffset[2];
bool _menuOverflow[20];
int _menuColumns;
Common::Point _menuPoint;
uint8 _numPages;
uint8 _numVisPages;
uint32 _clericSpellAvltyFlags;
uint32 _paladinSpellAvltyFlags;
bool _needRest;
int _menuNumItems;
bool _charSelectRedraw;
int _updateBoxIndex;
int _updateBoxColorIndex;
const uint8 *_highLightColorTable;
uint32 _highLightBoxTimer;
const bool _textInputForceUppercase;
const int _textInputHeight;
const int _textInputShadowOffset;
const Screen::FontId _menuFont;
const Screen::FontId _menuFont2;
const int _dlgButtonHeight1;
const int _dlgButtonHeight2;
const int _dlgButtonLabelYOffs;
const EoBRect16 *_highlightFrames;
static const EoBRect16 _highlightFramesDefault[20];
static const EoBRect16 _highlightFramesTransferZH[6];
static const uint8 _highlightColorTableVGA[];
static const uint8 _highlightColorTableEGA[];
static const uint8 _highlightColorTableAmiga[];
static const uint8 _highlightColorTablePC98[];
static const uint8 _highlightColorTableSegaCD[];
// FM-Towns / SegaCD specific
virtual uint16 checkClickableCharactersSelection();
virtual void printClickableCharacters(int page);
};
} // End of namespace Kyra
#endif // ENABLE_EOB
#endif
#endif // ENABLE_EOB || ENABLE_LOL

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
/* 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/>.
*
*/
#ifdef ENABLE_EOB
#ifndef KYRA_GUI_EOB_SEGACD_H
#define KYRA_GUI_EOB_SEGACD_H
#include "kyra/gui/gui_eob.h"
#ifdef ENABLE_EOB
namespace Kyra {
class GUI_EoB_SegaCD : public GUI_EoB {
public:
GUI_EoB_SegaCD(EoBEngine *vm);
~GUI_EoB_SegaCD() override;
protected:
int mapPointToEntry(const Common::Point &p) const override;
private:
void drawCampMenu() override;
void initMemorizePrayMenu(int spellType) override;
void initScribeScrollMenu() override;
void printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight) override;
void drawSaveSlotDialog(int x, int y, int id) override;
bool confirmDialogue(int id) override;
void displayTextBox(int id, int textColor, bool wait) override;
void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill) override;
void drawSaveSlotButton(int slot, int redrawBox, bool highlight) override;
int getHighlightSlot() override;
void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight) override;
void updateOptionsStrings() override;
void restParty_updateRestTime(int hours, bool init) override;
uint16 checkClickableCharactersSelection() override;
void printClickableCharacters(int page) override;
void printClickableCharacter(int id, int col);
char fetchClickableCharacter(int id) const;
const int _clickableCharactersNumPages;
const uint8 *_campMenu;
Button* _saveLoadCancelButton;
EoBEngine *_vm;
struct MenuButtonTiles {
uint16 nameTbl;
uint16 srcOffs;
};
static const MenuButtonTiles _menuButtonTiles[40];
};
} // End of namespace Kyra
#endif // ENABLE_EOB
#endif
#endif // ENABLE_EOB || ENABLE_LOL

1202
engines/kyra/gui/gui_hof.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
/* 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 KYRA_GUI_HOF_H
#define KYRA_GUI_HOF_H
#include "kyra/gui/gui_v2.h"
namespace Kyra {
class KyraEngine_HoF;
class Screen_HoF;
class GUI_HoF : public GUI_v2 {
friend class KyraEngine_HoF;
public:
GUI_HoF(KyraEngine_HoF *engine);
void initStaticData() override;
int optionsButton(Button *button);
void createScreenThumbnail(Graphics::Surface &dst) override;
private:
Common::String getMenuTitle(const Menu &menu) override;
Common::String getMenuItemTitle(const MenuItem &menuItem) override;
Common::String getMenuItemLabel(const MenuItem &menuItem) override;
uint8 defaultColor1() const override { return 0xCF; }
uint8 defaultColor2() const override { return 0xF8; }
uint8 menuItemLabelColor() const override { return 0xFC; }
uint8 textFieldColor1() const override { return 0xFD; }
uint8 textFieldColor2() const override { return 0xFA; }
uint8 textFieldColor3() const override { return 0xFE; }
void setupPalette() override;
void restorePalette() override;
void resetState(int item);
Common::String getTableString(int id, bool decode) override;
KyraEngine_HoF *_vm;
Screen_HoF *_screen;
int quitGame(Button *caller);
int loadMenu(Button *caller);
int audioOptions(Button *caller);
int gameOptions(Button *caller);
int gameOptionsTalkie(Button *caller);
int changeLanguage(Button *caller);
void setupOptionsButtons() override;
int sliderHandler(Button *caller) override;
void drawSliderBar(int slider, const uint8 *shape);
static const uint16 _menuStringsTalkie[];
static const uint16 _menuStringsOther[];
static const char *const _saveLoadStringsZH[2];
};
} // End of namespace Kyra
#endif

1225
engines/kyra/gui/gui_lok.cpp Normal file

File diff suppressed because it is too large Load Diff

194
engines/kyra/gui/gui_lok.h Normal file
View File

@@ -0,0 +1,194 @@
/* 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 KYRA_GUI_LOK_H
#define KYRA_GUI_LOK_H
#include "kyra/gui/gui_v1.h"
#include "kyra/graphics/screen_lok.h"
namespace Kyra {
#define GUI_V1_BUTTON(button, a, b, c, d, e, f, g, h, i, j, k) \
do { \
button.nextButton = 0; \
button.index = a; \
button.keyCode = button.keyCode2 = 0; \
button.data0Val1 = b; \
button.data1Val1 = c; \
button.data2Val1 = d; \
button.data0ShapePtr = button.data1ShapePtr = button.data2ShapePtr = 0; \
button.flags = e; \
button.dimTableIndex = f; \
button.x = g; \
button.y = h; \
button.width = i; \
button.height = j; \
button.flags2 = k; \
button.mouseWheel = 0; \
button.arg = 0; \
} while (0)
#define GUI_V1_MENU(menu, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) \
do { \
menu.x = a; \
menu.y = b; \
menu.width = c; \
menu.height = d; \
menu.bkgdColor = e; \
menu.color1 = f; \
menu.color2 = g; \
menu.menuNameString = h; \
menu.textColor = i; \
menu.titleX = j; \
menu.titleY = k; \
menu.highlightedItem = l; \
menu.numberOfItems = m; \
menu.scrollUpButtonX = n; \
menu.scrollUpButtonY = o; \
menu.scrollDownButtonX = p; \
menu.scrollDownButtonY = q; \
} while (0)
#define GUI_V1_MENU_ITEM(item, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) \
do { \
item.enabled = a; \
item.itemString = ""; \
item.x = e; \
item.y = g; \
item.width = h; \
item.height = i; \
item.textColor = j; \
item.highlightColor = k; \
item.titleX = l; \
item.bkgdColor = n; \
item.color1 = o; \
item.color2 = p; \
item.saveSlot = q; \
item.labelString = r; \
item.labelX = s; \
item.labelY = t; \
item.keyCode = v; \
} while (0)
class KyraEngine_LoK;
class GUI_LoK : public GUI_v1 {
friend class KyraEngine_LoK;
public:
GUI_LoK(KyraEngine_LoK *vm, Screen_LoK *screen);
~GUI_LoK() override;
void processButton(Button *button) override;
int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) override;
int buttonMenuCallback(Button *caller);
void createScreenThumbnail(Graphics::Surface &dst) override;
private:
void initStaticResource();
Button _menuButtonData[6];
Button _scrollUpButton;
Button _scrollDownButton;
Button *getButtonListData() override { return _menuButtonData; }
Button *getScrollUpButton() override { return &_scrollUpButton; }
Button *getScrollDownButton() override { return &_scrollDownButton; }
Menu *_menu;
bool _pressFlag;
void setGUILabels();
void setupSavegames(Menu &menu, int num);
int resumeGame(Button *button);
int loadGameMenu(Button *button);
int saveGameMenu(Button *button);
int gameControlsMenu(Button *button);
int quitPlaying(Button *button);
int quitConfirmYes(Button *button);
int quitConfirmNo(Button *button);
int loadGame(Button *button);
int saveGame(Button *button);
int savegameConfirm(Button *button);
int cancelSubMenu(Button *button);
int scrollUp(Button *button);
int scrollDown(Button *button);
int controlsChangeMusic(Button *button);
int controlsChangeSounds(Button *button);
int controlsChangeWalk(Button *button);
int controlsChangeText(Button *button);
int controlsChangeVoice(Button *button);
int controlsApply(Button *button);
bool quitConfirm(const char *str);
void getInput();
void updateSavegameString();
void redrawTextfield();
void fadePalette();
void restorePalette();
void setupControls(Menu &menu);
uint8 defaultColor1() const override { return 12; }
uint8 defaultColor2() const override { return 248; }
uint8 menuItemLabelColor() const override { return 253; }
Common::String getMenuTitle(const Menu &menu) override { return menu.menuNameString; }
Common::String getMenuItemTitle(const MenuItem &menuItem) override { return menuItem.itemString; }
Common::String getMenuItemLabel(const MenuItem &menuItem) override { return menuItem.labelString; }
KyraEngine_LoK *_vm;
Screen_LoK *_screen;
bool _menuRestoreScreen;
uint8 _toplevelMenu;
int _savegameOffset;
char _savegameName[35 * 4]; // allow extra space, since the string can be UTF-8, temporarily
char _savegameNames[5][35 * 4];
const char *_specialSavegameString;
bool _resetHanInput;
int _inputType;
// The purpose of these variables is improved handling of backspace character deletion for
// Hangul input. The original allows "deconstruction" of the last glyph, so why shouldn't we...
uint8 _inputState;
uint16 _backupChars[4];
int _saveLoadNumSlots;
Button::Callback _scrollUpFunctor;
Button::Callback _scrollDownFunctor;
Button::Callback getScrollUpButtonHandler() const override { return _scrollUpFunctor; }
Button::Callback getScrollDownButtonHandler() const override { return _scrollDownFunctor; }
const char *_voiceTextString;
const char *_textSpeedString;
const char *_onString;
const char *_offString;
const char *_confMusicMenuStrings[3];
uint8 _confMusicMenuMod;
};
} // End of namespace Kyra
#endif

2949
engines/kyra/gui/gui_lol.cpp Normal file

File diff suppressed because it is too large Load Diff

180
engines/kyra/gui/gui_lol.h Normal file
View File

@@ -0,0 +1,180 @@
/* 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/>.
*
*/
#ifdef ENABLE_LOL
#ifndef KYRA_GUI_LOL_H
#define KYRA_GUI_LOL_H
#include "kyra/gui/gui_v1.h"
namespace Kyra {
#define GUI_LOL_MENU(menu, a, b, c, d, e, f, g, i) \
do { \
const ScreenDim *dim = _screen->getScreenDim(a); \
menu.x = (dim->sx << 3); \
menu.y = (dim->sy); \
menu.width = (dim->w << 3); \
menu.height = (dim->h); \
if (_vm->gameFlags().use16ColorMode) { \
menu.bkgdColor = 0xCC; \
menu.color1 = 0xFF; \
menu.color2 = 0xDD; \
} else { \
menu.bkgdColor = 225; \
menu.color1 = 223; \
menu.color2 = 227; \
} \
menu.menuNameId = b; \
menu.highlightedItem = c; \
menu.numberOfItems = d; \
menu.titleX = (dim->sx << 3) + (dim->w << 2); \
menu.titleY = 6; \
menu.textColor = _vm->gameFlags().use16ColorMode ? 0xE1 : 254; \
menu.scrollUpButtonX = e; \
menu.scrollUpButtonY = f; \
menu.scrollDownButtonX = g; \
menu.scrollDownButtonY = i; \
} while (0)
#define GUI_LOL_MENU_ITEM(item, a, b, c, d, e, f, g) \
do { \
item.enabled = 1; \
item.itemId = a; \
item.itemString = ""; \
item.useItemString = false; \
item.x = b; \
item.y = c; \
item.width = d; \
item.height = e; \
item.textColor = _vm->gameFlags().use16ColorMode ? 0xC1 : 204; \
item.highlightColor = _vm->gameFlags().use16ColorMode ? 0xE1 : 254; \
item.titleX = -1; \
if (_vm->gameFlags().use16ColorMode) { \
item.bkgdColor = 0xCC; \
item.color1 = 0xFF; \
item.color2 = 0xDD; \
} else { \
item.bkgdColor = 225; \
item.color1 = 223; \
item.color2 = 227; \
} \
item.saveSlot = 0; \
item.labelId = f; \
item.labelString = 0; \
item.labelX = 0; \
item.labelY = 0; \
item.keyCode = g; \
} while (0)
class LoLEngine;
class Screen_LoL;
class GUI_LoL : public GUI_v1 {
friend class LoLEngine;
public:
GUI_LoL(LoLEngine *vm);
void initStaticData();
// button specific
void processButton(Button *button) override;
int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) override;
int redrawShadedButtonCallback(Button *button) override;
int redrawButtonCallback(Button *button) override;
int runMenu(Menu &menu);
// utilities for thumbnail creation
void createScreenThumbnail(Graphics::Surface &dst) override;
private:
void backupPage0();
void restorePage0();
void setupSaveMenuSlots(Menu &menu, int num);
void printMenuText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 flags) override;
int getMenuCenterStringX(const Common::String &str, int x1, int x2) override;
int getInput();
int clickedMainMenu(Button *button);
int clickedLoadMenu(Button *button);
int clickedSaveMenu(Button *button);
int clickedDeleteMenu(Button *button);
int clickedOptionsMenu(Button *button);
int clickedAudioMenu(Button *button);
int clickedDeathMenu(Button *button);
int clickedSavenameMenu(Button *button);
int clickedChoiceMenu(Button *button);
int scrollUp(Button *button);
int scrollDown(Button *button);
Button *getButtonListData() override { return _menuButtons; }
Button *getScrollUpButton() override { return &_scrollUpButton; }
Button *getScrollDownButton() override { return &_scrollDownButton; }
Button::Callback getScrollUpButtonHandler() const override { return _scrollUpFunctor; }
Button::Callback getScrollDownButtonHandler() const override { return _scrollDownFunctor; }
uint8 defaultColor1() const override { return 0xFE; }
uint8 defaultColor2() const override { return 0x00; }
uint8 menuItemLabelColor() const override { return 0xCC; }
Common::String getMenuTitle(const Menu &menu) override;
Common::String getMenuItemTitle(const MenuItem &menuItem) override;
Common::String getMenuItemLabel(const MenuItem &menuItem) override;
Button _menuButtons[10];
Button _scrollUpButton;
Button _scrollDownButton;
Menu _mainMenu, _gameOptions, _audioOptions, _choiceMenu, _loadMenu, _saveMenu, _deleteMenu, _savenameMenu, _deathMenu;
Menu *_currentMenu, *_lastMenu, *_newMenu;
int _menuResult;
char *_saveDescription;
LoLEngine *_vm;
Screen_LoL *_screen;
bool _pressFlag;
Button *_specialProcessButton;
Button *_backUpButtonList;
uint16 _flagsModifier;
int _savegameOffset;
int _sliderSfx;
Button::Callback _scrollUpFunctor;
Button::Callback _scrollDownFunctor;
void sortSaveSlots() override;
};
} // End of namespace Kyra
#endif
#endif // ENABLE_LOL

1701
engines/kyra/gui/gui_mr.cpp Normal file

File diff suppressed because it is too large Load Diff

89
engines/kyra/gui/gui_mr.h Normal file
View File

@@ -0,0 +1,89 @@
/* 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 KYRA_GUI_MR_H
#define KYRA_GUI_MR_H
#include "kyra/gui/gui_v2.h"
namespace Kyra {
class KyraEngine_MR;
class Screen_MR;
class GUI_MR : public GUI_v2 {
friend class KyraEngine_MR;
public:
GUI_MR(KyraEngine_MR *engine);
void initStaticData() override;
void flagButtonEnable(Button *button);
void flagButtonDisable(Button *button);
int redrawShadedButtonCallback(Button *button) override;
int redrawButtonCallback(Button *button) override;
int optionsButton(Button *button);
void createScreenThumbnail(Graphics::Surface &dst) override;
private:
Common::String getMenuTitle(const Menu &menu) override;
Common::String getMenuItemTitle(const MenuItem &menuItem) override;
Common::String getMenuItemLabel(const MenuItem &menuItem) override;
Common::String getTableString(int id, bool) override;
uint8 textFieldColor1() const override { return 0xFF; }
uint8 textFieldColor2() const override { return 0xCF; }
uint8 textFieldColor3() const override { return 0xBA; }
uint8 defaultColor1() const override { return 0xF0; }
uint8 defaultColor2() const override { return 0xD0; }
uint8 menuItemLabelColor() const override { return 0xFF; }
void resetState(int item);
int quitGame(Button *button);
int loadMenu(Button *button);
int loadSecondChance(Button *button);
int gameOptions(Button *button);
void setupOptionsButtons() override;
void resizeMenu(Menu &menu, int menuHeight, int menuTitleY, int menuItemYstart, int menuItemYinc, int menuItemHeight, int menuItemYend, int labelYstart, int labelYend);
void fontBasedMenuResize();
int audioOptions(Button *button);
int sliderHandler(Button *caller) override;
void drawSliderBar(int slider, const uint8 *shape);
int changeLanguage(Button *caller);
int toggleStudioSFX(Button *caller);
int toggleSkipSupport(Button *caller);
int toggleHeliumMode(Button *caller);
KyraEngine_MR *_vm;
Screen_MR *_screen;
};
} // End of namespace Kyra
#endif

View File

@@ -0,0 +1,134 @@
/* 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/>.
*
*/
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
#include "kyra/engine/kyra_rpg.h"
namespace Kyra {
void KyraRpgEngine::removeInputTop() {
if (!_eventList.empty()) {
if (_eventList.begin()->event.type == Common::EVENT_LBUTTONDOWN)
_mouseClick = 1;
else if (_eventList.begin()->event.type == Common::EVENT_RBUTTONDOWN)
_mouseClick = 2;
else
_mouseClick = 0;
_eventList.erase(_eventList.begin());
}
}
void KyraRpgEngine::gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor) {
w--;
h--;
if (fillColor != -1)
screen()->fillRect(x + 1, y + 1, x + w - 1, y + h - 1, fillColor);
screen()->drawClippedLine(x + 1, y, x + w, y, frameColor2);
screen()->drawClippedLine(x + w, y, x + w, y + h - 1, frameColor2);
screen()->drawClippedLine(x, y, x, y + h, frameColor1);
screen()->drawClippedLine(x, y + h, x + w, y + h, frameColor1);
}
void KyraRpgEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 cur, int32 max, int col1, int col2) {
if (max < 1)
return;
if (cur < 0)
cur = 0;
int32 e = MIN(cur, max);
if (!--w)
return;
if (!--h)
return;
int32 t = (e * w) / max;
if (!t && e)
t++;
if (t)
screen()->fillRect(x, y, x + t - 1, y + h, col1);
if (t < w && col2)
screen()->fillRect(x + t, y, x + w - 1, y + h, col2);
}
void KyraRpgEngine::gui_initButtonsFromList(const uint8 *list) {
while (*list != 0xFF)
gui_initButton(*list++);
}
void KyraRpgEngine::gui_resetButtonList() {
for (uint i = 0; i < ARRAYSIZE(_activeButtonData); ++i)
_activeButtonData[i].nextButton = 0;
gui_notifyButtonListChanged();
_activeButtons = 0;
}
void KyraRpgEngine::gui_notifyButtonListChanged() {
if (gui()) {
if (!_buttonListChanged && !_preserveEvents)
removeInputTop();
_buttonListChanged = true;
}
}
bool KyraRpgEngine::clickedShape(int shapeIndex) {
if (_clickedSpecialFlag != 0x40)
return true;
for (; shapeIndex; shapeIndex = _levelDecorationProperties[shapeIndex].next) {
if (_flags.gameID != GI_LOL)
shapeIndex--;
uint16 s = _levelDecorationProperties[shapeIndex].shapeIndex[1];
if (s == 0xFFFF)
continue;
int w = _flags.gameID == GI_LOL ? _levelDecorationShapes[s][3] : (_levelDecorationShapes[s][2] << 3);
int h = _levelDecorationShapes[s][_flags.gameID == GI_LOL ? 2 : 1];
int x = _levelDecorationProperties[shapeIndex].shapeX[1] + _clickedShapeXOffs;
int y = _levelDecorationProperties[shapeIndex].shapeY[1] + _clickedShapeYOffs;
if (_levelDecorationProperties[shapeIndex].flags & 1) {
if (_flags.gameID == GI_LOL)
w <<= 1;
else
x = 176 - x - w;
}
if (posWithinRect(_mouseX, _mouseY, x - 4, y - 4, x + w + 8, y + h + 8))
return true;
}
return false;
}
} // End of namespace Kyra
#endif // defined(ENABLE_EOB) || defined(ENABLE_LOL)

631
engines/kyra/gui/gui_v1.cpp Normal file
View File

@@ -0,0 +1,631 @@
/* 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 "kyra/gui/gui_v1.h"
#include "kyra/text/text.h"
#include "kyra/graphics/wsamovie.h"
#include "common/savefile.h"
#include "common/system.h"
namespace Kyra {
GUI_v1::GUI_v1(KyraEngine_v1 *kyra) : GUI(kyra), _text(kyra->text()) {
_menuButtonList = nullptr;
_menuLabelYOffset = (kyra->game() == GI_LOL || kyra->gameFlags().lang == Common::KO_KOR) ? 3 : 2;
_redrawButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawButtonCallback);
_redrawShadedButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawShadedButtonCallback);
_displayMenu = _displaySubMenu = _cancelSubMenu = false;
_lastScreenUpdate = 0;
}
Button *GUI_v1::addButtonToList(Button *list, Button *newButton) {
if (!newButton)
return list;
newButton->nextButton = nullptr;
if (list) {
Button *cur = list;
while (cur->nextButton)
cur = cur->nextButton;
cur->nextButton = newButton;
} else {
list = newButton;
}
return list;
}
void GUI_v1::initMenuLayout(Menu &menu) {
if (menu.x == -1)
menu.x = (320 - menu.width) >> 1;
if (menu.y == -1)
menu.y = (200 - menu.height) >> 1;
for (int i = 0; i < menu.numberOfItems; ++i) {
if (menu.item[i].x == -1)
menu.item[i].x = (menu.width - menu.item[i].width) >> 1;
}
}
void GUI_v1::initMenu(Menu &menu) {
_menuButtonList = nullptr;
int textX;
int textY;
int menu_x2 = menu.width + menu.x - 1;
int menu_y2 = menu.height + menu.y - 1;
_screen->fillRect(menu.x + 2, menu.y + 2, menu_x2 - 2, menu_y2 - 2, menu.bkgdColor);
_screen->drawShadedBox(menu.x, menu.y, menu_x2, menu_y2, menu.color1, menu.color2);
if (menu.titleX != -1)
textX = menu.titleX;
else
textX = getMenuCenterStringX(getMenuTitle(menu), menu.x, menu_x2);
textY = menu.y + menu.titleY;
if (_vm->game() == GI_LOL) {
printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 9);
} else {
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuTitle(menu), textX - 1, textY + 1, defaultColor1(), defaultColor2(), 0);
printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 0);
}
assert (menu.numberOfItems <= ARRAYSIZE(menu.item));
int x1, y1, x2, y2;
for (int i = 0; i < menu.numberOfItems; ++i) {
if (!menu.item[i].enabled)
continue;
x1 = menu.x + menu.item[i].x;
y1 = menu.y + menu.item[i].y;
x2 = x1 + menu.item[i].width - 1;
y2 = y1 + menu.item[i].height - 1;
Button *menuButtonData = getButtonListData() + i;
menuButtonData->nextButton = nullptr;
menuButtonData->x = x1;
menuButtonData->y = y1;
menuButtonData->width = menu.item[i].width - 1;
menuButtonData->height = menu.item[i].height - 1;
menuButtonData->buttonCallback = menu.item[i].callback;
menuButtonData->keyCode = menu.item[i].keyCode;
menuButtonData->keyCode2 = 0;
menuButtonData->arg = menu.item[i].itemId;
_menuButtonList = addButtonToList(_menuButtonList, menuButtonData);
_screen->fillRect(x1, y1, x2, y2, menu.item[i].bkgdColor);
_screen->drawShadedBox(x1, y1, x2, y2, menu.item[i].color1, menu.item[i].color2);
if (!getMenuItemTitle(menu.item[i]).empty()) {
if (menu.item[i].titleX != -1)
textX = x1 + menu.item[i].titleX + 3;
else
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
textY = y1 + _menuLabelYOffset;
if (_vm->game() == GI_LOL) {
if (i == menu.highlightedItem)
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
else
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
} else {
Screen::FontId of = _screen->_currentFont;
if (menu.item[i].saveSlot > 0)
_screen->setFont((_vm->gameFlags().lang == Common::ZH_CHN || _vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_CHINESE_FNT : (_vm->gameFlags().lang == Common::KO_KOR ? Screen::FID_KOREAN_FNT : Screen::FID_8_FNT));
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
if (i == menu.highlightedItem)
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 0);
else
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 0);
_screen->setFont(of);
}
}
}
for (int i = 0; i < menu.numberOfItems; ++i) {
if (!getMenuItemLabel(menu.item[i]).empty()) {
if (_vm->game() == GI_LOL) {
menu.item[i].labelX = menu.item[i].x - 1;
menu.item[i].labelY = menu.item[i].y + 3;
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menu.item[i].textColor, 0, 10);
} else {
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX - 1, menu.y + menu.item[i].labelY + 1, defaultColor1(), 0, 0);
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menuItemLabelColor(), 0, 0);
}
}
}
if (menu.scrollUpButtonX != -1) {
Button *scrollUpButton = getScrollUpButton();
scrollUpButton->x = menu.scrollUpButtonX + menu.x;
scrollUpButton->y = menu.scrollUpButtonY + menu.y;
scrollUpButton->buttonCallback = getScrollUpButtonHandler();
scrollUpButton->nextButton = nullptr;
scrollUpButton->mouseWheel = -1;
_menuButtonList = addButtonToList(_menuButtonList, scrollUpButton);
updateMenuButton(scrollUpButton);
Button *scrollDownButton = getScrollDownButton();
scrollDownButton->x = menu.scrollDownButtonX + menu.x;
scrollDownButton->y = menu.scrollDownButtonY + menu.y;
scrollDownButton->buttonCallback = getScrollDownButtonHandler();
scrollDownButton->nextButton = nullptr;
scrollDownButton->mouseWheel = 1;
_menuButtonList = addButtonToList(_menuButtonList, scrollDownButton);
updateMenuButton(scrollDownButton);
}
_screen->updateScreen();
}
void GUI_v1::processHighlights(Menu &menu) {
int x1, y1, x2, y2;
Common::Point p = _vm->getMousePos();
int mouseX = p.x;
int mouseY = p.y;
if (_vm->game() == GI_LOL && menu.highlightedItem != 255) {
// LoL doesn't have default highlighted items.
// We use a highlightedItem value of 255 for this.
// With LoL no highlighting should take place unless the
// mouse cursor moves over a button. The highlighting should end
// when the mouse cursor leaves the button.
if (menu.item[menu.highlightedItem].enabled)
redrawText(menu);
}
for (int i = 0; i < menu.numberOfItems; ++i) {
if (!menu.item[i].enabled)
continue;
x1 = menu.x + menu.item[i].x;
y1 = menu.y + menu.item[i].y;
x2 = x1 + menu.item[i].width;
y2 = y1 + menu.item[i].height;
if (mouseX > x1 && mouseX < x2 &&
mouseY > y1 && mouseY < y2) {
if (menu.highlightedItem != i || _vm->game() == GI_LOL) {
if (_vm->game() != GI_LOL) {
if (menu.item[menu.highlightedItem].enabled)
redrawText(menu);
}
menu.highlightedItem = i;
redrawHighlight(menu);
}
}
}
_screen->updateScreen();
}
void GUI_v1::redrawText(const Menu &menu) {
int textX;
int i = menu.highlightedItem;
int x1 = menu.x + menu.item[i].x;
int y1 = menu.y + menu.item[i].y;
int x2 = x1 + menu.item[i].width - 1;
if (menu.item[i].titleX >= 0)
textX = x1 + menu.item[i].titleX + 3;
else
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
int textY = y1 + _menuLabelYOffset;
if (_vm->game() == GI_LOL) {
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
} else {
Screen::FontId of = _screen->_currentFont;
if (menu.item[i].saveSlot > 0)
_screen->setFont((_vm->gameFlags().lang == Common::ZH_CHN || _vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_CHINESE_FNT : (_vm->gameFlags().lang == Common::KO_KOR ? Screen::FID_KOREAN_FNT : Screen::FID_8_FNT));
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 0);
_screen->setFont(of);
}
}
void GUI_v1::redrawHighlight(const Menu &menu) {
int textX;
int i = menu.highlightedItem;
int x1 = menu.x + menu.item[i].x;
int y1 = menu.y + menu.item[i].y;
int x2 = x1 + menu.item[i].width - 1;
if (menu.item[i].titleX != -1)
textX = x1 + menu.item[i].titleX + 3;
else
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
int textY = y1 + _menuLabelYOffset;
if (_vm->game() == GI_LOL) {
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
} else {
Screen::FontId of = _screen->_currentFont;
if (menu.item[i].saveSlot > 0)
_screen->setFont((_vm->gameFlags().lang == Common::ZH_CHN || _vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_CHINESE_FNT : (_vm->gameFlags().lang == Common::KO_KOR ? Screen::FID_KOREAN_FNT : Screen::FID_8_FNT));
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 0);
_screen->setFont(of);
}
}
void GUI_v1::updateAllMenuButtons() {
for (Button *cur = _menuButtonList; cur; cur = cur->nextButton)
updateMenuButton(cur);
}
void GUI_v1::updateMenuButton(Button *button) {
if (!_displayMenu)
return;
updateButton(button);
}
void GUI_v1::updateButton(Button *button) {
if (!button || (button->flags & 8))
return;
if (button->flags2 & 1)
button->flags2 &= 0xFFF7;
else
button->flags2 |= 8;
button->flags2 &= 0xFFFC;
if (button->flags2 & 4)
button->flags2 |= 0x10;
else
button->flags2 &= 0xEEEF;
button->flags2 &= 0xFFFB;
processButton(button);
}
int GUI_v1::redrawButtonCallback(Button *button) {
if (!_displayMenu)
return 0;
if (_vm->gameFlags().platform == Common::kPlatformAmiga)
_screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 17);
else
_screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 0xF8);
return 0;
}
int GUI_v1::redrawShadedButtonCallback(Button *button) {
if (!_displayMenu)
return 0;
if (_vm->gameFlags().platform == Common::kPlatformAmiga)
_screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 31, 18);
else
_screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 0xF9, 0xFA);
return 0;
}
void GUI_v1::checkTextfieldInput() {
Common::Event event;
uint32 now = _vm->_system->getMillis();
bool running = true;
int keys = 0;
while (_vm->_eventMan->pollEvent(event) && running) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL))
_vm->quitGame();
else
_keyPressed = event.kbd;
running = false;
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP: {
Common::Point pos = _vm->getMousePos();
_vm->_mouseX = pos.x;
_vm->_mouseY = pos.y;
keys = event.type == Common::EVENT_LBUTTONDOWN ? 199 : (200 | 0x800);
running = false;
} break;
case Common::EVENT_MOUSEMOVE: {
Common::Point pos = _vm->getMousePos();
_vm->_mouseX = pos.x;
_vm->_mouseY = pos.y;
_screen->updateBackendScreen(true);
_lastScreenUpdate = now;
} break;
default:
break;
}
}
if (now - _lastScreenUpdate > 50) {
_screen->updateBackendScreen(true);
_lastScreenUpdate = now;
}
processButtonList(_menuButtonList, keys | 0x8000, 0);
_vm->_system->delayMillis(3);
}
void GUI_v1::printMenuText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
_text->printText(str.c_str(), x, y, c0, c1, c2);
}
int GUI_v1::getMenuCenterStringX(const Common::String &str, int x1, int x2) {
return _text->getCenterStringX(str.c_str(), x1, x2);
}
#pragma mark -
MainMenu::MainMenu(KyraEngine_v1 *vm) : _vm(vm), _screen(nullptr) {
_screen = _vm->screen();
_nextUpdate = 0;
_system = g_system;
memset(&_static, 0, sizeof(_static));
memset(&_animIntern, 0, sizeof(_animIntern));
}
void MainMenu::init(StaticData data, Animation anim) {
_static = data;
_anim = anim;
_animIntern.curFrame = _anim.startFrame;
_animIntern.direction = 1;
}
void MainMenu::updateAnimation() {
if (_anim.anim) {
uint32 now = _system->getMillis();
if (now > _nextUpdate) {
_nextUpdate = now + _anim.delay * _vm->tickLength();
_anim.anim->displayFrame(_animIntern.curFrame, 0, 0, 0, 0, nullptr, nullptr);
_animIntern.curFrame += _animIntern.direction;
if (_animIntern.curFrame < _anim.startFrame) {
_animIntern.curFrame = _anim.startFrame;
_animIntern.direction = 1;
} else if (_animIntern.curFrame > _anim.endFrame) {
_animIntern.curFrame = _anim.endFrame;
_animIntern.direction = -1;
}
}
}
_screen->updateScreen();
}
bool MainMenu::getInput() {
Common::Event event;
Common::EventManager *eventMan = _vm->getEventManager();
bool updateScreen = false;
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONUP:
return true;
case Common::EVENT_MOUSEMOVE:
updateScreen = true;
break;
default:
break;
}
}
if (updateScreen)
_screen->updateBackendScreen(true);
return false;
}
int MainMenu::handle(int dim) {
int command = -1;
uint8 colorMap[16];
memset(colorMap, 0, sizeof(colorMap));
_screen->setTextColorMap(colorMap);
Screen::FontId oldFont = _screen->setFont(_static.font);
int charWidthBackUp = _screen->_charSpacing;
if (_vm->game() != GI_LOL)
_screen->_charSpacing = -2;
_screen->setScreenDim(dim);
int backUpX = _screen->_curDim->sx;
int backUpY = _screen->_curDim->sy;
int backUpWidth = _screen->_curDim->w;
int backUpHeight = _screen->_curDim->h;
_screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 0, 3);
int x = _screen->_curDim->sx << 3;
int y = _screen->_curDim->sy;
int width = _screen->_curDim->w << 3;
int height = _screen->_curDim->h;
if (_static.boxCoords) {
x = _static.boxCoords[0];
y = _static.boxCoords[1];
width = _static.boxCoords[2];
height = _static.boxCoords[3];
}
drawBox(x, y, width, height, 1);
drawBox(x + 1, y + 1, width - 2, height - 2, 0);
int selected = 0;
draw(selected);
while (!_screen->isMouseVisible())
_screen->showMouse();
int fh = _screen->getFontHeight() + _static.lineSpacingAdjust;
int textPos = ((_screen->_curDim->w >> 1) + _screen->_curDim->sx) << 3;
Common::Rect menuRect(x + 16, y + 4, x + width - 16, y + 4 + fh * _static.menuTable[3]);
while (!_vm->shouldQuit()) {
updateAnimation();
bool mousePressed = getInput();
Common::Point mouse = _vm->getMousePos();
if (menuRect.contains(mouse)) {
int item = (mouse.y - menuRect.top) / fh;
if (item != selected) {
printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[5], 0, 5, _static.strings[selected]);
printString("%s", textPos, menuRect.top + item * fh, _static.menuTable[6], 0, 5, _static.strings[item]);
selected = item;
}
if (mousePressed) {
for (int i = 0; i < 3; i++) {
printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[5], 0, 5, _static.strings[selected]);
_screen->updateScreen();
_system->delayMillis(50);
printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[6], 0, 5, _static.strings[selected]);
_screen->updateScreen();
_system->delayMillis(50);
}
command = item;
break;
}
}
_system->delayMillis(10);
}
if (_vm->shouldQuit())
command = -1;
_screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 3, 0);
_screen->_charSpacing = charWidthBackUp;
_screen->setFont(oldFont);
return command;
}
void MainMenu::draw(int select) {
int top = _static.boxCoords ? _static.boxCoords[1] : _screen->_curDim->sy;
top += _static.menuTable[1];
int fh = _screen->getFontHeight() + _static.lineSpacingAdjust;
for (int i = 0; i < _static.menuTable[3]; ++i) {
int curY = top + i * fh;
int color = (i == select) ? _static.menuTable[6] : _static.menuTable[5];
printString("%s", ((_screen->_curDim->w >> 1) + _screen->_curDim->sx) << 3, curY, color, 0, 5, _static.strings[i]);
}
}
void MainMenu::drawBox(int x, int y, int w, int h, int fill) {
--w; --h;
if (fill)
_screen->fillRect(x, y, x + w, y + h, _static.colorTable[0]);
_screen->drawClippedLine(x, y + h, x + w, y + h, _static.colorTable[1]);
_screen->drawClippedLine(x + w, y, x + w, y + h, _static.colorTable[1]);
_screen->drawClippedLine(x, y, x + w, y, _static.colorTable[2]);
_screen->drawClippedLine(x, y, x, y + h, _static.colorTable[2]);
_screen->setPagePixel(_screen->_curPage, x, y + h, _static.colorTable[3]);
_screen->setPagePixel(_screen->_curPage, x + w, y, _static.colorTable[3]);
}
void MainMenu::printString(const char *format, int x, int y, int col1, int col2, int flags, ...) {
if (!format)
return;
va_list vaList;
va_start(vaList, flags);
Common::String string = Common::String::vformat(format, vaList);
va_end(vaList);
Common::String revBuffer;
const char *cstr = string.c_str();
if (_vm->gameFlags().lang == Common::HE_ISR) {
for (int i = string.size() - 1; i >= 0; --i)
revBuffer += string[i];
cstr = revBuffer.c_str();
}
if (flags & 1)
x -= _screen->getTextWidth(cstr) >> 1;
if (flags & 2)
x -= _screen->getTextWidth(cstr);
if (_vm->gameFlags().use16ColorMode)
flags &= 3;
if (flags & 4) {
_screen->printText(cstr, x - 1, y, _static.altColor, col2);
_screen->printText(cstr, x, y + 1, _static.altColor, col2);
}
if (flags & 8) {
_screen->printText(cstr, x - 1, y, 227, col2);
_screen->printText(cstr, x, y + 1, 227, col2);
}
_screen->printText(cstr, x, y, col1, col2);
}
} // End of namespace Kyra

203
engines/kyra/gui/gui_v1.h Normal file
View File

@@ -0,0 +1,203 @@
/* 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 KYRA_GUI_V1_H
#define KYRA_GUI_V1_H
#include "kyra/gui/gui.h"
namespace Kyra {
struct MenuItem {
bool enabled;
Common::String itemString;
uint16 itemId;
bool useItemString;
int16 x, y;
uint16 width, height;
uint8 textColor, highlightColor;
int16 titleX;
uint8 color1, color2;
uint8 bkgdColor;
Button::Callback callback;
int16 saveSlot;
const char *labelString;
uint16 labelId;
int16 labelX, labelY;
uint16 keyCode;
};
struct Menu {
int16 x, y;
uint16 width, height;
uint8 bkgdColor;
uint8 color1, color2;
const char *menuNameString;
uint16 menuNameId;
uint8 textColor;
int16 titleX, titleY;
uint8 highlightedItem;
uint8 numberOfItems;
int16 scrollUpButtonX, scrollUpButtonY;
int16 scrollDownButtonX, scrollDownButtonY;
MenuItem item[7];
};
class TextDisplayer;
class GUI_v1 : public GUI {
public:
GUI_v1(KyraEngine_v1 *vm);
~GUI_v1() override {}
// button specific
virtual Button *addButtonToList(Button *list, Button *newButton);
void processButton(Button *button) override = 0;
int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) override = 0;
virtual int redrawShadedButtonCallback(Button *button);
virtual int redrawButtonCallback(Button *button);
// menu specific
virtual void initMenuLayout(Menu &menu);
void initMenu(Menu &menu);
void processHighlights(Menu &menu);
// utilities for thumbnail creation
void createScreenThumbnail(Graphics::Surface &dst) override = 0;
protected:
TextDisplayer *_text;
Button *_menuButtonList;
bool _displayMenu;
bool _displaySubMenu;
bool _cancelSubMenu;
virtual void printMenuText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
virtual int getMenuCenterStringX(const Common::String &str, int x1, int x2);
Button::Callback _redrawShadedButtonFunctor;
Button::Callback _redrawButtonFunctor;
virtual Button *getButtonListData() = 0;
virtual Button *getScrollUpButton() = 0;
virtual Button *getScrollDownButton() = 0;
virtual Button::Callback getScrollUpButtonHandler() const = 0;
virtual Button::Callback getScrollDownButtonHandler() const = 0;
virtual uint8 defaultColor1() const = 0;
virtual uint8 defaultColor2() const = 0;
virtual uint8 menuItemLabelColor() const = 0;
virtual Common::String getMenuTitle(const Menu &menu) = 0;
virtual Common::String getMenuItemTitle(const MenuItem &menuItem) = 0;
virtual Common::String getMenuItemLabel(const MenuItem &menuItem) = 0;
void updateAllMenuButtons();
void updateMenuButton(Button *button);
virtual void updateButton(Button *button);
void redrawText(const Menu &menu);
void redrawHighlight(const Menu &menu);
uint32 _lastScreenUpdate;
void checkTextfieldInput();
int _menuLabelYOffset;
};
class Movie;
class MainMenu {
public:
MainMenu(KyraEngine_v1 *vm);
virtual ~MainMenu() {}
struct Animation {
Animation() : anim(0), startFrame(0), endFrame(0), delay(0) {}
Movie *anim;
int startFrame;
int endFrame;
int delay;
};
struct StaticData {
const char *strings[5];
uint8 menuTable[7];
uint8 colorTable[4];
const uint8 *boxCoords;
Screen::FontId font;
int8 lineSpacingAdjust;
uint8 altColor;
};
void init(StaticData data, Animation anim);
int handle(int dim);
private:
KyraEngine_v1 *_vm;
Screen *_screen;
OSystem *_system;
StaticData _static;
struct AnimIntern {
int curFrame;
int direction;
};
Animation _anim;
AnimIntern _animIntern;
uint32 _nextUpdate;
void updateAnimation();
void draw(int select);
void drawBox(int x, int y, int w, int h, int fill);
bool getInput();
void printString(const char *string, int x, int y, int col1, int col2, int flags, ...) GCC_PRINTF(2, 8);
};
} // end of namesapce Kyra
#endif

911
engines/kyra/gui/gui_v2.cpp Normal file
View File

@@ -0,0 +1,911 @@
/* 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 "kyra/gui/gui_v2.h"
#include "kyra/graphics/screen_v2.h"
#include "kyra/text/text.h"
#include "kyra/engine/util.h"
#include "common/savefile.h"
#include "common/system.h"
namespace Kyra {
GUI_v2::GUI_v2(KyraEngine_v2 *vm) : GUI_v1(vm), _vm(vm), _screen(vm->screen_v2()) {
_backUpButtonList = _specialProcessButton = nullptr;
_buttonListChanged = false;
_flagsModifier = 0;
_currentMenu = nullptr;
_isDeathMenu = false;
_isSaveMenu = false;
_isLoadMenu = false;
_scrollUpFunctor = BUTTON_FUNCTOR(GUI_v2, this, &GUI_v2::scrollUpButton);
_scrollDownFunctor = BUTTON_FUNCTOR(GUI_v2, this, &GUI_v2::scrollDownButton);
_sliderHandlerFunctor = BUTTON_FUNCTOR(GUI_v2, this, &GUI_v2::sliderHandler);
_savegameOffset = 0;
_isDeleteMenu = false;
_saveMenuFont = Screen::FID_8_FNT;
_saveMenuCursor = Common::Rect(1, 1, 7, 8);
_saveLoadNumSlots = 5;
_isChoiceMenu = _isOptionsMenu = _madeSave = _loadedSave = _restartGame = _reloadTemporarySave = false;
_noLoadProcess = _noSaveProcess = _choice = _finishNameInput = _cancelNameInput = false;
_saveSlot = _slotToDelete = 0;
if (vm->game() == GI_KYRA2 && vm->gameFlags().lang == Common::ZH_TWN) {
_saveMenuFont = Screen::FID_CHINESE_FNT;
_saveMenuCursor = Common::Rect(0, 0, 8, 14);
_saveLoadNumSlots = 4;
}
if (vm->gameFlags().lang == Common::Language::ZH_TWN && vm->game() == GI_LOL) {
_saveMenuFont = Screen::FID_CHINESE_FNT;
}
}
Button *GUI_v2::addButtonToList(Button *list, Button *newButton) {
list = GUI_v1::addButtonToList(list, newButton);
_buttonListChanged = true;
return list;
}
void GUI_v2::processButton(Button *button) {
if (!button)
return;
if (button->flags & 8) {
if (button->flags & 0x10) {
// XXX
}
return;
}
int entry = button->flags2 & 5;
byte val1 = 0, val2 = 0, val3 = 0;
const uint8 *dataPtr = nullptr;
Button::Callback callback;
if (entry == 1) {
val1 = button->data1Val1;
dataPtr = button->data1ShapePtr;
callback = button->data1Callback;
val2 = button->data1Val2;
val3 = button->data1Val3;
} else if (entry == 4 || entry == 5) {
val1 = button->data2Val1;
dataPtr = button->data2ShapePtr;
callback = button->data2Callback;
val2 = button->data2Val2;
val3 = button->data2Val3;
} else {
val1 = button->data0Val1;
dataPtr = button->data0ShapePtr;
callback = button->data0Callback;
val2 = button->data0Val2;
val3 = button->data0Val3;
}
int x = 0, y = 0, x2 = 0, y2 = 0;
x = button->x;
if (x < 0)
x += _screen->getScreenDim(button->dimTableIndex)->w << 3;
x += _screen->getScreenDim(button->dimTableIndex)->sx << 3;
x2 = x + button->width - 1;
y = button->y;
if (y < 0)
y += _screen->getScreenDim(button->dimTableIndex)->h << 3;
y += _screen->getScreenDim(button->dimTableIndex)->sy << 3;
y2 = y + button->height - 1;
switch (val1 - 1) {
case 0:
_screen->drawShape(_screen->_curPage, dataPtr, x, y, button->dimTableIndex, 0x10);
break;
case 1:
_screen->printText((const char *)dataPtr, x, y, val2, val3);
break;
case 3:
if (callback)
(*callback)(button);
break;
case 4:
_screen->drawBox(x, y, x2, y2, val2);
break;
case 5:
_screen->fillRect(x, y, x2, y2, val2, -1, true);
break;
default:
break;
}
}
int GUI_v2::processButtonList(Button *buttonList, uint16 inputFlag, int8 mouseWheel) {
if (!buttonList)
return inputFlag & 0x7FFF;
if (_backUpButtonList != buttonList || _buttonListChanged) {
_specialProcessButton = nullptr;
//flagsModifier |= 0x2200;
_backUpButtonList = buttonList;
_buttonListChanged = false;
while (buttonList) {
processButton(buttonList);
buttonList = buttonList->nextButton;
}
}
Common::Point p = _vm->getMousePos();
int mouseX = _vm->_mouseX = p.x;
int mouseY = _vm->_mouseY = p.y;
uint16 flags = 0;
if (1/*!_screen_cursorDisable*/) {
uint16 inFlags = inputFlag & 0xFF;
uint16 temp = 0;
// HACK: inFlags == 200 is our left button (up)
if (inFlags == 199 || inFlags == 200)
temp = 0x1000;
if (inFlags == 198)
temp = 0x100;
if (inputFlag & 0x800)
temp <<= 2;
flags |= temp;
_flagsModifier &= ~((temp & 0x4400) >> 1);
_flagsModifier |= (temp & 0x1100) * 2;
flags |= _flagsModifier;
flags |= (_flagsModifier << 2) ^ 0x8800;
}
buttonList = _backUpButtonList;
if (_specialProcessButton) {
buttonList = _specialProcessButton;
if (_specialProcessButton->flags & 8)
_specialProcessButton = nullptr;
}
int returnValue = 0;
while (buttonList) {
if (buttonList->flags & 8) {
buttonList = buttonList->nextButton;
continue;
}
buttonList->flags2 &= ~0x18;
buttonList->flags2 |= (buttonList->flags2 & 3) << 3;
int x = buttonList->x;
if (x < 0)
x += _screen->getScreenDim(buttonList->dimTableIndex)->w << 3;
x += _screen->getScreenDim(buttonList->dimTableIndex)->sx << 3;
int y = buttonList->y;
if (y < 0)
y += _screen->getScreenDim(buttonList->dimTableIndex)->h;
y += _screen->getScreenDim(buttonList->dimTableIndex)->sy;
bool progress = false;
if (mouseX >= x && mouseY >= y && mouseX <= x + buttonList->width && mouseY <= y + buttonList->height)
progress = true;
buttonList->flags2 &= ~0x80;
uint16 inFlags = inputFlag & 0x7FFF;
if (inFlags) {
if (buttonList->keyCode == inFlags) {
progress = true;
flags = buttonList->flags & 0x0F00;
buttonList->flags2 |= 0x80;
inputFlag = 0;
_specialProcessButton = buttonList;
} else if (buttonList->keyCode2 == inFlags) {
flags = buttonList->flags & 0xF000;
if (!flags)
flags = buttonList->flags & 0x0F00;
progress = true;
buttonList->flags2 |= 0x80;
inputFlag = 0;
_specialProcessButton = buttonList;
}
}
bool unk1 = false;
if (mouseWheel && buttonList->mouseWheel == mouseWheel) {
progress = true;
unk1 = true;
}
if (!progress)
buttonList->flags2 &= ~6;
if ((flags & 0x3300) && (buttonList->flags & 4) && progress && (buttonList == _specialProcessButton || !_specialProcessButton)) {
buttonList->flags |= 6;
if (!_specialProcessButton)
_specialProcessButton = buttonList;
} else if ((flags & 0x8800) && !(buttonList->flags & 4) && progress) {
buttonList->flags2 |= 6;
} else {
buttonList->flags2 &= ~6;
}
bool progressSwitch = false;
if (!_specialProcessButton) {
progressSwitch = progress;
} else {
if (_specialProcessButton->flags & 0x40)
progressSwitch = (_specialProcessButton == buttonList);
else
progressSwitch = progress;
}
if (progressSwitch) {
if ((flags & 0x1100) && progress && !_specialProcessButton) {
inputFlag = 0;
_specialProcessButton = buttonList;
}
if ((buttonList->flags & flags) && (progress || !(buttonList->flags & 1))) {
uint16 combinedFlags = (buttonList->flags & flags);
combinedFlags = ((combinedFlags & 0xF000) >> 4) | (combinedFlags & 0x0F00);
combinedFlags >>= 8;
static const uint16 flagTable[] = {
0x000, 0x100, 0x200, 0x100, 0x400, 0x100, 0x400, 0x100, 0x800, 0x100,
0x200, 0x100, 0x400, 0x100, 0x400, 0x100
};
assert(combinedFlags < ARRAYSIZE(flagTable));
switch (flagTable[combinedFlags]) {
case 0x400:
if (!(buttonList->flags & 1) || ((buttonList->flags & 1) && _specialProcessButton == buttonList)) {
buttonList->flags2 ^= 1;
returnValue = buttonList->index | 0x8000;
unk1 = true;
}
if (!(buttonList->flags & 4)) {
buttonList->flags2 &= ~4;
buttonList->flags2 &= ~2;
}
break;
case 0x800:
if (!(buttonList->flags & 4)) {
buttonList->flags2 |= 4;
buttonList->flags2 |= 2;
}
if (!(buttonList->flags & 1))
unk1 = true;
break;
case 0x200:
if (buttonList->flags & 4) {
buttonList->flags2 |= 4;
buttonList->flags2 |= 2;
}
if (!(buttonList->flags & 1))
unk1 = true;
break;
case 0x100:
default:
buttonList->flags2 ^= 1;
returnValue = buttonList->index | 0x8000;
unk1 = true;
if (buttonList->flags & 4) {
buttonList->flags2 |= 4;
buttonList->flags2 |= 2;
}
_specialProcessButton = buttonList;
}
}
}
bool unk2 = false;
if ((flags & 0x2200) && progress) {
buttonList->flags2 |= 6;
if (!(buttonList->flags & 4) && !(buttonList->flags2 & 1)) {
unk2 = true;
buttonList->flags2 |= 1;
}
}
if ((flags & 0x8800) == 0x8800) {
_specialProcessButton = nullptr;
if (!progress || (buttonList->flags & 4))
buttonList->flags2 &= ~6;
}
if (!progress && buttonList == _specialProcessButton && !(buttonList->flags & 0x40))
_specialProcessButton = nullptr;
if ((buttonList->flags2 & 0x18) != ((buttonList->flags2 & 3) << 3))
processButton(buttonList);
if (unk2)
buttonList->flags2 &= ~1;
if (unk1) {
buttonList->flags2 &= 0xFF;
buttonList->flags2 |= flags;
if (buttonList->buttonCallback) {
_vm->removeInputTop();
if ((*buttonList->buttonCallback)(buttonList))
break;
}
if (buttonList->flags & 0x20)
break;
}
if (_specialProcessButton == buttonList && (buttonList->flags & 0x40))
break;
buttonList = buttonList->nextButton;
}
if (!returnValue)
returnValue = inputFlag & 0x7FFF;
return returnValue;
}
void GUI_v2::updateButton(Button *button) {
if (!button || (button->flags & 8))
return;
if (button->flags2 & 1)
button->flags2 |= 8;
else
button->flags2 |= ~8;
button->flags2 &= ~1;
if (button->flags2 & 4)
button->flags2 |= 0x10;
else
button->flags2 &= ~0x10;
button->flags2 &= ~4;
processButton(button);
}
void GUI_v2::getInput() {
if (!_displayMenu)
return;
_vm->checkInput(_menuButtonList);
_vm->removeInputTop();
if (_vm->shouldQuit()) {
_displayMenu = false;
_isLoadMenu = false;
_isSaveMenu = false;
_isOptionsMenu = false;
_isDeleteMenu = false;
}
_vm->delay(10);
}
void GUI_v2::renewHighlight(Menu &menu) {
if (!_displayMenu)
return;
MenuItem &item = menu.item[menu.highlightedItem];
int x = item.x + menu.x; int y = item.y + menu.y;
int x2 = x + item.width - 1; int y2 = y + item.height - 1;
redrawText(menu);
_screen->fillRect(x + 2, y + 2, x2 - 2, y2 - 2, item.bkgdColor);
redrawHighlight(menu);
_screen->updateScreen();
}
void GUI_v2::backUpPage1(uint8 *buffer) {
_screen->copyRegionToBuffer(1, 0, 0, 320, 200, buffer);
}
void GUI_v2::restorePage1(const uint8 *buffer) {
_screen->copyBlockToPage(1, 0, 0, 320, 200, buffer);
}
void GUI_v2::setupSavegameNames(Menu &menu, int num) {
for (int i = 0; i < num; ++i) {
menu.item[i].useItemString = true;
menu.item[i].itemString = "";
menu.item[i].saveSlot = -1;
menu.item[i].enabled = false;
}
int startSlot = 0;
if (_isSaveMenu && _savegameOffset == 0)
startSlot = 1;
KyraEngine_v2::SaveHeader header;
Common::InSaveFile *in;
for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); ++i) {
if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header)) != nullptr) {
Common::String s = header.description;
s = Util::convertString_GUItoKYRA(s);
if (_vm->gameFlags().lang == Common::JA_JPN || _vm->gameFlags().lang == Common::ZH_CHN || _vm->gameFlags().lang == Common::ZH_TWN) {
// Strip special characters from GMM save dialog which might get misinterpreted as 2-byte characters
for (Common::String::iterator ii = s.begin(); ii != s.end(); ++ii) {
if (*ii < 32) // due to the signed char type this will also clean up everything >= 0x80
*ii = ' ';
}
}
// Trim long GMM save descriptions to fit our save slots
_screen->_charSpacing = -2;
int fC = _screen->getTextWidth(s.c_str());
while (!s.empty() && fC > 240) {
s.deleteLastChar();
fC = _screen->getTextWidth(s.c_str());
}
_screen->_charSpacing = 0;
menu.item[i].saveSlot = _saveSlots[i + _savegameOffset];
menu.item[i].enabled = true;
menu.item[i].useItemString = true;
menu.item[i].itemString = Common::move(s);
delete in;
}
}
if (_savegameOffset == 0) {
if (_isSaveMenu) {
menu.item[0].saveSlot = -2;
menu.item[0].enabled = true;
menu.item[0].useItemString = true;
menu.item[0].itemString = getTableString(_vm->gameFlags().isTalkie ? 10 : 18);
} else {
menu.item[0].useItemString = true;
menu.item[0].itemString = getTableString(_vm->gameFlags().isTalkie ? 34 : 42, _vm->gameFlags().lang == Common::RU_RUS);
}
}
}
int GUI_v2::scrollUpButton(Button *button) {
updateMenuButton(button);
if (_savegameOffset == (_isDeleteMenu ? 1 : 0))
return 0;
--_savegameOffset;
if (_isLoadMenu) {
setupSavegameNames(_loadMenu, _saveLoadNumSlots);
// original calls something different here...
initMenu(_loadMenu);
} else if (_isSaveMenu || _isDeleteMenu) {
setupSavegameNames(_saveMenu, _saveLoadNumSlots);
// original calls something different here...
initMenu(_saveMenu);
}
return 0;
}
int GUI_v2::scrollDownButton(Button *button) {
updateMenuButton(button);
++_savegameOffset;
if (uint(_savegameOffset + _saveLoadNumSlots) >= _saveSlots.size())
_savegameOffset = MAX<int>(_saveSlots.size() - _saveLoadNumSlots, _isDeleteMenu ? 1 : 0);
if (_isLoadMenu) {
setupSavegameNames(_loadMenu, _saveLoadNumSlots);
// original calls something different here...
initMenu(_loadMenu);
} else if (_isSaveMenu || _isDeleteMenu) {
setupSavegameNames(_saveMenu, _saveLoadNumSlots);
// original calls something different here...
initMenu(_saveMenu);
}
return 0;
}
int GUI_v2::resumeGame(Button *caller) {
updateMenuButton(caller);
_displayMenu = false;
if (!(_vm->game() == GI_KYRA2 && _vm->gameFlags().lang == Common::ZH_TWN))
_screen->setFontStyles(_screen->_currentFont, Font::kStyleBorder);
return 0;
}
int GUI_v2::quitOptionsMenu(Button *caller) {
updateMenuButton(caller);
_isOptionsMenu = false;
return 0;
}
int GUI_v2::toggleWalkspeed(Button *caller) {
updateMenuButton(caller);
if (_vm->_configWalkspeed == 5)
_vm->_configWalkspeed = 3;
else
_vm->_configWalkspeed = 5;
_vm->setWalkspeed(_vm->_configWalkspeed);
setupOptionsButtons();
renewHighlight(_gameOptions);
return 0;
}
int GUI_v2::toggleText(Button *caller) {
updateMenuButton(caller);
if (_vm->textEnabled()) {
if (_vm->speechEnabled())
_vm->_configVoice = 1;
else
_vm->_configVoice = 3;
} else {
if (_vm->speechEnabled())
_vm->_configVoice = 2;
else
_vm->_configVoice = 0;
}
setupOptionsButtons();
renewHighlight(_gameOptions);
return 0;
}
int GUI_v2::clickLoadSlot(Button *caller) {
updateMenuButton(caller);
int index = caller->index - _menuButtons[0].index;
assert(index >= 0 && index <= 6);
MenuItem &item = _loadMenu.item[index];
if (item.saveSlot >= 0) {
_vm->_gameToLoad = item.saveSlot;
_isLoadMenu = false;
}
return 0;
}
int GUI_v2::cancelLoadMenu(Button *caller) {
updateMenuButton(caller);
_isLoadMenu = false;
_noLoadProcess = true;
return 0;
}
int GUI_v2::saveMenu(Button *caller) {
updateSaveFileList(_vm->_targetName);
updateMenuButton(caller);
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
_isSaveMenu = true;
_noSaveProcess = false;
_saveSlot = -1;
_savegameOffset = 0;
setupSavegameNames(_saveMenu, _saveLoadNumSlots);
initMenu(_saveMenu);
updateAllMenuButtons();
while (_isSaveMenu) {
processHighlights(_saveMenu);
getInput();
}
if (_noSaveProcess) {
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
initMenu(*_currentMenu);
updateAllMenuButtons();
return 0;
} else if (_saveSlot <= -1) {
return 0;
}
restorePage1(_vm->_screenBuffer);
restorePalette();
Graphics::Surface thumb;
createScreenThumbnail(thumb);
_vm->updatePlayTimer();
Util::convertString_KYRAtoGUI(_saveDescription, 81);
_vm->saveGameStateIntern(_saveSlot, _saveDescription, &thumb);
thumb.free();
_displayMenu = false;
if (!(_vm->game() == GI_KYRA2 && _vm->gameFlags().lang == Common::ZH_TWN))
_screen->setFontStyles(_screen->_currentFont, Font::kStyleBorder);
_madeSave = true;
return 0;
}
int GUI_v2::clickSaveSlot(Button *caller) {
updateMenuButton(caller);
int index = caller->index - _menuButtons[0].index;
assert(index >= 0 && index <= 6);
MenuItem &item = _saveMenu.item[index];
if (item.saveSlot >= 0) {
if (_isDeleteMenu) {
_slotToDelete = item.saveSlot;
_isDeleteMenu = false;
return 0;
} else {
_saveSlot = item.saveSlot;
Common::strlcpy(_saveDescription, item.itemString.c_str(), sizeof(_saveDescription));
}
} else if (item.saveSlot == -2) {
_saveSlot = getNextSavegameSlot();
memset(_saveDescription, 0, sizeof(_saveDescription));
}
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
initMenu(_savenameMenu);
_screen->fillRect(0x26, 0x5B, 0x11F, _vm->gameFlags().lang == Common::ZH_TWN ? 0x6b : 0x66, textFieldColor2());
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
const char *desc = nameInputProcess(_saveDescription, 0x27, 0x5C, textFieldColor1(), textFieldColor2(), textFieldColor3(), 0x50);
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
if (desc) {
_isSaveMenu = false;
_isDeleteMenu = false;
} else {
initMenu(_saveMenu);
}
return 0;
}
int GUI_v2::cancelSaveMenu(Button *caller) {
updateMenuButton(caller);
_isSaveMenu = false;
_isDeleteMenu = false;
_noSaveProcess = true;
return 0;
}
int GUI_v2::deleteMenu(Button *caller) {
updateSaveFileList(_vm->_targetName);
updateMenuButton(caller);
if (_saveSlots.size() < 2) {
_vm->snd_playSoundEffect(0x0D);
return 0;
}
do {
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
_savegameOffset = 1;
_saveMenu.menuNameId = _vm->gameFlags().isTalkie ? 35 : 1;
setupSavegameNames(_saveMenu, _saveLoadNumSlots);
initMenu(_saveMenu);
_isDeleteMenu = true;
_slotToDelete = -1;
updateAllMenuButtons();
while (_isDeleteMenu) {
processHighlights(_saveMenu);
getInput();
}
if (_slotToDelete < 1) {
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
initMenu(*_currentMenu);
updateAllMenuButtons();
_saveMenu.menuNameId = _vm->gameFlags().isTalkie ? 9 : 17;
return 0;
}
} while (choiceDialog(_vm->gameFlags().isTalkie ? 0x24 : 2, 1) == 0);
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
initMenu(*_currentMenu);
updateAllMenuButtons();
_vm->_saveFileMan->removeSavefile(_vm->getSavegameFilename(_slotToDelete));
Common::Array<int>::iterator i = Common::find(_saveSlots.begin(), _saveSlots.end(), _slotToDelete);
while (i != _saveSlots.end()) {
++i;
if (i == _saveSlots.end())
break;
// We are only renaming all savefiles until we get some slots missing
// Also not rename quicksave slot filenames
if (*(i - 1) != *i || *i >= 990)
break;
Common::String oldName = _vm->getSavegameFilename(*i);
Common::String newName = _vm->getSavegameFilename(*i - 1);
_vm->_saveFileMan->renameSavefile(oldName, newName);
}
_saveMenu.menuNameId = _vm->gameFlags().isTalkie ? 9 : 17;
return 0;
}
const char *GUI_v2::nameInputProcess(char *buffer, int x, int y, uint8 c1, uint8 c2, uint8 c3, int bufferSize) {
bool running = true;
int curPos = strlen(buffer);
uint8 keyLim = (_vm->gameFlags().lang == Common::JA_JPN || _vm->gameFlags().lang == Common::ZH_TWN) ? 128 : 226;
int x2 = x, y2 = y;
Screen::FontId of = _screen->setFont(_saveMenuFont);
_text->printText(buffer, x, y, c1, c2, c2);
for (int i = 0; i < curPos; ++i)
x2 += getCharWidth(buffer[i]);
drawTextfieldBlock(x2, y2, c3);
_screen->setFont(of);
_keyPressed.reset();
_cancelNameInput = _finishNameInput = false;
while (running && !_vm->shouldQuit()) {
of = _screen->setFont(_saveMenuFont);
checkTextfieldInput();
_screen->setFont(of);
processHighlights(_savenameMenu);
char inputKey = _keyPressed.ascii;
Util::convertISOToDOS(inputKey);
if (_keyPressed.keycode == Common::KEYCODE_RETURN || _keyPressed.keycode == Common::KEYCODE_KP_ENTER || _finishNameInput) {
if (checkSavegameDescription(buffer, curPos)) {
buffer[curPos] = 0;
running = false;
} else {
_finishNameInput = false;
}
} else if (_keyPressed.keycode == Common::KEYCODE_ESCAPE || _cancelNameInput) {
running = false;
return nullptr;
} else if ((_keyPressed.keycode == Common::KEYCODE_BACKSPACE || _keyPressed.keycode == Common::KEYCODE_DELETE) && curPos > 0) {
drawTextfieldBlock(x2, y2, c2);
--curPos;
x2 -= getCharWidth(buffer[curPos]);
drawTextfieldBlock(x2, y2, c3);
_screen->updateScreen();
_lastScreenUpdate = _vm->_system->getMillis();
} else if ((uint8)inputKey > 31 && (uint8)inputKey < keyLim && curPos < bufferSize) {
of = _screen->setFont(_saveMenuFont);
if (x2 + getCharWidth(inputKey) + 7 < 0x11F) {
buffer[curPos] = inputKey;
const char text[2] = { buffer[curPos], 0 };
if (_saveMenuFont == Screen::FID_CHINESE_FNT) {
drawTextfieldBlock(x2, y2, c2);
_text->printText(text, x2, y2, c1, c2, 0);
} else {
_text->printText(text, x2, y2, c1, c2, c2);
}
x2 += getCharWidth(inputKey);
drawTextfieldBlock(x2, y2, c3);
++curPos;
_screen->updateScreen();
_lastScreenUpdate = _vm->_system->getMillis();
}
_screen->setFont(of);
}
_keyPressed.reset();
}
return buffer;
}
int GUI_v2::finishSavename(Button *caller) {
updateMenuButton(caller);
_finishNameInput = true;
return 0;
}
int GUI_v2::cancelSavename(Button *caller) {
updateMenuButton(caller);
_cancelNameInput = true;
return 0;
}
bool GUI_v2::checkSavegameDescription(const char *buffer, int size) {
if (!buffer || !size)
return false;
if (buffer[0] == 0)
return false;
for (int i = 0; i < size; ++i) {
if (buffer[i] != ' ')
return true;
}
return false;
}
int GUI_v2::getCharWidth(uint8 c) {
Screen::FontId old = _screen->setFont(_saveMenuFont);
_screen->_charSpacing = -2;
int width = _screen->getCharWidth(c);
_screen->_charSpacing = 0;
_screen->setFont(old);
return width;
}
void GUI_v2::drawTextfieldBlock(int x, int y, uint8 c) {
_screen->fillRect(x + _saveMenuCursor.left, y + _saveMenuCursor.top, x + _saveMenuCursor.right, y + _saveMenuCursor.bottom, c);
}
bool GUI_v2::choiceDialog(int name, bool type) {
_choiceMenu.highlightedItem = 0;
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
if (type)
_choiceMenu.numberOfItems = 2;
else
_choiceMenu.numberOfItems = 1;
_choiceMenu.menuNameId = name;
initMenu(_choiceMenu);
_isChoiceMenu = true;
_choice = false;
while (_isChoiceMenu) {
processHighlights(_choiceMenu);
getInput();
}
restorePage1(_vm->_screenBuffer);
backUpPage1(_vm->_screenBuffer);
return _choice;
}
int GUI_v2::choiceYes(Button *caller) {
updateMenuButton(caller);
_choice = true;
_isChoiceMenu = false;
return 0;
}
int GUI_v2::choiceNo(Button *caller) {
updateMenuButton(caller);
_choice = false;
_isChoiceMenu = false;
return 0;
}
} // End of namespace Kyra

238
engines/kyra/gui/gui_v2.h Normal file
View File

@@ -0,0 +1,238 @@
/* 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 KYRA_GUI_V2_H
#define KYRA_GUI_V2_H
#include "kyra/gui/gui_v1.h"
namespace Kyra {
#define GUI_V2_BUTTON(button, a, b, c, d, e, f, h, i, j, k, l, m, n, o, p, q, r, s, t) \
do { \
button.nextButton = 0; \
button.index = a; \
button.keyCode = b; \
button.keyCode2 = c; \
button.data0Val1 = d; \
button.data1Val1 = e; \
button.data2Val1 = f; \
button.flags = h; \
button.data0ShapePtr = button.data1ShapePtr = button.data2ShapePtr = 0; \
button.dimTableIndex = i; \
button.x = j; \
button.y = k; \
button.width = l; \
button.height = m; \
button.data0Val2 = n; \
button.data0Val3 = o; \
button.data1Val2 = p; \
button.data1Val3 = q; \
button.data2Val2 = r; \
button.data2Val3 = s; \
button.flags2 = t; \
button.mouseWheel = 0; \
button.arg = 0; \
} while (0)
#define GUI_V2_MENU(menu, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) \
do { \
menu.x = a; \
menu.y = b; \
menu.width = c; \
menu.height = d; \
menu.bkgdColor = e; \
menu.color1 = f; \
menu.color2 = g; \
menu.menuNameId = h; \
menu.textColor = i; \
menu.titleX = j; \
menu.titleY = k; \
menu.highlightedItem = l; \
menu.numberOfItems = m; \
menu.scrollUpButtonX = n; \
menu.scrollUpButtonY = o; \
menu.scrollDownButtonX = p; \
menu.scrollDownButtonY = q; \
} while (0)
#define GUI_V2_MENU_ITEM(item, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) \
do { \
item.enabled = a; \
item.itemId = b; \
item.useItemString = false; \
item.x = c; \
item.y = d; \
item.width = e; \
item.height = f; \
item.textColor = g; \
item.highlightColor = h; \
item.titleX = i; \
item.bkgdColor = j; \
item.color1 = k; \
item.color2 = l; \
item.saveSlot = m; \
item.labelId = n; \
item.labelX = o; \
item.labelY = p; \
item.keyCode = q; \
} while (0)
class KyraEngine_v2;
class Screen_v2;
class GUI_v2 : public GUI_v1 {
public:
GUI_v2(KyraEngine_v2 *vm);
virtual void initStaticData() = 0;
Button *addButtonToList(Button *list, Button *newButton) override;
void processButton(Button *button) override;
int processButtonList(Button *button, uint16 inputFlag, int8 mouseWheel) override;
protected:
void updateButton(Button *button) override;
KyraEngine_v2 *_vm;
Screen_v2 *_screen;
bool _buttonListChanged;
Button *_backUpButtonList;
Button *_specialProcessButton;
uint16 _flagsModifier;
protected:
virtual void setupPalette() {}
virtual void restorePalette() {}
virtual Common::String getTableString(int id, bool decode = false) = 0;
virtual uint8 textFieldColor1() const = 0;
virtual uint8 textFieldColor2() const = 0;
virtual uint8 textFieldColor3() const = 0;
protected:
virtual void getInput();
Button _menuButtons[7];
Button _scrollUpButton;
Button _scrollDownButton;
Menu _mainMenu, _gameOptions, _audioOptions, _choiceMenu, _loadMenu, _saveMenu, _savenameMenu, _deathMenu;
Button *getButtonListData() override { return _menuButtons; }
Button *getScrollUpButton() override { return &_scrollUpButton; }
Button *getScrollDownButton() override { return &_scrollDownButton; }
int scrollUpButton(Button *button);
int scrollDownButton(Button *button);
Button::Callback _scrollUpFunctor;
Button::Callback _scrollDownFunctor;
Button::Callback getScrollUpButtonHandler() const override { return _scrollUpFunctor; }
Button::Callback getScrollDownButtonHandler() const override { return _scrollDownFunctor; }
Button _sliderButtons[3][4];
void renewHighlight(Menu &menu);
void backUpPage1(uint8 *buffer);
void restorePage1(const uint8 *buffer);
Menu *_currentMenu;
bool _isLoadMenu;
bool _isDeathMenu;
bool _isSaveMenu;
bool _isDeleteMenu;
bool _isChoiceMenu;
bool _isOptionsMenu;
bool _madeSave;
bool _loadedSave;
bool _restartGame;
bool _reloadTemporarySave;
int _savegameOffset;
void setupSavegameNames(Menu &menu, int num);
// main menu
int resumeGame(Button *caller);
// audio menu
static const int _sliderBarsPosition[];
// load menu
bool _noLoadProcess;
int clickLoadSlot(Button *caller);
int cancelLoadMenu(Button *caller);
// save menu
bool _noSaveProcess;
int _saveSlot;
char _saveDescription[0x51];
int saveMenu(Button *caller);
int clickSaveSlot(Button *caller);
int cancelSaveMenu(Button *caller);
int _saveLoadNumSlots;
// delete menu
int _slotToDelete;
int deleteMenu(Button *caller);
// options menu
int quitOptionsMenu(Button *caller);
int toggleWalkspeed(Button *caller);
int toggleText(Button *caller);
virtual void setupOptionsButtons() = 0;
// audio options
Button::Callback _sliderHandlerFunctor;
virtual int sliderHandler(Button *caller) = 0;
// savename menu
bool _finishNameInput, _cancelNameInput;
const char *nameInputProcess(char *buffer, int x, int y, uint8 c1, uint8 c2, uint8 c3, int bufferSize);
int finishSavename(Button *caller);
int cancelSavename(Button *caller);
bool checkSavegameDescription(const char *buffer, int size);
int getCharWidth(uint8 c);
void drawTextfieldBlock(int x, int y, uint8 c);
Screen::FontId _saveMenuFont;
Common::Rect _saveMenuCursor;
// choice menu
bool _choice;
bool choiceDialog(int name, bool type);
int choiceYes(Button *caller);
int choiceNo(Button *caller);
};
} // End of namespace Kyra
#endif

View File

@@ -0,0 +1,300 @@
/* 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 "kyra/kyra_v1.h"
#include "kyra/engine/util.h"
#include "common/savefile.h"
#include "common/system.h"
#include "graphics/thumbnail.h"
#include "graphics/surface.h"
#define CURRENT_SAVE_VERSION 24
#define GF_FLOPPY (1 << 0)
#define GF_TALKIE (1 << 1)
#define GF_FMTOWNS (1 << 2)
namespace Kyra {
WARN_UNUSED_RESULT KyraEngine_v1::ReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) {
uint32 type = in->readUint32BE();
header.originalSave = false;
header.oldHeader = false;
header.flags = 0;
if (type == MKTAG('K', 'Y', 'R', 'A') || type == MKTAG('A', 'R', 'Y', 'K')) { // old Kyra1 header ID
header.gameID = GI_KYRA1;
header.oldHeader = true;
} else if (type == MKTAG('H', 'O', 'F', 'S')) { // old Kyra2 header ID
header.gameID = GI_KYRA2;
header.oldHeader = true;
} else if (type == MKTAG('W', 'W', 'S', 'V')) {
header.gameID = in->readByte();
} else {
// try checking for original save header
const int descriptionSize[3] = { 30, 80, 60 };
char descriptionBuffer[81];
bool saveOk = false;
for (uint i = 0; i < ARRAYSIZE(descriptionSize) && !saveOk; ++i) {
if (in->size() < descriptionSize[i] + 6)
continue;
in->seek(0, SEEK_SET);
in->read(descriptionBuffer, descriptionSize[i]);
descriptionBuffer[descriptionSize[i]] = 0;
Util::convertString_KYRAtoGUI(descriptionBuffer, 81);
type = in->readUint32BE();
header.version = in->readUint16LE();
if (type == MKTAG('M', 'B', 'L', '3') && header.version == 100) {
saveOk = true;
header.description = descriptionBuffer;
header.gameID = GI_KYRA2;
break;
} else if (type == MKTAG('M', 'B', 'L', '4') && header.version == 102) {
saveOk = true;
header.description = descriptionBuffer;
header.gameID = GI_KYRA3;
break;
} else if (type == MKTAG('C','D','0','4')) {
header.version = in->readUint32BE();
// We don't check the minor version, since the original doesn't do that either and it isn't required.
if (header.version != MKTAG(' ','C','D','1'))
continue;
saveOk = true;
header.description = descriptionBuffer;
header.gameID = GI_LOL;
in->seek(6, SEEK_CUR);
break;
}
}
if (saveOk) {
header.originalSave = true;
header.description = descriptionBuffer;
return kRSHENoError;
} else {
return kRSHEInvalidType;
}
}
header.version = in->readUint32BE();
if (header.version > CURRENT_SAVE_VERSION || (header.oldHeader && header.version > 8) || (type == MKTAG('A', 'R', 'Y', 'K') && header.version > 3))
return kRSHEInvalidVersion;
// Versions prior to 9 are using a fixed length description field
if (header.version <= 8) {
char buffer[31];
in->read(buffer, 31);
// WORKAROUND: Old savegames could contain a missing termination 0 at the
// end so we manually add it.
buffer[30] = 0;
header.description = buffer;
} else {
header.description = "";
for (char c = 0; (c = in->readByte()) != 0;)
header.description += c;
}
if (header.version < 20)
header.description = Util::convertISOToUTF8(header.description);
if (header.version >= 2)
header.flags = in->readUint32BE();
if (header.version >= 14) {
if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
if (!skipThumbnail)
return kRSHEIoError;
}
} else {
header.thumbnail = nullptr;
}
if (header.version >= 21) {
header.timeDate.tm_sec = in->readSint32BE();
header.timeDate.tm_min = in->readSint32BE();
header.timeDate.tm_hour = in->readSint32BE();
header.timeDate.tm_mday = in->readSint32BE();
header.timeDate.tm_mon = in->readSint32BE();
header.timeDate.tm_year = in->readSint32BE();
header.timeDate.tm_wday = in->readSint32BE();
header.totalPlaySecs = in->readUint32BE();
} else {
header.totalPlaySecs = 0;
memset(&header.timeDate, 0, sizeof(TimeDate));
}
return ((in->err() || in->eos()) ? kRSHEIoError : kRSHENoError);
}
Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header, bool checkID) {
Common::SeekableReadStream *in = nullptr;
if (!(in = _saveFileMan->openForLoading(filename)))
return nullptr;
ReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, header);
if (errorCode != kRSHENoError) {
if (errorCode == kRSHEInvalidType)
warning("No ScummVM Kyra engine savefile header");
else if (errorCode == kRSHEInvalidVersion)
warning("Savegame is not the right version (%u, '%s')", header.version, header.oldHeader ? "true" : "false");
else if (errorCode == kRSHEIoError)
warning("Load failed '%s'", filename);
delete in;
return nullptr;
}
if (!header.originalSave) {
if (!header.oldHeader) {
if (header.gameID != _flags.gameID && checkID) {
warning("Trying to load saved game from other game (saved game: %u, running game: %u)", header.gameID, _flags.gameID);
delete in;
return nullptr;
}
}
if (header.version < 2) {
warning("Make sure your savefile was from this version! (too old savefile version to detect that)");
} else if (checkID) {
if ((header.flags & GF_FLOPPY) && (_flags.isTalkie || _flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) {
warning("Can not load DOS Floppy savefile for this (non DOS Floppy) gameversion");
delete in;
return nullptr;
} else if ((header.flags & GF_TALKIE) && !(_flags.isTalkie)) {
warning("Can not load DOS CD-ROM savefile for this (non DOS CD-ROM) gameversion");
delete in;
return nullptr;
} else if ((header.flags & GF_FMTOWNS) && !(_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)) {
warning("Can not load FM-TOWNS/PC98 savefile for this (non FM-TOWNS/PC98) gameversion");
delete in;
return nullptr;
}
}
}
return in;
}
Common::OutSaveFile *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const {
if (shouldQuit())
return nullptr;
Common::WriteStream *out = nullptr;
if (!(out = _saveFileMan->openForSaving(filename))) {
warning("Can't create file '%s', game not saved", filename);
return nullptr;
}
// Savegame version
out->writeUint32BE(MKTAG('W', 'W', 'S', 'V'));
out->writeByte(_flags.gameID);
out->writeUint32BE(CURRENT_SAVE_VERSION);
out->write(saveName, strlen(saveName) + 1);
if (_flags.isTalkie)
out->writeUint32BE(GF_TALKIE);
else if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98)
out->writeUint32BE(GF_FMTOWNS);
else
out->writeUint32BE(GF_FLOPPY);
if (out->err()) {
warning("Can't write file '%s'. (Disk full?)", filename);
delete out;
return nullptr;
}
Graphics::Surface *genThumbnail = nullptr;
if (!thumbnail)
thumbnail = genThumbnail = generateSaveThumbnail();
if (thumbnail)
Graphics::saveThumbnail(*out, *thumbnail);
else
Graphics::saveThumbnail(*out);
if (genThumbnail) {
genThumbnail->free();
delete genThumbnail;
}
TimeDate td;
_system->getTimeAndDate(td);
out->writeSint32BE(td.tm_sec);
out->writeSint32BE(td.tm_min);
out->writeSint32BE(td.tm_hour);
out->writeSint32BE(td.tm_mday);
out->writeSint32BE(td.tm_mon);
out->writeSint32BE(td.tm_year);
out->writeSint32BE(td.tm_wday);
out->writeUint32BE(_totalPlaySecs);
return new Common::OutSaveFile(out);
}
const char *KyraEngine_v1::getSavegameFilename(int num) {
_savegameFilename = getSavegameFilename(_targetName, num);
return _savegameFilename.c_str();
}
Common::String KyraEngine_v1::getSavegameFilename(const Common::String &target, int num) {
assert(num >= 0 && num <= 999);
return target + Common::String::format(".%03d", num);
}
bool KyraEngine_v1::saveFileLoadable(int slot) {
if (slot < 0 || slot > 999)
return false;
SaveHeader header;
Common::SeekableReadStream *in = openSaveForReading(getSavegameFilename(slot), header);
if (in) {
delete in;
return true;
}
return false;
}
void KyraEngine_v1::loadGameStateCheck(int slot) {
// FIXME: Instead of throwing away the error returned by
// loadGameState, we should use it / augment it.
if (loadGameState(slot).getCode() != Common::kNoError) {
const char *filename = getSavegameFilename(slot);
Common::String errorMessage = "Could not load savegame: '";
errorMessage += filename;
errorMessage += "'";
GUIErrorMessage(errorMessage);
error("%s", errorMessage.c_str());
}
}
} // End of namespace Kyra

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
/* 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 "kyra/engine/kyra_hof.h"
#include "kyra/graphics/screen_v2.h"
#include "kyra/sound/sound.h"
#include "kyra/engine/timer.h"
#include "common/savefile.h"
#include "common/substream.h"
#include "common/system.h"
namespace Kyra {
Common::Error KyraEngine_HoF::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
if (!out)
return _saveFileMan->getError();
_timer->saveDataToFile(*out);
out->writeUint32BE(sizeof(_flagsTable));
out->write(_flagsTable, sizeof(_flagsTable));
// usually we have to save the flag set by opcode 10 here
//out->writeUint16BE(word_2AB05);
out->writeSint16BE(_lastMusicCommand);
out->writeByte(_newChapterFile);
out->writeByte(_characterShapeFile);
out->writeByte(_cauldronState);
out->writeByte(_colorCodeFlag1);
out->writeByte(_colorCodeFlag2);
out->writeByte(_bookCurPage);
out->writeByte(_bookMaxPage);
for (int i = 0; i < 7; ++i)
out->writeByte(_presetColorCode[i]);
for (int i = 0; i < 7; ++i)
out->writeByte(_inputColorCode[i]);
for (int i = 0; i < 25; ++i)
out->writeSint16BE(_cauldronTable[i]);
for (int i = 0; i < 20; ++i)
out->writeSint16BE(_hiddenItems[i]);
for (int i = 0; i < 19; ++i)
out->write(_conversationState[i], 14);
out->write(_newSceneDlgState, 32);
out->writeSint16BE(_cauldronUseCount);
out->writeUint16BE(_mainCharacter.sceneId);
out->writeSint16BE(_mainCharacter.dlgIndex);
out->writeByte(_mainCharacter.height);
out->writeByte(_mainCharacter.facing);
out->writeUint16BE(_mainCharacter.animFrame);
for (int i = 0; i < 20; ++i)
out->writeUint16BE(_mainCharacter.inventory[i]);
out->writeSint16BE(_mainCharacter.x1);
out->writeSint16BE(_mainCharacter.y1);
out->writeSint16BE(_mainCharacter.x2);
out->writeSint16BE(_mainCharacter.y2);
for (int i = 0; i < 30; ++i) {
out->writeSint16BE(_itemList[i].id);
out->writeUint16BE(_itemList[i].sceneId);
out->writeSint16BE(_itemList[i].x);
out->writeByte(_itemList[i].y);
}
for (int i = 0; i < 72; ++i) {
out->write(_talkObjectList[i].filename, 13);
out->writeByte(_talkObjectList[i].scriptId);
out->writeSint16BE(_talkObjectList[i].x);
out->writeSint16BE(_talkObjectList[i].y);
out->writeByte(_talkObjectList[i].color);
}
for (int i = 0; i < 86; ++i) {
out->write(_sceneList[i].filename1, 10);
out->writeUint16BE(_sceneList[i].exit1);
out->writeUint16BE(_sceneList[i].exit2);
out->writeUint16BE(_sceneList[i].exit3);
out->writeUint16BE(_sceneList[i].exit4);
out->writeByte(_sceneList[i].flags);
out->writeByte(_sceneList[i].sound);
}
out->writeSint16BE(_itemInHand);
out->writeUint16BE(_sceneExit1);
out->writeUint16BE(_sceneExit2);
out->writeUint16BE(_sceneExit3);
out->writeUint16BE(_sceneExit4);
out->finalize();
// check for errors
if (out->err()) {
warning("Can't write file '%s'. (Disk full?)", fileName);
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Saved game '%s.'", saveName);
}
delete out;
return Common::kNoError;
}
Common::Error KyraEngine_HoF::loadGameState(int slot) {
const char *fileName = getSavegameFilename(slot);
SaveHeader header;
Common::InSaveFile *saveFile = openSaveForReading(fileName, header);
if (!saveFile) {
showMessageFromCCode(0x35, 0x84, 0);
snd_playSoundEffect(0x0D);
return Common::kUnknownError;
}
if (header.originalSave)
warning("Trying to load savegame from original interpreter, while this is possible, it is not officially supported");
bool setFlag1EE = (queryGameFlag(0x1EE) != 0);
_deathHandler = -1;
if (!_unkSceneScreenFlag1) {
_sound->beginFadeOut();
_system->delayMillis(5 * _tickLength);
_lastMusicCommand = -1;
}
int loadedZTable = _characterShapeFile;
Common::SeekableReadStreamEndianWrapper in(saveFile, !header.originalSave, DisposeAfterUse::YES);
_screen->hideMouse();
_screen->fadeToBlack(10);
_screen->fillRect(0, 0, 319, 143, 0, 0);
if (!header.originalSave) {
_timer->loadDataFromFile(in, header.version);
uint32 flagsSize = in.readUint32BE();
assert(flagsSize <= sizeof(_flagsTable));
in.read(_flagsTable, flagsSize);
}
// usually we have to save the flag set by opcode 10 here
//word_2AB05 = in.readUint16();
if (header.originalSave)
in.readUint16();
_lastMusicCommand = in.readSint16();
_newChapterFile = in.readByte();
_characterShapeFile = in.readByte();
_cauldronState = in.readByte();
_colorCodeFlag1 = in.readByte();
_colorCodeFlag2 = in.readByte();
_bookCurPage = in.readByte();
_bookMaxPage = in.readByte();
for (int i = 0; i < 7; ++i)
_presetColorCode[i] = in.readByte();
for (int i = 0; i < 7; ++i)
_inputColorCode[i] = in.readByte();
for (int i = 0; i < 25; ++i)
_cauldronTable[i] = in.readSint16();
for (int i = 0; i < 20; ++i)
_hiddenItems[i] = in.readSint16();
if (header.originalSave) {
assert(sizeof(_flagsTable) >= 0x41);
in.read(_flagsTable, 0x41);
}
for (int i = 0; i < 19; ++i)
in.read(_conversationState[i], 14);
if (!header.originalSave) {
in.read(_newSceneDlgState, 32);
} else {
for (int i = 0; i < 31; ++i)
_newSceneDlgState[i] = in.readUint16();
}
_cauldronUseCount = in.readSint16();
if (header.originalSave)
in.seek(6, SEEK_CUR);
_mainCharacter.sceneId = in.readUint16();
_mainCharacter.dlgIndex = in.readSint16();
_mainCharacter.height = in.readByte();
_mainCharacter.facing = in.readByte();
_mainCharacter.animFrame = in.readUint16();
if (header.version <= 10 || header.originalSave)
in.seek(3, SEEK_CUR);
for (int i = 0; i < 20; ++i)
_mainCharacter.inventory[i] = in.readUint16();
_mainCharacter.x1 = in.readSint16();
_mainCharacter.y1 = in.readSint16();
_mainCharacter.x2 = in.readSint16();
_mainCharacter.y2 = in.readSint16();
for (int i = 0; i < 30; ++i) {
_itemList[i].id = in.readSint16();
_itemList[i].sceneId = in.readUint16();
_itemList[i].x = in.readSint16();
_itemList[i].y = in.readByte();
if (header.version <= 9 || header.originalSave)
in.readUint16();
}
for (int i = 0; i < 72; ++i) {
in.read(_talkObjectList[i].filename, 13);
_talkObjectList[i].scriptId = in.readByte();
_talkObjectList[i].x = in.readSint16();
_talkObjectList[i].y = in.readSint16();
_talkObjectList[i].color = in.readByte();
}
for (int i = 0; i < 86; ++i) {
if (!header.originalSave) {
in.read(_sceneList[i].filename1, 10);
} else {
in.read(_sceneList[i].filename1, 9);
_sceneList[i].filename1[9] = 0;
}
_sceneList[i].exit1 = in.readUint16();
_sceneList[i].exit2 = in.readUint16();
_sceneList[i].exit3 = in.readUint16();
_sceneList[i].exit4 = in.readUint16();
_sceneList[i].flags = in.readByte();
_sceneList[i].sound = in.readByte();
}
_itemInHand = in.readSint16();
if (header.originalSave) {
uint32 currentTime = _system->getMillis();
for (int i = 0; i < 6; ++i)
_timer->setDelay(i, in.readSint32LE());
for (int i = 0; i < 6; ++i) {
if (in.readUint16LE())
_timer->enable(i);
else
_timer->disable(i);
}
for (int i = 0; i < 6; ++i)
_timer->setNextRun(i, currentTime + (in.readUint32LE() * _tickLength));
_timer->resetNextRun();
}
_sceneExit1 = in.readUint16();
_sceneExit2 = in.readUint16();
_sceneExit3 = in.readUint16();
_sceneExit4 = in.readUint16();
if (saveFile->err() || saveFile->eos()) {
warning("Load failed ('%s', '%s').", fileName, header.description.c_str());
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", header.description.c_str());
}
if (loadedZTable != _characterShapeFile)
loadCharacterShapes(_characterShapeFile);
_screen->loadBitmap("_PLAYFLD.CPS", 3, 3, nullptr);
if (!queryGameFlag(1))
_screen->copyRegion(0xCE, 0x90, 0xCE, 0x90, 0x2C, 0x2C, 2, 0, Screen::CR_NO_P_CHECK);
if (!queryGameFlag(2))
_screen->copyRegion(0xFA, 0x90, 0xFA, 0x90, 0x46, 0x2C, 2, 0, Screen::CR_NO_P_CHECK);
_screen->loadBitmap("_PLAYALL.CPS", 3, 3, nullptr);
if (queryGameFlag(1))
_screen->copyRegion(0xCE, 0x90, 0xCE, 0x90, 0x2C, 0x2C, 2, 0, Screen::CR_NO_P_CHECK);
if (queryGameFlag(2))
_screen->copyRegion(0xFA, 0x90, 0xFA, 0x90, 0x46, 0x2C, 2, 0, Screen::CR_NO_P_CHECK);
redrawInventory(0);
int cauldronUseCount = _cauldronUseCount;
setCauldronState(_cauldronState, 0);
_cauldronUseCount = cauldronUseCount;
_mainCharX = _mainCharacter.x2 = _mainCharacter.x1;
_mainCharY = _mainCharacter.y2 = _mainCharacter.y1;
_mainCharacter.facing = 4;
restartPlayTimerAt(header.totalPlaySecs);
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
setHandItem(_itemInHand);
if (_lastMusicCommand >= 0 && !_unkSceneScreenFlag1)
snd_playWanderScoreViaMap(_lastMusicCommand, 1);
while (!_screen->isMouseVisible())
_screen->showMouse();
setTimer1DelaySecs(7);
_shownMessage = " ";
_fadeMessagePalette = false;
if (setFlag1EE)
setGameFlag(0x1EE);
// We didn't explicitly set the walk speed, but it's saved as part of
// the _timers array, so we need to re-sync it with _configWalkspeed.
setWalkspeed(_configWalkspeed);
return Common::kNoError;
}
} // End of namespace Kyra

View File

@@ -0,0 +1,326 @@
/* 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 "kyra/engine/kyra_lok.h"
#include "kyra/graphics/animator_lok.h"
#include "kyra/resource/resource.h"
#include "kyra/sound/sound.h"
#include "kyra/engine/timer.h"
#include "common/savefile.h"
namespace Kyra {
Common::Error KyraEngine_LoK::loadGameState(int slot) {
const char *fileName = getSavegameFilename(slot);
SaveHeader header;
Common::InSaveFile *in = openSaveForReading(fileName, header);
if (!in)
return _saveFileMan->getError();
if (header.originalSave) {
// no support for original savefile in Kyrandia 1 (yet)
delete in;
return Common::kUnknownError;
}
snd_playSoundEffect(0x0A);
snd_playWanderScoreViaMap(0, 1);
// unloading the current voice file should fix some problems with voices
if (_currentRoom != 0xFFFF && _flags.isTalkie) {
assert(_currentRoom < _roomTableSize);
int tableId = _roomTable[_currentRoom].nameIndex;
assert(tableId < _roomFilenameTableSize);
_res->unloadPakFile(Common::String(_roomFilenameTable[tableId]) + ".VRM");
}
for (int i = 0; i < 11; i++) {
_characterList[i].sceneId = in->readUint16BE();
_characterList[i].height = in->readByte();
_characterList[i].facing = in->readByte();
_characterList[i].currentAnimFrame = in->readUint16BE();
//_characterList[i].unk6 = in->readUint32BE();
in->read(_characterList[i].inventoryItems, 10);
_characterList[i].x1 = in->readSint16BE();
_characterList[i].y1 = in->readSint16BE();
_characterList[i].x2 = in->readSint16BE();
_characterList[i].y2 = in->readSint16BE();
//_characterList[i].field_20 = in->readUint16BE();
//_characterList[i].field_23 = in->readUint16BE();
}
_marbleVaseItem = in->readSint16BE();
_itemInHand = (int8)in->readByte();
for (int i = 0; i < 4; ++i)
_birthstoneGemTable[i] = in->readByte();
for (int i = 0; i < 3; ++i)
_idolGemsTable[i] = in->readByte();
for (int i = 0; i < 3; ++i)
_foyerItemTable[i] = in->readByte();
_cauldronState = in->readByte();
for (int i = 0; i < 2; ++i)
_crystalState[i] = in->readByte();
_brandonStatusBit = in->readUint16BE();
_brandonStatusBit0x02Flag = in->readByte();
_brandonStatusBit0x20Flag = in->readByte();
in->read(_brandonPoisonFlagsGFX, 256);
_brandonInvFlag = in->readSint16BE();
_poisonDeathCounter = in->readByte();
_animator->_brandonDrawFrame = in->readUint16BE();
_timer->loadDataFromFile(*in, header.version);
memset(_flagsTable, 0, sizeof(_flagsTable));
uint32 flagsSize = in->readUint32BE();
assert(flagsSize <= sizeof(_flagsTable));
in->read(_flagsTable, flagsSize);
for (int i = 0; i < _roomTableSize; ++i) {
for (int item = 0; item < 12; ++item) {
_roomTable[i].itemsTable[item] = kItemNone;
_roomTable[i].itemsXPos[item] = 0xFFFF;
_roomTable[i].itemsYPos[item] = 0xFF;
_roomTable[i].needInit[item] = 0;
}
}
uint16 sceneId = 0;
while (true) {
sceneId = in->readUint16BE();
if (sceneId == 0xFFFF)
break;
assert(sceneId < _roomTableSize);
_roomTable[sceneId].nameIndex = in->readByte();
for (int i = 0; i < 12; i++) {
_roomTable[sceneId].itemsTable[i] = in->readByte();
_roomTable[sceneId].itemsXPos[i] = in->readUint16BE();
_roomTable[sceneId].itemsYPos[i] = in->readUint16BE();
_roomTable[sceneId].needInit[i] = in->readByte();
}
}
_sound->selectAudioResourceSet(kMusicIngame);
closeFinalWsa();
int lastMusicCommand = _lastMusicCommand = -1;
if (header.version >= 3)
lastMusicCommand = in->readSint16BE();
// Version 4 stored settings in the savegame. As of version 5, they are
// handled by the config manager.
if (header.version == 4) {
in->readByte(); // Text speed
in->readByte(); // Walk speed
in->readByte(); // Music
in->readByte(); // Sound
in->readByte(); // Voice
}
if (header.version >= 7) {
_curSfxFile = in->readByte();
// In the first version when this entry was introduced,
// it wasn't made sure that _curSfxFile was initialized
// so if it's out of bounds we just set it to 0.
if (_flags.platform == Common::kPlatformFMTowns) {
if (!_sound->hasSoundFile(_curSfxFile))
_curSfxFile = 0;
_sound->loadSoundFile(_curSfxFile);
}
}
loadMainScreen(8);
if (queryGameFlag(0x2D)) {
_screen->loadBitmap("AMULET3.CPS", 10, 10, nullptr);
if (!queryGameFlag(0xF1)) {
for (int i = 0x55; i <= 0x5A; ++i) {
if (queryGameFlag(i))
seq_createAmuletJewel(i - 0x55, 10, 1, 1);
}
}
_screen->copyRegion(8, 8, 8, 8, 304, 212, 10, 0);
}
setHandItem(_itemInHand);
// Will-O-Wisp uses a different shape size than Brandon's usual
// shape, thus we need to setup the correct size depending on
// his state over here. This fixes graphics glitches when loading
// saves, where Brandon is transformed into the Will-O-Wisp.
if (_brandonStatusBit & 2)
_animator->setBrandonAnimSeqSize(5, 48);
else
_animator->setBrandonAnimSeqSize(3, 48);
redrawInventory(0);
// Original hardcoded Brandon position for certain scenes:
// - SceneId 7 ("A ruined bridge") and flag 0x39 set, which seems
// to indicate that Herman is still in the scene.
// - SceneId 2 ("Inside the temple") and flag 0x2D not set, which
// indicates that the amulet is not obtained yet and thus Brynn
// is still inside the temple
if (_currentCharacter->sceneId == 7 && queryGameFlag(0x39)) {
_currentCharacter->x1 = 282;
_currentCharacter->y1 = 108;
_currentCharacter->facing = 5;
} else if (_currentCharacter->sceneId == 2 && !queryGameFlag(0x2D)) {
_currentCharacter->x1 = 294;
_currentCharacter->y1 = 132;
_currentCharacter->facing = 5;
}
_brandonPosX = _currentCharacter->x2 = _currentCharacter->x1;
_brandonPosY = _currentCharacter->y2 = _currentCharacter->y1;
// We need to reset the "_noDrawShapesFlag" flag of Animator_LoK
// over here. Else in certain cases restoring a savegame might
// result in no shapes being drawn at all. See bug report
// #4625 "KYRA1: Invisible Brandon" for an example of this.
_animator->_noDrawShapesFlag = 0;
restartPlayTimerAt(header.totalPlaySecs);
enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 1);
// Check if _lastMusicCommand changed during enterNewScene(). If it didn't (no song was
// started from script) and we do have the last song id from our savegame, then we start that...
// This way we avoid the "stuttering" we used to have from restarting the saved song and
// then the same song again directly afterwards from script...
if (_lastMusicCommand == -1 && lastMusicCommand != -1)
snd_playWanderScoreViaMap(lastMusicCommand, 1);
_animator->animRefreshNPC(0);
_animator->restoreAllObjectBackgrounds();
_animator->preserveAnyChangedBackgrounds();
_animator->prepDrawAllObjects();
_animator->copyChangedObjectsForward(0);
_screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
_screen->updateScreen();
setMousePos(_currentCharacter->x1, _currentCharacter->y1);
if (in->err() || in->eos()) {
warning("Load failed ('%s', '%s').", fileName, header.description.c_str());
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", header.description.c_str());
}
// We didn't explicitly set the walk speed, but it's saved as part of
// the _timers array, so we need to re-sync it with _configWalkspeed.
setWalkspeed(_configWalkspeed);
delete in;
return Common::kNoError;
}
Common::Error KyraEngine_LoK::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
if (shouldQuit())
return Common::kNoError;
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
if (!out)
return _saveFileMan->getError();
for (int i = 0; i < 11; i++) {
out->writeUint16BE(_characterList[i].sceneId);
out->writeByte(_characterList[i].height);
out->writeByte(_characterList[i].facing);
out->writeUint16BE(_characterList[i].currentAnimFrame);
//out->writeUint32BE(_characterList[i].unk6);
out->write(_characterList[i].inventoryItems, 10);
out->writeSint16BE(_characterList[i].x1);
out->writeSint16BE(_characterList[i].y1);
out->writeSint16BE(_characterList[i].x2);
out->writeSint16BE(_characterList[i].y2);
//out->writeUint16BE(_characterList[i].field_20);
//out->writeUint16BE(_characterList[i].field_23);
}
out->writeSint16BE(_marbleVaseItem);
out->writeByte(_itemInHand);
for (int i = 0; i < 4; ++i)
out->writeByte(_birthstoneGemTable[i]);
for (int i = 0; i < 3; ++i)
out->writeByte(_idolGemsTable[i]);
for (int i = 0; i < 3; ++i)
out->writeByte(_foyerItemTable[i]);
out->writeByte(_cauldronState);
for (int i = 0; i < 2; ++i)
out->writeByte(_crystalState[i]);
out->writeUint16BE(_brandonStatusBit);
out->writeByte(_brandonStatusBit0x02Flag);
out->writeByte(_brandonStatusBit0x20Flag);
out->write(_brandonPoisonFlagsGFX, 256);
out->writeSint16BE(_brandonInvFlag);
out->writeByte(_poisonDeathCounter);
out->writeUint16BE(_animator->_brandonDrawFrame);
_timer->saveDataToFile(*out);
out->writeUint32BE(sizeof(_flagsTable));
out->write(_flagsTable, sizeof(_flagsTable));
for (uint16 i = 0; i < _roomTableSize; i++) {
out->writeUint16BE(i);
out->writeByte(_roomTable[i].nameIndex);
for (int a = 0; a < 12; a++) {
out->writeByte(_roomTable[i].itemsTable[a]);
out->writeUint16BE(_roomTable[i].itemsXPos[a]);
out->writeUint16BE(_roomTable[i].itemsYPos[a]);
out->writeByte(_roomTable[i].needInit[a]);
}
}
// room table terminator
out->writeUint16BE(0xFFFF);
out->writeSint16BE(_lastMusicCommand);
out->writeByte(_curSfxFile);
out->finalize();
// check for errors
if (out->err()) {
warning("Can't write file '%s'. (Disk full?)", fileName);
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Saved game '%s.'", saveName);
}
delete out;
return Common::kNoError;
}
} // End of namespace Kyra

View File

@@ -0,0 +1,584 @@
/* 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/>.
*
*/
#ifdef ENABLE_LOL
#include "kyra/engine/lol.h"
#include "kyra/graphics/screen_lol.h"
#include "common/savefile.h"
#include "common/substream.h"
#include "common/memstream.h"
#include "graphics/scaler.h"
namespace Kyra {
Common::Error LoLEngine::loadGameState(int slot) {
const uint16 *cdf[] = { _charDefsMan, _charDefsWoman, _charDefsKieran, _charDefsMan, _charDefsAkshel };
const char *fileName = getSavegameFilename(slot);
SaveHeader header;
Common::InSaveFile *saveFile = openSaveForReading(fileName, header);
if (!saveFile) {
_txt->printMessage(2, "%s", getLangString(0x425D));
return Common::kNoError;
}
if (header.originalSave)
warning("Trying to load savegame from original interpreter, while this is possible, it is not officially supported");
_screen->fadeClearSceneWindow(10);
completeDoorOperations();
_screen->fillRect(112, 0, 287, 119, 0, 0);
_screen->updateScreen();
Common::SeekableReadStreamEndianWrapper in(saveFile, !header.originalSave, DisposeAfterUse::YES);
for (int i = 0; i < 4; i++) {
LoLCharacter *c = &_characters[i];
c->flags = in.readUint16();
in.read(c->name, 11);
c->raceClassSex = in.readByte();
c->id = in.readSint16();
c->curFaceFrame = in.readByte();
c->tempFaceFrame = in.readByte();
c->screamSfx = in.readByte();
if (header.originalSave)
in.skip(4);
for (int ii = 0; ii < 8; ii++)
c->itemsMight[ii] = in.readUint16();
for (int ii = 0; ii < 8; ii++)
c->protectionAgainstItems[ii] = in.readUint16();
c->itemProtection = in.readUint16();
c->hitPointsCur = in.readSint16();
c->hitPointsMax = in.readUint16();
c->magicPointsCur = in.readSint16();
c->magicPointsMax = in.readUint16();
c->field_41 = in.readByte();
c->damageSuffered = in.readUint16();
c->weaponHit = in.readUint16();
c->totalMightModifier = in.readUint16();
c->totalProtectionModifier = in.readUint16();
c->might = in.readUint16();
c->protection = in.readUint16();
c->nextAnimUpdateCountdown = in.readSint16();
for (int ii = 0; ii < 11; ii++)
c->items[ii] = in.readUint16();
for (int ii = 0; ii < 3; ii++)
c->skillLevels[ii] = in.readByte();
for (int ii = 0; ii < 3; ii++)
c->skillModifiers[ii] = in.readSByte();
for (int ii = 0; ii < 3; ii++)
c->experiencePts[ii] = in.readUint32();
for (int ii = 0; ii < 5; ii++)
c->characterUpdateEvents[ii] = in.readByte();
for (int ii = 0; ii < 5; ii++)
c->characterUpdateDelay[ii] = in.readByte();
if (c->flags & 1) {
loadCharFaceShapes(i, c->id);
c->defaultModifiers = cdf[c->raceClassSex];
}
}
if (header.version < 17)
in.skip(80);
_currentBlock = in.readUint16();
_partyPosX = in.readUint16();
_partyPosY = in.readUint16();
_updateFlags = in.readUint16();
_scriptDirection = in.readByte();
_selectedSpell = in.readByte();
if (header.originalSave)
in.skip(2);
_sceneDefaultUpdate = in.readByte();
_compassBroken = in.readByte();
_drainMagic = in.readByte();
_currentDirection = in.readUint16();
_compassDirection = in.readUint16();
_selectedCharacter = in.readSByte();
if (header.originalSave)
in.skip(1);
_currentLevel = in.readByte();
for (int i = 0; i < 48; i++)
_inventory[i] = in.readSint16();
_inventoryCurItem = in.readSint16();
_itemInHand = in.readSint16();
_lastMouseRegion = in.readSint16();
if (header.originalSave || header.version <= 15) {
uint16 flags[40];
memset(flags, 0, sizeof(flags));
if (header.version == 14) {
for (int i = 0; i < 16; i++)
flags[i] = in.readUint16();
flags[26] = in.readUint16();
flags[36] = in.readUint16();
} else if (header.originalSave || header.version == 15) {
for (int i = 0; i < 40; i++)
flags[i] = in.readUint16();
}
memset(_flagsTable, 0, sizeof(_flagsTable));
for (uint i = 0; i < ARRAYSIZE(flags); ++i) {
for (uint k = 0; k < 16; ++k) {
if (flags[i] & (1 << k))
setGameFlag(((i << 4) & 0xFFF0) | (k & 0x000F));
}
}
} else {
uint32 flagsSize = in.readUint32();
assert(flagsSize <= sizeof(_flagsTable));
in.read(_flagsTable, flagsSize);
}
if (header.originalSave)
in.skip(120);
for (int i = 0; i < 24; i++)
_globalScriptVars[i] = in.readUint16();
if (header.originalSave)
in.skip(152);
_brightness = in.readByte();
_lampOilStatus = in.readByte();
_lampEffect = in.readSByte();
if (header.originalSave)
in.skip(1);
_credits = in.readUint16();
for (int i = 0; i < 8; i++)
_globalScriptVars2[i] = in.readUint16();
in.read(_availableSpells, 7);
_hasTempDataFlags = in.readUint32();
uint8 *origCmp = 0;
if (header.originalSave) {
in.skip(6);
origCmp = new uint8[2496];
}
for (int i = 0; i < 400; i++) {
LoLItem *t = &_itemsInPlay[i];
t->nextAssignedObject = in.readUint16();
t->nextDrawObject = in.readUint16();
t->flyingHeight = in.readByte();
t->block = in.readUint16();
t->x = in.readUint16();
t->y = in.readUint16();
t->level = in.readSByte();
t->itemPropertyIndex = in.readUint16();
t->shpCurFrame_flg = in.readUint16();
if (header.version < 17)
in.skip(4);
}
for (int i = 0; i < 1024; i++) {
LevelBlockProperty *l = &_levelBlockProperties[i];
l->assignedObjects = l->drawObjects = 0;
l->direction = 5;
}
for (int i = 0; i < 29; i++) {
if (!(_hasTempDataFlags & (1 << i))) {
if (header.originalSave) {
if (in.size() - in.pos() >= 2500)
in.skip(2500);
}
continue;
}
if (_lvlTempData[i]) {
delete[] _lvlTempData[i]->wallsXorData;
delete[] _lvlTempData[i]->flags;
releaseMonsterTempData(_lvlTempData[i]);
releaseFlyingObjectTempData(_lvlTempData[i]);
releaseWallOfForceTempData(_lvlTempData[i]);
delete _lvlTempData[i];
}
_lvlTempData[i] = new LevelTempData;
_lvlTempData[i]->wallsXorData = new uint8[4096];
_lvlTempData[i]->flags = new uint16[1024];
LoLMonster *lm = new LoLMonster[30];
_lvlTempData[i]->monsters = lm;
FlyingObject *lf = new FlyingObject[_numFlyingObjects];
_lvlTempData[i]->flyingObjects = lf;
LevelTempData *l = _lvlTempData[i];
uint32 next = in.pos() + 2500;
if (header.originalSave) {
in.skip(4);
in.read(origCmp, in.readUint16());
_screen->decodeFrame4(origCmp, _tempBuffer5120, 5120);
memcpy(l->wallsXorData, _tempBuffer5120, 4096);
for (int ii = 0; ii < 1024; ii++)
l->flags[ii] = _tempBuffer5120[4096 + ii];
} else {
in.read(l->wallsXorData, 4096);
for (int ii = 0; ii < 1024; ii++)
l->flags[ii] = in.readByte();
}
if (header.originalSave)
l->monsterDifficulty = in.readUint16();
for (int ii = 0; ii < 30; ii++) {
LoLMonster *m = &lm[ii];
m->nextAssignedObject = in.readUint16();
m->nextDrawObject = in.readUint16();
m->flyingHeight = in.readByte();
m->block = in.readUint16();
m->x = in.readUint16();
m->y = in.readUint16();
m->shiftStep = in.readSByte();
m->destX = in.readUint16();
m->destY = in.readUint16();
m->destDirection = in.readByte();
m->hitOffsX = in.readSByte();
m->hitOffsY = in.readSByte();
m->currentSubFrame = in.readByte();
m->mode = in.readByte();
m->fightCurTick = in.readSByte();
m->id = in.readByte();
m->direction = in.readByte();
m->facing = in.readByte();
m->flags = in.readUint16();
m->damageReceived = in.readUint16();
m->hitPoints = in.readSint16();
m->speedTick = in.readByte();
m->type = in.readByte();
if (header.originalSave)
in.skip(4);
m->numDistAttacks = in.readByte();
m->curDistWeapon = in.readByte();
m->distAttackTick = in.readSByte();
m->assignedItems = in.readUint16();
m->properties = &_monsterProperties[m->type];
in.read(m->equipmentShapes, 4);
}
for (int ii = 0; ii < _numFlyingObjects; ii++) {
FlyingObject *m = &lf[ii];
m->enable = in.readByte();
m->objectType = in.readByte();
m->attackerId = in.readUint16();
m->item = in.readSint16();
m->x = in.readUint16();
m->y = in.readUint16();
m->flyingHeight = in.readByte();
m->direction = in.readByte();
m->distance = in.readByte();
m->field_D = in.readSByte();
m->c = in.readByte();
m->flags = in.readByte();
m->wallFlags = in.readByte();
}
if (header.originalSave)
in.seek(next, SEEK_SET);
else
l->monsterDifficulty = in.readByte();
}
delete[] origCmp;
calcCharPortraitXpos();
memset(_moneyColumnHeight, 0, sizeof(_moneyColumnHeight));
int t = _credits;
_credits = 0;
giveCredits(t, 0);
setHandItem(_itemInHand);
loadLevel(_currentLevel);
gui_drawPlayField();
restartPlayTimerAt(header.totalPlaySecs);
timerSpecialCharacterUpdate(0);
_flagsTable[73] |= 0x08;
while (!_screen->isMouseVisible())
_screen->showMouse();
return Common::kNoError;
}
Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
if (!out)
return _saveFileMan->getError();
completeDoorOperations();
generateTempData();
for (int i = 0; i < 4; i++) {
LoLCharacter *c = &_characters[i];
out->writeUint16BE(c->flags);
out->write(c->name, 11);
out->writeByte(c->raceClassSex);
out->writeSint16BE(c->id);
out->writeByte(c->curFaceFrame);
out->writeByte(c->tempFaceFrame);
out->writeByte(c->screamSfx);
for (int ii = 0; ii < 8; ii++)
out->writeUint16BE(c->itemsMight[ii]);
for (int ii = 0; ii < 8; ii++)
out->writeUint16BE(c->protectionAgainstItems[ii]);
out->writeUint16BE(c->itemProtection);
out->writeSint16BE(c->hitPointsCur);
out->writeUint16BE(c->hitPointsMax);
out->writeSint16BE(c->magicPointsCur);
out->writeUint16BE(c->magicPointsMax);
out->writeByte(c->field_41);
out->writeUint16BE(c->damageSuffered);
out->writeUint16BE(c->weaponHit);
out->writeUint16BE(c->totalMightModifier);
out->writeUint16BE(c->totalProtectionModifier);
out->writeUint16BE(c->might);
out->writeUint16BE(c->protection);
out->writeSint16BE(c->nextAnimUpdateCountdown);
for (int ii = 0; ii < 11; ii++)
out->writeUint16BE(c->items[ii]);
for (int ii = 0; ii < 3; ii++)
out->writeByte(c->skillLevels[ii]);
for (int ii = 0; ii < 3; ii++)
out->writeSByte(c->skillModifiers[ii]);
for (int ii = 0; ii < 3; ii++)
out->writeUint32BE(c->experiencePts[ii]);
for (int ii = 0; ii < 5; ii++)
out->writeByte(c->characterUpdateEvents[ii]);
for (int ii = 0; ii < 5; ii++)
out->writeByte(c->characterUpdateDelay[ii]);
}
out->writeUint16BE(_currentBlock);
out->writeUint16BE(_partyPosX);
out->writeUint16BE(_partyPosY);
out->writeUint16BE(_updateFlags);
out->writeByte(_scriptDirection);
out->writeByte(_selectedSpell);
out->writeByte(_sceneDefaultUpdate);
out->writeByte(_compassBroken);
out->writeByte(_drainMagic);
out->writeUint16BE(_currentDirection);
out->writeUint16BE(_compassDirection);
out->writeSByte(_selectedCharacter);
out->writeByte(_currentLevel);
for (int i = 0; i < 48; i++)
out->writeSint16BE(_inventory[i]);
out->writeSint16BE(_inventoryCurItem);
out->writeSint16BE(_itemInHand);
out->writeSint16BE(_lastMouseRegion);
out->writeUint32BE(ARRAYSIZE(_flagsTable));
out->write(_flagsTable, ARRAYSIZE(_flagsTable));
for (int i = 0; i < 24; i++)
out->writeUint16BE(_globalScriptVars[i]);
out->writeByte(_brightness);
out->writeByte(_lampOilStatus);
out->writeSByte(_lampEffect);
out->writeUint16BE(_credits);
for (int i = 0; i < 8; i++)
out->writeUint16BE(_globalScriptVars2[i]);
out->write(_availableSpells, 7);
out->writeUint32BE(_hasTempDataFlags);
resetItems(0);
for (int i = 0; i < 400; i++) {
LoLItem *t = &_itemsInPlay[i];
out->writeUint16BE(t->nextAssignedObject);
out->writeUint16BE(t->nextDrawObject);
out->writeByte(t->flyingHeight);
out->writeUint16BE(t->block);
out->writeUint16BE(t->x);
out->writeUint16BE(t->y);
out->writeSByte(t->level);
out->writeUint16BE(t->itemPropertyIndex);
out->writeUint16BE(t->shpCurFrame_flg);
}
addLevelItems();
for (int i = 0; i < 29; i++) {
LevelTempData *l = _lvlTempData[i];
if (!l || !(_hasTempDataFlags & (1 << i)))
continue;
out->write(l->wallsXorData, 4096);
for (int ii = 0; ii < 1024; ii++)
out->writeByte(l->flags[ii] & 0xFF);
LoLMonster *lm = (LoLMonster *)_lvlTempData[i]->monsters;
FlyingObject *lf = (FlyingObject *)_lvlTempData[i]->flyingObjects;
for (int ii = 0; ii < 30; ii++) {
LoLMonster *m = &lm[ii];
out->writeUint16BE(m->nextAssignedObject);
out->writeUint16BE(m->nextDrawObject);
out->writeByte(m->flyingHeight);
out->writeUint16BE(m->block);
out->writeUint16BE(m->x);
out->writeUint16BE(m->y);
out->writeSByte(m->shiftStep);
out->writeUint16BE(m->destX);
out->writeUint16BE(m->destY);
out->writeByte(m->destDirection);
out->writeSByte(m->hitOffsX);
out->writeSByte(m->hitOffsY);
out->writeByte(m->currentSubFrame);
out->writeByte(m->mode);
out->writeSByte(m->fightCurTick);
out->writeByte(m->id);
out->writeByte(m->direction);
out->writeByte(m->facing);
out->writeUint16BE(m->flags);
out->writeUint16BE(m->damageReceived);
out->writeSint16BE(m->hitPoints);
out->writeByte(m->speedTick);
out->writeByte(m->type);
out->writeByte(m->numDistAttacks);
out->writeByte(m->curDistWeapon);
out->writeSByte(m->distAttackTick);
out->writeUint16BE(m->assignedItems);
out->write(m->equipmentShapes, 4);
}
for (int ii = 0; ii < _numFlyingObjects; ii++) {
FlyingObject *m = &lf[ii];
out->writeByte(m->enable);
out->writeByte(m->objectType);
out->writeUint16BE(m->attackerId);
out->writeSint16BE(m->item);
out->writeUint16BE(m->x);
out->writeUint16BE(m->y);
out->writeByte(m->flyingHeight);
out->writeByte(m->direction);
out->writeByte(m->distance);
out->writeSByte(m->field_D);
out->writeByte(m->c);
out->writeByte(m->flags);
out->writeByte(m->wallFlags);
}
out->writeByte(l->monsterDifficulty);
}
out->finalize();
// check for errors
if (out->err()) {
warning("Can't write file '%s'. (Disk full?)", fileName);
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Saved game '%s.'", saveName);
}
delete out;
return Common::kNoError;
}
Graphics::Surface *LoLEngine::generateSaveThumbnail() const {
if (_flags.platform != Common::kPlatformPC98)
return 0;
uint8 *screenPal = new uint8[16 * 3];
assert(screenPal);
_screen->getRealPalette(0, screenPal);
uint8 *screenBuf = new uint8[Screen::SCREEN_W * Screen::SCREEN_H];
assert(screenBuf);
Graphics::Surface *dst = new Graphics::Surface();
assert(dst);
_screen->copyRegionToBuffer(0, 0, 0, 320, 200, screenBuf);
Screen_LoL::convertPC98Gfx(screenBuf, Screen::SCREEN_W, Screen::SCREEN_H, Screen::SCREEN_W);
::createThumbnail(dst, screenBuf, Screen::SCREEN_W, Screen::SCREEN_H, screenPal);
delete[] screenBuf;
delete[] screenPal;
return dst;
}
void LoLEngine::restoreBlockTempData(int levelIndex) {
memset(_tempBuffer5120, 0, 5120);
KyraRpgEngine::restoreBlockTempData(levelIndex);
restoreTempDataAdjustMonsterStrength(levelIndex - 1);
}
void *LoLEngine::generateMonsterTempData(LevelTempData *tmp) {
LoLMonster *m = new LoLMonster[30];
memcpy(m, _monsters, sizeof(LoLMonster) * 30);
tmp->monsterDifficulty = _monsterDifficulty;
return m;
}
void LoLEngine::restoreTempDataAdjustMonsterStrength(int index) {
if (_lvlTempData[index]->monsterDifficulty == _monsterDifficulty)
return;
uint16 d = (_monsterModifiers1[_lvlTempData[index]->monsterDifficulty] << 8) / _monsterModifiers1[_monsterDifficulty];
for (int i = 0; i < 30; i++) {
if (_monsters[i].mode >= 14 || _monsters[i].block == 0 || _monsters[i].hitPoints <= 0)
continue;
_monsters[i].hitPoints = (d * _monsters[i].hitPoints) >> 8;
if (_monsterDifficulty < _lvlTempData[index]->monsterDifficulty)
_monsters[i].hitPoints++;
if (_monsters[i].hitPoints == 0)
_monsters[i].hitPoints = 1;
}
}
void LoLEngine::restoreMonsterTempData(LevelTempData *tmp) {
memcpy(_monsters, tmp->monsters, sizeof(LoLMonster) * 30);
for (int i = 0; i < 30; i++) {
if (_monsters[i].block) {
_monsters[i].block = 0;
_monsters[i].properties = &_monsterProperties[_monsters[i].type];
placeMonster(&_monsters[i], _monsters[i].x, _monsters[i].y);
}
}
}
void LoLEngine::releaseMonsterTempData(LevelTempData *tmp) {
LoLMonster *p = (LoLMonster *)tmp->monsters;
delete[] p;
}
} // End of namespace Kyra
#endif // ENABLE_LOL

View File

@@ -0,0 +1,330 @@
/* 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/endian.h"
#include "common/savefile.h"
#include "common/substream.h"
#include "common/system.h"
#include "kyra/engine/kyra_mr.h"
#include "kyra/engine/timer.h"
namespace Kyra {
Common::Error KyraEngine_MR::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
if (!out)
return _saveFileMan->getError();
_timer->saveDataToFile(*out);
out->writeUint32BE(sizeof(_flagsTable));
out->write(_flagsTable, sizeof(_flagsTable));
out->writeSint16BE(_lastMusicCommand);
out->writeByte(_currentChapter);
out->writeByte(_characterShapeFile);
out->writeByte(_album.curPage);
out->writeSint16BE(_score);
out->writeSint16BE(_scoreMax);
out->writeByte(_malcolmsMood);
for (int i = 0; i < 30; ++i)
out->write(_conversationState[i], 30);
out->write(_newSceneDlgState, 40);
for (int i = 0; i < 100; ++i)
out->writeSint16BE(_hiddenItems[i]);
out->write(_scoreFlagTable, 26);
out->writeUint16BE(_mainCharacter.sceneId);
out->writeSint16BE(_mainCharacter.dlgIndex);
out->writeByte(_mainCharacter.height);
out->writeByte(_mainCharacter.facing);
out->writeUint16BE(_mainCharacter.animFrame);
out->writeByte(_mainCharacter.walkspeed);
for (int i = 0; i < 10; ++i)
out->writeUint16BE(_mainCharacter.inventory[i]);
out->writeSint16BE(_mainCharacter.x1);
out->writeSint16BE(_mainCharacter.y1);
out->writeSint16BE(_mainCharacter.x2);
out->writeSint16BE(_mainCharacter.y2);
out->writeSint16BE(_mainCharacter.x3);
out->writeSint16BE(_mainCharacter.y3);
for (int i = 0; i < 50; ++i) {
out->writeSint16BE(_itemList[i].id);
out->writeUint16BE(_itemList[i].sceneId);
out->writeSint16BE(_itemList[i].x);
out->writeSint16BE(_itemList[i].y);
}
for (int i = 0; i < 88; ++i) {
out->write(_talkObjectList[i].filename, 13);
out->writeByte(_talkObjectList[i].sceneAnim);
out->writeByte(_talkObjectList[i].sceneScript);
out->writeSint16BE(_talkObjectList[i].x);
out->writeSint16BE(_talkObjectList[i].y);
out->writeByte(_talkObjectList[i].color);
out->writeByte(_talkObjectList[i].sceneId);
}
for (int i = 0; i < 98; ++i) {
out->write(_sceneList[i].filename1, 10);
out->write(_sceneList[i].filename2, 10);
out->writeUint16BE(_sceneList[i].exit1);
out->writeUint16BE(_sceneList[i].exit2);
out->writeUint16BE(_sceneList[i].exit3);
out->writeUint16BE(_sceneList[i].exit4);
out->writeByte(_sceneList[i].flags);
out->writeByte(_sceneList[i].sound);
}
out->writeSint16BE(_itemInHand);
out->writeUint16BE(_sceneExit1);
out->writeUint16BE(_sceneExit2);
out->writeUint16BE(_sceneExit3);
out->writeUint16BE(_sceneExit4);
out->finalize();
// check for errors
if (out->err()) {
warning("Can't write file '%s'. (Disk full?)", fileName);
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Saved game '%s.'", saveName);
}
delete out;
return Common::kNoError;
}
Common::Error KyraEngine_MR::loadGameState(int slot) {
const char *fileName = getSavegameFilename(slot);
SaveHeader header;
Common::InSaveFile *saveFile = openSaveForReading(fileName, header);
if (!saveFile) {
showMessageFromCCode(17, 0xB3, 0);
snd_playSoundEffect(0x0D, 0xC8);
return Common::kUnknownError;
}
if (header.originalSave)
warning("Trying to load savegame from original interpreter, while this is possible, it is not officially supported");
if (_inventoryState) {
updateCharacterAnim(0);
restorePage3();
drawAnimObjects();
_inventoryState = true;
refreshAnimObjects(0);
hideInventory();
}
_deathHandler = -1;
if (!_unkSceneScreenFlag1)
_lastMusicCommand = -1;
int curShapes = _characterShapeFile;
Common::SeekableReadStreamEndianWrapper in(saveFile, !header.originalSave, DisposeAfterUse::YES);
_screen->hideMouse();
if (!header.originalSave) {
_timer->loadDataFromFile(in, header.version);
uint32 flagsSize = in.readUint32BE();
assert(flagsSize <= sizeof(_flagsTable));
in.read(_flagsTable, flagsSize);
}
_lastMusicCommand = in.readSint16();
_currentChapter = in.readByte();
_characterShapeFile = in.readByte();
if (header.version >= 12 || header.originalSave)
_album.curPage = in.readByte();
if (header.originalSave)
in.readByte();
_score = in.readSint16();
_scoreMax = in.readSint16();
_malcolmsMood = in.readByte();
if (header.originalSave)
in.seek(8, SEEK_CUR);
for (int i = 0; i < 30; ++i)
in.read(_conversationState[i], 30);
if (!header.originalSave) {
in.read(_newSceneDlgState, 40);
} else {
for (int i = 0; i < 40; ++i)
_newSceneDlgState[i] = in.readUint16();
}
for (int i = 0; i < 100; ++i)
_hiddenItems[i] = in.readSint16();
if (header.originalSave)
in.read(_flagsTable, 69);
in.read(_scoreFlagTable, 26);
_mainCharacter.sceneId = in.readUint16();
_mainCharacter.dlgIndex = in.readSint16();
_mainCharacter.height = in.readByte();
_mainCharacter.facing = in.readByte();
_mainCharacter.animFrame = in.readUint16();
if (!header.originalSave) {
_mainCharacter.walkspeed = in.readByte();
} else {
in.seek(2, SEEK_CUR);
_mainCharacter.walkspeed = in.readUint32();
}
for (int i = 0; i < 10; ++i)
_mainCharacter.inventory[i] = in.readUint16();
_mainCharacter.x1 = in.readSint16();
_mainCharacter.y1 = in.readSint16();
_mainCharacter.x2 = in.readSint16();
_mainCharacter.y2 = in.readSint16();
_mainCharacter.x3 = in.readSint16();
_mainCharacter.y3 = in.readSint16();
for (int i = 0; i < 50; ++i) {
_itemList[i].id = in.readSint16();
_itemList[i].sceneId = in.readUint16();
_itemList[i].x = in.readSint16();
_itemList[i].y = in.readSint16();
if (header.version <= 9 || header.originalSave)
in.readUint16();
}
for (int i = 0; i < 88; ++i) {
in.read(_talkObjectList[i].filename, 13);
_talkObjectList[i].sceneAnim = in.readByte();
_talkObjectList[i].sceneScript = in.readByte();
_talkObjectList[i].x = in.readSint16();
_talkObjectList[i].y = in.readSint16();
_talkObjectList[i].color = in.readByte();
if (header.version >= 13 || header.originalSave)
_talkObjectList[i].sceneId = in.readByte();
}
for (int i = 0; i < 98; ++i) {
if (!header.originalSave) {
in.read(_sceneList[i].filename1, 10);
} else {
in.read(_sceneList[i].filename1, 9);
_sceneList[i].filename1[9] = 0;
}
if (!header.originalSave) {
in.read(_sceneList[i].filename2, 10);
} else {
in.read(_sceneList[i].filename2, 9);
_sceneList[i].filename2[9] = 0;
}
_sceneList[i].exit1 = in.readUint16();
_sceneList[i].exit2 = in.readUint16();
_sceneList[i].exit3 = in.readUint16();
_sceneList[i].exit4 = in.readUint16();
_sceneList[i].flags = in.readByte();
_sceneList[i].sound = in.readByte();
}
_itemInHand = in.readSint16();
if (header.originalSave) {
uint32 currentTime = _system->getMillis();
for (int i = 0; i < 6; ++i)
_timer->setDelay(i, in.readSint32LE());
for (int i = 0; i < 6; ++i) {
if (in.readUint16LE())
_timer->enable(i);
else
_timer->disable(i);
}
for (int i = 0; i < 6; ++i)
_timer->setNextRun(i, currentTime + (in.readUint32LE() * _tickLength));
_timer->resetNextRun();
}
_sceneExit1 = in.readUint16();
_sceneExit2 = in.readUint16();
_sceneExit3 = in.readUint16();
_sceneExit4 = in.readUint16();
if (saveFile->err() || saveFile->eos()) {
warning("Load failed ('%s', '%s').", fileName, header.description.c_str());
return Common::kUnknownError;
} else {
debugC(1, kDebugLevelMain, "Loaded savegame '%s.'", header.description.c_str());
}
_loadingState = true;
updateCharacterAnim(0);
_loadingState = false;
if (curShapes != _characterShapeFile)
loadCharacterShapes(_characterShapeFile);
_mainCharX = _mainCharacter.x2 = _mainCharacter.x1;
_mainCharY = _mainCharacter.y2 = _mainCharacter.y1;
_mainCharacter.facing = 4;
_badConscienceShown = false;
_badConsciencePosition = false;
_goodConscienceShown = false;
_goodConsciencePosition = false;
restartPlayTimerAt(header.totalPlaySecs);
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
setHandItem(_itemInHand);
if (_lastMusicCommand >= 0 && !_unkSceneScreenFlag1)
snd_playWanderScoreViaMap(_lastMusicCommand, 1);
else if (_lastMusicCommand == -1)
snd_playWanderScoreViaMap(28, 1);
while (!_screen->isMouseVisible())
_screen->showMouse();
setCommandLineRestoreTimer(7);
_shownMessage = " ";
_restoreCommandLine = false;
// We didn't explicitly set the walk speed, but it's saved as part of
// the _timers array, so we need to re-sync it with _configWalkspeed.
setWalkspeed(_configWalkspeed);
return Common::kNoError;
}
} // End of namespace Kyra

View File

@@ -0,0 +1,126 @@
/* 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/>.
*
*/
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
#include "kyra/resource/resource.h"
#include "kyra/script/script_eob.h"
#include "common/system.h"
#include "common/savefile.h"
#include "common/substream.h"
namespace Kyra {
void KyraRpgEngine::generateTempData() {
int l = _currentLevel - 1;
if (_lvlTempData[l]) {
delete[] _lvlTempData[l]->wallsXorData;
delete[] _lvlTempData[l]->flags;
releaseMonsterTempData(_lvlTempData[l]);
releaseFlyingObjectTempData(_lvlTempData[l]);
releaseWallOfForceTempData(_lvlTempData[l]);
delete _lvlTempData[l];
}
_lvlTempData[l] = new LevelTempData;
_lvlTempData[l]->wallsXorData = new uint8[4096];
_lvlTempData[l]->flags = new uint16[1024];
const uint8 *p = getBlockFileData(_currentLevel);
uint16 len = READ_LE_UINT16(p + 4);
p += 6;
memset(_lvlTempData[l]->wallsXorData, 0, 4096);
memset(_lvlTempData[l]->flags, 0, 1024 * sizeof(uint16));
uint8 *d = _lvlTempData[l]->wallsXorData;
uint16 *df = _lvlTempData[l]->flags;
for (int i = 0; i < 1024; i++) {
for (int ii = 0; ii < 4; ii++)
*d++ = p[i * len + ii] ^ _levelBlockProperties[i].walls[ii];
*df++ = _levelBlockProperties[i].flags;
}
_lvlTempData[l]->monsters = generateMonsterTempData(_lvlTempData[l]);
_lvlTempData[l]->flyingObjects = generateFlyingObjectTempData(_lvlTempData[l]);
_lvlTempData[l]->wallsOfForce = generateWallOfForceTempData(_lvlTempData[l]);
_hasTempDataFlags |= (1 << l);
}
void KyraRpgEngine::restoreBlockTempData(int levelIndex) {
int l = levelIndex - 1;
const uint8 *p = getBlockFileData(levelIndex);
uint16 len = READ_LE_UINT16(p + 4);
p += 6;
memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty));
uint8 *t = _lvlTempData[l]->wallsXorData;
uint16 *t2 = _lvlTempData[l]->flags;
for (int i = 0; i < 1024; i++) {
for (int ii = 0; ii < 4; ii++)
_levelBlockProperties[i].walls[ii] = p[i * len + ii] ^ *t++;
_levelBlockProperties[i].flags = *t2++;
}
restoreMonsterTempData(_lvlTempData[l]);
restoreFlyingObjectTempData(_lvlTempData[l]);
restoreWallOfForceTempData(_lvlTempData[l]);
}
void KyraRpgEngine::releaseTempData() {
for (int i = 0; i < 29; i++) {
if (_lvlTempData[i]) {
delete[] _lvlTempData[i]->wallsXorData;
delete[] _lvlTempData[i]->flags;
releaseMonsterTempData(_lvlTempData[i]);
releaseFlyingObjectTempData(_lvlTempData[i]);
releaseWallOfForceTempData(_lvlTempData[i]);
delete _lvlTempData[i];
_lvlTempData[i] = 0;
}
}
}
void *KyraRpgEngine::generateFlyingObjectTempData(LevelTempData *tmp) {
assert(_flyingObjectStructSize == sizeof(EoBFlyingObject));
EoBFlyingObject *f = new EoBFlyingObject[_numFlyingObjects];
memcpy(f, _flyingObjectsPtr, sizeof(EoBFlyingObject) * _numFlyingObjects);
return f;
}
void KyraRpgEngine::restoreFlyingObjectTempData(LevelTempData *tmp) {
assert(_flyingObjectStructSize == sizeof(EoBFlyingObject));
memcpy(_flyingObjectsPtr, tmp->flyingObjects, sizeof(EoBFlyingObject) * _numFlyingObjects);
}
void KyraRpgEngine::releaseFlyingObjectTempData(LevelTempData *tmp) {
EoBFlyingObject *p = (EoBFlyingObject *)tmp->flyingObjects;
delete[] p;
}
} // End of namespace Kyra
#endif // ENABLE_EOB || ENABLE_LOL