aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/gui
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/gui')
-rw-r--r--engines/kyra/gui/debugger.cpp744
-rw-r--r--engines/kyra/gui/debugger.h138
-rw-r--r--engines/kyra/gui/gui.cpp134
-rw-r--r--engines/kyra/gui/gui.h137
-rw-r--r--engines/kyra/gui/gui_eob.cpp4339
-rw-r--r--engines/kyra/gui/gui_eob.h172
-rw-r--r--engines/kyra/gui/gui_hof.cpp1162
-rw-r--r--engines/kyra/gui/gui_hof.h84
-rw-r--r--engines/kyra/gui/gui_lok.cpp1133
-rw-r--r--engines/kyra/gui/gui_lok.h184
-rw-r--r--engines/kyra/gui/gui_lol.cpp2910
-rw-r--r--engines/kyra/gui/gui_lol.h179
-rw-r--r--engines/kyra/gui/gui_mr.cpp1584
-rw-r--r--engines/kyra/gui/gui_mr.h87
-rw-r--r--engines/kyra/gui/gui_rpg.cpp134
-rw-r--r--engines/kyra/gui/gui_v1.cpp620
-rw-r--r--engines/kyra/gui/gui_v1.h197
-rw-r--r--engines/kyra/gui/gui_v2.cpp879
-rw-r--r--engines/kyra/gui/gui_v2.h233
-rw-r--r--engines/kyra/gui/saveload.cpp278
-rw-r--r--engines/kyra/gui/saveload_eob.cpp1301
-rw-r--r--engines/kyra/gui/saveload_hof.cpp331
-rw-r--r--engines/kyra/gui/saveload_lok.cpp319
-rw-r--r--engines/kyra/gui/saveload_lol.cpp583
-rw-r--r--engines/kyra/gui/saveload_mr.cpp329
-rw-r--r--engines/kyra/gui/saveload_rpg.cpp127
26 files changed, 18318 insertions, 0 deletions
diff --git a/engines/kyra/gui/debugger.cpp b/engines/kyra/gui/debugger.cpp
new file mode 100644
index 0000000000..f8fa7f8502
--- /dev/null
+++ b/engines/kyra/gui/debugger.cpp
@@ -0,0 +1,744 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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, 0);
+ 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, argv[2]) ? "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->_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::String dir = ConfMan.get("savepath");
+ if (dir == "None")
+ dir.clear();
+
+ 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().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().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[] = { 1, 15, 15, 'U', 'D', 164, 'O', 'O' };
+
+ 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 = (uint8)'°';
+ else if (f & 8)
+ c = 216;
+ else if (f & 1)
+ c = 2;
+
+ if (_vm->_currentBlock == i) {
+ c = 'X';
+ } 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\n\n", 'X', 216, 'U', 'D', 'O', 164, '/', '°', 15, 1);
+
+ 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
diff --git a/engines/kyra/gui/debugger.h b/engines/kyra/gui/debugger.h
new file mode 100644
index 0000000000..86df8ec1e6
--- /dev/null
+++ b/engines/kyra/gui/debugger.h
@@ -0,0 +1,138 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
+
+ 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);
+ virtual ~Debugger_LoK() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
+
+ virtual void initialize();
+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);
+ virtual ~Debugger_v2() {}
+
+ virtual void initialize();
+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);
+
+ virtual void initialize();
+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);
+
+ virtual void initialize();
+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
diff --git a/engines/kyra/gui/gui.cpp b/engines/kyra/gui/gui.cpp
new file mode 100644
index 0000000000..dfceee86ea
--- /dev/null
+++ b/engines/kyra/gui/gui.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#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->screen()) {
+ _saveSlotsListUpdateNeeded = true;
+ _savegameListSize = 0;
+ _savegameList = 0;
+}
+
+GUI::~GUI() {
+ if (_savegameList) {
+ for (int i = 0; i < _savegameListSize; i++)
+ delete[] _savegameList[i];
+ delete[] _savegameList;
+ _savegameList = 0;
+ }
+}
+
+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];
+ memset(_savegameList, 0, _savegameListSize * sizeof(char *));
+
+ 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) {
+ *listEntry = new char[header.description.size() + 1];
+ Common::strlcpy(*listEntry, header.description.c_str(), header.description.size() + 1);
+ Util::convertISOToDOS(*listEntry);
+ delete in;
+ } else {
+ *listEntry = 0;
+ error("GUI::updateSavegameList(): Unexpected missing save file for slot: %d.", _saveSlots[i]);
+ }
+ }
+
+ } else {
+ _savegameList = 0;
+ }
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/gui/gui.h b/engines/kyra/gui/gui.h
new file mode 100644
index 0000000000..7b708c7436
--- /dev/null
+++ b/engines/kyra/gui/gui.h
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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();
+
+ uint32 _lastScreenUpdate;
+ char **_savegameList;
+ int _savegameListSize;
+ bool _saveSlotsListUpdateNeeded;
+
+ Common::KeyState _keyPressed;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
new file mode 100644
index 0000000000..761524177e
--- /dev/null
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -0,0 +1,4339 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/engine/eobcommon.h"
+#include "kyra/gui/gui_eob.h"
+#include "kyra/script/script_eob.h"
+#include "kyra/text/text_rpg.h"
+#include "kyra/engine/timer.h"
+#include "kyra/engine/util.h"
+
+#include "backends/keymapper/keymapper.h"
+#include "common/system.h"
+#include "common/savefile.h"
+#include "graphics/scaler.h"
+
+namespace Kyra {
+
+Button *EoBCoreEngine::gui_getButton(Button *buttonList, int index) {
+ while (buttonList) {
+ if (buttonList->index == index)
+ return buttonList;
+ buttonList = buttonList->nextButton;
+ }
+
+ return 0;
+}
+
+void EoBCoreEngine::gui_drawPlayField(bool refresh) {
+ _screen->loadEoBBitmap("PLAYFLD", _cgaMappingDeco, 5, 3, 2);
+ int cp = _screen->setCurPage(2);
+ gui_drawCompass(true);
+
+ if (refresh && !_sceneDrawPage2)
+ drawScene(0);
+
+ _screen->setCurPage(cp);
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
+
+ if (!_loading)
+ _screen->updateScreen();
+
+ _screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
+}
+
+void EoBCoreEngine::gui_restorePlayField() {
+ loadVcnData(0, (_flags.gameID == GI_EOB1) ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
+ _screen->_curPage = 0;
+ gui_drawPlayField(true);
+ gui_drawAllCharPortraitsWithStats();
+}
+
+void EoBCoreEngine::gui_drawAllCharPortraitsWithStats() {
+ for (int i = 0; i < 6; i++)
+ gui_drawCharPortraitWithStats(i);
+}
+
+void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
+ if (!testCharacter(index, 1))
+ return;
+
+ static const uint16 charPortraitPosX[] = { 8, 80, 184, 256 };
+ static const uint16 charPortraitPosY[] = { 2, 54, 106 };
+
+ EoBCharacter *c = &_characters[index];
+ int txtCol1 = 12;
+ int txtCol2 = 15;
+
+ if ((_flags.gameID == GI_EOB1 && c->flags & 6) || (_flags.gameID == GI_EOB2 && c->flags & 0x0E)) {
+ txtCol1 = 8;
+ txtCol2 = 6;
+ }
+
+ if (_currentControlMode == 0) {
+ int x2 = charPortraitPosX[index & 1];
+ int y2 = charPortraitPosY[index >> 1];
+ Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT);
+
+ _screen->copyRegion(176, 168, x2 , y2, 64, 24, 2, 2, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(240, 168, x2, y2 + 24, 64, 26, 2, 2, Screen::CR_NO_P_CHECK);
+ int cp = _screen->setCurPage(2);
+
+ if (index == _exchangeCharacterId)
+ _screen->printText(_characterGuiStringsSt[0], x2 + 2, y2 + 2, 8, guiSettings()->colors.fill);
+ else
+ _screen->printText(c->name, x2 + 2, y2 + (_flags.platform == Common::kPlatformFMTowns ? 1 : 2), txtCol1, guiSettings()->colors.fill);
+
+ gui_drawFaceShape(index);
+ gui_drawWeaponSlot(index, 0);
+ gui_drawWeaponSlot(index, 1);
+ gui_drawHitpoints(index);
+
+ if (testCharacter(index, 2))
+ gui_drawCharPortraitStatusFrame(index);
+
+ if (c->damageTaken > 0) {
+ _screen->drawShape(2, _redSplatShape, x2 + 13, y2 + 30, 0);
+ Common::String tmpStr = Common::String::format("%d", c->damageTaken);
+ _screen->printText(tmpStr.c_str(), x2 + 34 - tmpStr.size() * 3, y2 + 42, (_configRenderMode == Common::kRenderCGA) ? 12 : 15, 0);
+ }
+
+ _screen->setCurPage(cp);
+ _screen->setFont(cf);
+
+ if (!cp) {
+ _screen->copyRegion(x2, y2, charPortraitPosX[2 + (index & 1)], y2, 64, 50, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ }
+ } else if ((_currentControlMode == 1 || _currentControlMode == 2) && index == _updateCharNum) {
+ _screen->copyRegion(176, 0, 0, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK);
+ _screen->_curPage = 2;
+ gui_drawFaceShape(index);
+ _screen->printShadedText(c->name, 219, 6, txtCol2, guiSettings()->colors.fill);
+ gui_drawHitpoints(index);
+ gui_drawFoodStatusGraph(index);
+
+ if (_currentControlMode == 1) {
+ if (c->hitPointsCur == -10)
+ _screen->printShadedText(_characterGuiStringsSt[1], 247, 158, 6, guiSettings()->colors.extraFill);
+ else if (c->hitPointsCur < 1)
+ _screen->printShadedText(_characterGuiStringsSt[2], 226, 158, 6, guiSettings()->colors.extraFill);
+ else if (c->effectFlags & (_flags.gameID == GI_EOB1 ? 0x80 : 0x2000))
+ _screen->printShadedText(_characterGuiStringsSt[3], 220, 158, 6, guiSettings()->colors.extraFill);
+ else if (c->flags & 2)
+ _screen->printShadedText(_characterGuiStringsSt[4], 235, 158, 6, guiSettings()->colors.extraFill);
+ else if (c->flags & 4)
+ _screen->printShadedText(_characterGuiStringsSt[5], 232, 158, 6, guiSettings()->colors.extraFill);
+ else if (c->flags & 8)
+ _screen->printShadedText(_characterGuiStringsSt[6], 232, 158, 6, guiSettings()->colors.extraFill);
+
+ for (int i = 0; i < 27; i++)
+ gui_drawInventoryItem(i, 0, 2);
+ gui_drawInventoryItem(16, 1, 2);
+
+ } else {
+ static const uint16 cm2X1[] = { 179, 272, 301 };
+ static const uint16 cm2Y1[] = { 36, 51, 51 };
+ static const uint16 cm2X2[] = { 271, 300, 318 };
+ static const uint16 cm2Y2[] = { 165, 165, 147 };
+
+ for (int i = 0; i < 3; i++)
+ _screen->fillRect(cm2X1[i], cm2Y1[i], cm2X2[i], cm2Y2[i], guiSettings()->colors.extraFill);
+
+ _screen->printShadedText(_characterGuiStringsIn[0], 183, 42, 15, guiSettings()->colors.extraFill);
+ _screen->printText(_chargenClassStrings[c->cClass], 183, 55, 12, guiSettings()->colors.extraFill);
+ _screen->printText(_chargenAlignmentStrings[c->alignment], 183, 62, 12, guiSettings()->colors.extraFill);
+ _screen->printText(_chargenRaceSexStrings[c->raceSex], 183, 69, 12, guiSettings()->colors.extraFill);
+
+ for (int i = 0; i < 6; i++)
+ _screen->printText(_chargenStatStrings[6 + i], 183, 82 + i * 7, 12, guiSettings()->colors.extraFill);
+
+ _screen->printText(_characterGuiStringsIn[1], 183, 124, 12, guiSettings()->colors.extraFill);
+ _screen->printText(_characterGuiStringsIn[2], 239, 138, 12, guiSettings()->colors.extraFill);
+ _screen->printText(_characterGuiStringsIn[3], 278, 138, 12, guiSettings()->colors.extraFill);
+
+ _screen->printText(getCharStrength(c->strengthCur, c->strengthExtCur).c_str(), 275, 82, 15, guiSettings()->colors.extraFill);
+ _screen->printText(Common::String::format("%d", c->intelligenceCur).c_str(), 275, 89, 15, guiSettings()->colors.extraFill);
+ _screen->printText(Common::String::format("%d", c->wisdomCur).c_str(), 275, 96, 15, guiSettings()->colors.extraFill);
+ _screen->printText(Common::String::format("%d", c->dexterityCur).c_str(), 275, 103, 15, guiSettings()->colors.extraFill);
+ _screen->printText(Common::String::format("%d", c->constitutionCur).c_str(), 275, 110, 15, guiSettings()->colors.extraFill);
+ _screen->printText(Common::String::format("%d", c->charismaCur).c_str(), 275, 117, 15, guiSettings()->colors.extraFill);
+ _screen->printText(Common::String::format("%d", c->armorClass).c_str(), 275, 124, 15, guiSettings()->colors.extraFill);
+
+ for (int i = 0; i < 3; i++) {
+ int t = getCharacterClassType(c->cClass, i);
+ if (t == -1)
+ continue;
+
+ _screen->printText(_chargenClassStrings[t + 15], 180, 145 + 7 * i, 12, guiSettings()->colors.extraFill);
+ Common::String tmpStr = Common::String::format("%d", c->experience[i]);
+ _screen->printText(tmpStr.c_str(), 251 - tmpStr.size() * 3, 145 + 7 * i, 15, guiSettings()->colors.extraFill);
+ tmpStr = Common::String::format("%d", c->level[i]);
+ _screen->printText(tmpStr.c_str(), 286 - tmpStr.size() * 3, 145 + 7 * i, 15, guiSettings()->colors.extraFill);
+ }
+ }
+
+ _screen->_curPage = 0;
+ _screen->copyRegion(176, 0, 176, 0, 144, 168, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(0, 0, 176, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ }
+}
+
+void EoBCoreEngine::gui_drawFaceShape(int index) {
+ if (!testCharacter(index, 1))
+ return;
+
+ static const uint8 xCoords[] = { 8, 80 };
+ static const uint8 yCoords[] = { 11, 63, 115 };
+
+ int x = xCoords[index & 1];
+ int y = yCoords[index >> 1];
+
+ if (!_screen->_curPage)
+ x += 176;
+
+ if (_currentControlMode) {
+ if (_updateCharNum != index)
+ return;
+
+ x = 181;
+ y = 3;
+ }
+
+ EoBCharacter *c = &_characters[index];
+
+ if (c->hitPointsCur == -10) {
+ _screen->drawShape(_screen->_curPage, _deadCharShape, x, y, 0);
+ return;
+ }
+
+ if (_flags.gameID == GI_EOB1) {
+ if (c->effectFlags & 4) {
+ _screen->fillRect(x, y, x + 31, y + 31, 12);
+ return;
+ }
+ } else {
+ if (c->effectFlags & 0x140) {
+ _screen->setFadeTable(_blackFadingTable);
+ _screen->setShapeFadingLevel(1);
+ }
+
+ if (c->flags & 2) {
+ _screen->setFadeTable(_greenFadingTable);
+ _screen->setShapeFadingLevel(1);
+ }
+
+ if (c->flags & 8) {
+ _screen->setFadeTable(_blueFadingTable);
+ _screen->setShapeFadingLevel(1);
+ }
+ }
+
+ _screen->drawShape(_screen->_curPage, c->faceShape, x, y, 0);
+
+ if (c->hitPointsCur < 1)
+ _screen->drawShape(_screen->_curPage, _disabledCharGrid, x, y, 0);
+
+ if (c->flags & 8 || c->flags & 2 || c->effectFlags & 0x140) {
+ _screen->setFadeTable(_greyFadingTable);
+ _screen->setShapeFadingLevel(0);
+ }
+}
+
+void EoBCoreEngine::gui_drawWeaponSlot(int charIndex, int slot) {
+ static const uint8 xCoords[] = { 40, 112 };
+ static const uint8 yCoords[] = { 11, 27, 63, 79, 115, 131 };
+
+ int x = xCoords[charIndex & 1];
+ int y = yCoords[(charIndex & 6) + slot];
+
+ if (!_screen->_curPage)
+ x += 176;
+
+ int itm = _characters[charIndex].inventory[slot];
+ gui_drawBox(x, y, 31, 16, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+
+ if (_characters[charIndex].slotStatus[slot]) {
+ gui_drawWeaponSlotStatus(x, y, _characters[charIndex].slotStatus[slot]);
+ return;
+ }
+
+ if (itm)
+ drawItemIconShape(_screen->_curPage, itm, x + 8, y);
+ else if (!slot && _flags.gameID == GI_EOB2 && checkScriptFlags(0x80000000))
+ _screen->drawShape(_screen->_curPage, _itemIconShapes[103], x + 8, y, 0);
+ else
+ _screen->drawShape(_screen->_curPage, _itemIconShapes[85 + slot], x + 8, y, 0);
+
+ if ((_characters[charIndex].disabledSlots & (1 << slot)) || !validateWeaponSlotItem(charIndex, slot) || (_characters[charIndex].hitPointsCur <= 0) || (_characters[charIndex].flags & 0x0C))
+ _screen->drawShape(_screen->_curPage, _weaponSlotGrid, x, y, 0);
+}
+
+void EoBCoreEngine::gui_drawWeaponSlotStatus(int x, int y, int status) {
+ Common::String tmpStr;
+ Common::String tmpStr2;
+
+ if (status > -3 || status == -5)
+ _screen->drawShape(_screen->_curPage, _greenSplatShape, x - 1, y, 0);
+ else
+ gui_drawBox(x, y, 31, 16, guiSettings()->colors.warningFrame1, guiSettings()->colors.warningFrame2, guiSettings()->colors.warningFill);
+
+ switch (status + 5) {
+ case 0:
+ tmpStr = _characterGuiStringsWp[2];
+ break;
+ case 1:
+ tmpStr = _characterGuiStringsWr[2];
+ tmpStr2 = _characterGuiStringsWr[3];
+ break;
+ case 2:
+ tmpStr = _characterGuiStringsWr[0];
+ tmpStr2 = _characterGuiStringsWr[1];
+ break;
+ case 3:
+ tmpStr = _characterGuiStringsWp[1];
+ break;
+ case 4:
+ tmpStr = _characterGuiStringsWp[0];
+ break;
+ default:
+ tmpStr = Common::String::format("%d", status);
+ break;
+ }
+
+ int textColor = (_configRenderMode == Common::kRenderCGA) ? 2 : 15;
+
+ if (!tmpStr2.empty()) {
+ _screen->printText(tmpStr.c_str(), x + (16 - tmpStr.size() * 3), y + 2, textColor, 0);
+ _screen->printText(tmpStr2.c_str(), x + (16 - tmpStr.size() * 3), y + 9, textColor, 0);
+ } else {
+ _screen->printText(tmpStr.c_str(), x + (16 - tmpStr.size() * 3), y + 5, textColor, 0);
+ }
+}
+
+void EoBCoreEngine::gui_drawHitpoints(int index) {
+ if (!testCharacter(index, 1))
+ return;
+
+ if (_currentControlMode && (index != _updateCharNum))
+ return;
+
+ static const uint8 xCoords[] = { 23, 95 };
+ static const uint8 yCoords[] = { 46, 98, 150 };
+ static const uint8 barColor[] = { 3, 5, 8 };
+
+ int x = xCoords[index & 1];
+ int y = yCoords[index >> 1];
+ int w = 38;
+ int h = 3;
+
+ if (!_screen->_curPage)
+ x += 176;
+
+ if (_currentControlMode) {
+ x = 250;
+ y = 16;
+ w = 51;
+ h = 5;
+ }
+
+ EoBCharacter *c = &_characters[index];
+
+ if (_configHpBarGraphs) {
+ int bgCur = c->hitPointsCur + 10;
+ int bgMax = c->hitPointsMax + 10;
+ int col = ((bgMax / 3) > bgCur) ? 1 : 0;
+ if (bgCur <= 10)
+ col = 2;
+
+ if (!_currentControlMode)
+ _screen->printText(_characterGuiStringsHp[0], x - 13, y - 1, 12, 0);
+
+
+ gui_drawHorizontalBarGraph(x, y, w, h, bgCur, bgMax, barColor[col], guiSettings()->colors.barGraph);
+
+ } else {
+ Common::String tmpString = Common::String::format(_characterGuiStringsHp[1], c->hitPointsCur, c->hitPointsMax);
+
+ if (!_currentControlMode) {
+ x -= 13;
+ y -= 1;
+ }
+
+ _screen->printText(tmpString.c_str(), x, y, 12, guiSettings()->colors.fill);
+ }
+}
+
+void EoBCoreEngine::gui_drawFoodStatusGraph(int index) {
+ if (!_currentControlMode)
+ return;
+
+ if (!testCharacter(index, 1))
+ return;
+
+ EoBCharacter *c = &_characters[index];
+ if (!(c->flags & 1))
+ return;
+
+ if (index != _updateCharNum)
+ return;
+
+ uint8 col = c->food < 20 ? 8 : (c->food < 33 ? 5 : 3);
+ gui_drawHorizontalBarGraph(250, 25, 51, 5, c->food, 100, col, guiSettings()->colors.barGraph);
+}
+
+void EoBCoreEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2) {
+ gui_drawBox(x - 1, y - 1, w + 3, h + 2, guiSettings()->colors.frame2, guiSettings()->colors.frame1, -1);
+ KyraRpgEngine::gui_drawHorizontalBarGraph(x, y, w + 2, h, curVal, maxVal, col1, col2);
+}
+
+void EoBCoreEngine::gui_drawCharPortraitStatusFrame(int index) {
+ uint8 redGreenColor = (_partyEffectFlags & 0x20000) ? 4 : ((_configRenderMode == Common::kRenderCGA) ? 3 : 6);
+
+ static const uint8 xCoords[] = { 8, 80 };
+ static const uint8 yCoords[] = { 2, 54, 106 };
+ int x = xCoords[index & 1];
+ int y = yCoords[index >> 1];
+ int xOffset = (_configRenderMode == Common::kRenderCGA) ? 0 : 1;
+
+ if (!_screen->_curPage)
+ x += 176;
+
+ EoBCharacter *c = &_characters[index];
+
+ bool redGreen = ((c->effectFlags & 0x4818) || (_partyEffectFlags & 0x20000) || c->effectsRemainder[0] || c->effectsRemainder[1]) ? true : false;
+ bool yellow = ((c->effectFlags & 0x13000) || (_partyEffectFlags & 0x8420)) ? true : false;
+
+ if (redGreen || yellow) {
+ if (redGreen && !yellow) {
+ _screen->drawBox(x, y, x + 63, y + 49, redGreenColor);
+ return;
+ }
+
+ if (yellow && !redGreen) {
+ _screen->drawBox(x, y, x + 63, y + 49, 5);
+ return;
+ }
+
+ int iX = x;
+ int iY = y;
+
+ for (int i = 0; i < 64; i += 16) {
+ x = iX + i;
+ if (redGreen) {
+ _screen->drawClippedLine(x, y, x + 7, y, redGreenColor);
+ _screen->drawClippedLine(x + 8, y + 49, x + 15, y + 49, redGreenColor);
+ }
+ if (yellow) {
+ _screen->drawClippedLine(x + 8, y, x + 15, y, 5);
+ _screen->drawClippedLine(x, y + 49, x + 7, y + 49, 5);
+ }
+ }
+
+ x = iX;
+
+ for (int i = 1; i < 48; i += 12) {
+ y = iY + i - 1;
+
+ if (yellow) {
+ _screen->drawClippedLine(x, y + 1, x, y + 6, 5);
+ _screen->drawClippedLine(x + 63, y + 7, x + 63, y + 12, 5);
+ }
+ if (redGreen) {
+ _screen->drawClippedLine(x, y + 7, x, y + 12, redGreenColor);
+ _screen->drawClippedLine(x + 63, y + 1, x + 63, y + 6, redGreenColor);
+ }
+ }
+
+ } else {
+ _screen->drawClippedLine(x, y, x + 62, y, guiSettings()->colors.frame2);
+ _screen->drawClippedLine(x, y + 49, x + 62, y + 49, guiSettings()->colors.frame1);
+ _screen->drawClippedLine(x - xOffset, y, x - xOffset, y + 50, 12);
+ _screen->drawClippedLine(x + 63, y, x + 63, y + 50, 12);
+ }
+}
+
+void EoBCoreEngine::gui_drawInventoryItem(int slot, int special, int pageNum) {
+ int x = _inventorySlotsX[slot];
+ int y = _inventorySlotsY[slot];
+
+ int item = _characters[_updateCharNum].inventory[slot];
+ int cp = _screen->setCurPage(pageNum);
+
+ if (special) {
+ int wh = (slot == 25 || slot == 26) ? 10 : 18;
+
+ uint8 col1 = guiSettings()->colors.frame1;
+ uint8 col2 = guiSettings()->colors.frame2;
+ if (_configRenderMode == Common::kRenderCGA) {
+ col1 = 1;
+ col2 = 3;
+ }
+
+ gui_drawBox(x - 1, y - 1, wh, wh, col1, col2, slot == 16 ? -1 : guiSettings()->colors.fill);
+
+ if (slot == 16) {
+ _screen->fillRect(227, 65, 238, 69, 12);
+ int cnt = countQueuedItems(_characters[_updateCharNum].inventory[slot], -1, -1, 1, 1);
+ x = cnt >= 10 ? 227 : 233;
+ Common::String str = Common::String::format("%d", cnt);
+ _screen->printText(str.c_str(), x, 65, 15, 0);
+ }
+ }
+
+ if (slot != 16 && item) {
+ if (slot == 25 || slot == 26) {
+ x -= 4;
+ y -= 4;
+ }
+ drawItemIconShape(pageNum, item, x, y);
+ }
+ _screen->_curPage = cp;
+ _screen->updateScreen();
+}
+
+void EoBCoreEngine::gui_drawCompass(bool force) {
+ if (_currentDirection == _compassDirection && !force)
+ return;
+
+ static const uint8 shpX[2][3] = { { 0x70, 0x4D, 0x95 }, { 0x72, 0x4F, 0x97 } };
+ static const uint8 shpY[2][3] = { { 0x7F, 0x9A, 0x9A }, { 0x83, 0x9E, 0x9E } };
+ int g = _flags.gameID == GI_EOB1 ? 0 : 1;
+
+ for (int i = 0; i < 3; i++)
+ _screen->drawShape(_screen->_curPage, _compassShapes[(i << 2) + _currentDirection], shpX[g][i], shpY[g][i], 0);
+
+ _compassDirection = _currentDirection;
+}
+
+void EoBCoreEngine::gui_drawDialogueBox() {
+ _screen->set16bitShadingLevel(4);
+ gui_drawBox(0, 121, 320, 79, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+ txt()->clearCurDim();
+ _screen->set16bitShadingLevel(0);
+}
+
+void EoBCoreEngine::gui_drawSpellbook() {
+ _screen->setCurPage(2);
+ int numTab = (_flags.gameID == GI_EOB1) ? 5 : 6;
+ _screen->copyRegion(64, 121, 64, 121, 112, 56, 0, 2, Screen::CR_NO_P_CHECK);
+
+ for (int i = 0; i < numTab; i++) {
+ int col1 = 0;
+ int col2 = 1;
+ int col3 = 2;
+
+ if (_configRenderMode == Common::kRenderCGA) {
+ if (i == _openBookSpellLevel) {
+ col1 = 1;
+ col2 = 2;
+ col3 = 3;
+ }
+ } else {
+ col1 = guiSettings()->colors.inactiveTabFrame1;
+ col2 = guiSettings()->colors.inactiveTabFrame2;
+ col3 = guiSettings()->colors.inactiveTabFill;
+
+ if (i == _openBookSpellLevel) {
+ col1 = guiSettings()->colors.frame1;
+ col2 = guiSettings()->colors.frame2;
+ col3 = guiSettings()->colors.fill;
+ }
+ }
+
+ if (_flags.gameID == GI_EOB1) {
+ gui_drawBox(i * 21 + 71, 122, 21, 9, col1, col2, col3);
+ _screen->printText(_magicStrings7[i], i * 21 + 73, 123, 12, 0);
+ } else {
+ _screen->set16bitShadingLevel(4);
+ gui_drawBox(i * 18 + 68, 121, 18, 9, col1, col2, col3);
+ _screen->set16bitShadingLevel(0);
+ _screen->printText(Common::String::format("%d", i + 1).c_str(), i * 18 + 75, 123, 12, 0);
+ }
+ }
+
+ if (_flags.gameID == GI_EOB1)
+ gui_drawBox(71, 131, 105, 44, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+ else {
+ _screen->set16bitShadingLevel(4);
+ gui_drawBox(68, 130, 108, 47, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+ _screen->set16bitShadingLevel(0);
+ gui_drawBox(68, 168, 78, 9, guiSettings()->colors.extraFrame1, guiSettings()->colors.extraFrame2, guiSettings()->colors.extraFill);
+ gui_drawBox(146, 168, 14, 9, guiSettings()->colors.extraFrame1, guiSettings()->colors.extraFrame2, guiSettings()->colors.extraFill);
+ gui_drawBox(160, 168, 16, 9, guiSettings()->colors.extraFrame1, guiSettings()->colors.extraFrame2, guiSettings()->colors.extraFill);
+ gui_drawSpellbookScrollArrow(150, 169, 0);
+ gui_drawSpellbookScrollArrow(165, 169, 1);
+ }
+
+ int textCol1 = (_configRenderMode == Common::kRenderCGA) ? 3 : 15;
+ int textCol2 = 8;
+ int textXa = 74;
+ int textXs = 71;
+ int textY = 170;
+ int col3 = (_configRenderMode == Common::kRenderCGA) ? 2 : guiSettings()->colors.fill;
+ int col4 = guiSettings()->colors.extraFill;
+ int col5 = 12;
+
+ if (_flags.gameID == GI_EOB1) {
+ textCol2 = (_configRenderMode == Common::kRenderCGA) ? 12 : 11;
+ textXa = textXs = 73;
+ textY = 168;
+ col4 = col3;
+ col5 = textCol1;
+ }
+
+ for (int i = 0; i < 7; i++) {
+ int d = _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + i];
+ if (_openBookSpellSelectedItem == i) {
+ if (d >= 0 && i < 6 && (i + _openBookSpellListOffset) < 9) {
+ _screen->fillRect(textXs, 132 + 6 * i, textXs + _screen->getTextWidth(_openBookSpellList[d]) - 1, 137 + 6 * i, textCol2);
+ _screen->printText(_openBookSpellList[d], textXs, 132 + 6 * i, textCol1, textCol2);
+ } else if (i == 6) {
+ if (_flags.gameID == GI_EOB2)
+ _screen->fillRect(69, 169, 144, 175, textCol2);
+ _screen->printText(_magicStrings1[0], textXa, textY, textCol1, textCol2);
+ }
+ } else {
+ if (d >= 0 && i < 6 && (i + _openBookSpellListOffset) < 9)
+ _screen->printText(_openBookSpellList[d], textXs, 132 + 6 * i, textCol1, col3);
+ else
+ _screen->printText(_magicStrings1[0], textXa, textY, col5, col4);
+ }
+ }
+
+ if (_characters[_openBookChar].disabledSlots & 4) {
+ static const uint8 xpos[] = { 0x44, 0x62, 0x80, 0x90 };
+ static const uint8 ypos[] = { 0x82, 0x92, 0x98 };
+ for (int yc = 0; yc < 3; yc++) {
+ for (int xc = 0; xc < 4; xc++)
+ _screen->drawShape(_screen->_curPage, _weaponSlotGrid, xpos[xc], ypos[yc], 0);
+ }
+ }
+
+ if (_openBookAvailableSpells[_openBookSpellLevel * 10 + 6] <= 0)
+ _screen->drawShape(2, _blackBoxWideGrid, 146, 168, 0);
+
+ _screen->setCurPage(0);
+ _screen->copyRegion(64, 121, 64, 121, 112, 56, 2, 0, Screen::CR_NO_P_CHECK);
+ if (!_loading)
+ _screen->updateScreen();
+}
+
+void EoBCoreEngine::gui_drawSpellbookScrollArrow(int x, int y, int direction) {
+ static const uint8 x1[] = { 0, 2, 1, 0, 2, 2 };
+ static const uint8 x2[] = { 2, 4, 5, 6, 4, 4 };
+ if (direction) {
+ _screen->setPagePixel(_screen->_curPage, x + 3, y + 5, 12);
+ for (int i = 1; i < 6; i++)
+ _screen->drawClippedLine(x + x1[i], (5 - i) + y, x + x2[i], (5 - i) + y, 12);
+ } else {
+ _screen->setPagePixel(_screen->_curPage, x + 3, y, 12);
+ for (int i = 1; i < 6; i++)
+ _screen->drawClippedLine(x + x1[i], y + i, x + x2[i], y + i, 12);
+ }
+}
+
+void EoBCoreEngine::gui_updateSlotAfterScrollUse() {
+ _characters[_openBookChar].disabledSlots ^= (1 << (--_castScrollSlot));
+ setCharEventTimer(_openBookChar, 18, _castScrollSlot + 2, 1);
+ gui_drawCharPortraitWithStats(_openBookChar);
+ _openBookChar = _openBookCharBackup;
+ _openBookType = _openBookTypeBackup;
+ _castScrollSlot = 0;
+ gui_toggleButtons();
+}
+
+void EoBCoreEngine::gui_updateControls() {
+ Button b;
+ if (_currentControlMode)
+ clickedPortraitRestore(&b);
+ if (_updateFlags)
+ clickedSpellbookAbort(&b);
+}
+
+void EoBCoreEngine::gui_toggleButtons() {
+ if (_currentControlMode == 0)
+ gui_setPlayFieldButtons();
+ else if (_currentControlMode == 1)
+ gui_setInventoryButtons();
+ else if (_currentControlMode == 2)
+ gui_setStatsListButtons();
+}
+
+void EoBCoreEngine::gui_setPlayFieldButtons() {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_updateFlags ? _buttonList2 : _buttonList1);
+}
+
+void EoBCoreEngine::gui_setInventoryButtons() {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_updateFlags ? _buttonList5 : _buttonList3);
+}
+
+void EoBCoreEngine::gui_setStatsListButtons() {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_updateFlags ? _buttonList6 : _buttonList4);
+}
+
+void EoBCoreEngine::gui_setSwapCharacterButtons() {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_buttonList7);
+}
+
+void EoBCoreEngine::gui_setCastOnWhomButtons() {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_buttonList8);
+}
+
+void EoBCoreEngine::gui_initButton(int index, int, int, int) {
+ Button *b = 0;
+ int cnt = 1;
+
+ if (_flags.gameID == GI_EOB1 && index > 92)
+ return;
+
+ if (_activeButtons) {
+ Button *n = _activeButtons;
+ while (n->nextButton) {
+ ++cnt;
+ n = n->nextButton;
+ }
+
+ ++cnt;
+ b = n->nextButton = &_activeButtonData[cnt];
+ } else {
+ b = &_activeButtonData[0];
+ _activeButtons = b;
+ }
+
+ *b = Button();
+ b->data0Val2 = 12;
+ b->data1Val2 = b->data2Val2 = 15;
+ b->data3Val2 = 8;
+
+ b->index = index + 1;
+
+ const EoBGuiButtonDef *d = &_buttonDefs[index];
+ b->buttonCallback = _buttonCallbacks[index];
+
+ if (_flags.gameID == GI_EOB1) {
+ // EOB1 spellbook modifications
+ if (index > 60 && index < 66) {
+ d = &_buttonDefs[index + 34];
+ b->buttonCallback = _buttonCallbacks[index + 34];
+ } else if (index == 88) {
+ d = &_buttonDefs[index + 12];
+ b->buttonCallback = _buttonCallbacks[index + 12];
+ }
+ }
+
+ b->x = d->x;
+ b->y = d->y;
+ b->width = d->w;
+ b->height = d->h;
+
+ // EOB1 spellbook modifications
+ if (_flags.gameID == GI_EOB1 && ((index > 66 && index < 73) || (index > 76 && index < 79)))
+ b->y++;
+
+ b->flags = d->flags;
+ b->keyCode = d->keyCode;
+ b->keyCode2 = d->keyCode2;
+ b->arg = d->arg;
+}
+
+int EoBCoreEngine::clickedCharPortraitDefault(Button *button) {
+ if (!testCharacter(button->arg, 1))
+ return 1;
+
+ gui_processCharPortraitClick(button->arg);
+ return 0;
+}
+
+int EoBCoreEngine::clickedCamp(Button *button) {
+ gui_updateControls();
+ disableSysTimer(2);
+ int cd = _screen->curDimIndex();
+
+ for (int i = 0; i < 6; i++) {
+ if (!testCharacter(i, 1))
+ continue;
+ _characters[i].damageTaken = 0;
+ _characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
+ gui_drawCharPortraitWithStats(i);
+ }
+
+ _screen->copyPage(0, 7);
+
+ // Create a thumbnail from the screen for a possible savegame.
+ // This ensures that all special rendering (EGA dithering, 16bit rendering, Japanese font rendering) will be visible on the thumbnail.
+ ::createThumbnailFromScreen(&_thumbNail);
+
+ _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK);
+
+ _gui->runCampMenu();
+
+ _screen->copyRegion(0, 0, 0, 120, 176, 24, 12, 2, Screen::CR_NO_P_CHECK);
+ _screen->setScreenDim(cd);
+
+ _thumbNail.free();
+
+ drawScene(0);
+
+ for (int i = 0; i < 6; i++)
+ sortCharacterSpellList(i);
+
+ _screen->setCurPage(0);
+ const ScreenDim *dm = _screen->getScreenDim(10);
+ _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
+
+ _screen->updateScreen();
+
+ enableSysTimer(2);
+ advanceTimers(_restPartyElapsedTime);
+ _restPartyElapsedTime = 0;
+
+ checkPartyStatus(true);
+
+ return button->arg;
+}
+
+int EoBCoreEngine::clickedSceneDropPickupItem(Button *button) {
+ uint16 block = _currentBlock;
+ if (button->arg > 1) {
+ block = calcNewBlockPosition(_currentBlock, _currentDirection);
+ int f = _wllWallFlags[_levelBlockProperties[block].walls[_sceneDrawVarDown]];
+ if (!(f & 0x0B))
+ return 1;
+ }
+ int d = _dropItemDirIndex[(_currentDirection << 2) + button->arg];
+
+ if (_itemInHand) {
+ setItemPosition((Item *)&_levelBlockProperties[block & 0x3FF].drawObjects, block, _itemInHand, d);
+ setHandItem(0);
+ runLevelScript(block, 4);
+ } else {
+ d = getQueuedItem((Item *)&_levelBlockProperties[block].drawObjects, d, -1);
+ if (!d)
+ return 1;
+ setHandItem(d);
+ runLevelScript(block, 8);
+ }
+
+ _sceneUpdateRequired = true;
+ return 1;
+}
+
+int EoBCoreEngine::clickedCharPortrait2(Button *button) {
+ if (!_gui->_progress) {
+ if (!testCharacter(button->arg, 1))
+ return button->index;
+ }
+
+ _currentControlMode = 1;
+ if (!_gui->_progress)
+ _updateCharNum = button->arg;
+
+ _screen->copyRegion(176, 0, 0, 0, 144, 168, 0, 5, Screen::CR_NO_P_CHECK);
+ gui_drawCharPortraitWithStats(_updateCharNum);
+ gui_setInventoryButtons();
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedWeaponSlot(Button *button) {
+ if (!testCharacter(button->arg, 1))
+ return 1;
+
+ // Fix this using the coordinates from gui_drawWeaponSlot().
+ // The coordinates used in the original are slightly wrong
+ // (most noticeable for characters 5 and 6).
+ static const uint8 sY[] = { 27, 27, 79, 79, 131, 131 };
+ int slot = sY[button->arg] > _mouseY ? 0 : 1;
+
+ uint16 flags = _configMouseBtSwap ? _gui->_flagsMouseRight : _gui->_flagsMouseLeft;
+
+ if ((flags & 0x7F) == 1)
+ gui_processWeaponSlotClickLeft(button->arg, slot);
+ else
+ gui_processWeaponSlotClickRight(button->arg, slot);
+
+ return 1;
+}
+
+int EoBCoreEngine::clickedCharNameLabelRight(Button *button) {
+ if (!testCharacter(button->arg, 1))
+ return button->index;
+
+ if (_updateFlags) {
+ Button b;
+ clickedSpellbookAbort(&b);
+ }
+
+ if (_exchangeCharacterId == -1) {
+ _exchangeCharacterId = button->arg;
+ gui_setSwapCharacterButtons();
+ gui_drawCharPortraitWithStats(_exchangeCharacterId);
+ enableTimer(0);
+ } else {
+ int d = _exchangeCharacterId;
+ _exchangeCharacterId = -1;
+ exchangeCharacters(d, button->arg);
+
+ _timer->disable(0);
+ gui_drawCharPortraitWithStats(d);
+ gui_processCharPortraitClick(button->arg);
+ gui_drawCharPortraitWithStats(button->arg);
+ gui_setPlayFieldButtons();
+ setupCharacterTimers();
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedInventorySlot(Button *button) {
+ gui_processInventorySlotClick(button->arg);
+ return button->index;
+}
+
+int EoBCoreEngine::clickedEatItem(Button *button) {
+ eatItemInHand(_updateCharNum);
+ return button->index;
+}
+
+int EoBCoreEngine::clickedInventoryPrevChar(Button *button) {
+ if (_gui->_progress == 1)
+ _updateCharNum = 0;
+ else if (_gui->_progress == 2)
+ _updateCharNum = 1;
+ else
+ _updateCharNum = getNextValidCharIndex(_updateCharNum, -1);
+
+ gui_drawCharPortraitWithStats(_updateCharNum);
+ return button->index;
+}
+
+int EoBCoreEngine::clickedInventoryNextChar(Button *button) {
+ int oldVal = _updateCharNum;
+ int v = button->arg == 2 ? 2 : 0;
+
+ if (_gui->_progress == 1)
+ _updateCharNum = v + 2;
+ else if (_gui->_progress == 2)
+ _updateCharNum = v + 3;
+ else
+ _updateCharNum = getNextValidCharIndex(_updateCharNum, 1);
+
+ if (!testCharacter(_updateCharNum, 1)) {
+ _updateCharNum = oldVal;
+ return 1;
+ }
+
+ gui_drawCharPortraitWithStats(_updateCharNum);
+ return button->index;
+}
+
+int EoBCoreEngine::clickedSpellbookTab(Button *button) {
+ _openBookSpellLevel = button->arg;
+ _openBookSpellListOffset = 0;
+
+ for (_openBookSpellSelectedItem = 0; _openBookSpellSelectedItem < 6; _openBookSpellSelectedItem++) {
+ if (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellSelectedItem] > 0)
+ break;
+ }
+
+ gui_drawSpellbook();
+
+ _characters[_openBookChar].slotStatus[3] = _openBookSpellLevel;
+ _characters[_openBookChar].slotStatus[2] = _openBookSpellSelectedItem;
+ _characters[_openBookChar].slotStatus[4] = _openBookSpellListOffset;
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedSpellbookList(Button *button) {
+ int listIndex = button->arg;
+ bool spellLevelAvailable = false;
+
+ if (listIndex == 6) {
+ for (int i = 0; i < 10; i++) {
+ if (_openBookAvailableSpells[_openBookSpellLevel * 10 + i] > 0) {
+ spellLevelAvailable = true;
+ break;
+ }
+ }
+ if (!spellLevelAvailable)
+ return button->index;
+
+ int v = (_gui->_progress == 1) ? -1 : ((_gui->_progress == 2) ? 1 : 0);
+
+ _openBookSpellSelectedItem += _openBookSpellListOffset;
+ if (_openBookSpellSelectedItem == 12 || (_openBookSpellSelectedItem == 6 && _openBookSpellListOffset == 0))
+ _openBookSpellSelectedItem = 9;
+
+ do {
+ _openBookSpellSelectedItem += v;
+ int s = (_openBookSpellSelectedItem >= 0) ? _openBookSpellSelectedItem : 9;
+ _openBookSpellSelectedItem = (s <= 9) ? s : 0;
+ } while (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellSelectedItem] <= 0 && _openBookSpellSelectedItem != 9);
+
+ if (_openBookSpellSelectedItem >= 6) {
+ _openBookSpellListOffset = 6;
+ if (_openBookSpellSelectedItem == 9)
+ _openBookSpellSelectedItem = 6;
+ else
+ _openBookSpellSelectedItem -= 6;
+ } else {
+ _openBookSpellListOffset = 0;
+ }
+
+ if (_openBookSpellListOffset == 6 && _openBookAvailableSpells[_openBookSpellLevel * 10 + 6] <= 0)
+ _openBookSpellListOffset = 0;
+
+ gui_drawSpellbook();
+
+ } else {
+ if (listIndex == 7 || _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + listIndex] > 0) {
+ if (listIndex < 6) {
+ if (_openBookSpellListOffset + listIndex < 9)
+ _openBookSpellSelectedItem = listIndex;
+ else if (listIndex != 7)
+ return button->index;
+ } else if (listIndex != 7) {
+ return button->index;
+ }
+
+ if (_openBookSpellSelectedItem < 6 && ((_openBookSpellSelectedItem + _openBookSpellListOffset) < 9)) {
+ if (_characters[_openBookChar].disabledSlots & 4)
+ return button->index;
+
+ gui_drawSpellbook();
+
+ int s = _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem];
+ if (_openBookType == 1)
+ s += _clericSpellOffset;
+
+ castSpell(s, 0);
+
+ } else if ((_openBookSpellSelectedItem == 6 && listIndex == 7) || (_openBookSpellSelectedItem != 6 && listIndex == 6)) {
+ Button b;
+ clickedSpellbookAbort(&b);
+ }
+ }
+ }
+
+ _characters[_openBookChar].slotStatus[2] = _openBookSpellSelectedItem;
+ _characters[_openBookChar].slotStatus[4] = _openBookSpellListOffset;
+ return button->index;
+}
+
+int EoBCoreEngine::clickedCastSpellOnCharacter(Button *button) {
+ _activeSpellCharId = button->arg & 0xFF;
+
+ if (_activeSpellCharId == 0xFF) {
+ printWarning(_magicStrings3[_flags.gameID == GI_EOB1 ? 2 : 1]);
+ if (_castScrollSlot) {
+ gui_updateSlotAfterScrollUse();
+ } else {
+ gui_toggleButtons();
+ gui_drawSpellbook();
+ }
+ } else {
+ if (_characters[_activeSpellCharId].flags & 1)
+ startSpell(_activeSpell);
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedInventoryNextPage(Button *button) {
+ if (_currentControlMode == 2) {
+ gui_setInventoryButtons();
+ _currentControlMode = 1;
+ } else {
+ gui_setStatsListButtons();
+ _currentControlMode = 2;
+ }
+
+ gui_drawCharPortraitWithStats(_updateCharNum);
+ return button->index;
+}
+
+int EoBCoreEngine::clickedPortraitRestore(Button *button) {
+ _currentControlMode = 0;
+ _screen->_curPage = 2;
+ _screen->fillRect(0, 0, 143, 167, 0);
+ _screen->copyRegion(0, 0, 0, 0, 144, 168, 5, _screen->_curPage, Screen::CR_NO_P_CHECK);
+ gui_drawAllCharPortraitsWithStats();
+ _screen->_curPage = 0;
+ _screen->copyRegion(0, 0, 176, 0, 144, 168, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ gui_setPlayFieldButtons();
+ return button->index;
+}
+
+int EoBCoreEngine::clickedUpArrow(Button *button) {
+ int b = calcNewBlockPositionAndTestPassability(_currentBlock, _currentDirection);
+
+ if (b == -1) {
+ notifyBlockNotPassable();
+ } else {
+ moveParty(b);
+ _sceneDefaultUpdate = 1;
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedDownArrow(Button *button) {
+ int b = calcNewBlockPositionAndTestPassability(_currentBlock, (_currentDirection + 2) & 3);
+
+ if (b == -1) {
+ notifyBlockNotPassable();
+ } else {
+ moveParty(b);
+ _sceneDefaultUpdate = 1;
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedLeftArrow(Button *button) {
+ int b = calcNewBlockPositionAndTestPassability(_currentBlock, (_currentDirection - 1) & 3);
+
+ if (b == -1) {
+ notifyBlockNotPassable();
+ } else {
+ moveParty(b);
+ _sceneDefaultUpdate = 1;
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedRightArrow(Button *button) {
+ int b = calcNewBlockPositionAndTestPassability(_currentBlock, (_currentDirection + 1) & 3);
+
+ if (b == -1) {
+ notifyBlockNotPassable();
+ } else {
+ moveParty(b);
+ _sceneDefaultUpdate = 1;
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedTurnLeftArrow(Button *button) {
+ _currentDirection = (_currentDirection - 1) & 3;
+ //_keybControlUnk = -1;
+ _sceneDefaultUpdate = 1;
+ _sceneUpdateRequired = true;
+ return button->index;
+}
+
+int EoBCoreEngine::clickedTurnRightArrow(Button *button) {
+ _currentDirection = (_currentDirection + 1) & 3;
+ //_keybControlUnk = -1;
+ _sceneDefaultUpdate = 1;
+ _sceneUpdateRequired = true;
+ return button->index;
+}
+
+int EoBCoreEngine::clickedAbortCharSwitch(Button *button) {
+ _timer->disable(0);
+ int c = _exchangeCharacterId;
+ _exchangeCharacterId = -1;
+ gui_drawCharPortraitWithStats(c);
+ gui_setPlayFieldButtons();
+ return button->index;
+}
+
+int EoBCoreEngine::clickedSceneThrowItem(Button *button) {
+ if (!_itemInHand)
+ return button->index;
+
+ if (launchObject(_updateCharNum, _itemInHand, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + button->arg], _currentDirection, _items[_itemInHand].type)) {
+ setHandItem(0);
+ _sceneUpdateRequired = true;
+ }
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedSceneSpecial(Button *button) {
+ _clickedSpecialFlag = 0x40;
+ return specialWallAction(calcNewBlockPosition(_currentBlock, _currentDirection), _currentDirection);
+}
+
+int EoBCoreEngine::clickedSpellbookAbort(Button *button) {
+ _updateFlags = 0;
+ _screen->fillRect(64, 121, 175, 176, 0, 0);
+ _screen->fillRect(64, 121, 175, 176, 0, 2);
+ _screen->copyRegion(0, 0, 64, 121, 112, 56, 10, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ gui_drawCompass(true);
+ gui_toggleButtons();
+ return button->index;
+}
+
+int EoBCoreEngine::clickedSpellbookScroll(Button *button) {
+ if (_openBookAvailableSpells[_openBookSpellLevel * 10] > 0) {
+ _openBookSpellListOffset ^= 6;
+ _openBookSpellSelectedItem = 0;
+ } else {
+ _openBookSpellListOffset = 6;
+ }
+
+ _characters[_openBookChar].slotStatus[2] = _openBookSpellSelectedItem;
+ _characters[_openBookChar].slotStatus[4] = _openBookSpellListOffset;
+
+ gui_drawSpellbook();
+
+ return button->index;
+}
+
+int EoBCoreEngine::clickedUnk(Button *button) {
+ return button->index;
+}
+
+void EoBCoreEngine::gui_processCharPortraitClick(int index) {
+ if (index == _updateCharNum)
+ return;
+
+ int a = _updateCharNum;
+ _updateCharNum = index;
+
+ gui_drawCharPortraitWithStats(a);
+ gui_drawCharPortraitWithStats(index);
+}
+
+void EoBCoreEngine::gui_processWeaponSlotClickLeft(int charIndex, int slotIndex) {
+ int itm = _characters[charIndex].inventory[slotIndex];
+ if (_items[itm].flags & 0x20)
+ return;
+
+ int ih = _itemInHand;
+ int t = _items[ih].type;
+ uint16 v = (ih) ? _itemTypes[t].invFlags : 0xFFFF;
+
+ if (v & _slotValidationFlags[slotIndex]) {
+ setHandItem(itm);
+ _characters[charIndex].inventory[slotIndex] = ih;
+ gui_drawCharPortraitWithStats(charIndex);
+ }
+
+ recalcArmorClass(charIndex);
+}
+
+void EoBCoreEngine::gui_processWeaponSlotClickRight(int charIndex, int slotIndex) {
+ if (!testCharacter(charIndex, 0x0D))
+ return;
+
+ Item itm = _characters[charIndex].inventory[slotIndex];
+ int wslot = slotIndex < 2 ? slotIndex : -1;
+
+ if (slotIndex < 2 && (!validateWeaponSlotItem(charIndex, slotIndex) || (!_currentControlMode && (_characters[charIndex].disabledSlots & (1 << slotIndex)))))
+ return;
+
+ if (!itemUsableByCharacter(charIndex, itm))
+ _txt->printMessage(_itemMisuseStrings[0], -1, _characters[charIndex].name);
+
+ if (!itm && slotIndex > 1)
+ return;
+
+ int8 tp = _items[itm].type;
+ int8 vl = _items[itm].value;
+ uint8 ep = _itemTypes[tp].extraProperties & 0x7F;
+
+ switch (ep) {
+ case 0:
+ case 16:
+ // Item automatically used when worn
+ _txt->printMessage(_itemMisuseStrings[1]);
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ // Weapons
+ if (!_currentControlMode)
+ useSlotWeapon(charIndex, slotIndex, itm);
+ break;
+
+ case 4:
+ case 8:
+ case 12:
+ case 13:
+ case 15:
+ // Item not used that way
+ _txt->printMessage(_itemMisuseStrings[2]);
+ break;
+
+ case 5:
+ case 6:
+ // Cleric holy symbol / mage spell book
+ if (!_currentControlMode)
+ useMagicBookOrSymbol(charIndex, ep == 6 ? 1 : 0);
+ break;
+
+ case 7:
+ // Food ration
+ // Don't do anything if mouse control is enabled (we don't support anything else)
+ // eatItemInHand(charIndex);
+ break;
+
+ case 10:
+ if (_flags.gameID == GI_EOB1)
+ vl += _clericSpellOffset;
+ // fall through
+ case 9:
+ // Mage/Cleric Scroll
+ if (!_currentControlMode)
+ useMagicScroll(charIndex, vl, wslot);
+ break;
+
+ case 11:
+ // Letters, Notes, Maps
+ displayParchment(vl);
+ break;
+
+ case 14:
+ // Potion
+ usePotion(charIndex, slotIndex);
+ break;
+
+ case 18:
+ useWand(charIndex, slotIndex);
+ break;
+
+ case 19:
+ // eob2 horn
+ useHorn(charIndex, slotIndex);
+ break;
+
+ case 20:
+ if (vl == 1)
+ inflictCharacterDamage(charIndex, 200);
+ else
+ useMagicScroll(charIndex, 55, slotIndex);
+ deleteInventoryItem(charIndex, slotIndex);
+ break;
+
+ default:
+ break;
+ }
+
+ if (_flags.gameID == GI_EOB1 || (ep == 1 && charIndex >= 2))
+ return;
+
+ _lastUsedItem = itm;
+ runLevelScript(calcNewBlockPosition(_currentBlock, _currentDirection), 0x100);
+ _lastUsedItem = 0;
+}
+
+void EoBCoreEngine::gui_processInventorySlotClick(int slot) {
+ int itm = _characters[_updateCharNum].inventory[slot];
+ int ih = _itemInHand;
+ if (!validateInventorySlotForItem(ih, _updateCharNum, slot))
+ return;
+
+ if (slot == 16) {
+ if (ih) {
+ setItemPosition(&_characters[_updateCharNum].inventory[16], -2, ih, 0);
+ gui_drawInventoryItem(slot, 1, 0);
+ setHandItem(0);
+
+ } else {
+ itm = getQueuedItem(&_characters[_updateCharNum].inventory[16], 0, -1);
+ gui_drawInventoryItem(slot, 1, 0);
+ setHandItem(itm);
+ }
+
+ } else {
+ setHandItem(itm);
+ _characters[_updateCharNum].inventory[slot] = ih;
+ gui_drawInventoryItem(slot, 1, 0);
+ recalcArmorClass(_updateCharNum);
+ }
+}
+
+GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) {
+ _menuStringsPrefsTemp = new char*[4];
+ memset(_menuStringsPrefsTemp, 0, 4 * sizeof(char *));
+
+ _saveSlotStringsTemp = new char*[6];
+ for (int i = 0; i < 6; i++) {
+ _saveSlotStringsTemp[i] = new char[26];
+ memset(_saveSlotStringsTemp[i], 0, 26);
+ }
+ _saveSlotIdTemp = new int16[6];
+ _savegameOffset = 0;
+ _saveSlotX = _saveSlotY = 0;
+
+ _specialProcessButton = _backupButtonList = 0;
+ _flagsMouseLeft = _flagsMouseRight = _flagsModifier = 0;
+ _backupButtonList = 0;
+ _progress = 0;
+ _prcButtonUnk3 = 1;
+ _cflag = 0xFFFF;
+
+ _menuLineSpacing = 0;
+ _menuLastInFlags = 0;
+ _menuCur = 0;
+ _menuNumItems = 0;
+
+ _numPages = (_vm->game() == GI_EOB2) ? 8 : 5;
+ _numVisPages = (_vm->game() == GI_EOB2) ? 6 : 5;
+ _clericSpellAvltyFlags = (_vm->game() == GI_EOB2) ? 0xF7FFFFFF : 0x7BFFFF;
+ _paladinSpellAvltyFlags = (_vm->game() == GI_EOB2) ? 0xA9BBD1D : 0x800FF2;
+ _numAssignedSpellsOfType = new int8[72];
+ memset(_numAssignedSpellsOfType, 0, 72);
+
+ _charSelectRedraw = false;
+
+ _highLightColorTable = (_vm->game() == GI_EOB1 && (_vm->_configRenderMode == Common::kRenderCGA || _vm->_configRenderMode == Common::kRenderEGA)) ? _highlightColorTableEGA : _highlightColorTableVGA;
+ _updateBoxIndex = -1;
+ _highLightBoxTimer = 0;
+ _updateBoxColorIndex = 0;
+
+ _needRest = false;
+}
+
+GUI_EoB::~GUI_EoB() {
+ if (_menuStringsPrefsTemp) {
+ for (int i = 0; i < 4; i++)
+ delete[] _menuStringsPrefsTemp[i];
+ delete[] _menuStringsPrefsTemp;
+ }
+
+ if (_saveSlotStringsTemp) {
+ for (int i = 0; i < 6; i++)
+ delete[] _saveSlotStringsTemp[i];
+ delete[] _saveSlotStringsTemp;
+ }
+
+ delete[] _saveSlotIdTemp;
+
+ delete[] _numAssignedSpellsOfType;
+}
+
+void GUI_EoB::processButton(Button *button) {
+ if (!button->data0Val1 && !button->data2Val1 && !button->data1Val1)
+ return;
+
+ if ((button->flags & 0x18) == 0x18)
+ return;
+
+ int sd = button->dimTableIndex;
+ const ScreenDim *dm = _screen->getScreenDim(sd);
+
+ int fx = button->x;
+ if (fx < 0)
+ fx += (dm->w << 3);
+
+ int sx = fx + (dm->sx << 3);
+
+ int fy = button->y;
+ if (fy < 0)
+ fy += dm->h;
+
+ int sy = fy + dm->sy;
+
+ uint16 fw = button->width;
+ uint16 fh = button->height;
+
+ uint8 col1 = button->data1Val1;
+ uint8 col2 = button->data1Val3;
+
+ int fx2 = sx + fw - 1;
+ int fy2 = sy + fh - 1;
+
+ if (button->flags2 & 1) {
+ if (button->data1Val1 == 1) {
+ if (button->data0Val1 == 1) {
+ _screen->drawShape(_screen->_curPage, button->data1ShapePtr, fx, fy, sd);
+ } else if (button->data0Val1 == 2) {
+ if (!(button->flags2 & 4))
+ _screen->printText((const char *)button->data1ShapePtr, sx, sy, col1, col2);
+ } else if (button->data0Val1 == 3) {
+ // nullsub (at least EOBII)
+ } else if (button->data0Val1 == 4) {
+ if (button->data1Callback)
+ (*button->data1Callback)(button);
+ }
+ } else if (button->data1Val1 == 2) {
+ if (!(button->flags2 & 4))
+ _screen->drawBox(sx, sy, fx2, fy2, col1);
+ } else if (button->data1Val1 == 3) {
+ // nullsub (at least EOBII)
+ } else if (button->data1Val1 == 4) {
+ if (button->data1Callback)
+ (*button->data1Callback)(button);
+ }
+ }
+
+ if (button->flags2 & 4) {
+ if (button->data2Val1 == 1) {
+ if (button->data0Val1 == 1) {
+ _screen->drawShape(_screen->_curPage, button->data2ShapePtr, fx, fy, sd);
+ } else if (button->data0Val1 == 2) {
+ if (button->flags2 & 1)
+ _screen->printText((const char *)button->data2ShapePtr, sx, sy, button->data3Val2, button->data3Val3);
+ else
+ _screen->printText((const char *)button->data2ShapePtr, sx, sy, button->data2Val2, button->data2Val3);
+ } else if (button->data0Val1 == 3) {
+ // nullsub (at least EOBII)
+ } else if (button->data0Val1 == 4) {
+ if (button->data2Callback)
+ (*button->data2Callback)(button);
+ }
+ } else if (button->data2Val1 == 2) {
+ _screen->drawBox(sx, sy, fx2, fy2, (button->flags2 & 1) ? button->data3Val2 : button->data2Val2);
+ } else if (button->data2Val1 == 3) {
+ // nullsub (at least EOBII)
+ } else if (button->data2Val1 == 4) {
+ if (button->data2Callback)
+ (*button->data2Callback)(button);
+ }
+ }
+
+ if (!(button->flags2 & 5)) {
+ if (button->data0Val1 == 1) {
+ _screen->drawShape(_screen->_curPage, button->data0ShapePtr, fx, fy, sd);
+ } else if (button->data0Val1 == 2) {
+ _screen->printText((const char *)button->data0ShapePtr, sx, sy, button->data0Val2, button->data0Val3);
+ } else if (button->data0Val1 == 3) {
+ // nullsub (at least EOBII)
+ } else if (button->data0Val1 == 4) {
+ if (button->data0Callback)
+ (*button->data0Callback)(button);
+ } else if (button->data0Val1 == 5) {
+ _screen->drawBox(sx, sy, fx2, fy2, button->data0Val2);
+ } else {
+ if (!button->data0Val1) {
+ if (button->data1Val1 == 2 || button->data2Val1 == 2) {
+ _screen->drawBox(sx, sy, fx2, fy2, button->data0Val2);
+ } else {
+ // nullsub (at least EOBII)
+ }
+ }
+ }
+ }
+}
+
+int GUI_EoB::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 mouseWheel) {
+ _progress = 0;
+ uint16 in = inputFlags & 0xFF;
+ uint16 buttonReleaseFlag = 0;
+ bool clickEvt = false;
+ //_vm->_processingButtons = true;
+ _flagsMouseLeft = (_vm->_mouseClick == 1) ? 2 : 4;
+ _flagsMouseRight = (_vm->_mouseClick == 2) ? 2 : 4;
+ _vm->_mouseClick = 0;
+
+ if (mouseWheel) {
+ return 204 + mouseWheel;
+ } else if (in >= 199 && in <= 202) {
+ buttonReleaseFlag = (inputFlags & 0x800) ? 3 : 1;
+ if (in < 201)
+ _flagsMouseLeft = buttonReleaseFlag;
+ else
+ _flagsMouseRight = buttonReleaseFlag;
+
+ ////////////////////////////
+ if (!buttonList && !(inputFlags & 0x800))
+ return inputFlags & 0xFF;
+ ////////////////////////////
+
+ inputFlags = 0;
+ clickEvt = true;
+ } else if (inputFlags & 0x8000) {
+ inputFlags &= 0xFF;
+ }
+
+ uint16 result = 0;
+ bool runLoop = true;
+
+ if (!buttonList)
+ return inputFlags;
+
+ if (_vm->_buttonListChanged || (buttonList != _backupButtonList)) {
+ _backupButtonList = buttonList;
+ _flagsModifier = 0;
+
+ while (runLoop) {
+ processButton(buttonList);
+ _flagsModifier |= (buttonList->flags & 0xAA04);
+
+ // UNUSED
+ //if (buttonList->flags2 & 0x20) {
+ // if (_processButtonListExtraCallback)
+ // this->*_processButtonListExtraCallback(buttonList);
+ //}
+
+ if (buttonList->nextButton)
+ buttonList = buttonList->nextButton;
+ else
+ runLoop = false;
+ }
+
+ _vm->_buttonListChanged = false;
+
+ _specialProcessButton = 0;
+ _prcButtonUnk3 = 1;
+ _cflag = 0xFFFF;
+ }
+
+ int sd = 0;
+ const ScreenDim *dm = _screen->getScreenDim(sd);
+
+ int x1 = dm->sx << 3;
+ int y1 = dm->sy;
+ int w1 = dm->w << 3;
+ int h1 = dm->h;
+
+ uint16 v8 = 0;
+ uint16 v18 = 0;
+ uint16 v16 = 0;
+
+ if (_specialProcessButton)
+ buttonList = _specialProcessButton;
+
+ while (runLoop) {
+ if (buttonList->flags & 8) {
+ buttonList = buttonList->nextButton;
+ runLoop = buttonList ? true : false;
+ continue;
+ }
+
+ int vc = 0;
+ int v6 = 0;
+ uint16 iFlag = buttonList->index | 0x8000;
+ uint16 flgs2 = buttonList->flags2;
+ uint16 flgs = buttonList->flags;
+
+ if (flgs2 & 1)
+ flgs2 |= 8;
+ else
+ flgs2 &= 0xFFF7;
+
+ if (flgs2 & 4)
+ flgs2 |= 0x10;
+ else
+ flgs2 &= 0xFFEF;
+
+ uint16 vL = 0;
+ uint16 vR = 0;
+
+ if (inputFlags) {
+ if (buttonList->keyCode == inputFlags) {
+ _progress = 1;
+ _flagsMouseLeft = 1;
+ flgs2 ^= 1;
+ result = iFlag;
+ v6 = 1;
+ } else if (buttonList->keyCode2 == inputFlags) {
+ _progress = 2;
+ _flagsMouseRight = 1;
+ result = iFlag;
+ v6 = 1;
+ }
+ } else if (_flagsModifier || clickEvt) {
+ vL = flgs & 0xF00;
+ vR = flgs & 0xF000;
+
+ if (_prcButtonUnk3) {
+ if (sd != buttonList->dimTableIndex) {
+ sd = buttonList->dimTableIndex;
+ dm = _screen->getScreenDim(sd);
+ x1 = dm->sx << 3;
+ y1 = dm->sy;
+ w1 = dm->w << 3;
+ h1 = dm->h;
+ }
+
+ int x2 = x1;
+ if (buttonList->x < 0)
+ x2 += w1;
+ x2 += buttonList->x;
+
+ int y2 = y1;
+ if (buttonList->y < 0)
+ y2 += h1;
+ y2 += buttonList->y;
+
+ if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width) && _vm->_mouseY >= y2 && _vm->_mouseY <= (y2 + buttonList->height)) {
+ flgs2 |= 2;
+
+ if (vL) {
+ switch (_flagsMouseLeft - 1) {
+ case 0:
+ v18 = 1;
+
+ if ((flgs & 4) && buttonList->data2Val1) {
+ flgs2 |= 4;
+ vc = 1;
+ } else {
+ flgs2 &= 0xFFFB;
+ }
+
+ if (flgs & 0x100) {
+ v6 = 1;
+ if (!(flgs & 1)) {
+ flgs2 ^= 1;
+ result = iFlag;
+ }
+ }
+
+ if (flgs & 0x40) {
+ _specialProcessButton = buttonList;
+ v8 = 1;
+ }
+
+ _cflag = flgs;
+ break;
+
+ case 1:
+ if (flgs != _cflag)
+ break;
+
+ if ((flgs & 4) && buttonList->data2Val1) {
+ flgs2 |= 4;
+ vc = 1;
+ } else {
+ flgs2 &= 0xFFFB;
+ }
+
+ if (!(flgs & 0x200))
+ break;
+
+ v6 = 1;
+
+ if (flgs & 1)
+ break;
+
+ flgs2 |= 1;
+ result = iFlag;
+ break;
+
+ case 2:
+ if (_cflag != flgs)
+ break;
+
+ if (flgs & 0x400) {
+ v6 = 1;
+ if (flgs & 1) {
+ flgs2 ^= 1;
+ result = iFlag;
+ }
+ }
+
+ if ((flgs & 2) && (flgs2 & 1))
+ flgs2 &= 0xFFFE;
+ break;
+
+ case 3:
+ if ((flgs & 4) || (!buttonList->data2Val1))
+ flgs2 &= 0xFFFB;
+ else
+ flgs2 |= 4;
+
+ if (flgs & 0x800) {
+ v6 = 1;
+ break;
+ }
+
+ if ((flgs & 2) && (flgs2 & 1))
+ flgs2 &= 0xFFFE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (vR && !v6 && !vc) {
+ switch (_flagsMouseRight - 1) {
+ case 0:
+ v18 = 1;
+
+ if ((flgs & 4) && buttonList->data2Val1)
+ flgs2 |= 4;
+ else
+ flgs2 &= 0xFFFB;
+
+ if (flgs & 0x1000) {
+ v6 = 1;
+ if (!(flgs & 1)) {
+ flgs2 ^= 1;
+ result = iFlag;
+ }
+ }
+
+ if (flgs & 0x40) {
+ _specialProcessButton = buttonList;
+ v8 = 1;
+ }
+
+ _cflag = flgs;
+ break;
+
+ case 1:
+ if (flgs != _cflag)
+ break;
+
+ if ((flgs & 4) && buttonList->data2Val1)
+ flgs2 |= 4;
+ else
+ flgs2 &= 0xFFFB;
+
+ if (!(flgs & 0x2000))
+ break;
+
+ v6 = 1;
+
+ if (flgs & 1)
+ break;
+
+ flgs2 |= 1;
+ result = iFlag;
+ break;
+ case 2:
+ if (_cflag != flgs)
+ break;
+
+ if (flgs & 0x4000) {
+ v6 = 1;
+ if (flgs & 1) {
+ flgs2 ^= 1;
+ result = iFlag;
+ }
+ }
+
+ if ((flgs & 2) && (flgs2 & 1))
+ flgs2 &= 0xFFFE;
+ break;
+
+ case 3:
+ if ((flgs & 4) || (!buttonList->data2Val1))
+ flgs2 &= 0xFFFB;
+ else
+ flgs2 |= 4;
+
+ if (flgs & 0x8000) {
+ v6 = 1;
+ break;
+ }
+
+ if ((flgs & 2) && (flgs2 & 1))
+ flgs2 &= 0xFFFE;
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else { // if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width)....)
+ flgs2 &= 0xFFF9;
+
+ if ((flgs & 0x40) && (!(flgs & 0x80)) && _specialProcessButton && !v8) {
+ static const uint16 flagsTable[] = { 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 };
+
+ if (vL) {
+ v16 = flagsTable[_flagsMouseLeft - 1];
+ if (v16 & flgs)
+ v6 = 1;
+ }
+
+ if (vR && !v6) {
+ v16 = flagsTable[_flagsMouseRight + 3];
+ if (v16 & flgs)
+ v6 = 1;
+ }
+
+ if (!v6) {
+ _specialProcessButton = 0;
+ _prcButtonUnk3 = 1;
+ }
+ }
+
+ if ((flgs & 2) && (flgs2 & 1))
+ flgs2 &= 0xFFFE;
+ } // end if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width)....)
+ } // end if (_prcButtonUnk3)
+ } // end if (_flagsModifier || clickEvt)
+
+ buttonList->flags = flgs;
+ buttonList->flags2 = flgs2;
+
+ bool f21 = (flgs2 & 8) ? true : false;
+ bool f22 = (flgs2 & 1) ? true : false;
+ bool f23 = (flgs2 & 0x10) ? true : false;
+ bool f24 = (flgs2 & 4) ? true : false;
+
+ if (f21 != f22 || f23 != f24)
+ processButton(buttonList);
+
+ if (v6 && buttonList->buttonCallback)
+ runLoop = !(*buttonList->buttonCallback)(buttonList);
+
+ if ((flgs2 & 2) && (flgs & 0x20))
+ runLoop = false;
+
+ if (_specialProcessButton && ((vL && _flagsMouseLeft == 3) || (vR && _flagsMouseRight == 3))) {
+ _specialProcessButton = 0;
+ _prcButtonUnk3 = 1;
+ runLoop = false;
+ }
+
+ if (_specialProcessButton && !v8)
+ runLoop = false;
+
+ buttonList = buttonList->nextButton;
+ if (!buttonList)
+ runLoop = false;
+ };
+
+ if ((_flagsMouseLeft == 1 || _flagsMouseRight == 1) && !v18)
+ _cflag = 0xFFFF;
+
+ if (!result)
+ result = inputFlags;
+
+ return result;
+}
+
+void GUI_EoB::simpleMenu_setup(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int itemOffset, int lineSpacing) {
+ simpleMenu_initMenuItemsMask(sd, maxItem, menuItemsMask, itemOffset);
+
+ const ScreenDim *dm = _screen->getScreenDim(19 + sd);
+ int x = (_screen->_curDim->sx + dm->sx) << 3;
+ int y = _screen->_curDim->sy + dm->sy;
+
+ int v = simpleMenu_getMenuItem(_menuCur, menuItemsMask, itemOffset);
+
+ for (int i = 0; i < _menuNumItems; i++) {
+ int item = simpleMenu_getMenuItem(i, menuItemsMask, itemOffset);
+ int ty = y + i * (lineSpacing + _screen->getFontHeight());
+ _screen->printShadedText(strings[item], x, ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : dm->unkA, 0);
+ if (item == v)
+ _screen->printText(strings[item], x, ty, dm->unkC, 0);
+ }
+
+ _screen->updateScreen();
+ _menuLineSpacing = lineSpacing;
+ _menuLastInFlags = 0;
+ _vm->removeInputTop();
+}
+
+int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int32 menuItemsMask, int itemOffset) {
+ const ScreenDim *dm = _screen->getScreenDim(19 + sd);
+ int h = _menuNumItems - 1;
+ int currentItem = _menuCur % _menuNumItems;
+ int newItem = currentItem;
+ int result = -1;
+ int lineH = (_menuLineSpacing + _screen->getFontHeight());
+ int lineS1 = _menuLineSpacing >> 1;
+ int x = (_screen->_curDim->sx + dm->sx) << 3;
+ int y = _screen->_curDim->sy + dm->sy;
+
+ int inFlag = _vm->checkInput(0, false, 0) & 0x8FF;
+ _vm->removeInputTop();
+ Common::Point mousePos = _vm->getMousePos();
+
+ int x1 = (_screen->_curDim->sx << 3) + (dm->sx * _screen->getFontWidth());
+ int y1 = _screen->_curDim->sy + dm->sy - lineS1;
+ int x2 = x1 + (dm->w * _screen->getFontWidth()) - 1;
+ int y2 = y1 + _menuNumItems * lineH - 1;
+ if (_vm->posWithinRect(mousePos.x, mousePos.y, x1, y1, x2, y2))
+ newItem = (mousePos.y - y1) / lineH;
+
+ if (inFlag == 199 || inFlag == 201) {
+ if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, x1, y1, x2, y2))
+ result = newItem = (_vm->_mouseY - y1) / lineH;
+ } else if (inFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inFlag == _vm->_keyMap[Common::KEYCODE_KP5]) {
+ result = newItem;
+ } else if (inFlag == _vm->_keyMap[Common::KEYCODE_HOME] || inFlag == _vm->_keyMap[Common::KEYCODE_KP7] || inFlag == _vm->_keyMap[Common::KEYCODE_PAGEUP] || inFlag == _vm->_keyMap[Common::KEYCODE_KP9]) {
+ newItem = 0;
+ } else if (inFlag == _vm->_keyMap[Common::KEYCODE_END] || inFlag == _vm->_keyMap[Common::KEYCODE_KP1] || inFlag == _vm->_keyMap[Common::KEYCODE_PAGEDOWN] || inFlag == _vm->_keyMap[Common::KEYCODE_KP3]) {
+ newItem = h;
+ } else if (inFlag == _vm->_keyMap[Common::KEYCODE_UP] || inFlag == _vm->_keyMap[Common::KEYCODE_KP8]) {
+ if (--newItem < 0)
+ newItem = h;
+ } else if (inFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inFlag == _vm->_keyMap[Common::KEYCODE_KP2]) {
+ if (++newItem > h)
+ newItem = 0;
+ } else {
+ _menuLastInFlags = inFlag;
+ }
+
+ if (newItem != currentItem) {
+ _screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : dm->unkA, 0);
+ _screen->printText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], x, y + newItem * lineH , dm->unkC, 0);
+ _screen->updateScreen();
+ }
+
+ if (result != -1) {
+ result = simpleMenu_getMenuItem(result, menuItemsMask, itemOffset);
+ simpleMenu_flashSelection(strings[result], x, y + newItem * lineH, dm->unkA, dm->unkC, 0);
+ }
+
+ _menuCur = newItem;
+
+ return result;
+}
+
+int GUI_EoB::simpleMenu_getMenuItem(int index, int32 menuItemsMask, int itemOffset) {
+ if (menuItemsMask == -1)
+ return index;
+
+ int res = 0;
+ int i = index;
+
+ for (; i; res++) {
+ if (menuItemsMask & (1 << (res + itemOffset)))
+ i--;
+ }
+
+ while (!(menuItemsMask & (1 << (res + itemOffset))))
+ res++;
+
+ return res;
+}
+
+void GUI_EoB::simpleMenu_flashSelection(const char *str, int x, int y, int color1, int color2, int color3) {
+ for (int i = 0; i < 3; i++) {
+ _screen->printText(str, x, y, color2, color3);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(32);
+ _screen->printText(str, x, y, color1, color3);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(32);
+ }
+}
+
+void GUI_EoB::runCampMenu() {
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+
+ Button *highlightButton = 0;
+ Button *prevHighlightButton = 0;
+
+ int newMenu = 0;
+ int lastMenu = -1;
+ bool redrawPortraits = false;
+ bool keepButtons = false;
+
+ _charSelectRedraw = false;
+ _needRest = false;
+ Button *buttonList = 0;
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ if (newMenu == 2)
+ updateOptionsStrings();
+
+ if (newMenu != -1) {
+ if (!keepButtons) {
+ releaseButtons(buttonList);
+
+ _vm->_menuDefs[0].titleStrId = newMenu ? 1 : 56;
+ if (newMenu == 2)
+ _vm->_menuDefs[2].titleStrId = 57;
+ else if (newMenu == 1)
+ _vm->_menuDefs[1].titleStrId = 58;
+
+ buttonList = initMenu(newMenu);
+
+ if (newMenu != lastMenu) {
+ highlightButton = buttonList;
+ prevHighlightButton = 0;
+ }
+ }
+
+ lastMenu = newMenu;
+ newMenu = -1;
+ keepButtons = false;
+ }
+
+ int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE])
+ inputFlag = 0x8007;
+ else if (prevHighlightButton && (inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]))
+ inputFlag = 0x8000 + prevHighlightButton->index;
+
+ Button *clickedButton = _vm->gui_getButton(buttonList, inputFlag & 0x7FFF);
+
+ if (clickedButton) {
+ drawMenuButton(prevHighlightButton, false, false, true);
+ drawMenuButton(clickedButton, true, true, true);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButton(clickedButton, false, true, true);
+ _screen->updateScreen();
+ highlightButton = clickedButton;
+ prevHighlightButton = 0;
+ }
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP3] || inputFlag == _vm->_keyMap[Common::KEYCODE_PAGEDOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP1] || inputFlag == _vm->_keyMap[Common::KEYCODE_END]) {
+ highlightButton = _vm->gui_getButton(buttonList, _vm->_menuDefs[lastMenu].firstButtonStrId + _vm->_menuDefs[lastMenu].numButtons);
+ inputFlag = _vm->_keyMap[Common::KEYCODE_UP];
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP7] || inputFlag == _vm->_keyMap[Common::KEYCODE_HOME] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP9] || inputFlag == _vm->_keyMap[Common::KEYCODE_PAGEUP]) {
+ highlightButton = _vm->gui_getButton(buttonList, _vm->_menuDefs[lastMenu].firstButtonStrId + 1);
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP8] || inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP2] || inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN]) {
+ if (prevHighlightButton) {
+ int dir = (inputFlag == _vm->_keyMap[Common::KEYCODE_UP]) ? -1 : 1;
+ int s = prevHighlightButton->index + dir;
+ if (lastMenu == 2 && _vm->gameFlags().platform == Common::kPlatformFMTowns)
+ s += 32;
+ int a = _vm->_menuDefs[lastMenu].firstButtonStrId + 1;
+ int b = a + _vm->_menuDefs[lastMenu].numButtons - 1;
+
+ do {
+ if (s < a)
+ s = b;
+ if (s > b)
+ s = a;
+ if (_vm->_menuButtonDefs[s - 1].flags & 2)
+ break;
+ s += dir;
+ } while (!_vm->shouldQuit());
+
+ if (lastMenu == 2 && _vm->gameFlags().platform == Common::kPlatformFMTowns)
+ s -= 32;
+ highlightButton = _vm->gui_getButton(buttonList, s);
+ }
+
+ } else if (inputFlag > 0x8000 && inputFlag < 0x8011) {
+ int i = 0;
+ int cnt = 0;
+
+ switch (inputFlag) {
+ case 0x8001:
+ if (restParty())
+ runLoop = false;
+ else
+ _needRest = false;
+ redrawPortraits = true;
+ newMenu = 0;
+ break;
+
+ case 0x8002:
+ runMemorizePrayMenu(selectCharacterDialogue(23), 0);
+ newMenu = 0;
+ break;
+
+ case 0x8003:
+ runMemorizePrayMenu(selectCharacterDialogue(26), 1);
+ newMenu = 0;
+ break;
+
+ case 0x8004:
+ scribeScrollDialogue();
+ newMenu = 0;
+ break;
+
+ case 0x8005:
+ newMenu = 2;
+ break;
+
+ case 0x8006:
+ newMenu = 1;
+ break;
+
+ case 0x8007:
+ if (_needRest)
+ displayTextBox(44);
+ // fall through
+
+ case 0x800C:
+ case 0x8010:
+ if (lastMenu == 1 || lastMenu == 2)
+ newMenu = 0;
+ else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE])
+ newMenu = 0;
+ else
+ runLoop = false;
+ break;
+
+ case 0x8008:
+ if (runLoadMenu(0, 0))
+ runLoop = false;
+ else
+ newMenu = 1;
+ break;
+
+ case 0x8009:
+ if (runSaveMenu(0, 0))
+ displayTextBox(14);
+ newMenu = 1;
+ break;
+
+ case 0x800A:
+ for (; i < 6; i++) {
+ if (_vm->testCharacter(i, 1))
+ cnt++;
+ }
+
+ if (cnt > 4) {
+ _vm->dropCharacter(selectCharacterDialogue(53));
+ _vm->gui_drawPlayField(false);
+ _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK);
+ _screen->setFont(Screen::FID_6_FNT);
+ _vm->gui_drawAllCharPortraitsWithStats();
+ _screen->setFont(Screen::FID_8_FNT);
+ } else {
+ displayTextBox(45);
+ }
+
+ newMenu = 0;
+ break;
+
+ case 0x800B:
+ if (confirmDialogue(46))
+ _vm->quitGame();
+ newMenu = 0;
+ break;
+
+ case 0x800D:
+ _vm->_configSounds ^= true;
+ _vm->_configMusic = _vm->_configSounds ? 1 : 0;
+ keepButtons = true;
+ newMenu = 2;
+ break;
+
+ case 0x800E:
+ _vm->_configHpBarGraphs ^= true;
+ newMenu = 2;
+ redrawPortraits = keepButtons = true;
+ break;
+
+ case 0x800F:
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ _vm->_config2431 ^= true;
+ newMenu = 2;
+ redrawPortraits = keepButtons = true;
+ } else {
+ newMenu = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ lastMenu = -1;
+
+ } else {
+ Common::Point p = _vm->getMousePos();
+ for (Button *b = buttonList; b; b = b->nextButton) {
+ if ((b->arg & 2) && _vm->posWithinRect(p.x, p.y, b->x, b->y, b->x + b->width, b->y + b->height))
+ highlightButton = b;
+ }
+ }
+
+ if (_charSelectRedraw || redrawPortraits) {
+ for (int i = 0; i < 6; i++) {
+ _vm->gui_drawCharPortraitWithStats(i);
+ _vm->sortCharacterSpellList(i);
+ }
+ }
+
+ _charSelectRedraw = redrawPortraits = false;
+
+ if (prevHighlightButton != highlightButton && newMenu == -1 && runLoop) {
+ drawMenuButton(prevHighlightButton, false, false, true);
+ drawMenuButton(highlightButton, false, true, false);
+ _screen->updateScreen();
+ prevHighlightButton = highlightButton;
+ }
+ }
+
+ _screen->setFont(of);
+ releaseButtons(buttonList);
+ _vm->writeSettings();
+}
+
+bool GUI_EoB::runLoadMenu(int x, int y) {
+ const ScreenDim *dm = _screen->getScreenDim(11);
+ int xo = dm->sx;
+ int yo = dm->sy;
+ bool result = false;
+
+ _screen->modifyScreenDim(11, dm->sx + (x >> 3), dm->sy + y, dm->w, dm->h);
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ updateSaveSlotsList(_vm->_targetName);
+ int slot = selectSaveSlotDialogue(x, y, 1);
+ if (slot > 5) {
+ runLoop = result = false;
+ } else if (slot >= 0) {
+ if (_saveSlotIdTemp[slot] == -1) {
+ messageDialogue(11, 65, 6);
+ } else {
+ if (_vm->loadGameState(_saveSlotIdTemp[slot]).getCode() != Common::kNoError)
+ messageDialogue(11, 16, 6);
+ runLoop = false;
+ result = true;
+ }
+ }
+ }
+
+ _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
+ return result;
+}
+
+bool GUI_EoB::confirmDialogue2(int dim, int id, int deflt) {
+ int od = _screen->curDimIndex();
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+ _screen->setScreenDim(dim);
+
+ drawTextBox(dim, id);
+
+ int16 x[2];
+ x[0] = (_screen->_curDim->sx << 3) + 8;
+ x[1] = (_screen->_curDim->sx + _screen->_curDim->w - 5) << 3;
+ int16 y = _screen->_curDim->sy + _screen->_curDim->h - 21;
+ int newHighlight = deflt ^ 1;
+ int lastHighlight = -1;
+
+ for (int i = 0; i < 2; i++)
+ drawMenuButtonBox(x[i], y, 32, 14, false, false);
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ Common::Point p = _vm->getMousePos();
+ if (_vm->posWithinRect(p.x, p.y, x[0], y, x[0] + 32, y + 14))
+ newHighlight = 0;
+ else if (_vm->posWithinRect(p.x, p.y, x[1], y, x[1] + 32, y + 14))
+ newHighlight = 1;
+
+ int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP6]) {
+ newHighlight ^= 1;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_n]) {
+ newHighlight = 1;
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_y]) {
+ newHighlight = 0;
+ runLoop = false;
+ } else if (inputFlag == 199 || inputFlag == 201) {
+ if (_vm->posWithinRect(p.x, p.y, x[0], y, x[0] + 32, y + 14)) {
+ newHighlight = 0;
+ runLoop = false;
+ } else if (_vm->posWithinRect(p.x, p.y, x[1], y, x[1] + 32, y + 14)) {
+ newHighlight = 1;
+ runLoop = false;
+ }
+ }
+
+ if (newHighlight != lastHighlight) {
+ for (int i = 0; i < 2; i++)
+ _screen->printShadedText(_vm->_menuYesNoStrings[i], x[i] + 16 - (_screen->getTextWidth(_vm->_menuYesNoStrings[i]) / 2) + 1, y + 3, i == newHighlight ? 6 : 15, 0);
+ _screen->updateScreen();
+ lastHighlight = newHighlight;
+ }
+ }
+
+ drawMenuButtonBox(x[newHighlight], y, 32, 14, true, true);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButtonBox(x[newHighlight], y, 32, 14, false, true);
+ _screen->updateScreen();
+
+ _screen->copyRegion(0, _screen->_curDim->h, _screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->setFont(of);
+ _screen->setScreenDim(od);
+
+ return newHighlight == 0;
+}
+
+void GUI_EoB::messageDialogue(int dim, int id, int buttonTextCol) {
+ int od = _screen->curDimIndex();
+ _screen->setScreenDim(dim);
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+
+ drawTextBox(dim, id);
+ const ScreenDim *dm = _screen->getScreenDim(dim);
+
+ int bx = ((dm->sx + dm->w) << 3) - (_screen->getTextWidth(_vm->_menuOkString) + 16);
+ int by = dm->sy + dm->h - 19;
+ int bw = _screen->getTextWidth(_vm->_menuOkString) + 7;
+
+ drawMenuButtonBox(bx, by, bw, 14, false, false);
+ _screen->printShadedText(_vm->_menuOkString, bx + 4, by + 3, buttonTextCol, 0);
+ _screen->updateScreen();
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == 199 || inputFlag == 201) {
+ if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, bx, by, bx + bw, by + 14))
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inputFlag == _vm->_keyMap[Common::KEYCODE_o]) {
+ runLoop = false;
+ }
+ }
+
+ drawMenuButtonBox(bx, by, bw, 14, true, true);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButtonBox(bx, by, bw, 14, false, true);
+ _screen->updateScreen();
+
+ _screen->copyRegion(0, dm->h, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->setScreenDim(od);
+ _screen->setFont(of);
+ dm = _screen->getScreenDim(dim);
+}
+
+void GUI_EoB::messageDialogue2(int dim, int id, int buttonTextCol) {
+ _screen->_curPage = 2;
+ _screen->setClearScreenDim(dim);
+ drawMenuButtonBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, false, false);
+ _screen->printShadedText(getMenuString(id), (_screen->_curDim->sx << 3) + 5, _screen->_curDim->sy + 5, 15, 0);
+ _screen->_curPage = 0;
+ _screen->copyRegion(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, 2, 0, Screen::CR_NO_P_CHECK);
+
+ int x = (_screen->_curDim->sx << 3) + (_screen->_curDim->w << 2) - (_screen->getTextWidth(_vm->_menuOkString) / 2);
+ int y = _screen->_curDim->sy + _screen->_curDim->h - 21;
+ int w = _screen->getTextWidth(_vm->_menuOkString) + 8;
+ drawMenuButtonBox(x, y, w, 14, false, false);
+ _screen->printShadedText(_vm->_menuOkString, x + 4, y + 3, buttonTextCol, 0);
+ _screen->updateScreen();
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == 199 || inputFlag == 201) {
+ if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, x, y, x + w, y + 14))
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inputFlag == _vm->_keyMap[Common::KEYCODE_o]) {
+ runLoop = false;
+ }
+ }
+
+ _screen->set16bitShadingLevel(4);
+ _vm->gui_drawBox(x, y, w, 14, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill, -1);
+ _screen->set16bitShadingLevel(0);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButtonBox(x, y, w, 14, false, false);
+ _screen->printShadedText(_vm->_menuOkString, x + 4, y + 3, buttonTextCol, 0);
+ _screen->updateScreen();
+
+}
+
+void GUI_EoB::updateBoxFrameHighLight(int box) {
+ if (_updateBoxIndex == box) {
+ if (_updateBoxIndex == -1)
+ return;
+
+ if (_vm->_system->getMillis() <= _highLightBoxTimer)
+ return;
+
+ if (!_highLightColorTable[_updateBoxColorIndex])
+ _updateBoxColorIndex = 0;
+
+ const EoBRect16 *r = &_highlightFrames[_updateBoxIndex];
+ _screen->drawBox(r->x1, r->y1, r->x2, r->y2, _highLightColorTable[_updateBoxColorIndex++]);
+ _screen->updateScreen();
+
+ _highLightBoxTimer = _vm->_system->getMillis() + _vm->_tickLength;
+
+ } else {
+ if (_updateBoxIndex != -1) {
+ const EoBRect16 *r = &_highlightFrames[_updateBoxIndex];
+ _screen->drawBox(r->x1, r->y1, r->x2, r->y2, 12);
+ _screen->updateScreen();
+ }
+
+ _updateBoxColorIndex = 0;
+ _updateBoxIndex = box;
+ _highLightBoxTimer = _vm->_system->getMillis();
+ }
+}
+
+int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColor1, int textColor2, int cursorColor) {
+#ifdef ENABLE_KEYMAPPER
+ Common::Keymapper *const keymapper = _vm->getEventManager()->getKeymapper();
+ keymapper->pushKeymap(Common::kGlobalKeymapName);
+#endif
+
+ uint8 cursorState = 1;
+ char sufx[3] = " \0";
+
+ int len = strlen(dest);
+ if (len > destMaxLen) {
+ len = destMaxLen;
+ dest[destMaxLen] = 0;
+ }
+
+ int pos = len;
+ if (len >= destMaxLen)
+ pos--;
+
+ _screen->copyRegion((x - 1) << 3, y, 0, 191, (destMaxLen + 2) << 3, 9, 0, 2, Screen::CR_NO_P_CHECK);
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns)
+ _screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
+ _screen->printShadedText(dest, x << 3, y, textColor1, textColor2);
+
+ uint32 next = _vm->_system->getMillis() + 2 * _vm->_tickLength;
+ sufx[0] = (pos < len) ? dest[pos] : 32;
+ _screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor);
+
+ _menuCur = -1;
+ printKatakanaOptions(0);
+
+ int in = 0;
+
+ do {
+ in = 0;
+ _keyPressed.reset();
+
+ while (!in && !_vm->shouldQuit()) {
+ if (next <= _vm->_system->getMillis()) {
+ if (cursorState) {
+ _screen->copyRegion((pos + 1) << 3, 191, (x + pos) << 3, y, 8, 9, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->printShadedText(sufx, (x + pos) << 3, y, textColor1, textColor2);
+ } else {
+ _screen->fillRect((x + pos) << 3, y, ((x + pos) << 3) + 7, y + 7, cursorColor);
+ _screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor);
+ }
+
+ _screen->updateScreen();
+ cursorState ^= 1;
+ next = _vm->_system->getMillis() + 4 * _vm->_tickLength;
+ }
+
+ _vm->updateInput();
+ in = checkKatakanaSelection();
+
+ for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
+ if (evt->event.type == Common::EVENT_KEYDOWN) {
+ _keyPressed = evt->event.kbd;
+ in = _keyPressed.ascii;
+
+ if (_vm->_flags.platform == Common::kPlatformFMTowns && _keyPressed.ascii > 31 && _keyPressed.ascii < 123) {
+ Common::String s;
+ s.insertChar(in & 0xff, 0);
+ s = _vm->convertAsciiToSjis(s);
+ if (s.empty()) {
+ in = 0;
+ } else {
+ _csjis[0] = s[0];
+ _csjis[1] = s[1];
+ _csjis[2] = 0;
+ in = 0x89;
+ }
+ }
+ }
+ }
+ _vm->removeInputTop();
+ }
+
+ if (_keyPressed.keycode == Common::KEYCODE_BACKSPACE) {
+ if (pos > 0 && pos < len ) {
+ for (int i = pos; i < len; i++) {
+ if (dest[i * 2] & 0x80) {
+ dest[(i - 1) * 2] = dest[i * 2];
+ dest[(i - 1) * 2 + 1] = dest[i * 2 + 1];
+ } else {
+ dest[i - 1] = dest[i];
+ }
+ }
+ }
+
+ if (pos > 0) {
+ if (dest[(len - 1) * 2] & 0x80)
+ dest[--len * 2] = 0;
+ else
+ dest[--len] = 0;
+ pos--;
+ }
+
+ } else if (_keyPressed.keycode == Common::KEYCODE_LEFT || _keyPressed.keycode == Common::KEYCODE_KP4) {
+ if (pos > 0)
+ pos--;
+
+ } else if (_keyPressed.keycode == Common::KEYCODE_RIGHT || _keyPressed.keycode == Common::KEYCODE_KP6) {
+ if (pos < len && pos < (destMaxLen - 1))
+ pos++;
+
+ } else if ((in > 31 && in < 126) || (in == 0x89)) {
+ if (!(in == 32 && pos == 0)) {
+ if (in >= 97 && in <= 122)
+ in -= 32;
+
+ if (pos < len) {
+ for (int i = destMaxLen - 2; i >= pos; i--) {
+ if (in == 0x89) {
+ dest[(i + 1) * 2] = dest[i * 2];
+ dest[(i + 1) * 2 + 1] = dest[i * 2 + 1];
+ } else {
+ dest[i + 1] = dest[i];
+ }
+ }
+
+ if (in == 0x89) {
+ dest[pos * 2] = _csjis[0];
+ dest[pos++ * 2 + 1] = _csjis[1];
+ if (len == destMaxLen)
+ dest[len * 2] = 0;
+ } else {
+ dest[pos++] = in;
+ if (len == destMaxLen)
+ dest[len] = 0;
+ }
+ } else {
+ if (pos == destMaxLen) {
+ pos--;
+ len--;
+ }
+
+ if (in == 0x89) {
+ dest[pos * 2] = _csjis[0];
+ dest[pos * 2 + 1] = _csjis[1];
+ dest[++pos * 2] = 0;
+ } else {
+ dest[pos++] = in;
+ dest[pos] = 0;
+ }
+ }
+
+ if (++len > destMaxLen)
+ len = destMaxLen;
+
+ if (pos > (destMaxLen - 1))
+ pos = (destMaxLen - 1);
+ }
+ }
+
+ _screen->copyRegion(0, 191, (x - 1) << 3, y, (destMaxLen + 2) << 3, 9, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->printShadedText(dest, x << 3, y, textColor1, textColor2);
+
+ if (_vm->_flags.platform == Common::kPlatformFMTowns) {
+ if (pos < len) {
+ sufx[0] = dest[pos * 2];
+ sufx[1] = dest[pos * 2 + 1];
+ } else {
+ sufx[0] = 32;
+ sufx[1] = 0;
+ }
+ } else {
+ sufx[0] = (pos < len) ? dest[pos] : 32;
+ }
+
+ if (cursorState)
+ _screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor);
+ else
+ _screen->printShadedText(sufx, (x + pos) << 3, y, textColor1, textColor2);
+ _screen->updateScreen();
+
+ } while (_keyPressed.keycode != Common::KEYCODE_RETURN && _keyPressed.keycode != Common::KEYCODE_ESCAPE && !_vm->shouldQuit());
+
+#ifdef ENABLE_KEYMAPPER
+ keymapper->popKeymap(Common::kGlobalKeymapName);
+#endif
+
+ return _keyPressed.keycode == Common::KEYCODE_ESCAPE ? -1 : len;
+}
+
+int GUI_EoB::checkKatakanaSelection() {
+ if (_vm->_flags.platform != Common::kPlatformFMTowns)
+ return 0;
+
+ static uint16 kanaSelXCrds[] = { 224, 272, 186 };
+ Common::Point mousePos = _vm->getMousePos();
+ int highlight = -1;
+ _csjis[0] = _csjis[2] = 0;
+
+ for (int y = 112; y < 168; y += 16) {
+ for (int x = 152; x < 288; x += 8) {
+ if (!_vm->posWithinRect(mousePos.x, mousePos.y, x, y, x + 9, y + 9))
+ continue;
+
+ int lineOffs = (y - 112) >> 4;
+ int column = (x - 152) >> 2;
+
+ _csjis[0] = _vm->_katakanaLines[_currentKanaPage * 4 + lineOffs][column];
+ _csjis[1] = _vm->_katakanaLines[_currentKanaPage * 4 + lineOffs][column + 1];
+
+ if (_csjis[0] != '\x81' || _csjis[1] != '\x40') {
+ highlight = lineOffs << 8 | column;
+ _screen->printShadedText(_csjis, x & ~7, y & ~15, 6, 0);
+ }
+
+ x = 288; y = 168;
+ }
+ }
+
+ if (highlight == -1) {
+ for (int i = 0; i < 3; i++) {
+ if (!_vm->posWithinRect(mousePos.x, mousePos.y, kanaSelXCrds[i], 176, kanaSelXCrds[i] + _screen->getTextWidth(_vm->_katakanaSelectStrings[i]), 184))
+ continue;
+
+ highlight = 0x400 | i;
+ _screen->printShadedText(_vm->_katakanaSelectStrings[i], kanaSelXCrds[i], 176, 6, 0);
+ i = 3;
+ }
+ }
+
+ int in = 0;
+ for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
+ if (evt->event.type == Common::EVENT_LBUTTONDOWN)
+ in = 1;
+ }
+
+ if ((highlight == -1 || highlight == _menuCur) && !in)
+ return 0;
+
+ if (_menuCur != -1) {
+ if (_menuCur & 0x400) {
+ _screen->printShadedText(_vm->_katakanaSelectStrings[_menuCur & 3], kanaSelXCrds[_menuCur & 3], 176, 15, 0);
+ } else {
+ char osjis[3];
+ osjis[0] = _vm->_katakanaLines[_currentKanaPage * 4 + (_menuCur >> 8)][_menuCur & 0xFF];
+ osjis[1] = _vm->_katakanaLines[_currentKanaPage * 4 + (_menuCur >> 8)][(_menuCur & 0xFF) + 1];
+ osjis[2] = 0;
+ _screen->printShadedText(osjis, 152 + ((_menuCur & 0xFF) << 2), 112 + ((_menuCur >> 4) & ~0x0F), 15, 0);
+ }
+ }
+
+ _menuCur = highlight;
+
+ if (in && highlight != -1) {
+ if (highlight & 0x400) {
+ switch (highlight & 3) {
+ case 0:
+ printKatakanaOptions((_currentKanaPage + 1) % 3);
+ break;
+ case 1:
+ _keyPressed.keycode = Common::KEYCODE_RETURN;
+ break;
+ case 2:
+ _keyPressed.keycode = Common::KEYCODE_BACKSPACE;
+ break;
+ default:
+ break;
+ }
+ } else if (_csjis[0]) {
+ if (_csjis[0] == '\x81' && _csjis[1] == '\x51')
+ _csjis[1] = '\x40';
+ return 0x89;
+ }
+ }
+
+ return in;
+}
+
+void GUI_EoB::printKatakanaOptions(int page) {
+ if (_vm->_flags.platform != Common::kPlatformFMTowns)
+ return;
+
+ _currentKanaPage = page;
+ _screen->copyRegion(160, 44, 144, 108, 160, 84, 2, 0, Screen::CR_NO_P_CHECK);
+ for (int i = 0; i < 4; i++)
+ _screen->printShadedText(_vm->_katakanaLines[page * 4 + i], 152, (i << 4) + 112, 15, 0);
+
+ static uint16 kanaSelCrds[] = { 224, 272, 186 };
+ for (int i = 0; i < 3; i++)
+ _screen->printShadedText(_vm->_katakanaSelectStrings[i], kanaSelCrds[i], 176, 15, 0);
+}
+
+void GUI_EoB::transferWaitBox() {
+ const ScreenDim *dm = _screen->getScreenDim(11);
+ int xo = dm->sx;
+ int yo = dm->sy;
+ _screen->modifyScreenDim(11, dm->sx + 9, dm->sy + 24, dm->w, dm->h);
+ displayTextBox(-4);
+ _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
+}
+
+Common::String GUI_EoB::transferTargetMenu(Common::Array<Common::String> &targets) {
+ if (_savegameList) {
+ for (int i = 0; i < _savegameListSize; i++)
+ delete[] _savegameList[i];
+ delete[] _savegameList;
+ }
+
+ _savegameListSize = targets.size();
+ _savegameList = new char*[_savegameListSize];
+ memset(_savegameList, 0, _savegameListSize * sizeof(char *));
+
+ Common::StringArray::iterator ii = targets.begin();
+ for (int i = 0; i < _savegameListSize; ++i) {
+ _savegameList[i] = new char[(*ii).size() + 1];
+ strcpy(_savegameList[i], (*ii++).c_str());
+ }
+
+ const ScreenDim *dm = _screen->getScreenDim(11);
+ int xo = dm->sx;
+ int yo = dm->sy;
+ _screen->modifyScreenDim(11, dm->sx + 9, dm->sy + 14, dm->w, dm->h);
+
+ int slot = 0;
+ do {
+ slot = selectSaveSlotDialogue(72, 14, 2);
+ if (slot == 6)
+ break;
+ } while (_saveSlotIdTemp[slot] == -1);
+
+ _screen->copyRegion(72, 14, 72, 14, 176, 144, 12, 0, Screen::CR_NO_P_CHECK);
+ _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
+
+ return (slot < 6) ? _savegameList[_savegameOffset + slot] : Common::String();
+}
+
+bool GUI_EoB::transferFileMenu(Common::String &targetName, Common::String &selection) {
+ updateSaveSlotsList(targetName, true);
+ _saveSlotsListUpdateNeeded = true;
+ selection.clear();
+
+ if (!_savegameListSize)
+ return false;
+
+ const ScreenDim *dm = _screen->getScreenDim(11);
+ int xo = dm->sx;
+ int yo = dm->sy;
+ _screen->modifyScreenDim(11, dm->sx + 9, dm->sy + 14, dm->w, dm->h);
+
+ int slot = 0;
+ do {
+ slot = selectSaveSlotDialogue(72, 14, 4);
+ if (slot == 6)
+ break;
+
+ if (_saveSlotIdTemp[slot] == -1)
+ messageDialogue(11, 65, 6);
+ else {
+ _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
+ selection = _vm->getSavegameFilename(targetName, _saveSlotIdTemp[slot]);
+ return true;
+ }
+ } while (_saveSlotIdTemp[slot] == -1);
+
+ _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
+ return true;
+}
+
+void GUI_EoB::createScreenThumbnail(Graphics::Surface &dst) {
+ dst.copyFrom(_vm->_thumbNail);
+}
+
+void GUI_EoB::simpleMenu_initMenuItemsMask(int menuId, int maxItem, int32 menuItemsMask, int itemOffset) {
+ if (menuItemsMask == -1) {
+ _menuNumItems = _screen->getScreenDim(19 + menuId)->h;
+ _menuCur = _screen->getScreenDim(19 + menuId)->unk8;
+ return;
+ }
+
+ _menuNumItems = 0;
+
+ for (int i = 0; i < maxItem; i++) {
+ if (menuItemsMask & (1 << (i + itemOffset)))
+ _menuNumItems++;
+ }
+
+ _menuCur = 0;
+}
+
+bool GUI_EoB::runSaveMenu(int x, int y) {
+ const ScreenDim *dm = _screen->getScreenDim(11);
+ int xo = dm->sx;
+ int yo = dm->sy;
+ bool result = false;
+
+ _screen->modifyScreenDim(11, dm->sx + (x >> 3), dm->sy + y, dm->w, dm->h);
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ updateSaveSlotsList(_vm->_targetName);
+ int slot = selectSaveSlotDialogue(x, y, 0);
+ if (slot > 5) {
+ runLoop = result = false;
+ } else if (slot >= 0) {
+ bool useSlot = (_saveSlotIdTemp[slot] == -1);
+ if (useSlot)
+ _saveSlotStringsTemp[slot][0] = 0;
+ else
+ useSlot = confirmDialogue2(11, 55, 1);
+
+ if (!useSlot)
+ continue;
+
+ int fx = (x + 1) << 3;
+ int fy = y + slot * 17 + 23;
+ Screen::FontId of = _screen->_currentFont;
+ _screen->set16bitShadingLevel(4);
+
+ for (int in = -1; in == -1 && !_vm->shouldQuit();) {
+ _screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill);
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ TimeDate td;
+ _vm->_system->getTimeAndDate(td);
+ Common::strlcpy(_saveSlotStringsTemp[slot], Common::String::format(_vm->_saveNamePatterns[_vm->_currentLevel * 2 + _vm->_currentSub], td.tm_mon + 1, td.tm_mday, td.tm_hour, td.tm_min).c_str(), 25);
+ in = strlen(_saveSlotStringsTemp[slot]);
+ of = _vm->screen()->setFont(Screen::FID_6_FNT);
+ y++;
+ } else {
+ in = getTextInput(_saveSlotStringsTemp[slot], x + 1, fy, 19, 2, 0, 8);
+ }
+ if (in == -1) {
+ useSlot = false;
+ break;
+ }
+
+ if (!strlen(_saveSlotStringsTemp[slot])) {
+ messageDialogue(11, 54, 6);
+ in = -1;
+ }
+ }
+
+ if (!useSlot) {
+ continue;
+ }
+
+ _screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill);
+ _screen->printShadedText(_saveSlotStringsTemp[slot], (x + 1) << 3, fy, 15, 0);
+ _screen->set16bitShadingLevel(0);
+ _screen->setFont(of);
+ _screen->updateScreen();
+
+ Graphics::Surface thumb;
+ createScreenThumbnail(thumb);
+ Common::Error err = _vm->saveGameStateIntern(_savegameOffset + slot, _saveSlotStringsTemp[slot], &thumb);
+ thumb.free();
+
+ if (err.getCode() == Common::kNoError)
+ result = true;
+ else
+ messageDialogue(11, 15, 6);
+
+ runLoop = false;
+ }
+ }
+
+ _screen->modifyScreenDim(11, xo, yo, dm->w, dm->h);
+ return result;
+}
+
+int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
+ _saveSlotX = _saveSlotY = 0;
+ int col1 = (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : 15;
+ _screen->setCurPage(2);
+
+ _savegameOffset = 0;
+
+ drawMenuButtonBox(0, 0, 176, 144, false, false);
+ const char *title = (id < 2) ? _vm->_saveLoadStrings[2 + id] : _vm->_transferStringsScummVM[id - 1];
+ _screen->printShadedText(title, 52, 5, col1, 0);
+
+ _screen->copyRegion(0, 0, x, y, 176, 144, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->setCurPage(0);
+ _screen->updateScreen();
+
+ _saveSlotX = x;
+ _saveSlotY = y;
+ int lastHighlight = -1;
+ int lastOffset = -1;
+ int newHighlight = 0;
+ int slot = -1;
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
+ newHighlight = 6;
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP2]) {
+ if (++newHighlight > 5) {
+ newHighlight = 5;
+ if (++_savegameOffset > 984)
+ _savegameOffset = 984;
+ else
+ lastOffset = -1;
+ }
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP8]) {
+ if (--newHighlight < 0) {
+ newHighlight = 0;
+ if (--_savegameOffset < 0)
+ _savegameOffset = 0;
+ else
+ lastOffset = -1;
+ }
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_PAGEDOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP1]) {
+ _savegameOffset += 6;
+ if (_savegameOffset > 984)
+ _savegameOffset = 984;
+ else
+ lastOffset = -1;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_PAGEUP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP7]) {
+ _savegameOffset -= 6;
+ if (_savegameOffset < 0)
+ _savegameOffset = 0;
+ else
+ lastOffset = -1;
+ } else if (inputFlag == 205) {
+ if (++_savegameOffset > 984)
+ _savegameOffset = 984;
+ else
+ lastOffset = -1;
+ } else if (inputFlag == 203) {
+ if (--_savegameOffset < 0)
+ _savegameOffset = 0;
+ else
+ lastOffset = -1;
+ } else {
+ slot = getHighlightSlot();
+ if (slot != -1) {
+ newHighlight = slot;
+ if (inputFlag == 199)
+ runLoop = false;
+ }
+ }
+
+ if (lastOffset != _savegameOffset) {
+ lastHighlight = -1;
+ setupSaveMenuSlots();
+ for (int i = 0; i < 7; i++)
+ drawSaveSlotButton(i, 1, col1);
+ lastOffset = _savegameOffset;
+ }
+
+ if (lastHighlight != newHighlight) {
+ drawSaveSlotButton(lastHighlight, 0, col1);
+ drawSaveSlotButton(newHighlight, 0, 6);
+
+ // Display highlighted slot index in the bottom left corner to avoid people getting lost with the 990 save slots
+ _screen->setFont(Screen::FID_6_FNT);
+ int sli = (newHighlight == 6) ? _savegameOffset : (_savegameOffset + newHighlight);
+ _screen->set16bitShadingLevel(4);
+ _screen->printText(Common::String::format("%03d/989", sli).c_str(), _saveSlotX + 5, _saveSlotY + 135, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
+ _screen->set16bitShadingLevel(0);
+ _screen->setFont(Screen::FID_8_FNT);
+
+ _screen->updateScreen();
+ lastHighlight = newHighlight;
+ }
+ }
+
+ drawSaveSlotButton(newHighlight, 2, 6);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawSaveSlotButton(newHighlight, 1, 6);
+ _screen->updateScreen();
+
+ return newHighlight;
+}
+
+void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
+ if (charIndex == -1)
+ return;
+
+ uint8 np[8];
+ memset(np, 0, sizeof(np));
+ uint32 avltyFlags = 0;
+ int li = 0;
+ int lv = 0;
+
+ EoBCharacter *c = &_vm->_characters[charIndex];
+ int8 wm = c->wisdomCur - 12;
+ if (wm < 0)
+ wm = 0;
+
+ if (spellType) {
+ li = _vm->getCharacterLevelIndex(2, c->cClass);
+
+ if (li == -1) {
+ li = _vm->getCharacterLevelIndex(4, c->cClass);
+
+ if (li != -1) {
+ lv = c->level[li] - 1;
+ if (lv < 0)
+ lv = 0;
+
+ for (int i = 0; i < _numPages; i++)
+ np[i] = _vm->_numSpellsPal[lv * _numPages + i];
+
+ avltyFlags = _paladinSpellAvltyFlags;
+ }
+
+ } else {
+ lv = c->level[li] - 1;
+ for (int i = 0; i < _numPages; i++) {
+ np[i] = _vm->_numSpellsCleric[lv * _numPages + i];
+ if (np[i])
+ np[i] += _vm->_numSpellsWisAdj[wm * _numPages + i];
+ }
+ avltyFlags = _clericSpellAvltyFlags;
+ }
+
+ } else {
+ li = _vm->getCharacterLevelIndex(1, c->cClass);
+
+ if (li == -1) {
+ if (_vm->checkInventoryForRings(charIndex, 1)) {
+ np[3] <<= 1;
+ np[4] <<= 1;
+ }
+
+ } else {
+ lv = c->level[li] - 1;
+ for (int i = 0; i < _numPages; i++)
+ np[i] = _vm->_numSpellsMage[lv * _numPages + i];
+
+ avltyFlags = c->mageSpellsAvailableFlags;
+ }
+ }
+
+ int8 *menuSpellMap = new int8[88];
+ memset(menuSpellMap, 0, 88);
+ int8 *numAssignedSpellsPerBookPage = new int8[8];
+ memset(numAssignedSpellsPerBookPage, 0, 8);
+ memset(_numAssignedSpellsOfType, 0, 72);
+ int8 *lh = new int8[40];
+ memset(lh, 0, 40);
+
+ memcpy(lh, spellType ? _vm->_spellLevelsCleric : _vm->_spellLevelsMage, spellType ? _vm->_spellLevelsClericSize : _vm->_spellLevelsMageSize);
+ int8 *charSpellList = spellType ? c->clericSpells : c->mageSpells;
+
+ for (int i = 0; i < 80; i++) {
+ int8 s = charSpellList[i];
+ if (s == 0 || (_vm->game() == GI_EOB2 && s == 29))
+ continue;
+
+ if (s < 0)
+ s = -s;
+ else
+ _numAssignedSpellsOfType[s * 2 - 1]++;
+
+ s--;
+ _numAssignedSpellsOfType[s * 2]++;
+ numAssignedSpellsPerBookPage[lh[s] - 1]++;
+ }
+
+ for (int i = 0; i < 32; i++) {
+ if (!(avltyFlags & (1 << i)))
+ continue;
+
+ int d = lh[i] - 1;
+ if (d < 0)
+ continue;
+
+ if (!spellType || (spellType && np[d])) {
+ menuSpellMap[d * 11]++;
+ menuSpellMap[d * 11 + menuSpellMap[d * 11]] = i + 1;
+ }
+ }
+
+ Button *buttonList = initMenu(4);
+
+ int lastHighLightText = -1;
+ int lastHighLightButton = -1;
+ int newHighLightButton = 0;
+ int newHighLightText = 0;
+ bool updateDesc = true;
+ bool updateList = true;
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ updateBoxFrameHighLight(charIndex);
+
+ if (newHighLightButton < 0)
+ newHighLightButton = 7;
+ if (newHighLightButton > 7)
+ newHighLightButton = 0;
+
+ Button *b = 0;
+
+ if (lastHighLightButton != newHighLightButton) {
+ if (lastHighLightButton >= 0)
+ drawMenuButton(_vm->gui_getButton(buttonList, lastHighLightButton + 26), false, false, true);
+ drawMenuButton(_vm->gui_getButton(buttonList, newHighLightButton + 26), false, true, true);
+ newHighLightText = 0;
+ lastHighLightText = -1;
+ lastHighLightButton = newHighLightButton;
+ updateDesc = updateList = true;
+ }
+
+ if (updateList) {
+ updateList = false;
+ _screen->setCurPage(2);
+ for (int ii = 1; ii < 9; ii++)
+ memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + ii], ii - 1, spellType, false, false);
+
+ _screen->setCurPage(0);
+ _screen->copyRegion(0, 50, 0, 50, 176, 72, 2, 0, Screen::CR_NO_P_CHECK);
+ lastHighLightText = -1;
+ }
+
+ if (updateDesc) {
+ updateDesc = false;
+ _screen->set16bitShadingLevel(4);
+ _screen->printShadedText(Common::String::format(_vm->_menuStringsMgc[1], np[lastHighLightButton] - numAssignedSpellsPerBookPage[lastHighLightButton], np[lastHighLightButton]).c_str(), 8, 38, 9, _vm->guiSettings()->colors.fill);
+ _screen->set16bitShadingLevel(0);
+ }
+
+ if (newHighLightText < 0)
+ newHighLightText = menuSpellMap[lastHighLightButton * 11] - 1;
+
+ if (menuSpellMap[lastHighLightButton * 11] <= newHighLightText)
+ newHighLightText = 0;
+
+ if (newHighLightText != lastHighLightText) {
+ memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1], lastHighLightText, spellType, true, false);
+ memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + newHighLightText + 1], newHighLightText, spellType, true, true);
+ lastHighLightText = newHighLightText;
+ }
+
+ int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) {
+ inputFlag = 0x801A + ((lastHighLightButton + 1) % _numVisPages);
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT]) {
+ inputFlag = lastHighLightButton ? 0x8019 + lastHighLightButton : 0x8019 + _numVisPages;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
+ inputFlag = 0x8018;
+ } else {
+ Common::Point p = _vm->getMousePos();
+ if (_vm->posWithinRect(p.x, p.y, 8, 50, 168, 122)) {
+ newHighLightText = (p.y - 50) / 9;
+ if (menuSpellMap[lastHighLightButton * 11] - 1 < newHighLightText)
+ newHighLightText = menuSpellMap[lastHighLightButton * 11] - 1;
+ }
+ }
+
+ if (inputFlag & 0x8000) {
+ b = _vm->gui_getButton(buttonList, inputFlag & 0x7FFF);
+ drawMenuButton(b, true, true, true);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButton(b, false, false, true);
+ _screen->updateScreen();
+ }
+
+ if (inputFlag == 0x8019 || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+ if (np[lastHighLightButton] > numAssignedSpellsPerBookPage[lastHighLightButton] && lastHighLightText != -1) {
+ _numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1] * 2 - 2]++;
+ numAssignedSpellsPerBookPage[lastHighLightButton]++;
+ memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1], lastHighLightText, spellType, false, true);
+ updateDesc = true;
+ }
+
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP_MINUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_MINUS]) {
+ if (np[lastHighLightButton] && _numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1] * 2 - 2]) {
+ _numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1] * 2 - 2]--;
+ numAssignedSpellsPerBookPage[lastHighLightButton]--;
+ memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1], lastHighLightText, spellType, false, true);
+ updateDesc = true;
+ }
+
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP8]) {
+ newHighLightText = lastHighLightText - 1;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP2]) {
+ newHighLightText = lastHighLightText + 1;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_END] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP1]) {
+ newHighLightText = menuSpellMap[lastHighLightButton * 11] - 1;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_HOME] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP7]) {
+ newHighLightText = 0;
+ } else if (inputFlag == 0x8017) {
+ if (numAssignedSpellsPerBookPage[lastHighLightButton]) {
+ for (int i = 1; i <= menuSpellMap[lastHighLightButton * 11]; i++) {
+ numAssignedSpellsPerBookPage[lastHighLightButton] -= _numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + i] * 2 - 2];
+ _numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + i] * 2 - 2] = 0;
+ }
+
+ updateDesc = updateList = true;
+ }
+
+ } else if (inputFlag == 0x8018) {
+ _vm->gui_drawAllCharPortraitsWithStats();
+ runLoop = false;
+
+ } else if (inputFlag & 0x8000) {
+ newHighLightButton = inputFlag - 0x801A;
+ if (newHighLightButton == lastHighLightButton)
+ drawMenuButton(_vm->gui_getButton(buttonList, inputFlag & 0x7FFF), false, true, true);
+ }
+ }
+
+ releaseButtons(buttonList);
+ updateBoxFrameHighLight(-1);
+
+ _screen->setFont(Screen::FID_6_FNT);
+ _vm->gui_drawCharPortraitWithStats(charIndex);
+ _screen->setFont(Screen::FID_8_FNT);
+
+ memset(charSpellList, 0, 80);
+ if (spellType && _vm->game() == GI_EOB2)
+ charSpellList[0] = 29;
+
+ for (int i = 0; i < 32; i++) {
+ if (_numAssignedSpellsOfType[i * 2] < _numAssignedSpellsOfType[i * 2 + 1])
+ _numAssignedSpellsOfType[i * 2 + 1] = _numAssignedSpellsOfType[i * 2];
+
+ if (_numAssignedSpellsOfType[i * 2 + 1]) {
+ _numAssignedSpellsOfType[i * 2]--;
+ _numAssignedSpellsOfType[i * 2 + 1]--;
+
+ int pg = lh[i] - 1;
+ for (int ii = 0; ii < 10; ii++) {
+ if (!charSpellList[pg * 10 + ii]) {
+ charSpellList[pg * 10 + ii] = i + 1;
+ break;
+ }
+ }
+ i--;
+
+ } else if (_numAssignedSpellsOfType[i * 2]) {
+ _numAssignedSpellsOfType[i * 2]--;
+
+ _needRest = true;
+ int pg = lh[i] - 1;
+ for (int ii = 0; ii < 10; ii++) {
+ if (!charSpellList[pg * 10 + ii]) {
+ charSpellList[pg * 10 + ii] = -(i + 1);
+ break;
+ }
+ }
+ i--;
+ }
+ }
+
+ delete[] menuSpellMap;
+ delete[] numAssignedSpellsPerBookPage;
+ delete[] lh;
+}
+
+
+void GUI_EoB::scribeScrollDialogue() {
+ int16 *scrollInvSlot = new int16[32];
+ int16 *scrollCharacter = new int16[32];
+ int16 *menuItems = new int16[6];
+ int numScrolls = 0;
+
+ for (int i = 0; i < 32; i++) {
+ for (int ii = 0; ii < 6; ii++) {
+ scrollInvSlot[i] = _vm->checkInventoryForItem(ii, 34, i + 1) + 1;
+ if (scrollInvSlot[i] > 0) {
+ numScrolls++;
+ scrollCharacter[i] = ii;
+ break;
+ }
+ }
+ }
+
+ if (numScrolls) {
+ int csel = selectCharacterDialogue(49);
+ if (csel != -1) {
+
+ EoBCharacter *c = &_vm->_characters[csel];
+ int s = 0;
+
+ for (int i = 0; i < 32 && s < 6; i++) {
+ if (!scrollInvSlot[i])
+ continue;
+
+ if (c->mageSpellsAvailableFlags & (1 << i))
+ scrollInvSlot[i] = 0;
+ else
+ menuItems[s++] = i + 1;
+ }
+
+ if (s) {
+ Button *buttonList = 0;
+ bool redraw = true;
+ int lastHighLight = -1;
+ int newHighLight = 0;
+
+ while (s && !_vm->shouldQuit()) {
+ if (redraw) {
+ s = 0;
+ for (int i = 0; i < 32 && s < 6; i++) {
+ if (!scrollInvSlot[i])
+ continue;
+ menuItems[s++] = i + 1;
+ }
+
+ if (!s)
+ break;
+
+ releaseButtons(buttonList);
+ buttonList = initMenu(6);
+
+ for (int i = 0; i < s; i++)
+ _screen->printShadedText(_vm->_mageSpellList[menuItems[i]], 8, 9 * i + 50, 15, 0);
+
+ redraw = false;
+ lastHighLight = -1;
+ newHighLight = 0;
+ }
+
+ if (lastHighLight != newHighLight) {
+ if (lastHighLight >= 0)
+ _screen->printText(_vm->_mageSpellList[menuItems[lastHighLight]], 8, 9 * lastHighLight + 50, 15, 0);
+ lastHighLight = newHighLight;
+ _screen->printText(_vm->_mageSpellList[menuItems[lastHighLight]], 8, 9 * lastHighLight + 50, 6, 0);
+ _screen->updateScreen();
+ }
+
+ int inputFlag = _vm->checkInput(buttonList, false, 0);
+ _vm->removeInputTop();
+
+ if (inputFlag == 0) {
+ Common::Point p = _vm->getMousePos();
+ if (_vm->posWithinRect(p.x, p.y, 8, 50, 176, s * 9 + 49))
+ newHighLight = (p.y - 50) / 9;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP2] || inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN]) {
+ newHighLight = (newHighLight + 1) % s;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP8] || inputFlag == _vm->_keyMap[Common::KEYCODE_UP]) {
+ newHighLight = (newHighLight + s - 1) % s;
+ } else if (inputFlag == 0x8023 || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
+ s = 0;
+ } else if (inputFlag == 0x8024) {
+ newHighLight = (_vm->_mouseY - 50) / 9;
+ if (newHighLight >= 0 && newHighLight < s) {
+ inputFlag = _vm->_keyMap[Common::KEYCODE_SPACE];
+ } else {
+ inputFlag = 0;
+ newHighLight = lastHighLight;
+ }
+ }
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5]) {
+ int t = menuItems[newHighLight] - 1;
+ Item scItem = _vm->_characters[scrollCharacter[t]].inventory[scrollInvSlot[t] - 1];
+ c->mageSpellsAvailableFlags |= (1 << t);
+ _vm->_characters[scrollCharacter[t]].inventory[scrollInvSlot[t] - 1] = 0;
+ _vm->gui_drawCharPortraitWithStats(_vm->_characters[scrollCharacter[t]].id);
+ scrollInvSlot[t] = 0;
+ _vm->_items[scItem].block = -1;
+ redraw = true;
+ s--;
+ }
+ }
+
+ releaseButtons(buttonList);
+
+ } else {
+ displayTextBox(51);
+ }
+ }
+ } else {
+ displayTextBox(50);
+ }
+
+ delete[] menuItems;
+ delete[] scrollCharacter;
+ delete[] scrollInvSlot;
+}
+
+bool GUI_EoB::restParty() {
+ static const int8 eob1healSpells[] = { 2, 15, 20, 24 };
+ static const int8 eob2healSpells[] = { 3, 16, 20, 28 };
+ const int8 *spells = _vm->game() == GI_EOB1 ? eob1healSpells : eob2healSpells;
+
+ uint8 crs[6];
+ memset(crs, 0, 6);
+ int hours = 0;
+
+ if (_vm->_inf->preventRest()) {
+ assert(_vm->_menuStringsRest3[0]);
+ _vm->restParty_displayWarning(_vm->_menuStringsRest3[0]);
+ return true;
+ }
+
+ if (_vm->restParty_updateMonsters())
+ return true;
+
+ if (_vm->restParty_extraAbortCondition())
+ return true;
+
+ drawMenuButtonBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, false, false);
+
+ int nonPoisoned = 0;
+ for (int i = 0; i < 6; i++) {
+ if (!_vm->testCharacter(i, 1))
+ continue;
+ nonPoisoned |= _vm->testCharacter(i, 0x10);
+ }
+
+ if (!nonPoisoned) {
+ if (!confirmDialogue(59))
+ return false;
+ }
+
+ int8 *list = 0;
+ bool useHealers = false;
+ bool res = false;
+ bool restLoop = true;
+ bool restContinue = false;
+ int injured = _vm->restParty_getCharacterWithLowestHp();
+
+ if (injured > 0) {
+ for (int i = 0; i < 6; i++) {
+ if (!_vm->testCharacter(i, 13))
+ continue;
+ if (_vm->getCharacterLevelIndex(2, _vm->_characters[i].cClass) == -1 && _vm->getCharacterLevelIndex(4, _vm->_characters[i].cClass) == -1)
+ continue;
+ if (_vm->checkInventoryForItem(i, 30, -1) == -1)
+ continue;
+ if (_vm->restParty_checkHealSpells(i)) {
+ useHealers = confirmDialogue(40);
+ break;
+ }
+ }
+ }
+
+ _screen->setClearScreenDim(7);
+ _screen->setFont(Screen::FID_6_FNT);
+
+ restParty_updateRestTime(hours, true);
+
+ for (int l = 0; !res && restLoop && !_vm->shouldQuit();) {
+ l++;
+
+ // Regenerate spells
+ for (int i = 0; i < 6; i++) {
+ crs[i]++;
+
+ if (!_vm->_characters[i].food)
+ continue;
+ if (!_vm->testCharacter(i, 5))
+ continue;
+
+ if (_vm->checkInventoryForItem(i, 30, -1) != -1) {
+ list = _vm->_characters[i].clericSpells;
+
+ for (int ii = 0; ii < 80; ii++) {
+ if ((ii / 10 + 48) >= crs[i])
+ break;
+
+ if (*list >= 0) {
+ list++;
+ continue;
+ }
+
+ *list *= -1;
+ crs[i] = 48;
+ _vm->_txt->printMessage(Common::String::format(_vm->_menuStringsRest2[0], _vm->_characters[i].name, _vm->_spells[_vm->_clericSpellOffset + *list].name).c_str());
+ _vm->delay(80);
+ break;
+ }
+ }
+
+ if (_vm->checkInventoryForItem(i, 29, -1) != -1) {
+ list = _vm->_characters[i].mageSpells;
+
+ for (int ii = 0; ii < 80; ii++) {
+ if ((ii / 6 + 48) >= crs[i])
+ break;
+
+ if (*list >= 0) {
+ list++;
+ continue;
+ }
+
+ *list *= -1;
+ crs[i] = 48;
+ _vm->_txt->printMessage(Common::String::format(_vm->_menuStringsRest2[1], _vm->_characters[i].name, _vm->_spells[*list].name).c_str());
+ _vm->delay(80);
+ break;
+ }
+ }
+ }
+
+ // Heal party members
+ if (useHealers) {
+ for (int i = 0; i < 6 && injured; i++) {
+ if (_vm->getCharacterLevelIndex(2, _vm->_characters[i].cClass) == -1 && _vm->getCharacterLevelIndex(4, _vm->_characters[i].cClass) == -1)
+ continue;
+ if (_vm->checkInventoryForItem(i, 30, -1) == -1)
+ continue;
+
+ list = 0;
+ if (crs[i] >= 48) {
+ for (int ii = 0; !list && ii < 3; ii++)
+ list = (int8 *)memchr(_vm->_characters[i].clericSpells, -spells[ii], 80);
+ }
+
+ if (list)
+ break;
+
+ list = _vm->_characters[i].clericSpells;
+ for (int ii = 0; ii < 80 && injured; ii++) {
+ int healHp = 0;
+ if (*list == spells[0])
+ healHp = _vm->rollDice(1, 8, 0);
+ else if (*list == spells[1])
+ healHp = _vm->rollDice(2, 8, 1);
+ else if (*list == spells[2])
+ healHp = _vm->rollDice(3, 8, 3);
+
+ if (!healHp) {
+ list++;
+ continue;
+ }
+
+ *list *= -1;
+ list++;
+
+ crs[i] = 0;
+ injured--;
+
+ _vm->_txt->printMessage(Common::String::format(_vm->_menuStringsRest2[2], _vm->_characters[i].name, _vm->_characters[injured].name).c_str());
+ _vm->delay(80);
+
+ _vm->_characters[injured].hitPointsCur += healHp;
+ if (_vm->_characters[injured].hitPointsCur > _vm->_characters[injured].hitPointsMax)
+ _vm->_characters[injured].hitPointsCur = _vm->_characters[injured].hitPointsMax;
+
+ _vm->gui_drawCharPortraitWithStats(injured++);
+ }
+ }
+ }
+
+ if (l == 6) {
+ l = 0;
+ restParty_updateRestTime(++hours, false);
+ _vm->_restPartyElapsedTime += (32760 * _vm->_tickLength);
+
+ // Update poisoning
+ for (int i = 0; i < 6; i++) {
+ if (!_vm->testCharacter(i, 1))
+ continue;
+ if (_vm->testCharacter(i, 16))
+ continue;
+ _vm->inflictCharacterDamage(i, 10);
+ _vm->delayWithTicks(5);
+ }
+
+ if (!(hours % 8)) {
+ bool starving = false;
+ for (int i = 0; i < 6; i++) {
+ // Add Lay On Hands spell
+ if (_vm->_characters[i].cClass == 2) {
+ list = (int8 *)memchr(_vm->_characters[i].clericSpells, spells[3], 10);
+ if (list) {
+ *list = spells[3];
+ } else {
+ list = (int8 *)memchr(_vm->_characters[i].clericSpells, -spells[3], 10);
+ if (list) {
+ *list = spells[3];
+ } else if (!memchr(_vm->_characters[i].clericSpells, spells[3], 10)) {
+ list = (int8 *)memchr(_vm->_characters[i].clericSpells, 0, 10);
+ *list = spells[3];
+ }
+ }
+ }
+
+ if (!_vm->testCharacter(i, 3))
+ continue;
+
+ // Update hitpoints and food status
+ if (_vm->_characters[i].food) {
+ if (_vm->_characters[i].hitPointsCur < _vm->_characters[i].hitPointsMax) {
+ _vm->_characters[i].hitPointsCur++;
+ _screen->setFont(Screen::FID_6_FNT);
+ _vm->gui_drawCharPortraitWithStats(i);
+ }
+
+ if (!_vm->checkInventoryForRings(i, 2)) {
+ if (_vm->_characters[i].food <= 5) {
+ _vm->_characters[i].food = 0;
+ starving = true;
+ } else {
+ _vm->_characters[i].food -= 5;
+ }
+ }
+ } else {
+ if ((hours % 24) || (_vm->_characters[i].hitPointsCur <= -10))
+ continue;
+ _vm->inflictCharacterDamage(i, 1);
+ starving = true;
+ _screen->setFont(Screen::FID_6_FNT);
+ _vm->gui_drawCharPortraitWithStats(i);
+ }
+ }
+
+ if (starving) {
+ if (!confirmDialogue(47)) {
+ restContinue = false;
+ restLoop = false;
+ }
+ restParty_updateRestTime(hours, true);
+ }
+ injured = restLoop ? _vm->restParty_getCharacterWithLowestHp() : 0;
+ }
+ }
+
+ if (!_vm->restParty_checkSpellsToLearn() && restLoop && !restContinue && injured) {
+ restContinue = confirmDialogue(41);
+ restParty_updateRestTime(hours, true);
+ if (!restContinue)
+ restLoop = false;
+ }
+
+ int in = _vm->checkInput(0, false, 0);
+ _vm->removeInputTop();
+ if (in)
+ restLoop = false;
+
+ if (restLoop) {
+ res = _vm->restParty_updateMonsters();
+ if (!res)
+ res = _vm->checkPartyStatus(false);
+ }
+
+ if (!_vm->restParty_checkSpellsToLearn()) {
+ if (!restContinue) {
+ if (!useHealers)
+ restLoop = false;
+ }
+ if (!injured)
+ restLoop = false;
+ }
+ }
+
+ _vm->removeInputTop();
+ _screen->setScreenDim(4);
+ _screen->setFont(Screen::FID_8_FNT);
+
+ if (!res) {
+ if (!injured)
+ displayTextBox(43);
+ if (hours > 4)
+ _vm->restParty_npc();
+ }
+
+ return res;
+}
+
+bool GUI_EoB::confirmDialogue(int id) {
+ int od = _screen->curDimIndex();
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+
+ Button *buttonList = initMenu(5);
+
+ _screen->printShadedText(getMenuString(id), (_screen->_curDim->sx + 1) << 3, _screen->_curDim->sy + 4, 15, 0);
+
+ int newHighlight = 0;
+ int lastHighlight = -1;
+ bool result = false;
+
+ for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+ if (newHighlight != lastHighlight) {
+ if (lastHighlight != -1)
+ drawMenuButton(_vm->gui_getButton(buttonList, lastHighlight + 33), false, false, true);
+ drawMenuButton(_vm->gui_getButton(buttonList, newHighlight + 33), false, true, true);
+ _screen->updateScreen();
+ lastHighlight = newHighlight;
+ }
+
+ int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF;
+ _vm->removeInputTop();
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+ result = lastHighlight == 0;
+ inputFlag = 0x8021 + lastHighlight;
+ runLoop = false;
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) {
+ newHighlight ^= 1;
+ } else if (inputFlag == 0x8021) {
+ result = true;
+ runLoop = false;
+ } else if (inputFlag == 0x8022) {
+ result = false;
+ runLoop = false;
+ } else {
+ Common::Point p = _vm->getMousePos();
+ for (Button *b = buttonList; b; b = b->nextButton) {
+ if ((b->arg & 2) && _vm->posWithinRect(p.x, p.y, b->x, b->y, b->x + b->width, b->y + b->height))
+ newHighlight = b->index - 33;
+ }
+ }
+
+ if (!runLoop) {
+ Button *b = _vm->gui_getButton(buttonList, lastHighlight + 33);
+ drawMenuButton(b, true, true, true);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButton(b, false, true, true);
+ _screen->updateScreen();
+ }
+ }
+
+ releaseButtons(buttonList);
+
+ _screen->setFont(of);
+ _screen->setScreenDim(od);
+
+ return result;
+}
+
+int GUI_EoB::selectCharacterDialogue(int id) {
+ uint8 flags = (id == 26) ? (_vm->game() == GI_EOB1 ? 0x04 : 0x14) : 0x02;
+ _vm->removeInputTop();
+
+ _charSelectRedraw = false;
+ bool starvedUnconscious = false;
+ int count = 0;
+ int result = -1;
+ int found[6];
+
+ for (int i = 0; i < 6; i++) {
+ found[i] = -1;
+
+ if (!_vm->testCharacter(i, 1))
+ continue;
+
+ if (!(_vm->_classModifierFlags[_vm->_characters[i].cClass] & flags) && (id != 53))
+ continue;
+
+ if (id != 53 && (!_vm->_characters[i].food || !_vm->testCharacter(i, 4))) {
+ starvedUnconscious = true;
+ } else {
+ found[i] = 0;
+ result = i;
+ count++;
+ }
+ }
+
+ if (!count) {
+ int eid = 0;
+ if (id == 23)
+ eid = (starvedUnconscious || _vm->game() == GI_EOB1) ? 28 : 72;
+ else if (id == 26)
+ eid = (starvedUnconscious || _vm->game() == GI_EOB1) ? 27 : 73;
+ else if (id == 49)
+ eid = 52;
+
+ displayTextBox(eid);
+ return -1;
+ }
+
+ static const uint16 selX[] = { 184, 256, 184, 256, 184, 256 };
+ static const uint8 selY[] = { 2, 2, 54, 54, 106, 106};
+
+ for (int i = 0; i < 6; i++) {
+ if (found[i] != -1 || !_vm->testCharacter(i, 1))
+ continue;
+
+ _screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i], selY[i], 0);
+ _screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i] + 16, selY[i], 0);
+ _screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i] + 32, selY[i], 0);
+ _screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i] + 48, selY[i], 0);
+ _charSelectRedraw = true;
+ }
+
+ if (count == 1) {
+ int l = _vm->getCharacterLevelIndex(4, _vm->_characters[result].cClass);
+
+ if (l == -1)
+ return result;
+
+ if (_vm->_characters[result].level[l] > 8)
+ return result;
+
+ displayTextBox(24);
+ return -1;
+ }
+
+ _vm->_menuDefs[3].titleStrId = id;
+ Button *buttonList = initMenu(3);
+
+ result = -2;
+ int hlCur = -1;
+ for (int i = 0; i < 6; ++i) {
+ if (found[i] != -1) {
+ hlCur = i;
+ break;
+ }
+ }
+
+ Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
+
+ while (result == -2 && !_vm->shouldQuit()) {
+ int inputFlag = _vm->checkInput(buttonList, false, 0);
+ _vm->removeInputTop();
+
+ updateBoxFrameHighLight(hlCur);
+
+ if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP8] || inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_a] || inputFlag == _vm->_keyMap[Common::KEYCODE_w]) {
+ updateBoxFrameHighLight(-1);
+ _vm->gui_drawCharPortraitWithStats(hlCur--);
+ if (hlCur < 0)
+ hlCur = 5;
+ while (found[hlCur]) {
+ if (--hlCur < 0)
+ hlCur = 5;
+ }
+
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP2] || inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_z] || inputFlag == _vm->_keyMap[Common::KEYCODE_s]) {
+ updateBoxFrameHighLight(-1);
+ _vm->gui_drawCharPortraitWithStats(hlCur++);
+ if (hlCur == 6)
+ hlCur = 0;
+ while (found[hlCur]) {
+ if (++hlCur == 6)
+ hlCur = 0;
+ }
+
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+ if (hlCur >= 0)
+ result = hlCur;
+
+ } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE] || inputFlag == 0x8010) {
+ _screen->setFont(Screen::FID_8_FNT);
+ drawMenuButton(buttonList, true, true, true);
+ _screen->updateScreen();
+ _vm->_system->delayMillis(80);
+ drawMenuButton(buttonList, false, false, true);
+ _screen->updateScreen();
+ _screen->setFont(Screen::FID_6_FNT);
+ result = -1;
+
+ } else if (inputFlag > 0x8010 && inputFlag < 0x8017) {
+ result = inputFlag - 0x8011;
+ if (found[result])
+ result = -2;
+ }
+ }
+
+ updateBoxFrameHighLight(-1);
+ if (hlCur >= 0)
+ _vm->gui_drawCharPortraitWithStats(hlCur);
+
+ _screen->setFont(Screen::FID_8_FNT);
+
+ if (result != -1 && id != 53) {
+ if (flags & 4) {
+ int lv = _vm->getCharacterLevelIndex(4, _vm->_characters[result].cClass);
+ if (lv != -1) {
+ if (_vm->_characters[result].level[lv] < 9) {
+ displayTextBox(24);
+ result = -1;
+ }
+ }
+ } else {
+ if (_vm->checkInventoryForItem(result, 29, -1) == -1) {
+ displayTextBox(25);
+ result = -1;
+ }
+ }
+ }
+
+ releaseButtons(buttonList);
+ _screen->setFont(of);
+
+ return result;
+}
+
+void GUI_EoB::displayTextBox(int id) {
+ int op = _screen->setCurPage(2);
+ int od = _screen->curDimIndex();
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+ _screen->setClearScreenDim(11);
+ const ScreenDim *dm = _screen->getScreenDim(11);
+
+ drawMenuButtonBox(dm->sx << 3, dm->sy, dm->w << 3, dm->h, false, false);
+ _screen->printShadedText(getMenuString(id), (dm->sx << 3) + 5, dm->sy + 5, 15, 0);
+ _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ for (uint32 timeOut = _vm->_system->getMillis() + 1440; _vm->_system->getMillis() < timeOut && !_vm->shouldQuit();) {
+ int in = _vm->checkInput(0, false, 0);
+ _vm->removeInputTop();
+ if (in && !(in & 0x800))
+ break;
+ _vm->_system->delayMillis(4);
+ }
+
+ _screen->setCurPage(op);
+ _screen->setFont(of);
+ _screen->setScreenDim(od);
+}
+
+Button *GUI_EoB::initMenu(int id) {
+ _screen->setCurPage(2);
+
+ EoBMenuDef *m = &_vm->_menuDefs[id];
+
+ if (m->dim) {
+ const ScreenDim *dm = _screen->getScreenDim(m->dim);
+ _screen->fillRect(dm->sx << 3, dm->sy, ((dm->sx + dm->w) << 3) - 1, (dm->sy + dm->h) - 1, _vm->guiSettings()->colors.fill);
+ _screen->setScreenDim(m->dim);
+ drawMenuButtonBox(dm->sx << 3, dm->sy, dm->w << 3, dm->h, false, false);
+ }
+
+ _screen->printShadedText(getMenuString(m->titleStrId), 5, 5, m->titleCol, 0);
+
+ Button *buttons = 0;
+ for (int i = 0; i < m->numButtons; i++) {
+ const EoBMenuButtonDef *df = &_vm->_menuButtonDefs[m->firstButtonStrId + i];
+ Button *b = new Button;
+ b->index = m->firstButtonStrId + i + 1;
+ if (id == 4 && _vm->game() == GI_EOB1)
+ b->index -= 14;
+ else if (id == 2 && _vm->gameFlags().platform == Common::kPlatformFMTowns)
+ b->index -= 32;
+
+ b->data0Val2 = 12;
+ b->data1Val2 = b->data2Val2 = 15;
+ b->data3Val2 = 8;
+ b->flags = 0x1100;
+ b->keyCode = df->keyCode;
+ b->keyCode2 = df->keyCode | 0x100;
+ b->x = df->x;
+ b->y = df->y;
+ b->width = df->width;
+ b->height = df->height;
+ b->extButtonDef = df;
+ b->arg = df->flags;
+
+ drawMenuButton(b, false, false, false);
+ buttons = linkButton(buttons, b);
+ }
+
+ _screen->copyRegion(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, 2, 0, Screen::CR_NO_P_CHECK);
+ _vm->gui_notifyButtonListChanged();
+ _screen->setCurPage(0);
+ _screen->updateScreen();
+
+ return buttons;
+}
+
+void GUI_EoB::drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill) {
+ if (!b)
+ return;
+
+ const EoBMenuButtonDef *d = (const EoBMenuButtonDef *)b->extButtonDef;
+
+ if (d->flags & 1)
+ drawMenuButtonBox(b->x, b->y, b->width, b->height, clicked, noFill);
+
+ if (d->labelId) {
+ const char *s = getMenuString(d->labelId);
+
+ int xOffs = 4;
+ int yOffs = 3;
+
+ if (d->flags & 4) {
+ xOffs = ((b->width - _screen->getTextWidth(s)) >> 1) + 1;
+ yOffs = (b->height - 7) >> 1;
+ }
+
+ int col1 = (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : 15;
+
+ if (noFill || clicked)
+ _screen->printText(s, b->x + xOffs, b->y + yOffs, highlight ? 6 : col1, 0);
+ else
+ _screen->printShadedText(s, b->x + xOffs, b->y + yOffs, highlight ? 6 : col1, 0);
+ }
+}
+
+void GUI_EoB::drawMenuButtonBox(int x, int y, int w, int h, bool clicked, bool noFill) {
+ uint8 col1 = _vm->guiSettings()->colors.frame1;
+ uint8 col2 = _vm->guiSettings()->colors.frame2;
+
+ if (clicked)
+ col1 = col2 = _vm->guiSettings()->colors.fill;
+
+ _screen->set16bitShadingLevel(4);
+ _vm->gui_drawBox(x, y, w, h, col1, col2, -1);
+ _vm->gui_drawBox(x + 1, y + 1, w - 2, h - 2, _vm->guiSettings()->colors.frame1, _vm->guiSettings()->colors.frame2, noFill ? -1 : _vm->guiSettings()->colors.fill);
+ _screen->set16bitShadingLevel(0);
+}
+
+void GUI_EoB::drawTextBox(int dim, int id) {
+ int od = _screen->curDimIndex();
+ _screen->setScreenDim(dim);
+ const ScreenDim *dm = _screen->getScreenDim(dim);
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+
+ if (dm->w <= 22 && dm->h <= 84)
+ _screen->copyRegion(dm->sx << 3, dm->sy, 0, dm->h, dm->w << 3, dm->h, 0, 2, Screen::CR_NO_P_CHECK);
+
+ _screen->setCurPage(2);
+
+ drawMenuButtonBox(0, 0, dm->w << 3, dm->h, false, false);
+ _screen->printShadedText(getMenuString(id), 5, 5, 15, 0);
+
+ _screen->setCurPage(0);
+ _screen->copyRegion(0, 0, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ _screen->setScreenDim(od);
+ _screen->setFont(of);
+}
+
+void GUI_EoB::drawSaveSlotButton(int slot, int redrawBox, int textCol) {
+ if (slot < 0)
+ return;
+
+ int x = _saveSlotX + 4;
+ int y = _saveSlotY + slot * 17 + 20;
+ int w = 167;
+ char slotString[26];
+ Common::strlcpy(slotString, slot < 6 ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], _vm->gameFlags().platform == Common::kPlatformFMTowns ? 25 : 20);
+
+ if (slot >= 6) {
+ x = _saveSlotX + 118;
+ y = _saveSlotY + 126;
+ w = 53;
+ }
+
+ if (redrawBox)
+ drawMenuButtonBox(x, y, w, 14, (redrawBox - 1) ? true : false, false);
+
+ Screen::FontId fnt = _screen->_currentFont;
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ fnt = _vm->screen()->setFont(Screen::FID_6_FNT);
+ y++;
+ }
+
+ _screen->printShadedText(slotString, x + 4, y + 3, textCol, 0);
+ _vm->screen()->setFont(fnt);
+}
+
+void GUI_EoB::memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight) {
+ if (bookPageIndex < 0)
+ return;
+
+ int y = bookPageIndex * 9 + 50;
+ int col1 = (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : 15;
+ _screen->set16bitShadingLevel(4);
+
+ if (spellId) {
+ Common::String s;
+ if (_vm->_flags.platform == Common::kPlatformFMTowns) {
+ s = spellType ? _vm->_clericSpellList[spellId] : _vm->_mageSpellList[spellId];
+ for (int i = s.size() >> 1; i < 17; ++i)
+ s.insertChar(' ', s.size());
+ s.insertChar((char)(_numAssignedSpellsOfType[spellId * 2 - 2] + 48), s.size());
+ } else {
+ s = Common::String::format(_vm->_menuStringsMgc[0], spellType ? _vm->_clericSpellList[spellId] : _vm->_mageSpellList[spellId], _numAssignedSpellsOfType[spellId * 2 - 2]);
+ }
+
+ if (noFill)
+ _screen->printText(s.c_str(), 8, y, highLight ? 6 : col1, 0);
+ else
+ _screen->printShadedText(s.c_str(), 8, y, highLight ? 6 : col1, _vm->guiSettings()->colors.fill);
+ } else {
+ _screen->fillRect(6, y, 168, y + 8, _vm->guiSettings()->colors.fill);
+ }
+
+ _screen->set16bitShadingLevel(0);
+}
+
+void GUI_EoB::updateOptionsStrings() {
+ for (int i = 0; i < 4; i++) {
+ delete[] _menuStringsPrefsTemp[i];
+ _menuStringsPrefsTemp[i] = new char[strlen(_vm->_menuStringsPrefs[i]) + 8];
+ }
+
+ Common::strlcpy(_menuStringsPrefsTemp[0], Common::String::format(_vm->_menuStringsPrefs[0], _vm->_menuStringsOnOff[_vm->_configMusic ? 0 : 1]).c_str(), strlen(_vm->_menuStringsPrefs[0]) + 8);
+ Common::strlcpy(_menuStringsPrefsTemp[1], Common::String::format(_vm->_menuStringsPrefs[1], _vm->_menuStringsOnOff[_vm->_configSounds ? 0 : 1]).c_str(), strlen(_vm->_menuStringsPrefs[1]) + 8);
+ Common::strlcpy(_menuStringsPrefsTemp[2], Common::String::format(_vm->_menuStringsPrefs[2], _vm->_menuStringsOnOff[_vm->_configHpBarGraphs ? 0 : 1]).c_str(), strlen(_vm->_menuStringsPrefs[2]) + 8);
+ Common::strlcpy(_menuStringsPrefsTemp[3], Common::String::format(_vm->_menuStringsPrefs[3], _vm->gameFlags().platform == Common::kPlatformFMTowns ? _vm->_2431Strings[_vm->_config2431 ? 0 : 1] : _vm->_menuStringsOnOff[_vm->_configMouse ? 0 : 1]).c_str(), strlen(_vm->_menuStringsPrefs[3]) + 8);
+}
+
+const char *GUI_EoB::getMenuString(int id) {
+ static const char empty[] = "";
+
+ if (id >= 69)
+ return _vm->_menuStringsTransfer[id - 69];
+ else if (id == 68)
+ return _vm->_transferStringsScummVM[0];
+ else if (id == 67)
+ return _vm->_menuStringsDefeat[0];
+ else if (id == 66)
+ return _vm->_errorSlotEmptyString;
+ else if (id == 65)
+ return _vm->_errorSlotEmptyString;
+ else if (id >= 63)
+ return _vm->_menuStringsSpec[id - 63];
+ else if (id >= 60)
+ return _vm->_menuStringsSpellNo[id - 60];
+ else if (id == 59)
+ return _vm->_menuStringsPoison[0];
+ else if (id >= 56)
+ return _vm->_menuStringsHead[id - 56];
+ else if (id == 55)
+ return _vm->_menuStringsDrop2[_vm->game() == GI_EOB1 ? 1 : 2];
+ else if (id == 54)
+ return _vm->_errorSlotNoNameString;
+ else if (id == 53)
+ return _vm->_menuStringsDrop2[0];
+ else if (id >= 48)
+ return _vm->_menuStringsScribe[id - 48];
+ else if (id == 47)
+ return _vm->_menuStringsStarve[0];
+ else if (id == 46)
+ return _vm->_menuStringsExit[0];
+ else if (id == 45)
+ return _vm->_menuStringsDrop[0];
+ else if (id >= 40)
+ return _vm->_menuStringsRest[id - 40];
+ else if (id >= 23)
+ return _vm->_menuStringsSpells[id - 23];
+ else if (id >= 21)
+ return _vm->_menuStringsOnOff[id - 21];
+ else if (id >= 17)
+ return _menuStringsPrefsTemp[id - 17];
+ else if (id >= 9)
+ return _vm->_menuStringsSaveLoad[id - 9];
+ else if (id >= 1)
+ return _vm->_menuStringsMain[id - 1];
+ else if (id < 0)
+ return _vm->_transferStringsScummVM[-id];
+ return empty;
+}
+
+Button *GUI_EoB::linkButton(Button *list, Button *newbt) {
+ if (!list) {
+ list = newbt;
+ return list;
+ }
+
+ if (!newbt)
+ return list;
+
+ Button *resList = list;
+ while (list->nextButton)
+ list = list->nextButton;
+ list->nextButton = newbt;
+ newbt->nextButton = 0;
+
+ return resList;
+}
+
+void GUI_EoB::releaseButtons(Button *list) {
+ while (list) {
+ Button *n = list->nextButton;
+ delete list;
+ list = n;
+ }
+ _vm->gui_notifyButtonListChanged();
+}
+
+void GUI_EoB::setupSaveMenuSlots() {
+ for (int i = 0; i < 6; ++i) {
+ if (_savegameOffset + i < _savegameListSize) {
+ if (_savegameList[i + _savegameOffset]) {
+ Common::strlcpy(_saveSlotStringsTemp[i], _savegameList[i + _savegameOffset], 25);
+ _saveSlotIdTemp[i] = i + _savegameOffset;
+ continue;
+ }
+ }
+ Common::strlcpy(_saveSlotStringsTemp[i], _vm->_saveLoadStrings[1], 25);
+ _saveSlotIdTemp[i] = -1;
+ }
+}
+
+int GUI_EoB::getHighlightSlot() {
+ int res = -1;
+ Common::Point p = _vm->getMousePos();
+
+ for (int i = 0; i < 6; i++) {
+ int y = _saveSlotY + i * 17 + 20;
+ if (_vm->posWithinRect(p.x, p.y, _saveSlotX + 4, y, _saveSlotX + 167, y + 14)) {
+ res = i;
+ break;
+ }
+ }
+
+ if (_vm->posWithinRect(p.x, p.y, _saveSlotX + 118, _saveSlotY + 126, _saveSlotX + 171, _saveSlotY + 140))
+ res = 6;
+
+ return res;
+}
+
+void GUI_EoB::sortSaveSlots() {
+ Common::sort(_saveSlots.begin(), _saveSlots.end(), Common::Less<int>());
+}
+
+void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+ int od = _screen->curDimIndex();
+ _screen->setScreenDim(10);
+
+ if (init) {
+ _screen->setCurPage(0);
+ _vm->_txt->clearCurDim();
+ drawMenuButtonBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, false, false);
+ _screen->copyRegion(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, 0, 2, Screen::CR_NO_P_CHECK);
+ _screen->printShadedText(getMenuString(42), (_screen->_curDim->sx + 1) << 3, _screen->_curDim->sy + 5, 9, 0);
+ }
+
+ _screen->setCurPage(0);
+ _screen->set16bitShadingLevel(4);
+ _screen->fillRect((_screen->_curDim->sx + 1) << 3, _screen->_curDim->sy + 20, ((_screen->_curDim->sx + 19) << 3) + 1, _screen->_curDim->sy + 29, _vm->guiSettings()->colors.fill);
+ _screen->printShadedText(Common::String::format(_vm->_menuStringsRest2[3], hours).c_str(), (_screen->_curDim->sx + 1) << 3, _screen->_curDim->sy + 20, 15, _vm->guiSettings()->colors.fill);
+ _screen->set16bitShadingLevel(0);
+ _screen->updateScreen();
+ _vm->delay(160);
+ _screen->setScreenDim(od);
+ _screen->setFont(of);
+}
+
+const EoBRect16 GUI_EoB::_highlightFrames[] = {
+ { 0x00B7, 0x0001, 0x00F7, 0x0034 },
+ { 0x00FF, 0x0001, 0x013F, 0x0034 },
+ { 0x00B7, 0x0035, 0x00F7, 0x0068 },
+ { 0x00FF, 0x0035, 0x013F, 0x0068 },
+ { 0x00B7, 0x0069, 0x00F7, 0x009C },
+ { 0x00FF, 0x0069, 0x013F, 0x009C },
+ { 0x0010, 0x003F, 0x0030, 0x0060 },
+ { 0x0050, 0x003F, 0x0070, 0x0060 },
+ { 0x0010, 0x007F, 0x0030, 0x00A0 },
+ { 0x0050, 0x007F, 0x0070, 0x00A0 },
+ { 0x00B0, 0x0042, 0x00D0, 0x0061 },
+ { 0x00D0, 0x0042, 0x00F0, 0x0061 },
+ { 0x00F0, 0x0042, 0x0110, 0x0061 },
+ { 0x0110, 0x0042, 0x0130, 0x0061 },
+ { 0x0004, 0x0018, 0x0024, 0x0039 },
+ { 0x00A3, 0x0018, 0x00C3, 0x0039 },
+ { 0x0004, 0x0040, 0x0024, 0x0061 },
+ { 0x00A3, 0x0040, 0x00C3, 0x0061 },
+ { 0x0004, 0x0068, 0x0024, 0x0089 },
+ { 0x00A3, 0x0068, 0x00C3, 0x0089 }
+};
+
+const uint8 GUI_EoB::_highlightColorTableVGA[] = { 0x0F, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0x0C, 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0x00 };
+
+const uint8 GUI_EoB::_highlightColorTableEGA[] = { 0x0C, 0x0D, 0x0E, 0x0F, 0x0E, 0x0D, 0x00 };
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
new file mode 100644
index 0000000000..eadb7b0801
--- /dev/null
+++ b/engines/kyra/gui/gui_eob.h
@@ -0,0 +1,172 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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);
+ virtual ~GUI_EoB();
+
+ void initStaticData() {}
+
+ // button specific
+ void processButton(Button *button);
+ int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel);
+
+ // Non button based menu handling (main menu, character generation)
+ void simpleMenu_setup(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int unk, int lineSpacing);
+ int simpleMenu_process(int sd, const char *const *strings, void *b, int32 menuItemsMask, int unk);
+
+ // Button based menus (camp menu, load menu)
+ void runCampMenu();
+ bool runLoadMenu(int x, int y);
+
+ bool confirmDialogue2(int dim, int id, int deflt);
+ void messageDialogue(int dim, int id, int buttonTextCol);
+ void messageDialogue2(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);
+
+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);
+
+ bool runSaveMenu(int x, int y);
+ int selectSaveSlotDialogue(int x, int y, int id);
+ void runMemorizePrayMenu(int charIndex, int spellType);
+ void scribeScrollDialogue();
+ bool restParty();
+
+ bool confirmDialogue(int id);
+ int selectCharacterDialogue(int id);
+ void displayTextBox(int id);
+
+ Button *initMenu(int id);
+ 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);
+ void drawSaveSlotButton(int slot, int redrawBox, int textCol);
+ void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight);
+ void updateOptionsStrings();
+ const char *getMenuString(int id);
+
+ Button *linkButton(Button *list, Button *newbt);
+ void releaseButtons(Button *list);
+
+ void setupSaveMenuSlots();
+ int getHighlightSlot();
+ void sortSaveSlots();
+
+ void restParty_updateRestTime(int hours, bool init);
+
+ char **_menuStringsPrefsTemp;
+ char **_saveSlotStringsTemp;
+ int16 *_saveSlotIdTemp;
+ int _savegameOffset;
+ int16 _saveSlotX;
+ int16 _saveSlotY;
+
+ EoBCoreEngine *_vm;
+ Screen_EoB *_screen;
+
+ bool _pressFlag;
+
+ Button *_specialProcessButton;
+ Button *_backupButtonList;
+ uint16 _flagsMouseLeft;
+ uint16 _flagsMouseRight;
+ uint16 _flagsModifier;
+ uint16 _progress;
+ uint16 _prcButtonUnk3;
+ uint16 _cflag;
+
+ int _menuLineSpacing;
+ int _menuLastInFlags;
+
+ uint8 _numPages;
+ uint8 _numVisPages;
+ int8 *_numAssignedSpellsOfType;
+ uint32 _clericSpellAvltyFlags;
+ uint32 _paladinSpellAvltyFlags;
+ bool _needRest;
+
+ int _menuCur;
+ int _menuNumItems;
+ bool _charSelectRedraw;
+
+ int _updateBoxIndex;
+ int _updateBoxColorIndex;
+ const uint8 *_highLightColorTable;
+ uint32 _highLightBoxTimer;
+
+ static const EoBRect16 _highlightFrames[];
+ static const uint8 _highlightColorTableVGA[];
+ static const uint8 _highlightColorTableEGA[];
+
+ // FM-Towns specific
+ int checkKatakanaSelection();
+ void printKatakanaOptions(int page);
+
+ int _currentKanaPage;
+ char _csjis[3];
+};
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
+
+#endif
+
+#endif // ENABLE_EOB || ENABLE_LOL
diff --git a/engines/kyra/gui/gui_hof.cpp b/engines/kyra/gui/gui_hof.cpp
new file mode 100644
index 0000000000..0b5d7eac85
--- /dev/null
+++ b/engines/kyra/gui/gui_hof.cpp
@@ -0,0 +1,1162 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/gui/gui_hof.h"
+#include "kyra/engine/kyra_hof.h"
+#include "kyra/engine/timer.h"
+#include "kyra/resource/resource.h"
+#include "kyra/sound/sound.h"
+
+#include "common/system.h"
+
+#include "graphics/scaler.h"
+
+namespace Kyra {
+
+void KyraEngine_HoF::loadButtonShapes() {
+ const uint8 *src = _screen->getCPagePtr(3);
+ _screen->loadBitmap("_BUTTONS.CSH", 3, 3, 0);
+
+ _gui->_scrollUpButton.data0ShapePtr = _buttonShapes[0] = _screen->makeShapeCopy(src, 0);
+ _gui->_scrollUpButton.data2ShapePtr = _buttonShapes[1] = _screen->makeShapeCopy(src, 1);
+ _gui->_scrollUpButton.data1ShapePtr = _buttonShapes[2] = _screen->makeShapeCopy(src, 2);
+ _gui->_scrollDownButton.data0ShapePtr = _buttonShapes[3] = _screen->makeShapeCopy(src, 3);
+ _gui->_scrollDownButton.data2ShapePtr = _buttonShapes[4] = _screen->makeShapeCopy(src, 4);
+ _gui->_scrollDownButton.data1ShapePtr = _buttonShapes[5] = _screen->makeShapeCopy(src, 5);
+ _buttonShapes[6] = _screen->makeShapeCopy(src, 6);
+ _buttonShapes[7] = _screen->makeShapeCopy(src, 7);
+ _buttonShapes[8] = _screen->makeShapeCopy(src, 6);
+ _buttonShapes[9] = _screen->makeShapeCopy(src, 7);
+ _buttonShapes[10] = _screen->makeShapeCopy(src, 10);
+ _buttonShapes[11] = _screen->makeShapeCopy(src, 11);
+ _buttonShapes[16] = _screen->makeShapeCopy(src, 16);
+ _buttonShapes[17] = _screen->makeShapeCopy(src, 17);
+ _buttonShapes[18] = _screen->makeShapeCopy(src, 18);
+}
+
+void KyraEngine_HoF::setupLangButtonShapes() {
+ switch (_lang) {
+ case 0:
+ _inventoryButtons[0].data0ShapePtr = _buttonShapes[6];
+ _inventoryButtons[0].data1ShapePtr = _inventoryButtons[0].data2ShapePtr = _buttonShapes[7];
+ break;
+
+ case 1:
+ _inventoryButtons[0].data0ShapePtr = _buttonShapes[8];
+ _inventoryButtons[0].data1ShapePtr = _inventoryButtons[0].data2ShapePtr = _buttonShapes[9];
+ break;
+
+ case 2:
+ _inventoryButtons[0].data0ShapePtr = _buttonShapes[10];
+ _inventoryButtons[0].data1ShapePtr = _inventoryButtons[0].data2ShapePtr = _buttonShapes[11];
+ break;
+
+ default:
+ _inventoryButtons[0].data0ShapePtr = _buttonShapes[6];
+ _inventoryButtons[0].data1ShapePtr = _inventoryButtons[0].data2ShapePtr = _buttonShapes[7];
+ }
+}
+
+GUI_HoF::GUI_HoF(KyraEngine_HoF *vm) : GUI_v2(vm), _vm(vm), _screen(_vm->_screen) {
+}
+
+const char *GUI_HoF::getMenuTitle(const Menu &menu) {
+ if (!menu.menuNameId)
+ return 0;
+
+ return _vm->getTableString(menu.menuNameId, _vm->_optionsBuffer, 1);
+}
+
+const char *GUI_HoF::getMenuItemTitle(const MenuItem &menuItem) {
+ if (!menuItem.itemId)
+ return 0;
+
+ // Strings 41-45 are menu labels, those must be handled uncompressed!
+ if (menuItem.itemId >= 41 && menuItem.itemId <= 45)
+ return _vm->getTableString(menuItem.itemId, _vm->_optionsBuffer, 0);
+ else
+ return _vm->getTableString(menuItem.itemId, _vm->_optionsBuffer, 1);
+}
+
+const char *GUI_HoF::getMenuItemLabel(const MenuItem &menuItem) {
+ if (!menuItem.labelId)
+ return 0;
+
+ return _vm->getTableString(menuItem.labelId, _vm->_optionsBuffer, 1);
+}
+
+char *GUI_HoF::getTableString(int id) {
+ return _vm->getTableString(id, _vm->_optionsBuffer, 0);
+}
+
+#pragma mark -
+
+
+int KyraEngine_HoF::buttonInventory(Button *button) {
+ if (!_screen->isMouseVisible())
+ return 0;
+
+ int inventorySlot = button->index - 6;
+
+ Item item = _mainCharacter.inventory[inventorySlot];
+ if (_itemInHand == kItemNone) {
+ if (item == kItemNone)
+ return 0;
+ clearInventorySlot(inventorySlot, 0);
+ snd_playSoundEffect(0x0B);
+ setMouseCursor(item);
+ int string = (_lang == 1) ? getItemCommandStringPickUp(item) : 7;
+ updateCommandLineEx(item+54, string, 0xD6);
+ _itemInHand = (int16)item;
+ _mainCharacter.inventory[inventorySlot] = kItemNone;
+ } else {
+ if (_mainCharacter.inventory[inventorySlot] != kItemNone) {
+ if (checkInventoryItemExchange(_itemInHand, inventorySlot))
+ return 0;
+
+ item = _mainCharacter.inventory[inventorySlot];
+ snd_playSoundEffect(0x0B);
+ clearInventorySlot(inventorySlot, 0);
+ drawInventoryShape(0, _itemInHand, inventorySlot);
+ setMouseCursor(item);
+ int string = (_lang == 1) ? getItemCommandStringPickUp(item) : 7;
+ updateCommandLineEx(item+54, string, 0xD6);
+ _mainCharacter.inventory[inventorySlot] = _itemInHand;
+ setHandItem(item);
+ } else {
+ snd_playSoundEffect(0x0C);
+ drawInventoryShape(0, _itemInHand, inventorySlot);
+ _screen->setMouseCursor(0, 0, getShapePtr(0));
+ int string = (_lang == 1) ? getItemCommandStringInv(_itemInHand) : 8;
+ updateCommandLineEx(_itemInHand+54, string, 0xD6);
+ _mainCharacter.inventory[inventorySlot] = _itemInHand;
+ _itemInHand = kItemNone;
+ }
+ }
+
+ return 0;
+}
+
+int KyraEngine_HoF::scrollInventory(Button *button) {
+ Item *src = _mainCharacter.inventory;
+ Item *dst = &_mainCharacter.inventory[10];
+ Item temp[5];
+
+ memcpy(temp, src, sizeof(Item)*5);
+ memcpy(src, src+5, sizeof(Item)*5);
+ memcpy(src+5, dst, sizeof(Item)*5);
+ memcpy(dst, dst+5, sizeof(Item)*5);
+ memcpy(dst+5, temp, sizeof(Item)*5);
+ _screen->copyRegion(0x46, 0x90, 0x46, 0x90, 0x71, 0x2E, 0, 2);
+ redrawInventory(2);
+ scrollInventoryWheel();
+ return 0;
+}
+
+int KyraEngine_HoF::getInventoryItemSlot(Item item) {
+ for (int i = 0; i < 20; ++i) {
+ if (_mainCharacter.inventory[i] == item)
+ return i;
+ }
+ return -1;
+}
+
+int KyraEngine_HoF::findFreeVisibleInventorySlot() {
+ for (int i = 0; i < 10; ++i) {
+ if (_mainCharacter.inventory[i] == kItemNone)
+ return i;
+ }
+ return -1;
+}
+
+void KyraEngine_HoF::removeSlotFromInventory(int slot) {
+ _mainCharacter.inventory[slot] = kItemNone;
+ if (slot < 10) {
+ clearInventorySlot(slot, 0);
+ }
+}
+
+bool KyraEngine_HoF::checkInventoryItemExchange(Item handItem, int slot) {
+ bool removeItem = false;
+ Item newItem = kItemNone;
+
+ Item invItem = _mainCharacter.inventory[slot];
+
+ for (const uint16 *table = _itemMagicTable; *table != 0xFFFF; table += 4) {
+ if (table[0] != handItem || table[1] != (uint16)invItem)
+ continue;
+
+ if (table[3] == 0xFFFF)
+ continue;
+
+ removeItem = (table[3] == 1);
+ newItem = (Item)table[2];
+
+ snd_playSoundEffect(0x68);
+ _mainCharacter.inventory[slot] = newItem;
+ clearInventorySlot(slot, 0);
+ drawInventoryShape(0, newItem, slot);
+
+ if (removeItem)
+ removeHandItem();
+
+ if (_lang != 1)
+ updateCommandLineEx(newItem+54, 0x2E, 0xD6);
+
+ return true;
+ }
+
+ return false;
+}
+
+void KyraEngine_HoF::drawInventoryShape(int page, Item item, int slot) {
+ _screen->drawShape(page, getShapePtr(item+64), _inventoryX[slot], _inventoryY[slot], 0, 0);
+}
+
+void KyraEngine_HoF::clearInventorySlot(int slot, int page) {
+ _screen->drawShape(page, getShapePtr(240+slot), _inventoryX[slot], _inventoryY[slot], 0, 0);
+}
+
+void KyraEngine_HoF::redrawInventory(int page) {
+ int pageBackUp = _screen->_curPage;
+ _screen->_curPage = page;
+
+ const Item *inventory = _mainCharacter.inventory;
+ for (int i = 0; i < 10; ++i) {
+ clearInventorySlot(i, page);
+ if (inventory[i] != kItemNone) {
+ _screen->drawShape(page, getShapePtr(inventory[i]+64), _inventoryX[i], _inventoryY[i], 0, 0);
+ drawInventoryShape(page, inventory[i], i);
+ }
+ }
+ _screen->updateScreen();
+
+ _screen->_curPage = pageBackUp;
+}
+
+void KyraEngine_HoF::scrollInventoryWheel() {
+ WSAMovie_v2 movie(this);
+ movie.open("INVWHEEL.WSA", 0, 0);
+ int frames = movie.opened() ? movie.frames() : 6;
+ memcpy(_screenBuffer, _screen->getCPagePtr(2), 64000);
+ uint8 overlay[0x100];
+ _screen->generateOverlay(_screen->getPalette(0), overlay, 0, 50);
+ _screen->copyRegion(0x46, 0x90, 0x46, 0x79, 0x71, 0x17, 0, 2, Screen::CR_NO_P_CHECK);
+ snd_playSoundEffect(0x25);
+
+ bool breakFlag = false;
+ for (int i = 0; i <= 6 && !breakFlag; ++i) {
+ if (movie.opened()) {
+ movie.displayFrame(i % frames, 0, 0, 0, 0, 0, 0);
+ _screen->updateScreen();
+ }
+
+ uint32 endTime = _system->getMillis() + _tickLength;
+
+ int y = (i * 981) >> 8;
+ if (y >= 23 || i == 6) {
+ y = 23;
+ breakFlag = true;
+ }
+
+ _screen->applyOverlay(0x46, 0x79, 0x71, 0x17, 2, overlay);
+ _screen->copyRegion(0x46, y+0x79, 0x46, 0x90, 0x71, 0x2E, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ delayUntil(endTime);
+ }
+
+ _screen->copyBlockToPage(2, 0, 0, 320, 200, _screenBuffer);
+ movie.close();
+}
+
+// spellbook specific code
+
+int KyraEngine_HoF::bookButton(Button *button) {
+ if (!queryGameFlag(1)) {
+ objectChat(getTableString(0xEB, _cCodeBuffer, 1), 0, 0x83, 0xEB);
+ return 0;
+ }
+
+ if (!_screen->isMouseVisible())
+ return 0;
+
+ if (queryGameFlag(0xE5)) {
+ snd_playSoundEffect(0x0D);
+ return 0;
+ }
+
+ if (_itemInHand == 72) {
+ if (!queryGameFlag(0xE2)) {
+ _bookMaxPage += 2;
+ removeHandItem();
+ snd_playSoundEffect(0x6C);
+ setGameFlag(0xE2);
+ }
+
+ if (!queryGameFlag(0x18A) && queryGameFlag(0x170)) {
+ _bookMaxPage += 2;
+ removeHandItem();
+ snd_playSoundEffect(0x6C);
+ setGameFlag(0x18A);
+ }
+
+ return 0;
+ }
+
+ if (_mouseState != -1) {
+ snd_playSoundEffect(0x0D);
+ return 0;
+ }
+
+ _screen->hideMouse();
+ showMessage(0, 0xCF);
+ displayInvWsaLastFrame();
+ _bookNewPage = _bookCurPage;
+
+ if (_screenBuffer) {
+ memcpy(_screenBuffer, _screen->getCPagePtr(0), 64000);
+ }
+
+ _screen->copyPalette(2, 0);
+ _screen->fadeToBlack(7, &_updateFunctor);
+ _screen->loadPalette("_BOOK.COL", _screen->getPalette(0));
+ loadBookBkgd();
+ showBookPage();
+ _screen->copyRegion(0, 0, 0, 0, 0x140, 0xC8, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ int oldItemInHand = _itemInHand;
+ removeHandItem();
+ _screen->fadePalette(_screen->getPalette(0), 7);
+ _screen->showMouse();
+
+ bookLoop();
+
+ _screen->fadeToBlack(7);
+ _screen->hideMouse();
+ setHandItem(oldItemInHand);
+ updateMouse();
+ restorePage3();
+
+ if (_screenBuffer) {
+ _screen->copyBlockToPage(0, 0, 0, 320, 200, _screenBuffer);
+ }
+
+ setHandItem(_itemInHand);
+ _screen->copyPalette(0, 2);
+ _screen->fadePalette(_screen->getPalette(0), 7, &_updateFunctor);
+ _screen->showMouse();
+
+ if (!queryGameFlag(4) && !queryGameFlag(0xB8)) {
+ objectChat(getTableString(0xEC, _cCodeBuffer, 1), 0, 0x83, 0xEC);
+ objectChat(getTableString(0xED, _cCodeBuffer, 1), 0, 0x83, 0xED);
+ objectChat(getTableString(0xEE, _cCodeBuffer, 1), 0, 0x83, 0xEE);
+ objectChat(getTableString(0xEF, _cCodeBuffer, 1), 0, 0x83, 0xEF);
+ setGameFlag(4);
+ }
+
+ return 0;
+}
+
+void KyraEngine_HoF::loadBookBkgd() {
+ char filename[16];
+
+ if (_flags.isTalkie)
+ strcpy(filename, (_bookBkgd == 0) ? "_XBOOKD.CPS" : "_XBOOKC.CPS");
+ else
+ strcpy(filename, (_bookBkgd == 0) ? "_BOOKD.CPS" : "_BOOKC.CPS");
+
+ _bookBkgd ^= 1;
+
+ if (_flags.isTalkie) {
+ if (!_bookCurPage)
+ strcpy(filename, "_XBOOKB.CPS");
+ if (_bookCurPage == _bookMaxPage)
+ strcpy(filename, "_XBOOKA.CPS");
+
+ switch (_lang) {
+ case 0:
+ filename[1] = 'E';
+ break;
+
+ case 1:
+ filename[1] = 'F';
+ break;
+
+ case 2:
+ filename[1] = 'G';
+ break;
+
+ default:
+ warning("loadBookBkgd unsupported language");
+ filename[1] = 'E';
+ }
+ } else {
+ if (!_bookCurPage)
+ strcpy(filename, "_BOOKB.CPS");
+ if (_bookCurPage == _bookMaxPage)
+ strcpy(filename, "_BOOKA.CPS");
+ }
+
+ _screen->loadBitmap(filename, 3, 3, 0);
+}
+
+void KyraEngine_HoF::showBookPage() {
+ char filename[16];
+
+ sprintf(filename, "PAGE%.01X.%s", _bookCurPage, _languageExtension[_lang]);
+ uint8 *leftPage = _res->fileData(filename, 0);
+ if (!leftPage) {
+ // some floppy version use a TXT extension
+ sprintf(filename, "PAGE%.01X.TXT", _bookCurPage);
+ leftPage = _res->fileData(filename, 0);
+ }
+
+ int leftPageY = _bookPageYOffset[_bookCurPage];
+
+ sprintf(filename, "PAGE%.01X.%s", _bookCurPage+1, _languageExtension[_lang]);
+ uint8 *rightPage = 0;
+ if (_bookCurPage != _bookMaxPage) {
+ rightPage = _res->fileData(filename, 0);
+ if (!rightPage) {
+ sprintf(filename, "PAGE%.01X.TXT", _bookCurPage);
+ rightPage = _res->fileData(filename, 0);
+ }
+ }
+
+ int rightPageY = _bookPageYOffset[_bookCurPage+1];
+
+ if (leftPage) {
+ bookDecodeText(leftPage);
+ bookPrintText(2, leftPage, 20, leftPageY+20, 0x31);
+ delete[] leftPage;
+ }
+
+ if (rightPage) {
+ bookDecodeText(rightPage);
+ bookPrintText(2, rightPage, 176, rightPageY+20, 0x31);
+ delete[] rightPage;
+ }
+}
+
+void KyraEngine_HoF::bookLoop() {
+ Button bookButtons[5];
+
+ GUI_V2_BUTTON(bookButtons[0], 0x24, 0, 0, 1, 1, 1, 0x4487, 0, 0x82, 0xBE, 0x0A, 0x0A, 0xC7, 0xCF, 0xC7, 0xCF, 0xC7, 0xCF, 0);
+ bookButtons[0].buttonCallback = BUTTON_FUNCTOR(KyraEngine_HoF, this, &KyraEngine_HoF::bookPrevPage);
+ GUI_V2_BUTTON(bookButtons[1], 0x25, 0, 0, 1, 1, 1, 0x4487, 0, 0xB1, 0xBE, 0x0A, 0x0A, 0xC7, 0xCF, 0xC7, 0xCF, 0xC7, 0xCF, 0);
+ bookButtons[1].buttonCallback = BUTTON_FUNCTOR(KyraEngine_HoF, this, &KyraEngine_HoF::bookNextPage);
+ GUI_V2_BUTTON(bookButtons[2], 0x26, 0, 0, 1, 1, 1, 0x4487, 0, 0x8F, 0xBE, 0x21, 0x0A, 0xC7, 0xCF, 0xC7, 0xCF, 0xC7, 0xCF, 0);
+ bookButtons[2].buttonCallback = BUTTON_FUNCTOR(KyraEngine_HoF, this, &KyraEngine_HoF::bookClose);
+ GUI_V2_BUTTON(bookButtons[3], 0x27, 0, 0, 1, 1, 1, 0x4487, 0, 0x08, 0x08, 0x90, 0xB4, 0xC7, 0xCF, 0xC7, 0xCF, 0xC7, 0xCF, 0);
+ bookButtons[3].buttonCallback = BUTTON_FUNCTOR(KyraEngine_HoF, this, &KyraEngine_HoF::bookPrevPage);
+ GUI_V2_BUTTON(bookButtons[4], 0x28, 0, 0, 1, 1, 1, 0x4487, 0, 0xAA, 0x08, 0x8E, 0xB4, 0xC7, 0xCF, 0xC7, 0xCF, 0xC7, 0xCF, 0);
+ bookButtons[4].buttonCallback = BUTTON_FUNCTOR(KyraEngine_HoF, this, &KyraEngine_HoF::bookNextPage);
+
+ Button *buttonList = 0;
+
+ for (uint i = 0; i < ARRAYSIZE(bookButtons); ++i)
+ buttonList = _gui->addButtonToList(buttonList, &bookButtons[i]);
+
+ showBookPage();
+ _bookShown = true;
+ while (_bookShown && !shouldQuit()) {
+ checkInput(buttonList);
+ removeInputTop();
+
+ if (_bookCurPage != _bookNewPage) {
+ _bookCurPage = _bookNewPage;
+ _screen->clearPage(2);
+ loadBookBkgd();
+ showBookPage();
+ snd_playSoundEffect(0x64);
+ _screen->copyRegion(0, 0, 0, 0, 0x140, 0xC8, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ }
+ _system->delayMillis(10);
+ }
+ _screen->clearPage(2);
+}
+
+void KyraEngine_HoF::bookDecodeText(uint8 *str) {
+ uint8 *dst = str, *op = str;
+ while (*op != 0x1A) {
+ while (*op != 0x1A && *op != 0x0D)
+ *dst++ = *op++;
+
+ if (*op == 0x1A)
+ break;
+
+ op += 2;
+ *dst++ = 0x0D;
+ }
+ *dst = 0;
+}
+
+void KyraEngine_HoF::bookPrintText(int dstPage, const uint8 *str, int x, int y, uint8 color) {
+ int curPageBackUp = _screen->_curPage;
+ _screen->_curPage = dstPage;
+
+ _screen->setTextColor(_bookTextColorMap, 0, 3);
+ Screen::FontId oldFont = _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_BOOKFONT_FNT);
+ _screen->_charWidth = -2;
+
+ _screen->printText((const char *)str, x, y, color, (_flags.lang == Common::JA_JPN) ? 0xF6 : 0);
+
+ _screen->_charWidth = 0;
+ _screen->setFont(oldFont);
+ _screen->_curPage = curPageBackUp;
+}
+
+int KyraEngine_HoF::bookPrevPage(Button *button) {
+ _bookNewPage = MAX<int>(_bookCurPage-2, 0);
+ return 0;
+}
+
+int KyraEngine_HoF::bookNextPage(Button *button) {
+ _bookNewPage = MIN<int>(_bookCurPage+2, _bookMaxPage);
+ return 0;
+}
+
+int KyraEngine_HoF::bookClose(Button *button) {
+ _bookShown = false;
+ return 0;
+}
+
+// cauldron specific code
+
+int KyraEngine_HoF::cauldronClearButton(Button *button) {
+ if (!queryGameFlag(2)) {
+ updateCharFacing();
+ objectChat(getTableString(0xF0, _cCodeBuffer, 1), 0, 0x83, 0xF0);
+ return 0;
+ }
+
+ if (queryGameFlag(0xE4)) {
+ snd_playSoundEffect(0x0D);
+ return 0;
+ }
+
+ _screen->hideMouse();
+ displayInvWsaLastFrame();
+ snd_playSoundEffect(0x25);
+ loadInvWsa("PULL.WSA", 1, 6, 0, -1, -1, 1);
+ loadInvWsa("CAULD00.WSA", 1, 7, 0, 0xD4, 0x0F, 1);
+ showMessage(0, 0xCF);
+ setCauldronState(0, 0);
+ clearCauldronTable();
+ snd_playSoundEffect(0x57);
+ loadInvWsa("CAULDFIL.WSA", 1, 7, 0, -1, -1, 1);
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine_HoF::cauldronButton(Button *button) {
+ if (!queryGameFlag(2)) {
+ objectChat(getTableString(0xF0, _cCodeBuffer, 1), 0, 0x83, 0xF0);
+ return 0;
+ }
+
+ if (!_screen->isMouseVisible() || _mouseState < -1)
+ return 0;
+
+ if (queryGameFlag(0xE4)) {
+ snd_playSoundEffect(0x0D);
+ return 0;
+ }
+
+ updateCharFacing();
+
+ for (int i = 0; _cauldronProtectedItems[i] != -1; ++i) {
+ if (_itemInHand == _cauldronProtectedItems[i]) {
+ objectChat(getTableString(0xF1, _cCodeBuffer, 1), 0, 0x83, 0xF1);
+ return 0;
+ }
+ }
+
+ if (_itemInHand == -1) {
+ listItemsInCauldron();
+ return 0;
+ }
+
+ for (int i = 0; _cauldronBowlTable[i] != -1; i += 2) {
+ if (_itemInHand == _cauldronBowlTable[i]) {
+ addFrontCauldronTable(_itemInHand);
+ setHandItem(_cauldronBowlTable[i+1]);
+ if (!updateCauldron()) {
+ _cauldronState = 0;
+ cauldronRndPaletteFade();
+ }
+ return 0;
+ }
+ }
+
+ if (_itemInHand == 18) {
+ const int16 *magicTable = (_mainCharacter.sceneId == 77) ? _cauldronMagicTableScene77 : _cauldronMagicTable;
+ while (magicTable[0] != -1) {
+ if (_cauldronState == magicTable[0]) {
+ setHandItem(magicTable[1]);
+ snd_playSoundEffect(0x6C);
+ ++_cauldronUseCount;
+ if (_cauldronStateTable[_cauldronState] <= _cauldronUseCount && _cauldronUseCount) {
+ showMessage(0, 0xCF);
+ setCauldronState(0, true);
+ clearCauldronTable();
+ }
+ return 0;
+ }
+ magicTable += 2;
+ }
+ } else if (_itemInHand >= 0) {
+ int item = _itemInHand;
+ cauldronItemAnim(item);
+ addFrontCauldronTable(item);
+ if (!updateCauldron()) {
+ _cauldronState = 0;
+ cauldronRndPaletteFade();
+ }
+ }
+
+ return 0;
+}
+
+#pragma mark -
+
+int GUI_HoF::optionsButton(Button *button) {
+ PauseTimer pause(*_vm->_timer);
+
+ _restartGame = false;
+ _reloadTemporarySave = false;
+
+ updateButton(&_vm->_inventoryButtons[0]);
+
+ if (!_screen->isMouseVisible() && button)
+ return 0;
+
+ _vm->showMessage(0, 0xCF);
+
+ if (_vm->_mouseState < -1) {
+ _vm->_mouseState = -1;
+ _screen->setMouseCursor(1, 1, _vm->getShapePtr(0));
+ return 0;
+ }
+
+ int oldHandItem = _vm->_itemInHand;
+ _screen->setMouseCursor(0, 0, _vm->getShapePtr(0));
+ _vm->displayInvWsaLastFrame();
+ _displayMenu = true;
+
+ for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i) {
+ _menuButtons[i].data0Val1 = _menuButtons[i].data1Val1 = _menuButtons[i].data2Val1 = 4;
+ _menuButtons[i].data0Callback = _redrawShadedButtonFunctor;
+ _menuButtons[i].data1Callback = _menuButtons[i].data2Callback = _redrawButtonFunctor;
+ }
+
+ initMenuLayout(_mainMenu);
+ initMenuLayout(_gameOptions);
+ initMenuLayout(_audioOptions);
+ initMenuLayout(_choiceMenu);
+ _loadMenu.numberOfItems = 6;
+ initMenuLayout(_loadMenu);
+ initMenuLayout(_saveMenu);
+ initMenuLayout(_savenameMenu);
+ initMenuLayout(_deathMenu);
+
+ _currentMenu = &_mainMenu;
+
+ if (_vm->_menuDirectlyToLoad) {
+ backUpPage1(_vm->_screenBuffer);
+ setupPalette();
+
+ _loadedSave = false;
+
+ loadMenu(0);
+
+ if (_loadedSave) {
+ if (_restartGame)
+ _vm->_itemInHand = kItemNone;
+ } else {
+ restorePage1(_vm->_screenBuffer);
+ restorePalette();
+ }
+
+ resetState(-1);
+ _vm->_menuDirectlyToLoad = false;
+ return 0;
+ }
+
+ if (!button) {
+ _currentMenu = &_deathMenu;
+ _isDeathMenu = true;
+ } else {
+ _isDeathMenu = false;
+ }
+
+ backUpPage1(_vm->_screenBuffer);
+ setupPalette();
+ initMenu(*_currentMenu);
+ _madeSave = false;
+ _loadedSave = false;
+ updateAllMenuButtons();
+
+ if (_isDeathMenu) {
+ while (!_screen->isMouseVisible())
+ _screen->showMouse();
+ }
+
+ while (_displayMenu) {
+ processHighlights(*_currentMenu);
+ getInput();
+ }
+
+ if (_vm->_runFlag && !_loadedSave && !_madeSave) {
+ restorePalette();
+ restorePage1(_vm->_screenBuffer);
+ }
+
+ if (_vm->_runFlag)
+ updateMenuButton(&_vm->_inventoryButtons[0]);
+
+ resetState(oldHandItem);
+
+ if (!_loadedSave && _reloadTemporarySave) {
+ _vm->_unkSceneScreenFlag1 = true;
+ _vm->loadGameStateCheck(999);
+ //_vm->_saveFileMan->removeSavefile(_vm->getSavegameFilename(999));
+ _vm->_unkSceneScreenFlag1 = false;
+ }
+
+ return 0;
+}
+
+#pragma mark -
+
+void GUI_HoF::createScreenThumbnail(Graphics::Surface &dst) {
+ uint8 screenPal[768];
+ _screen->getRealPalette(1, screenPal);
+ ::createThumbnail(&dst, _vm->_screenBuffer, Screen::SCREEN_W, Screen::SCREEN_H, screenPal);
+}
+
+void GUI_HoF::setupPalette() {
+ _screen->copyPalette(1, 0);
+
+ Palette &pal = _screen->getPalette(0);
+ for (int i = 0; i < 741; ++i)
+ pal[i] >>= 1;
+
+ if (_isDeathMenu)
+ _screen->fadePalette(_screen->getPalette(0), 0x64);
+ else
+ _screen->setScreenPalette(_screen->getPalette(0));
+}
+
+void GUI_HoF::restorePalette() {
+ _screen->copyPalette(0, 1);
+ _screen->setScreenPalette(_screen->getPalette(0));
+}
+
+void GUI_HoF::resetState(int item) {
+ _vm->_timer->resetNextRun();
+ _vm->setNextIdleAnimTimer();
+ _isDeathMenu = false;
+ if (!_loadedSave) {
+ _vm->_itemInHand = kItemNone;
+ _vm->setHandItem(item);
+ } else {
+ _vm->setHandItem(_vm->_itemInHand);
+ _vm->setTimer1DelaySecs(7);
+ _vm->_shownMessage = " ";
+ _vm->_fadeMessagePalette = false;
+ }
+ _buttonListChanged = true;
+}
+
+void GUI_HoF::drawSliderBar(int slider, const uint8 *shape) {
+ const int menuX = _audioOptions.x;
+ const int menuY = _audioOptions.y;
+ int x = menuX + _sliderBarsPosition[slider*2+0] + 10;
+ int y = menuY + _sliderBarsPosition[slider*2+1];
+
+ int position = 0;
+ if (_vm->gameFlags().isTalkie) {
+ position = _vm->getVolume(KyraEngine_v1::kVolumeEntry(slider));
+ } else {
+ if (slider < 2)
+ position = _vm->getVolume(KyraEngine_v1::kVolumeEntry(slider));
+ else if (slider == 2)
+ position = (_vm->_configWalkspeed == 3) ? 97 : 2;
+ else if (slider == 3)
+ position = _vm->_configTextspeed;
+ }
+
+ position = CLIP(position, 2, 97);
+ _screen->drawShape(0, shape, x+position, y, 0, 0);
+}
+
+#pragma mark -
+
+int GUI_HoF::quitGame(Button *caller) {
+ updateMenuButton(caller);
+ if (choiceDialog(_vm->gameFlags().isTalkie ? 0xF : 0x17, 1)) {
+ _displayMenu = false;
+ _vm->_runFlag = false;
+ _vm->_sound->beginFadeOut();
+ _screen->fadeToBlack();
+ _screen->clearCurPage();
+ }
+
+ if (_vm->_runFlag) {
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ }
+
+ return 0;
+}
+
+int GUI_HoF::audioOptions(Button *caller) {
+ updateMenuButton(caller);
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ initMenu(_audioOptions);
+ const int menuX = _audioOptions.x;
+ const int menuY = _audioOptions.y;
+ const int maxButton = 3; // 2 if voc is disabled
+
+ for (int i = 0; i < maxButton; ++i) {
+ int x = menuX + _sliderBarsPosition[i*2+0];
+ int y = menuY + _sliderBarsPosition[i*2+1];
+ _screen->drawShape(0, _vm->_buttonShapes[16], x, y, 0, 0);
+ drawSliderBar(i, _vm->_buttonShapes[17]);
+ _sliderButtons[0][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[0][i].x = x;
+ _sliderButtons[0][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[0][i]);
+ _sliderButtons[2][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[2][i].x = x + 10;
+ _sliderButtons[2][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[2][i]);
+ _sliderButtons[1][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[1][i].x = x + 120;
+ _sliderButtons[1][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[1][i]);
+ }
+
+ _isOptionsMenu = true;
+ updateAllMenuButtons();
+ bool speechEnabled = _vm->speechEnabled();
+ while (_isOptionsMenu) {
+ processHighlights(_audioOptions);
+ getInput();
+ }
+
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ if (speechEnabled && !_vm->textEnabled() && (!_vm->speechEnabled() || _vm->getVolume(KyraEngine_v1::kVolumeSpeech) == 2)) {
+ _vm->_configVoice = 0;
+ choiceDialog(0x1D, 0);
+ }
+
+ _vm->writeSettings();
+
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ return 0;
+}
+
+int GUI_HoF::gameOptions(Button *caller) {
+ updateMenuButton(caller);
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ initMenu(_gameOptions);
+ _isOptionsMenu = true;
+
+ const int menuX = _gameOptions.x;
+ const int menuY = _gameOptions.y;
+
+ for (int i = 0; i < 4; ++i) {
+ int x = menuX + _sliderBarsPosition[i*2+0];
+ int y = menuY + _sliderBarsPosition[i*2+1];
+ _screen->drawShape(0, _vm->_buttonShapes[16], x, y, 0, 0);
+ drawSliderBar(i, _vm->_buttonShapes[17]);
+ _sliderButtons[0][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[0][i].x = x;
+ _sliderButtons[0][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[0][i]);
+ _sliderButtons[2][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[2][i].x = x + 10;
+ _sliderButtons[2][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[2][i]);
+ _sliderButtons[1][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[1][i].x = x + 120;
+ _sliderButtons[1][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[1][i]);
+ }
+
+ while (_isOptionsMenu) {
+ processHighlights(_gameOptions);
+ getInput();
+ }
+
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+
+ _vm->writeSettings();
+
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+
+ return 0;
+}
+
+int GUI_HoF::gameOptionsTalkie(Button *caller) {
+ updateMenuButton(caller);
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ bool textEnabled = _vm->textEnabled();
+ int lang = _vm->_lang;
+
+ setupOptionsButtons();
+ initMenu(_gameOptions);
+ _isOptionsMenu = true;
+
+ while (_isOptionsMenu) {
+ processHighlights(_gameOptions);
+ getInput();
+ }
+
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+
+ if (textEnabled && !_vm->textEnabled() && !_vm->speechEnabled()) {
+ _vm->_configVoice = 1;
+ _vm->setVolume(KyraEngine_v1::kVolumeSpeech, 75);
+ choiceDialog(0x1E, 0);
+ }
+
+ if (_vm->_lang != lang) {
+ _reloadTemporarySave = true;
+
+ Graphics::Surface thumb;
+ createScreenThumbnail(thumb);
+ _vm->saveGameStateIntern(999, "Autosave", &thumb);
+ thumb.free();
+
+ _vm->_lastAutosave = _vm->_system->getMillis();
+
+ _vm->loadCCodeBuffer("C_CODE.XXX");
+ if (_vm->_flags.isTalkie)
+ _vm->loadOptionsBuffer("OPTIONS.XXX");
+ else
+ _vm->_optionsBuffer = _vm->_cCodeBuffer;
+ _vm->loadChapterBuffer(_vm->_newChapterFile);
+ _vm->loadNPCScript();
+ _vm->setupLangButtonShapes();
+ }
+
+ _vm->writeSettings();
+
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ return 0;
+}
+
+int GUI_HoF::changeLanguage(Button *caller) {
+ updateMenuButton(caller);
+ ++_vm->_lang;
+ _vm->_lang %= 3;
+ setupOptionsButtons();
+ renewHighlight(_gameOptions);
+ return 0;
+}
+
+void GUI_HoF::setupOptionsButtons() {
+ if (_vm->_configWalkspeed == 3)
+ _gameOptions.item[0].itemId = 28;
+ else
+ _gameOptions.item[0].itemId = 27;
+
+ if (_vm->textEnabled())
+ _gameOptions.item[2].itemId = 18;
+ else
+ _gameOptions.item[2].itemId = 17;
+
+ switch (_vm->_lang) {
+ case 0:
+ _gameOptions.item[1].itemId = 31;
+ break;
+
+ case 1:
+ _gameOptions.item[1].itemId = 32;
+ break;
+
+ case 2:
+ _gameOptions.item[1].itemId = 33;
+ break;
+
+ default:
+ break;
+ }
+}
+
+int GUI_HoF::sliderHandler(Button *caller) {
+ int button = 0;
+ if (caller->index >= 24 && caller->index <= 27)
+ button = caller->index - 24;
+ else if (caller->index >= 28 && caller->index <= 31)
+ button = caller->index - 28;
+ else
+ button = caller->index - 32;
+
+ assert(button >= 0 && button <= 3);
+
+ int oldVolume = 0;
+
+ if (_vm->gameFlags().isTalkie) {
+ oldVolume = _vm->getVolume(KyraEngine_v1::kVolumeEntry(button));
+ } else {
+ if (button < 2)
+ oldVolume = _vm->getVolume(KyraEngine_v1::kVolumeEntry(button));
+ else if (button == 2)
+ oldVolume = (_vm->_configWalkspeed == 3) ? 97 : 2;
+ else if (button == 3)
+ oldVolume = _vm->_configTextspeed;
+ }
+
+ int newVolume = oldVolume;
+
+ if (caller->index >= 24 && caller->index <= 27)
+ newVolume -= 10;
+ else if (caller->index >= 28 && caller->index <= 31)
+ newVolume += 10;
+ else
+ newVolume = _vm->_mouseX - caller->x - 7;
+
+ newVolume = CLIP(newVolume, 2, 97);
+
+ if (newVolume == oldVolume)
+ return 0;
+
+ int lastMusicCommand = -1;
+ bool playSoundEffect = false;
+
+ drawSliderBar(button, _vm->_buttonShapes[18]);
+
+ if (_vm->gameFlags().isTalkie) {
+ if (button == 2) {
+ if (_vm->textEnabled())
+ _vm->_configVoice = 2;
+ else
+ _vm->_configVoice = 1;
+ }
+
+ _vm->setVolume(KyraEngine_v1::kVolumeEntry(button), newVolume);
+
+ switch (button) {
+ case 0:
+ lastMusicCommand = _vm->_lastMusicCommand;
+ break;
+
+ case 1:
+ playSoundEffect = true;
+ break;
+
+ case 2:
+ _vm->playVoice(90, 28);
+ break;
+
+ default:
+ return 0;
+ }
+ } else {
+ if (button < 2) {
+ _vm->setVolume(KyraEngine_v1::kVolumeEntry(button), newVolume);
+ if (button == 0)
+ lastMusicCommand = _vm->_lastMusicCommand;
+ else
+ playSoundEffect = true;
+ } else if (button == 2) {
+ _vm->_configWalkspeed = (newVolume > 48) ? 3 : 5;
+ _vm->setWalkspeed(_vm->_configWalkspeed);
+ } else if (button == 3) {
+ _vm->_configTextspeed = newVolume;
+ }
+ }
+
+ drawSliderBar(button, _vm->_buttonShapes[17]);
+ if (playSoundEffect)
+ _vm->snd_playSoundEffect(0x18);
+ else if (lastMusicCommand >= 0)
+ _vm->snd_playWanderScoreViaMap(lastMusicCommand, 0);
+
+ _screen->updateScreen();
+ return 0;
+}
+
+int GUI_HoF::loadMenu(Button *caller) {
+ updateSaveFileList(_vm->_targetName);
+
+ if (!_vm->_menuDirectlyToLoad) {
+ updateMenuButton(caller);
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ }
+
+ _savegameOffset = 0;
+ setupSavegameNames(_loadMenu, 5);
+ initMenu(_loadMenu);
+ _isLoadMenu = true;
+ _noLoadProcess = false;
+ _vm->_gameToLoad = -1;
+ updateAllMenuButtons();
+
+ _screen->updateScreen();
+ while (_isLoadMenu) {
+ processHighlights(_loadMenu);
+ getInput();
+ }
+
+ if (_noLoadProcess) {
+ if (!_vm->_menuDirectlyToLoad) {
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ }
+ } else if (_vm->_gameToLoad >= 0) {
+ restorePage1(_vm->_screenBuffer);
+ restorePalette();
+ _vm->loadGameStateCheck(_vm->_gameToLoad);
+ if (_vm->_gameToLoad == 0) {
+ _restartGame = true;
+ for (int i = 0; i < 23; ++i)
+ _vm->resetCauldronStateTable(i);
+ _vm->runStartScript(1, 1);
+ }
+ _displayMenu = false;
+ _loadedSave = true;
+ }
+
+ return 0;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_hof.h b/engines/kyra/gui/gui_hof.h
new file mode 100644
index 0000000000..1ff91e4ecb
--- /dev/null
+++ b/engines/kyra/gui/gui_hof.h
@@ -0,0 +1,84 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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();
+
+ int optionsButton(Button *button);
+
+ void createScreenThumbnail(Graphics::Surface &dst);
+private:
+ const char *getMenuTitle(const Menu &menu);
+ const char *getMenuItemTitle(const MenuItem &menuItem);
+ const char *getMenuItemLabel(const MenuItem &menuItem);
+
+ uint8 defaultColor1() const { return 0xCF; }
+ uint8 defaultColor2() const { return 0xF8; }
+
+ uint8 textFieldColor1() const { return 0xFD; }
+ uint8 textFieldColor2() const { return 0xFA; }
+ uint8 textFieldColor3() const { return 0xFE; }
+
+ void setupPalette();
+ void restorePalette();
+
+ void resetState(int item);
+
+ char *getTableString(int id);
+
+ 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();
+
+ int sliderHandler(Button *caller);
+ void drawSliderBar(int slider, const uint8 *shape);
+
+ static const uint16 _menuStringsTalkie[];
+ static const uint16 _menuStringsOther[];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/gui/gui_lok.cpp b/engines/kyra/gui/gui_lok.cpp
new file mode 100644
index 0000000000..188e992f8d
--- /dev/null
+++ b/engines/kyra/gui/gui_lok.cpp
@@ -0,0 +1,1133 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/gui/gui_lok.h"
+#include "kyra/engine/kyra_lok.h"
+#include "kyra/graphics/animator_lok.h"
+#include "kyra/text/text.h"
+#include "kyra/engine/timer.h"
+#include "kyra/engine/util.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "graphics/scaler.h"
+
+namespace Kyra {
+
+void KyraEngine_LoK::initMainButtonList() {
+ _buttonList = &_buttonData[0];
+ for (int i = 0; _buttonDataListPtr[i]; ++i)
+ _buttonList = _gui->addButtonToList(_buttonList, _buttonDataListPtr[i]);
+}
+
+int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
+ int itemOffset = caller->index - 2;
+ Item inventoryItem = (int8)_currentCharacter->inventoryItems[itemOffset];
+ if (_itemInHand == kItemNone) {
+ if (inventoryItem == kItemNone) {
+ snd_playSoundEffect(0x36);
+ return 0;
+ } else {
+ _screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12);
+ snd_playSoundEffect(0x35);
+ setMouseItem(inventoryItem);
+ updateSentenceCommand(_itemList[getItemListIndex(inventoryItem)], _takenList[0], 179);
+ _itemInHand = inventoryItem;
+ _currentCharacter->inventoryItems[itemOffset] = kItemNone;
+ }
+ } else {
+ if (inventoryItem != kItemNone) {
+ snd_playSoundEffect(0x35);
+ _screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12);
+ _screen->drawShape(0, _shapes[216 + _itemInHand], _itemPosX[itemOffset], _itemPosY[itemOffset], 0, 0);
+ setMouseItem(inventoryItem);
+ // TODO: Proper support for both taken strings in Amiga version
+ if (_flags.platform == Common::kPlatformAmiga)
+ updateSentenceCommand(_itemList[getItemListIndex(inventoryItem)], _takenList[0], 179);
+ else
+ updateSentenceCommand(_itemList[getItemListIndex(inventoryItem)], _takenList[1], 179);
+ _currentCharacter->inventoryItems[itemOffset] = _itemInHand;
+ _itemInHand = inventoryItem;
+ } else {
+ snd_playSoundEffect(0x32);
+ _screen->drawShape(0, _shapes[216 + _itemInHand], _itemPosX[itemOffset], _itemPosY[itemOffset], 0, 0);
+ _screen->setMouseCursor(1, 1, _shapes[0]);
+ updateSentenceCommand(_itemList[getItemListIndex(_itemInHand)], _placedList[0], 179);
+ _currentCharacter->inventoryItems[itemOffset] = _itemInHand;
+ _itemInHand = kItemNone;
+ }
+ }
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine_LoK::buttonAmuletCallback(Button *caller) {
+ if (!(_deathHandler & 8))
+ return 1;
+ int jewel = caller->index - 0x14;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return 1;
+ }
+ if (!queryGameFlag(0x2D))
+ return 1;
+ if (_itemInHand != kItemNone) {
+ assert(_putDownFirst);
+ characterSays(2000, _putDownFirst[0], 0, -2);
+ return 1;
+ }
+ if (queryGameFlag(0xF1)) {
+ assert(_waitForAmulet);
+ characterSays(2001, _waitForAmulet[0], 0, -2);
+ return 1;
+ }
+ if (!queryGameFlag(0x55 + jewel)) {
+ assert(_blackJewel);
+ _animator->makeBrandonFaceMouse();
+ drawJewelPress(jewel, 1);
+ characterSays(2002, _blackJewel[0], 0, -2);
+ return 1;
+ }
+ drawJewelPress(jewel, 0);
+ drawJewelsFadeOutStart();
+ drawJewelsFadeOutEnd(jewel);
+
+ _emc->init(&_scriptClick, &_scriptClickData);
+ _scriptClick.regs[3] = 0;
+ _scriptClick.regs[6] = jewel;
+ _emc->start(&_scriptClick, 4);
+
+ while (_emc->isValid(&_scriptClick))
+ _emc->run(&_scriptClick);
+
+ if (_scriptClick.regs[3])
+ return 1;
+
+ _unkAmuletVar = 1;
+ switch (jewel - 1) {
+ case 0:
+ if (_brandonStatusBit & 1) {
+ seq_brandonHealing2();
+ } else if (_brandonStatusBit == 0) {
+ seq_brandonHealing();
+ assert(_healingTip);
+ characterSays(2003, _healingTip[0], 0, -2);
+ }
+ break;
+
+ case 1:
+ seq_makeBrandonInv();
+ break;
+
+ case 2:
+ if (_brandonStatusBit & 1) {
+ assert(_wispJewelStrings);
+ characterSays(2004, _wispJewelStrings[0], 0, -2);
+ } else {
+ if (_brandonStatusBit & 2) {
+ // XXX
+ seq_makeBrandonNormal2();
+ // XXX
+ } else {
+ // do not check for item in hand again as in the original since some strings are missing
+ // in the cd version
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ snd_playWanderScoreViaMap(1, 0);
+ seq_makeBrandonWisp();
+ snd_playWanderScoreViaMap(17, 0);
+ } else {
+ seq_makeBrandonWisp();
+ }
+ setGameFlag(0x9E);
+ }
+ }
+ break;
+
+ case 3:
+ seq_dispelMagicAnimation();
+ assert(_magicJewelString);
+ characterSays(2007, _magicJewelString[0], 0, -2);
+ break;
+
+ default:
+ break;
+ }
+ _unkAmuletVar = 0;
+ return 1;
+}
+
+#pragma mark -
+
+GUI_LoK::GUI_LoK(KyraEngine_LoK *vm, Screen_LoK *screen) : GUI_v1(vm), _vm(vm), _screen(screen) {
+ _lastScreenUpdate = 0;
+ _menu = 0;
+ _pressFlag = false;
+ initStaticResource();
+ _scrollUpFunctor = BUTTON_FUNCTOR(GUI_LoK, this, &GUI_LoK::scrollUp);
+ _scrollDownFunctor = BUTTON_FUNCTOR(GUI_LoK, this, &GUI_LoK::scrollDown);
+}
+
+GUI_LoK::~GUI_LoK() {
+ delete[] _menu;
+}
+
+void GUI_LoK::createScreenThumbnail(Graphics::Surface &dst) {
+ uint8 *screen = new uint8[Screen::SCREEN_W * Screen::SCREEN_H];
+ if (screen) {
+ _screen->queryPageFromDisk("SEENPAGE.TMP", 0, screen);
+ uint8 screenPal[768];
+
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
+ _screen->getRealPalette(0, &screenPal[ 0]);
+ _screen->getRealPalette(1, &screenPal[96]);
+
+ // Set the interface palette text color to white
+ screenPal[96 + 16 * 3 + 0] = 0xFF;
+ screenPal[96 + 16 * 3 + 1] = 0xFF;
+ screenPal[96 + 16 * 3 + 2] = 0xFF;
+
+ if (_screen->isInterfacePaletteEnabled()) {
+ for (int y = 0; y < 64; ++y) {
+ for (int x = 0; x < 320; ++x) {
+ screen[(y + 136) * Screen::SCREEN_W + x] += 32;
+ }
+ }
+ }
+
+ } else {
+ _screen->getRealPalette(2, screenPal);
+ }
+
+ ::createThumbnail(&dst, screen, Screen::SCREEN_W, Screen::SCREEN_H, screenPal);
+ }
+ delete[] screen;
+}
+
+int GUI_LoK::processButtonList(Button *list, uint16 inputFlag, int8 mouseWheel) {
+ if ((inputFlag & 0xFF) == 199)
+ _pressFlag = true;
+ else if ((inputFlag & 0xFF) == 200)
+ _pressFlag = false;
+
+ int returnValue = 0;
+ while (list) {
+ if (list->flags & 8) {
+ list = list->nextButton;
+ continue;
+ }
+
+ if (mouseWheel && list->mouseWheel == mouseWheel && list->buttonCallback) {
+ if ((*list->buttonCallback)(list))
+ break;
+ }
+
+ int x = list->x;
+ int y = list->y;
+ assert(_screen->getScreenDim(list->dimTableIndex) != 0);
+
+ if (x < 0)
+ x += _screen->getScreenDim(list->dimTableIndex)->w << 3;
+ x += _screen->getScreenDim(list->dimTableIndex)->sx << 3;
+
+ if (y < 0)
+ y += _screen->getScreenDim(list->dimTableIndex)->h;
+ y += _screen->getScreenDim(list->dimTableIndex)->sy;
+
+ if (_vm->_mouseX >= x && _vm->_mouseY >= y && x + list->width >= _vm->_mouseX && y + list->height >= _vm->_mouseY) {
+ int processMouseClick = 0;
+ if (list->flags & 0x400) {
+ if ((inputFlag & 0xFF) == 199 || _pressFlag) {
+ if (!(list->flags2 & 1)) {
+ list->flags2 |= 1;
+ list->flags2 |= 4;
+ processButton(list);
+ _screen->updateScreen();
+ inputFlag = 0;
+ }
+ } else if ((inputFlag & 0xFF) == 200) {
+ if (list->flags2 & 1) {
+ list->flags2 &= 0xFFFE;
+ processButton(list);
+ processMouseClick = 1;
+ inputFlag = 0;
+ }
+ }
+ }
+
+ if (processMouseClick) {
+ if (list->buttonCallback) {
+ if ((*list->buttonCallback)(list))
+ break;
+ }
+ }
+ } else {
+ if (list->flags2 & 1) {
+ list->flags2 &= 0xFFFE;
+ processButton(list);
+ }
+
+ if (list->flags2 & 4) {
+ list->flags2 &= 0xFFFB;
+ processButton(list);
+ _screen->updateScreen();
+ }
+ }
+
+ list = list->nextButton;
+ }
+
+ if (!returnValue)
+ returnValue = inputFlag & 0xFF;
+
+ return returnValue;
+}
+
+void GUI_LoK::processButton(Button *button) {
+ if (!button)
+ return;
+
+ int processType = 0;
+ const uint8 *shape = 0;
+ Button::Callback callback;
+
+ int flags = (button->flags2 & 5);
+ if (flags == 1) {
+ processType = button->data2Val1;
+ if (processType == 1)
+ shape = button->data2ShapePtr;
+ else if (processType == 4)
+ callback = button->data2Callback;
+ } else if (flags == 4 || flags == 5) {
+ processType = button->data1Val1;
+ if (processType == 1)
+ shape = button->data1ShapePtr;
+ else if (processType == 4)
+ callback = button->data1Callback;
+ } else {
+ processType = button->data0Val1;
+ if (processType == 1)
+ shape = button->data0ShapePtr;
+ else if (processType == 4)
+ callback = button->data0Callback;
+ }
+
+ int x = button->x;
+ int y = button->y;
+ assert(_screen->getScreenDim(button->dimTableIndex) != 0);
+ if (x < 0)
+ x += _screen->getScreenDim(button->dimTableIndex)->w << 3;
+
+ if (y < 0)
+ y += _screen->getScreenDim(button->dimTableIndex)->h;
+
+ if (processType == 1 && shape)
+ _screen->drawShape(_screen->_curPage, shape, x, y, button->dimTableIndex, 0x10);
+ else if (processType == 4 && callback)
+ (*callback)(button);
+}
+
+void GUI_LoK::setGUILabels() {
+ int offset = 0;
+ int offsetOptions = 0;
+ int offsetMainMenu = 0;
+ int offsetOn = 0;
+ int offsetPC98 = 0;
+
+ int walkspeedGarbageOffset = 36;
+ int menuLabelGarbageOffset = 0;
+
+ if (_vm->gameFlags().isTalkie) {
+ if (_vm->gameFlags().lang == Common::EN_ANY)
+ offset = 52;
+ else if (_vm->gameFlags().lang == Common::DE_DEU)
+ offset = 30;
+ else if (_vm->gameFlags().lang == Common::FR_FRA || _vm->gameFlags().lang == Common::IT_ITA)
+ offset = 6;
+ offsetOn = offsetMainMenu = offsetOptions = offset;
+ walkspeedGarbageOffset = 48;
+ } else if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
+ if (_vm->gameFlags().lang == Common::EN_ANY) {
+ offset = offsetOn = 23;
+ offsetOptions = 32;
+ walkspeedGarbageOffset = 2;
+ offsetMainMenu = 23;
+ } else if (_vm->gameFlags().lang == Common::DE_DEU) {
+ offset = offsetOn = 12;
+ offsetOptions = 21;
+ walkspeedGarbageOffset = 3;
+ offsetMainMenu = 12;
+ }
+ } else if (_vm->gameFlags().lang == Common::ES_ESP) {
+ offsetOn = offsetMainMenu = offsetOptions = offset = -4;
+ menuLabelGarbageOffset = 72;
+ } else if (_vm->gameFlags().lang == Common::IT_ITA) {
+ offsetOn = offsetMainMenu = offsetOptions = offset = 32;
+ } else if (_vm->gameFlags().lang == Common::DE_DEU) {
+ offset = offsetMainMenu = offsetOn = offsetOptions = 24;
+ } else if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ offset = 1;
+ offsetOptions = 10;
+ offsetOn = 0;
+ walkspeedGarbageOffset = 0;
+ } else if (_vm->gameFlags().platform == Common::kPlatformPC98) {
+ offsetMainMenu = offsetOptions = offsetOn = offset = 47;
+ offsetPC98 = 1;
+ }
+
+ assert(offset + (_vm->gameFlags().isTalkie ? 28 : 23) < _vm->_guiStringsSize);
+ assert(offsetOptions + 27 < _vm->_guiStringsSize);
+ assert(offsetMainMenu + 19 < _vm->_guiStringsSize);
+
+ // The Legend of Kyrandia
+ _menu[0].menuNameString = _vm->_guiStrings[0];
+ // Load a Game
+ _menu[0].item[0].itemString = _vm->_guiStrings[1];
+ // Save a Game
+ _menu[0].item[1].itemString = _vm->_guiStrings[2];
+ // Game controls
+ _menu[0].item[2].itemString = _vm->_guiStrings[3];
+ // Quit playing
+ _menu[0].item[3].itemString = _vm->_guiStrings[4];
+ // Resume game
+ _menu[0].item[4].itemString = _vm->_guiStrings[5];
+
+ // Cancel
+ _menu[2].item[5].itemString = _vm->_guiStrings[10 + offsetPC98];
+
+ // Enter a description of your saved game:
+ _menu[3].menuNameString = _vm->_guiStrings[11 + offsetPC98];
+ // Save
+ _menu[3].item[0].itemString = _vm->_guiStrings[12 + offsetPC98];
+ // Cancel
+ _menu[3].item[1].itemString = _vm->_guiStrings[10 + offsetPC98];
+
+ // Rest in peace, Brandon
+ _menu[4].menuNameString = _vm->_guiStrings[13 + offsetPC98];
+ // Load a game
+ _menu[4].item[0].itemString = _vm->_guiStrings[1];
+ // Quit playing
+ _menu[4].item[1].itemString = _vm->_guiStrings[4];
+
+ // Game Controls
+ _menu[5].menuNameString = _vm->_guiStrings[6];
+ // Yes
+ _menu[1].item[0].itemString = _vm->_guiStrings[22 + offset];
+ // No
+ _menu[1].item[1].itemString = _vm->_guiStrings[23 + offset];
+
+ // Music is
+ _menu[5].item[0].labelString = _vm->_guiStrings[26 + offsetOptions];
+ // Sounds are
+ _menu[5].item[1].labelString = _vm->_guiStrings[27 + offsetOptions];
+ // Walk speed
+ _menu[5].item[2].labelString = &_vm->_guiStrings[24 + offsetOptions][walkspeedGarbageOffset];
+ // Text speed
+ _menu[5].item[4].labelString = _vm->_guiStrings[25 + offsetOptions];
+ // Main Menu
+ _menu[5].item[5].itemString = &_vm->_guiStrings[19 + offsetMainMenu][menuLabelGarbageOffset];
+
+ if (_vm->gameFlags().isTalkie)
+ // Text & Voice
+ _voiceTextString = _vm->_guiStrings[28 + offset];
+
+ _textSpeedString = _vm->_guiStrings[25 + offsetOptions];
+ _onString = _vm->_guiStrings[20 + offsetOn];
+ _offString = _vm->_guiStrings[21 + offset];
+ _onCDString = _vm->_guiStrings[21];
+}
+
+int GUI_LoK::buttonMenuCallback(Button *caller) {
+ PauseTimer pause(*_vm->_timer);
+
+ _displayMenu = true;
+
+ assert(_vm->_guiStrings);
+ assert(_vm->_configStrings);
+
+ /*
+ for (int i = 0; i < _vm->_guiStringsSize; i++)
+ debug("GUI string %i: %s", i, _vm->_guiStrings[i]);
+
+ for (int i = 0; i < _vm->_configStringsSize; i++)
+ debug("Config string %i: %s", i, _vm->_configStrings[i]);
+ */
+
+ setGUILabels();
+ if (_vm->_currentCharacter->sceneId == 210 && _vm->_deathHandler == -1) {
+ _vm->snd_playSoundEffect(0x36);
+ return 0;
+ }
+
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
+ _screen->setPaletteIndex(0x10, 0x3F, 0x3F, 0x3F);
+ _screen->setInterfacePalette(_screen->getPalette(1), 0x3F, 0x3F, 0x3F);
+ } else {
+ _screen->setPaletteIndex(0xFE, 60, 60, 0);
+ }
+
+ for (int i = 0; i < 6; i++) {
+ _menuButtonData[i].data0Val1 = _menuButtonData[i].data1Val1 = _menuButtonData[i].data2Val1 = 4;
+ _menuButtonData[i].data0Callback = _redrawShadedButtonFunctor;
+ _menuButtonData[i].data1Callback = _redrawButtonFunctor;
+ _menuButtonData[i].data2Callback = _redrawButtonFunctor;
+ }
+
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+ fadePalette();
+
+ for (int i = 0; i < 5; i++)
+ initMenuLayout(_menu[i]);
+
+ _menuRestoreScreen = true;
+ _keyPressed.reset();
+
+ _toplevelMenu = 0;
+ if (_vm->_menuDirectlyToLoad) {
+ loadGameMenu(0);
+ } else {
+ if (!caller)
+ _toplevelMenu = 4;
+
+ initMenu(_menu[_toplevelMenu]);
+ updateAllMenuButtons();
+ }
+
+ while (_displayMenu && !_vm->shouldQuit()) {
+ processHighlights(_menu[_toplevelMenu]);
+ getInput();
+ }
+
+ if (_menuRestoreScreen) {
+ restorePalette();
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ } else {
+ _screen->deletePageFromDisk(0);
+ }
+
+ return 0;
+}
+
+void GUI_LoK::getInput() {
+ uint32 now = _vm->_system->getMillis();
+
+ _vm->checkInput(_menuButtonList);
+ _vm->removeInputTop();
+
+ if (now - _lastScreenUpdate > 50) {
+ _vm->_system->updateScreen();
+ _lastScreenUpdate = now;
+ }
+
+ _vm->_system->delayMillis(3);
+}
+
+int GUI_LoK::resumeGame(Button *button) {
+ updateMenuButton(button);
+ _displayMenu = false;
+
+ return 0;
+}
+
+void GUI_LoK::setupSavegames(Menu &menu, int num) {
+ Common::InSaveFile *in;
+ uint8 startSlot;
+ assert(num <= 5);
+
+ if (_savegameOffset == 0) {
+ menu.item[0].itemString = _specialSavegameString;
+ menu.item[0].enabled = 1;
+ menu.item[0].saveSlot = 0;
+ startSlot = 1;
+ } else {
+ startSlot = 0;
+ }
+
+ for (int i = startSlot; i < num; ++i)
+ menu.item[i].enabled = 0;
+
+ KyraEngine_LoK::SaveHeader header;
+ for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); i++) {
+ if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header))) {
+ Common::strlcpy(_savegameNames[i], header.description.c_str(), ARRAYSIZE(_savegameNames[0]));
+
+ // Trim long GMM save descriptions to fit our save slots
+ _screen->_charWidth = -2;
+ int fC = _screen->getTextWidth(_savegameNames[i]);
+ while (_savegameNames[i][0] && (fC > 240)) {
+ _savegameNames[i][strlen(_savegameNames[i]) - 1] = 0;
+ fC = _screen->getTextWidth(_savegameNames[i]);
+ }
+ _screen->_charWidth = 0;
+
+ Util::convertISOToDOS(_savegameNames[i]);
+
+ menu.item[i].itemString = _savegameNames[i];
+ menu.item[i].enabled = 1;
+ menu.item[i].saveSlot = _saveSlots[i + _savegameOffset];
+ delete in;
+ }
+ }
+}
+
+int GUI_LoK::saveGameMenu(Button *button) {
+ updateSaveFileList(_vm->_targetName);
+
+ updateMenuButton(button);
+ _menu[2].item[5].enabled = true;
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _menu[2].menuNameString = _vm->_guiStrings[8]; // Select a position to save to:
+ _specialSavegameString = _vm->_guiStrings[_vm->gameFlags().platform == Common::kPlatformPC98 ? 10 : 9]; // [ EMPTY SLOT ]
+ for (int i = 0; i < 5; i++)
+ _menu[2].item[i].callback = BUTTON_FUNCTOR(GUI_LoK, this, &GUI_LoK::saveGame);
+
+ _savegameOffset = 0;
+ setupSavegames(_menu[2], 5);
+
+ initMenu(_menu[2]);
+ updateAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ while (_displaySubMenu && !_vm->shouldQuit()) {
+ processHighlights(_menu[2]);
+ getInput();
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[0]);
+ updateAllMenuButtons();
+ } else {
+ _displayMenu = false;
+ }
+ return 0;
+}
+
+int GUI_LoK::loadGameMenu(Button *button) {
+ updateSaveFileList(_vm->_targetName);
+
+ if (_vm->_menuDirectlyToLoad) {
+ _menu[2].item[5].enabled = false;
+ } else {
+ updateMenuButton(button);
+ _menu[2].item[5].enabled = true;
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _specialSavegameString = _vm->_newGameString[0]; //[ START A NEW GAME ]
+ _menu[2].menuNameString = _vm->_guiStrings[7]; // Which game would you like to reload?
+ for (int i = 0; i < 5; i++)
+ _menu[2].item[i].callback = BUTTON_FUNCTOR(GUI_LoK, this, &GUI_LoK::loadGame);
+
+ _savegameOffset = 0;
+ setupSavegames(_menu[2], 5);
+
+ initMenu(_menu[2]);
+ updateAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ _vm->_gameToLoad = -1;
+
+ while (_displaySubMenu && !_vm->shouldQuit()) {
+ processHighlights(_menu[2]);
+ getInput();
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[_toplevelMenu]);
+ updateAllMenuButtons();
+ } else {
+ restorePalette();
+ if (_vm->_gameToLoad != -1)
+ _vm->loadGameStateCheck(_vm->_gameToLoad);
+ _displayMenu = false;
+ _menuRestoreScreen = false;
+ }
+ return 0;
+}
+
+void GUI_LoK::redrawTextfield() {
+ _screen->fillRect(38, 91, 287, 102, _vm->gameFlags().platform == Common::kPlatformAmiga ? 18 : 250);
+ _text->printText(_savegameName, 38, 92, 253, 0, 0);
+
+ _screen->_charWidth = -2;
+ int width = _screen->getTextWidth(_savegameName);
+ _screen->fillRect(39 + width, 93, 45 + width, 100, _vm->gameFlags().platform == Common::kPlatformAmiga ? 31 : 254);
+ _screen->_charWidth = 0;
+
+ _screen->updateScreen();
+}
+
+void GUI_LoK::updateSavegameString() {
+ int length;
+
+ if (_keyPressed.keycode) {
+ length = strlen(_savegameName);
+ _screen->_charWidth = -2;
+ int width = _screen->getTextWidth(_savegameName) + 7;
+ _screen->_charWidth = 0;
+
+ char inputKey = _keyPressed.ascii;
+ Util::convertISOToDOS(inputKey);
+
+ if ((uint8)inputKey > 31 && (uint8)inputKey < (_vm->gameFlags().lang == Common::JA_JPN ? 128 : 226)) {
+ if ((length < ARRAYSIZE(_savegameName) - 1) && (width <= 240)) {
+ _savegameName[length] = inputKey;
+ _savegameName[length + 1] = 0;
+ redrawTextfield();
+ }
+ } else if (_keyPressed.keycode == Common::KEYCODE_BACKSPACE ||
+ _keyPressed.keycode == Common::KEYCODE_DELETE) {
+ if (length > 0) {
+ _savegameName[length - 1] = 0;
+ redrawTextfield();
+ }
+ } else if (_keyPressed.keycode == Common::KEYCODE_RETURN ||
+ _keyPressed.keycode == Common::KEYCODE_KP_ENTER) {
+ _displaySubMenu = false;
+ }
+ }
+
+ _keyPressed.reset();
+}
+
+int GUI_LoK::saveGame(Button *button) {
+ g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ updateMenuButton(button);
+ _vm->_gameToLoad = _menu[2].item[button->index - 0xC].saveSlot;
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ initMenu(_menu[3]);
+ updateAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ Screen::FontId cf = _screen->setFont(Screen::FID_8_FNT);
+
+ if (_savegameOffset == 0 && _vm->_gameToLoad == 0) {
+ _savegameName[0] = 0;
+ } else {
+ for (int i = 0; i < 5; i++) {
+ if (_menu[2].item[i].saveSlot == _vm->_gameToLoad) {
+ Common::strlcpy(_savegameName, _menu[2].item[i].itemString, 31);
+ break;
+ }
+ }
+ }
+ redrawTextfield();
+
+ _screen->setFont(cf);
+
+ while (_displaySubMenu && !_vm->shouldQuit()) {
+ checkTextfieldInput();
+ cf = _screen->setFont(Screen::FID_8_FNT);
+ updateSavegameString();
+ _screen->setFont(cf);
+ processHighlights(_menu[3]);
+ }
+
+ if (_cancelSubMenu) {
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+ initMenu(_menu[2]);
+ updateAllMenuButtons();
+ } else {
+ if (_savegameOffset == 0 && _vm->_gameToLoad == 0)
+ _vm->_gameToLoad = getNextSavegameSlot();
+ if (_vm->_gameToLoad > 0) {
+ Util::convertDOSToISO(_savegameName);
+
+ Graphics::Surface thumb;
+ createScreenThumbnail(thumb);
+ _vm->saveGameStateIntern(_vm->_gameToLoad, _savegameName, &thumb);
+ thumb.free();
+ }
+ }
+
+ g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ return 0;
+}
+
+int GUI_LoK::savegameConfirm(Button *button) {
+ updateMenuButton(button);
+ _displaySubMenu = false;
+
+ return 0;
+}
+
+int GUI_LoK::loadGame(Button *button) {
+ updateMenuButton(button);
+ _displaySubMenu = false;
+ _vm->_gameToLoad = _menu[2].item[button->index - 0xC].saveSlot;
+
+ return 0;
+}
+
+int GUI_LoK::cancelSubMenu(Button *button) {
+ updateMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = true;
+
+ return 0;
+}
+
+int GUI_LoK::quitPlaying(Button *button) {
+ updateMenuButton(button);
+
+ if (quitConfirm(_vm->_guiStrings[_vm->gameFlags().platform == Common::kPlatformPC98 ? 15 : 14])) { // Are you sure you want to quit playing?
+ _vm->quitGame();
+ } else {
+ initMenu(_menu[_toplevelMenu]);
+ updateAllMenuButtons();
+ }
+
+ return 0;
+}
+
+bool GUI_LoK::quitConfirm(const char *str) {
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _menu[1].menuNameString = str;
+ initMenuLayout(_menu[1]);
+ initMenu(_menu[1]);
+
+ _displaySubMenu = true;
+ _cancelSubMenu = true;
+
+ while (_displaySubMenu && !_vm->shouldQuit()) {
+ processHighlights(_menu[1]);
+ getInput();
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ return !_cancelSubMenu;
+}
+
+int GUI_LoK::quitConfirmYes(Button *button) {
+ updateMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = false;
+
+ return 0;
+}
+
+int GUI_LoK::quitConfirmNo(Button *button) {
+ updateMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = true;
+
+ return 0;
+}
+
+int GUI_LoK::gameControlsMenu(Button *button) {
+ _vm->readSettings();
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_vm->gameFlags().isTalkie) {
+ //_menu[5].width = 230;
+
+ for (int i = 0; i < 5; i++) {
+ //_menu[5].item[i].labelX = 24;
+ //_menu[5].item[i].x = 115;
+ //_menu[5].item[i].width = 94;
+ }
+
+ _menu[5].item[3].labelString = _voiceTextString; //"Voice / Text "
+ _menu[5].item[3].callback = BUTTON_FUNCTOR(GUI_LoK, this, &GUI_LoK::controlsChangeVoice);
+
+ } else {
+ //_menu[5].height = 136;
+ //_menu[5].item[5].y = 110;
+ _menu[5].item[4].enabled = 0;
+ _menu[5].item[3].labelString = _textSpeedString; // "Text speed "
+ _menu[5].item[3].callback = BUTTON_FUNCTOR(GUI_LoK, this, &GUI_LoK::controlsChangeText);
+ }
+
+ setupControls(_menu[5]);
+
+ updateAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ while (_displaySubMenu && !_vm->shouldQuit()) {
+ processHighlights(_menu[5]);
+ getInput();
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[_toplevelMenu]);
+ updateAllMenuButtons();
+ }
+ return 0;
+}
+
+void GUI_LoK::setupControls(Menu &menu) {
+ switch (_vm->_configMusic) {
+ case 0:
+ menu.item[0].itemString = _offString; //"Off"
+ break;
+ case 1:
+ menu.item[0].itemString = _onString; //"On"
+ break;
+ case 2:
+ menu.item[0].itemString = _onCDString; //"On + CD"
+ break;
+ }
+
+ if (_vm->_configSounds)
+ menu.item[1].itemString = _onString; //"On"
+ else
+ menu.item[1].itemString = _offString; //"Off"
+
+
+ switch (_vm->_configWalkspeed) {
+ case 0:
+ menu.item[2].itemString = _vm->_configStrings[0]; //"Slowest"
+ break;
+ case 1:
+ menu.item[2].itemString = _vm->_configStrings[1]; //"Slow"
+ break;
+ case 2:
+ menu.item[2].itemString = _vm->_configStrings[2]; //"Normal"
+ break;
+ case 3:
+ menu.item[2].itemString = _vm->_configStrings[3]; //"Fast"
+ break;
+ case 4:
+ menu.item[2].itemString = _vm->_configStrings[4]; //"Fastest"
+ break;
+ default:
+ menu.item[2].itemString = "ERROR";
+ }
+
+ int textControl = 3;
+ int clickableOffset = 8;
+ if (_vm->gameFlags().isTalkie) {
+ textControl = 4;
+ clickableOffset = 11;
+
+ if (_vm->_configVoice == 0) {
+ menu.item[4].enabled = 1;
+ menu.item[4].labelString = _textSpeedString;
+ } else {
+ menu.item[4].enabled = 0;
+ menu.item[4].labelString = 0;
+ }
+
+ switch (_vm->_configVoice) {
+ case 0:
+ menu.item[3].itemString = _vm->_configStrings[5]; //"Text only"
+ break;
+ case 1:
+ menu.item[3].itemString = _vm->_configStrings[6]; //"Voice only"
+ break;
+ case 2:
+ menu.item[3].itemString = _vm->_configStrings[7]; //"Voice & Text"
+ break;
+ default:
+ menu.item[3].itemString = "ERROR";
+ }
+ } else {
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga)
+ clickableOffset = 5;
+
+ menu.item[4].enabled = 0;
+ menu.item[4].labelString = 0;
+ }
+
+ switch (_vm->_configTextspeed) {
+ case 0:
+ menu.item[textControl].itemString = _vm->_configStrings[1]; //"Slow"
+ break;
+ case 1:
+ menu.item[textControl].itemString = _vm->_configStrings[2]; //"Normal"
+ break;
+ case 2:
+ menu.item[textControl].itemString = _vm->_configStrings[3]; //"Fast"
+ break;
+ case 3:
+ menu.item[textControl].itemString = _vm->_configStrings[clickableOffset]; //"Clickable"
+ break;
+ default:
+ menu.item[textControl].itemString = "ERROR";
+ }
+
+ initMenuLayout(menu);
+ initMenu(menu);
+}
+
+int GUI_LoK::controlsChangeMusic(Button *button) {
+ updateMenuButton(button);
+
+ _vm->_configMusic = (_vm->_configMusic + 1) % ((_vm->gameFlags().platform == Common::kPlatformFMTowns) ? 3 : 2);
+ setupControls(_menu[5]);
+ return 0;
+}
+
+int GUI_LoK::controlsChangeSounds(Button *button) {
+ updateMenuButton(button);
+
+ _vm->_configSounds = !_vm->_configSounds;
+ setupControls(_menu[5]);
+ return 0;
+}
+
+int GUI_LoK::controlsChangeWalk(Button *button) {
+ updateMenuButton(button);
+
+ _vm->_configWalkspeed = (_vm->_configWalkspeed + 1) % 5;
+ _vm->setWalkspeed(_vm->_configWalkspeed);
+ setupControls(_menu[5]);
+ return 0;
+}
+
+int GUI_LoK::controlsChangeText(Button *button) {
+ updateMenuButton(button);
+
+ _vm->_configTextspeed = (_vm->_configTextspeed + 1) % 4;
+ setupControls(_menu[5]);
+ return 0;
+}
+
+int GUI_LoK::controlsChangeVoice(Button *button) {
+ updateMenuButton(button);
+
+ _vm->_configVoice = (_vm->_configVoice + 1) % 3;
+ setupControls(_menu[5]);
+ return 0;
+}
+
+int GUI_LoK::controlsApply(Button *button) {
+ _vm->writeSettings();
+ return cancelSubMenu(button);
+}
+
+int GUI_LoK::scrollUp(Button *button) {
+ updateMenuButton(button);
+
+ if (_savegameOffset > 0) {
+ _savegameOffset--;
+ setupSavegames(_menu[2], 5);
+ initMenu(_menu[2]);
+ }
+ return 0;
+}
+
+int GUI_LoK::scrollDown(Button *button) {
+ updateMenuButton(button);
+
+ _savegameOffset++;
+ if (uint(_savegameOffset + 5) >= _saveSlots.size())
+ _savegameOffset = MAX<int>(_saveSlots.size() - 5, 0);
+ setupSavegames(_menu[2], 5);
+ initMenu(_menu[2]);
+
+ return 0;
+}
+
+void GUI_LoK::fadePalette() {
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga)
+ return;
+
+ static const int16 menuPalIndexes[] = {248, 249, 250, 251, 252, 253, 254, -1};
+ int index = 0;
+
+ _screen->copyPalette(2, 0);
+
+ for (int i = 0; i < 768; i++)
+ _screen->getPalette(0)[i] >>= 1;
+
+ while (menuPalIndexes[index] != -1) {
+ _screen->getPalette(0).copy(_screen->getPalette(2), menuPalIndexes[index], 1);
+ ++index;
+ }
+
+ _screen->fadePalette(_screen->getPalette(0), 2);
+}
+
+void GUI_LoK::restorePalette() {
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga)
+ return;
+
+ _screen->copyPalette(0, 2);
+ _screen->fadePalette(_screen->getPalette(0), 2);
+}
+
+#pragma mark -
+
+void KyraEngine_LoK::drawAmulet() {
+ static const int16 amuletTable1[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x150, 0x155, 0x15A, 0x15F, 0x164, 0x145, -1};
+ static const int16 amuletTable3[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x14F, 0x154, 0x159, 0x15E, 0x163, 0x144, -1};
+ static const int16 amuletTable2[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x152, 0x157, 0x15C, 0x161, 0x166, 0x147, -1};
+ static const int16 amuletTable4[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x151, 0x156, 0x15B, 0x160, 0x165, 0x146, -1};
+
+ resetGameFlag(0xF1);
+ _screen->hideMouse();
+
+ int i = 0;
+ while (amuletTable1[i] != -1) {
+ if (queryGameFlag(87))
+ _screen->drawShape(0, _shapes[amuletTable1[i]], _amuletX[0], _amuletY[0], 0, 0);
+
+ if (queryGameFlag(89))
+ _screen->drawShape(0, _shapes[amuletTable2[i]], _amuletX[1], _amuletY[1], 0, 0);
+
+ if (queryGameFlag(86))
+ _screen->drawShape(0, _shapes[amuletTable3[i]], _amuletX[2], _amuletY[2], 0, 0);
+
+ if (queryGameFlag(88))
+ _screen->drawShape(0, _shapes[amuletTable4[i]], _amuletX[3], _amuletY[3], 0, 0);
+
+ _screen->updateScreen();
+ delayWithTicks(3);
+ i++;
+ }
+ _screen->showMouse();
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_lok.h b/engines/kyra/gui/gui_lok.h
new file mode 100644
index 0000000000..fe0a5a7b43
--- /dev/null
+++ b/engines/kyra/gui/gui_lok.h
@@ -0,0 +1,184 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 = d; \
+ 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();
+
+ void processButton(Button *button);
+ int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel);
+
+ int buttonMenuCallback(Button *caller);
+
+ void createScreenThumbnail(Graphics::Surface &dst);
+private:
+ void initStaticResource();
+
+ Button _menuButtonData[6];
+ Button _scrollUpButton;
+ Button _scrollDownButton;
+ Button *getButtonListData() { return _menuButtonData; }
+ Button *getScrollUpButton() { return &_scrollUpButton; }
+ Button *getScrollDownButton() { 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 { return 12; }
+ uint8 defaultColor2() const { return 248; }
+
+ const char *getMenuTitle(const Menu &menu) { return menu.menuNameString; }
+ const char *getMenuItemTitle(const MenuItem &menuItem) { return menuItem.itemString; }
+ const char *getMenuItemLabel(const MenuItem &menuItem) { return menuItem.labelString; }
+
+ KyraEngine_LoK *_vm;
+ Screen_LoK *_screen;
+
+ bool _menuRestoreScreen;
+ uint8 _toplevelMenu;
+ int _savegameOffset;
+ char _savegameName[35];
+ char _savegameNames[5][35];
+ const char *_specialSavegameString;
+
+ Button::Callback _scrollUpFunctor;
+ Button::Callback _scrollDownFunctor;
+ Button::Callback getScrollUpButtonHandler() const { return _scrollUpFunctor; }
+ Button::Callback getScrollDownButtonHandler() const { return _scrollDownFunctor; }
+
+ const char *_voiceTextString;
+ const char *_textSpeedString;
+ const char *_onString;
+ const char *_offString;
+ const char *_onCDString;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/gui/gui_lol.cpp b/engines/kyra/gui/gui_lol.cpp
new file mode 100644
index 0000000000..0c21227b19
--- /dev/null
+++ b/engines/kyra/gui/gui_lol.cpp
@@ -0,0 +1,2910 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef ENABLE_LOL
+
+#include "kyra/gui/gui_lol.h"
+#include "kyra/engine/lol.h"
+#include "kyra/graphics/screen_lol.h"
+#include "kyra/resource/resource.h"
+#include "kyra/engine/util.h"
+#include "kyra/sound/sound.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+
+#include "graphics/scaler.h"
+
+#include "backends/keymapper/keymapper.h"
+
+#include "base/version.h"
+
+namespace Kyra {
+
+void LoLEngine::gui_drawPlayField() {
+ _screen->loadBitmap("PLAYFLD.CPS", 3, 3, 0);
+
+ if (_flagsTable[31] & 0x40) {
+ // copy compass shape
+ static const int cx[] = { 112, 152, 224 };
+ _screen->copyRegion(cx[_flags.isTalkie ? _lang : 0], 32, 288, 0, 32, 32, 2, 2, Screen::CR_NO_P_CHECK);
+ _compassDirection = -1;
+ }
+
+ if (_flagsTable[31] & 0x10)
+ // draw automap book
+ _screen->drawShape(2, _gameShapes[_flags.isTalkie ? 78 : 76], 290, 32, 0, 0);
+
+ int cp = _screen->setCurPage(2);
+
+ if (_flagsTable[31] & 0x20) {
+ gui_drawScroll();
+ } else {
+ _selectedSpell = 0;
+ }
+
+ if (_flagsTable[31] & 0x08)
+ resetLampStatus();
+
+ updateDrawPage2();
+ gui_drawScene(2);
+
+ gui_drawAllCharPortraitsWithStats();
+ gui_drawInventory();
+ gui_drawMoneyBox(_screen->_curPage);
+
+ _screen->setCurPage(cp);
+ _screen->copyPage(2, 0);
+ updateDrawPage2();
+}
+
+void LoLEngine::gui_drawScene(int pageNum) {
+ if (!(_updateFlags & 1) && _weaponsDisabled == false && _partyAwake && _vcnBlocks)
+ drawScene(pageNum);
+}
+
+void LoLEngine::gui_drawInventory() {
+ if (!_currentControlMode || !_needSceneRestore) {
+ for (int i = 0; i < 9; i++)
+ gui_drawInventoryItem(i);
+ }
+}
+
+void LoLEngine::gui_drawInventoryItem(int index) {
+ static const uint16 inventoryXpos[] = { 0x6A, 0x7F, 0x94, 0xA9, 0xBE, 0xD3, 0xE8, 0xFD, 0x112 };
+ int x = inventoryXpos[index];
+ int item = _inventoryCurItem + index;
+ if (item > 47)
+ item -= 48;
+
+ int flag = item & 1 ? 0 : 1;
+
+ _screen->drawShape(_screen->_curPage, _gameShapes[4], x, 179, 0, flag);
+ if (_inventory[item])
+ _screen->drawShape(_screen->_curPage, getItemIconShapePtr(_inventory[item]), x + 1, 180, 0, 0);
+}
+
+void LoLEngine::gui_drawScroll() {
+ _screen->copyRegion(112, 0, 12, 0, 87, 15, 2, 2, Screen::CR_NO_P_CHECK);
+ Screen::FontId of = _screen->setFont(Screen::FID_9_FNT);
+ int h = 0;
+
+ for (int i = 0; i < 7; i++) {
+ if (_availableSpells[i] != -1)
+ h += 9;
+ }
+
+ if (h == 18)
+ h = 27;
+
+ if (h) {
+ _screen->copyRegion(201, 1, 17, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(208, 1, 89, 15, 6, h, 2, 2, Screen::CR_NO_P_CHECK);
+ _screen->fillRect(21, 15, 89, h + 15, _flags.use16ColorMode ? 0xBB : 206);
+ }
+
+ _screen->copyRegion(112, 16, 12, h + 15, 87, 14, 2, 2, Screen::CR_NO_P_CHECK);
+
+ int y = 15;
+ for (int i = 0; i < 7; i++) {
+ if (_availableSpells[i] == -1)
+ continue;
+ uint8 col = (i == _selectedSpell) ? (_flags.use16ColorMode ? 0x88 : 132) : (_flags.use16ColorMode ? 0x44 : 1);
+ _screen->fprintString("%s", 24, y, col, 0, 0, getLangString(_spellProperties[_availableSpells[i]].spellNameCode));
+ y += 9;
+ }
+ _screen->setFont(of);
+}
+
+void LoLEngine::gui_highlightSelectedSpell(bool mode) {
+ int y = 15;
+ Screen::FontId of = _screen->setFont(Screen::FID_9_FNT);
+ for (int i = 0; i < 7; i++) {
+ if (_availableSpells[i] == -1)
+ continue;
+ uint8 col = (mode && (i == _selectedSpell)) ? (_flags.use16ColorMode ? 0x88 : 132) : (_flags.use16ColorMode ? 0x44 : 1);
+ _screen->fprintString("%s", 24, y, col, 0, 0, getLangString(_spellProperties[_availableSpells[i]].spellNameCode));
+ y += 9;
+ }
+ _screen->setFont(of);
+}
+
+void LoLEngine::gui_displayCharInventory(int charNum) {
+ static const uint8 inventoryTypes[] = { 0, 1, 2, 6, 3, 1, 1, 3, 5, 4 };
+
+ int cp = _screen->setCurPage(2);
+ LoLCharacter *l = &_characters[charNum];
+
+ int id = l->id;
+ if (id < 0)
+ id = -id;
+
+ if (id != _lastCharInventory) {
+ char file[13];
+ sprintf(file, "invent%d.cps", inventoryTypes[id]);
+ _screen->loadBitmap(file, 3, 3, 0);
+ _screen->copyRegion(0, 0, 112, 0, 208, 120, 2, 6);
+ } else {
+ _screen->copyRegion(112, 0, 0, 0, 208, 120, 6, 2);
+ }
+
+ _screen->copyRegion(80, 143, 80, 143, 232, 35, 0, 2);
+ gui_drawAllCharPortraitsWithStats();
+
+ if (_flags.use16ColorMode)
+ _screen->fprintString("%s", 156, 8, 0xE1, 0, 1, l->name);
+ else
+ _screen->fprintString("%s", 157, 9, 254, 0, 5, l->name);
+
+ gui_printCharInventoryStats(charNum);
+
+ for (int i = 0; i < 11; i++)
+ gui_drawCharInventoryItem(i);
+
+ Screen::FontId of = _screen->setFont(Screen::FID_9_FNT);
+ _screen->fprintString("%s", 182, 103, _flags.use16ColorMode ? 0xBB : 172, 0, 5, getLangString(0x4033));
+ _screen->setFont(of);
+
+ static const uint16 statusFlags[] = { 0x0080, 0x0000, 0x1000, 0x0002, 0x100, 0x0001, 0x0000, 0x0000 };
+
+ memset(_charStatusFlags, 0xFF, sizeof(_charStatusFlags));
+ int x = 0;
+ int32 c = 0;
+
+ for (int i = 0; i < 3; i++) {
+ if (!(l->flags & statusFlags[i << 1]))
+ continue;
+
+ uint8 *shp = _gameShapes[statusFlags[(i << 1) + 1]];
+ _screen->drawShape(_screen->_curPage, shp, 108 + x, 98, 0, 0);
+ x += (shp[3] + 2);
+ _charStatusFlags[c] = statusFlags[(i << 1) + 1];
+ c++;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ int32 b = l->experiencePts[i] - _expRequirements[l->skillLevels[i] - 1];
+ int32 e = _expRequirements[l->skillLevels[i]] - _expRequirements[l->skillLevels[i] - 1];
+
+ while (e & 0xFFFF8000) {
+ e >>= 1;
+ c = b;
+ b >>= 1;
+
+ if (c && !b)
+ b = 1;
+ }
+
+ if (_flags.use16ColorMode)
+ gui_drawHorizontalBarGraph(154, 66 + i * 8, 34, 5, b, e, 0x88, 0);
+ else
+ gui_drawHorizontalBarGraph(154, 64 + i * 10, 34, 5, b, e, 132, 0);
+ }
+
+ _screen->drawClippedLine(14, 120, 194, 120, 1);
+ _screen->copyRegion(0, 0, 112, 0, 208, 121, 2, 0);
+ _screen->copyRegion(80, 143, 80, 143, 232, 35, 2, 0);
+
+ _screen->setCurPage(cp);
+}
+
+void LoLEngine::gui_printCharInventoryStats(int charNum) {
+ for (int i = 0; i < 5; i++)
+ gui_printCharacterStats(i, 1, calculateCharacterStats(charNum, i));
+
+ _charInventoryUnk |= (1 << charNum);
+}
+
+void LoLEngine::gui_printCharacterStats(int index, int redraw, int value) {
+ uint32 offs = _screen->_curPage ? 0 : 112;
+ int y = 0;
+ int col = 0;
+
+ if (index < 2) {
+ // might
+ // protection
+ if (_flags.use16ColorMode) {
+ y = (index + 2) << 3;
+ col = 0xA1;
+ if (redraw)
+ _screen->fprintString("%s", offs + 108, y, col, 0, 0, getLangString(0x4014 + index));
+ } else {
+ y = index * 10 + 22;
+ col = 158;
+ if (redraw)
+ _screen->fprintString("%s", offs + 108, y, col, 0, 4, getLangString(0x4014 + index));
+ }
+ } else {
+ //skills
+ int s = index - 2;
+ y = s * 10 + 62;
+ if (_flags.use16ColorMode) {
+ y = (s + 8) << 3;
+ col = _characters[_selectedCharacter].flags & (0x200 << s) ? 0xE1 : 0x81;
+ if (redraw)
+ _screen->fprintString("%s", offs + 108, y, col, 0, 0, getLangString(0x4014 + index));
+ } else {
+ y = s * 10 + 62;
+ col = _characters[_selectedCharacter].flags & (0x200 << s) ? 254 : 180;
+ if (redraw)
+ _screen->fprintString("%s", offs + 108, y, col, 0, 4, getLangString(0x4014 + index));
+ }
+ }
+
+ if (offs)
+ _screen->copyRegion(294, y, 182 + offs, y, 18, 8, 6, _screen->_curPage, Screen::CR_NO_P_CHECK);
+
+ Screen::FontId of = (_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? _screen->setFont(Screen::FID_SJIS_FNT) : _screen->_currentFont;
+ _screen->fprintString("%d", 200 + offs, y, col, 0, _flags.use16ColorMode ? 2 : 6, value);
+ _screen->setFont(of);
+}
+
+void LoLEngine::gui_changeCharacterStats(int charNum) {
+ int tmp[5];
+ int inc[5];
+ bool prc = false;
+
+ for (int i = 0; i < 5; i++) {
+ tmp[i] = calculateCharacterStats(charNum, i);
+ int diff = tmp[i] - _charStatsTemp[i];
+ inc[i] = diff / 15;
+
+ if (diff) {
+ prc = true;
+ if (!inc[i])
+ inc[i] = (diff < 0) ? -1 : 1;
+ }
+ }
+
+ if (!prc)
+ return;
+
+ do {
+ prc = false;
+
+ for (int i = 0; i < 5; i++) {
+ if (tmp[i] == _charStatsTemp[i])
+ continue;
+
+ _charStatsTemp[i] += inc[i];
+
+ if ((inc[i] > 0 && tmp[i] < _charStatsTemp[i]) || (inc[i] < 0 && tmp[i] > _charStatsTemp[i]))
+ _charStatsTemp[i] = tmp[i];
+
+ gui_printCharacterStats(i, 0, _charStatsTemp[i]);
+ prc = true;
+ }
+
+ delay(_tickLength, true);
+
+ } while (prc);
+}
+
+void LoLEngine::gui_drawCharInventoryItem(int itemIndex) {
+ static const uint8 slotShapes[] = { 0x30, 0x34, 0x30, 0x34, 0x2E, 0x2F, 0x32, 0x33, 0x31, 0x35, 0x35 };
+ //2Eh, 32h, 2Eh, 32h, 2Ch, 2Dh, 30h, 31h, 2Fh, 33h, 33h
+ const uint8 *coords = &_charInvDefs[_charInvIndex[_characters[_selectedCharacter].raceClassSex] * 22 + itemIndex * 2];
+ uint8 x = *coords++;
+ uint8 y = *coords;
+
+ if (y == 0xFF)
+ return;
+
+ if (!_screen->_curPage)
+ x += 112;
+
+ int i = _characters[_selectedCharacter].items[itemIndex];
+ int shapeNum = i ? ((itemIndex < 9) ? 4 : 5) : (_flags.isTalkie ? slotShapes[itemIndex] : slotShapes[itemIndex] - 2);
+ _screen->drawShape(_screen->_curPage, _gameShapes[shapeNum], x, y, 0, 0);
+
+ if (itemIndex > 8) {
+ x -= 5;
+ y -= 5;
+ }
+
+ if (i)
+ _screen->drawShape(_screen->_curPage, getItemIconShapePtr(i), x + 1, y + 1, 0, 0);
+}
+
+void LoLEngine::gui_drawAllCharPortraitsWithStats() {
+ int numChars = countActiveCharacters();
+ if (!numChars)
+ return;
+
+ for (int i = 0; i < numChars; i++)
+ gui_drawCharPortraitWithStats(i);
+}
+
+void LoLEngine::gui_drawCharPortraitWithStats(int charNum) {
+ if (!(_characters[charNum].flags & 1) || _updateFlags & 2)
+ return;
+
+ Screen::FontId tmpFid = _screen->setFont(Screen::FID_6_FNT);
+ int cp = _screen->setCurPage(6);
+
+ gui_drawBox(0, 0, 66, 34, 1, 1, -1);
+ gui_drawCharFaceShape(charNum, 0, 1, _screen->_curPage);
+
+ if (_flags.use16ColorMode) {
+ gui_drawLiveMagicBar(33, 32, _characters[charNum].magicPointsCur, 0, _characters[charNum].magicPointsMax, 5, 32, 0xAA, 0x44, 0);
+ gui_drawLiveMagicBar(39, 32, _characters[charNum].hitPointsCur, 0, _characters[charNum].hitPointsMax, 5, 32, 0x66, 0x44, 1);
+ _screen->printText(getLangString(0x4253), 33, 1, 0x99, 0);
+ _screen->printText(getLangString(0x4254), 39, 1, 0x55, 0);
+ } else {
+ gui_drawLiveMagicBar(33, 32, _characters[charNum].magicPointsCur, 0, _characters[charNum].magicPointsMax, 5, 32, 162, 1, 0);
+ gui_drawLiveMagicBar(39, 32, _characters[charNum].hitPointsCur, 0, _characters[charNum].hitPointsMax, 5, 32, 154, 1, 1);
+ _screen->printText((_flags.platform == Common::kPlatformDOS && !_flags.isTalkie) ? "M" : getLangString(0x4253), 33, 1, 160, 0);
+ _screen->printText((_flags.platform == Common::kPlatformDOS && !_flags.isTalkie) ? "H" : getLangString(0x4254), 39, 1, 152, 0);
+ }
+
+ int spellLevels = 0;
+ if (_availableSpells[_selectedSpell] != -1) {
+ for (int i = 0; i < 4; i++) {
+ if (_spellProperties[_availableSpells[_selectedSpell]].mpRequired[i] <= _characters[charNum].magicPointsCur
+ && _spellProperties[_availableSpells[_selectedSpell]].hpRequired[i] <= _characters[charNum].hitPointsCur)
+ spellLevels++;
+ }
+ }
+
+ if (_characters[charNum].flags & 0x10) {
+ // magic submenu open
+ _screen->drawShape(_screen->_curPage, _gameShapes[_flags.isTalkie ? 73 : 71], 44, 0, 0, 0);
+ if (spellLevels < 4)
+ _screen->drawGridBox(44, (spellLevels << 3) + 1, 22, 32 - (spellLevels << 3), 1);
+ } else {
+ // magic submenu closed
+ int handIndex = 0;
+ if (_characters[charNum].items[0]) {
+ if (_itemProperties[_itemsInPlay[_characters[charNum].items[0]].itemPropertyIndex].might != -1)
+ handIndex = _itemsInPlay[_characters[charNum].items[0]].itemPropertyIndex;
+ }
+
+ handIndex = _gameShapeMap[(_itemProperties[handIndex].shpIndex << 1) + 1];
+ if (handIndex == _gameShapeMap[1]) { // draw raceClassSex specific hand shape
+ handIndex = _characters[charNum].raceClassSex - 1;
+ if (handIndex < 0)
+ handIndex = 0;
+ handIndex += (_flags.isTalkie ? 68 : 66);
+ }
+
+ // draw hand/weapon
+ _screen->drawShape(_screen->_curPage, _gameShapes[handIndex], 44, 0, 0, 0);
+ // draw magic symbol
+ _screen->drawShape(_screen->_curPage, _gameShapes[(_flags.isTalkie ? 72 : 70) + _characters[charNum].field_41], 44, 17, 0, 0);
+
+ if (spellLevels == 0)
+ _screen->drawGridBox(44, 17, 22, 16, 1);
+ }
+
+ uint16 f = _characters[charNum].flags & 0x314C;
+ if ((f == 0 && _weaponsDisabled) || (f && (f != 4 || _characters[charNum].weaponHit == 0 || (_characters[charNum].weaponHit && _weaponsDisabled))))
+ _screen->drawGridBox(44, 0, 22, 34, 1);
+
+ if (_characters[charNum].weaponHit) {
+ _screen->drawShape(_screen->_curPage, _gameShapes[_flags.isTalkie ? 34 : 32], 44, 0, 0, 0);
+ _screen->fprintString("%d", 57, 7, _flags.use16ColorMode ? 0x33 : 254, 0, 1, _characters[charNum].weaponHit);
+ }
+ if (_characters[charNum].damageSuffered)
+ _screen->fprintString("%d", 17, 28, _flags.use16ColorMode ? 0x33 : 254, 0, 1, _characters[charNum].damageSuffered);
+
+ uint8 col = (charNum != _selectedCharacter || countActiveCharacters() == 1) ? 1 : 212;
+ if (_flags.use16ColorMode)
+ col = (charNum != _selectedCharacter || countActiveCharacters() == 1) ? 0x44 : 0x22;
+ _screen->drawBox(0, 0, 65, 33, col);
+
+ _screen->copyRegion(0, 0, _activeCharsXpos[charNum], 143, 66, 34, _screen->_curPage, cp, Screen::CR_NO_P_CHECK);
+
+ _screen->setCurPage(cp);
+ _screen->setFont(tmpFid);
+}
+
+void LoLEngine::gui_drawCharFaceShape(int charNum, int x, int y, int pageNum) {
+ if (_characters[charNum].curFaceFrame < 7 && _characters[charNum].tempFaceFrame)
+ _characters[charNum].curFaceFrame = _characters[charNum].tempFaceFrame;
+
+ if (_characters[charNum].tempFaceFrame == 0 && _characters[charNum].curFaceFrame > 1 && _characters[charNum].curFaceFrame < 7)
+ _characters[charNum].curFaceFrame = _characters[charNum].tempFaceFrame;
+
+ int frm = (_characters[charNum].flags & 0x1108 && _characters[charNum].curFaceFrame < 7) ? 1 : _characters[charNum].curFaceFrame;
+
+ if (_characters[charNum].hitPointsCur <= (_characters[charNum].hitPointsMax >> 1))
+ frm += 14;
+
+ if (!pageNum)
+ _screen->hideMouse();
+
+ _screen->drawShape(pageNum, _characterFaceShapes[frm][charNum], x, y, 0, 0x100, _screen->_paletteOverlay2, (_characters[charNum].flags & 0x80 ? 1 : 0));
+
+ if (_characters[charNum].flags & 0x40)
+ // draw spider web
+ _screen->drawShape(pageNum, _gameShapes[21], x, y, 0, 0);
+
+ if (!pageNum)
+ _screen->showMouse();
+}
+
+void LoLEngine::gui_highlightPortraitFrame(int charNum) {
+ if (charNum != _selectedCharacter) {
+ int o = _selectedCharacter;
+ _selectedCharacter = charNum;
+ gui_drawCharPortraitWithStats(o);
+ }
+
+ gui_drawCharPortraitWithStats(charNum);
+}
+
+void LoLEngine::gui_drawLiveMagicBar(int x, int y, int curPoints, int unk, int maxPoints, int w, int h, int col1, int col2, int flag) {
+ w--;
+ h--;
+
+ if (maxPoints < 1)
+ return;
+
+ int t = (curPoints < 1) ? 0 : curPoints;
+ curPoints = (maxPoints < t) ? maxPoints : t;
+
+ int barHeight = (curPoints * h) / maxPoints;
+
+ if (barHeight < 1 && curPoints > 0)
+ barHeight = 1;
+
+ _screen->drawClippedLine(x - 1, y - h, x - 1, y, _flags.use16ColorMode ? 0x44 : 1);
+
+ if (flag) {
+ t = maxPoints >> 1;
+ if (t > curPoints)
+ col1 = _flags.use16ColorMode ? 0xBB : 144;
+ t = maxPoints >> 2;
+ if (t > curPoints)
+ col1 = _flags.use16ColorMode ? 0x88 : 132;
+ }
+
+ if (barHeight > 0)
+ _screen->fillRect(x, y - barHeight, x + w, y, col1);
+
+ if (barHeight < h)
+ _screen->fillRect(x, y - h, x + w, y - barHeight, col2);
+
+ if (unk > 0 && unk < maxPoints)
+ _screen->drawBox(x, y - barHeight, x + w, y, col1 - 2);
+}
+
+void LoLEngine::calcCharPortraitXpos() {
+ int nc = countActiveCharacters();
+
+ if (_currentControlMode && !textEnabled()) {
+ int t = (280 - (nc * 33)) / (nc + 1);
+ for (int i = 0; i < nc; i++)
+ _activeCharsXpos[i] = i * 33 + t * (i + 1) + 10;
+
+ } else {
+ int t = (235 - (nc * 66)) / (nc + 1);
+ for (int i = 0; i < nc; i++)
+ _activeCharsXpos[i] = i * 66 + t * (i + 1) + 83;
+ }
+}
+
+void LoLEngine::gui_drawMoneyBox(int pageNum) {
+ static const uint16 moneyX256[] = { 0x128, 0x134, 0x12B, 0x131, 0x12E};
+ static const uint16 moneyY256[] = { 0x73, 0x73, 0x74, 0x74, 0x75};
+ static const uint16 moneyX16[] = { 0x127, 0x133, 0x12A, 0x130, 0x12D};
+ static const uint16 moneyY16[] = { 0x74, 0x74, 0x75, 0x75, 0x76};
+
+ int backupPage = _screen->_curPage;
+ _screen->_curPage = pageNum;
+
+ const uint16 *moneyX;
+ const uint16 *moneyY;
+
+ if (_flags.use16ColorMode) {
+ moneyX = moneyX16;
+ moneyY = moneyY16;
+ _screen->fillRect(291, 98, 315, 118, 0x11, pageNum);
+ } else {
+ moneyX = moneyX256;
+ moneyY = moneyY256;
+ _screen->fillRect(292, 97, 316, 118, 252, pageNum);
+ }
+
+ for (int i = 0; i < 5; i++) {
+ if (!_moneyColumnHeight[i])
+ continue;
+
+ uint8 h = _moneyColumnHeight[i] - 1;
+ _screen->drawClippedLine(moneyX[i], moneyY[i], moneyX[i], moneyY[i] - h, _flags.use16ColorMode ? 1 : 0xD2);
+ _screen->drawClippedLine(moneyX[i] + 1, moneyY[i], moneyX[i] + 1, moneyY[i] - h, _flags.use16ColorMode ? 2 : 0xD1);
+ _screen->drawClippedLine(moneyX[i] + 2, moneyY[i], moneyX[i] + 2, moneyY[i] - h, _flags.use16ColorMode ? 3 : 0xD0);
+ _screen->drawClippedLine(moneyX[i] + 3, moneyY[i], moneyX[i] + 3, moneyY[i] - h, _flags.use16ColorMode ? 2 : 0xD1);
+ _screen->drawClippedLine(moneyX[i] + 4, moneyY[i], moneyX[i] + 4, moneyY[i] - h, _flags.use16ColorMode ? 1 : 0xD2);
+ }
+
+ Screen::FontId backupFont = _screen->setFont(Screen::FID_6_FNT);
+ if (_flags.use16ColorMode)
+ _screen->fprintString("%d", 304, 99, 0x33, 0, 1, _credits);
+ else
+ _screen->fprintString("%d", 305, 98, 254, 0, 1, _credits);
+
+ _screen->setFont(backupFont);
+ _screen->_curPage = backupPage;
+
+ if (pageNum == 6) {
+ if (_flags.use16ColorMode)
+ _screen->copyRegion(291, 98, 291, 98, 24, 20, 6, 0);
+ else
+ _screen->copyRegion(292, 97, 292, 97, 25, 22, 6, 0);
+ }
+}
+
+void LoLEngine::gui_drawCompass() {
+ if (!(_flagsTable[31] & 0x40))
+ return;
+
+ if (_compassDirection == -1) {
+ _compassDirectionIndex = -1;
+ _compassDirection = _currentDirection << 6;
+ }
+
+ int t = ((_compassDirection + 4) >> 3) & 0x1F;
+
+ if (t == _compassDirectionIndex)
+ return;
+
+ _compassDirectionIndex = t;
+
+ if (!_screen->_curPage)
+ _screen->hideMouse();
+
+ const CompassDef *c = &_compassDefs[t];
+
+ int compassShp = 22;
+ int compassPtr = 23;
+ if (_flags.isTalkie) {
+ compassShp += _lang;
+ compassPtr = 25;
+ }
+
+ _screen->drawShape(_screen->_curPage, _gameShapes[compassShp], 294, 3, 0, 0);
+ _screen->drawShape(_screen->_curPage, _gameShapes[compassPtr + c->shapeIndex], 298 + c->x, c->y + 9, 0, c->flags | 0x300, _screen->_paletteOverlay1, 1);
+ _screen->drawShape(_screen->_curPage, _gameShapes[compassPtr + c->shapeIndex], 299 + c->x, c->y + 8, 0, c->flags);
+
+ if (!_screen->_curPage)
+ _screen->showMouse();
+}
+
+int LoLEngine::gui_enableControls() {
+ _floatingCursorControl = 0;
+
+ int start = 74;
+ int end = 83;
+
+ if (_flags.isTalkie) {
+ start = 76;
+ end = 85;
+ }
+
+ if (!_currentControlMode) {
+ for (int i = start; i < end; i++)
+ gui_toggleButtonDisplayMode(i, 2);
+ }
+
+ gui_toggleFightButtons(false);
+ return 1;
+}
+
+int LoLEngine::gui_disableControls(int controlMode) {
+ if (_currentControlMode)
+ return 0;
+
+ _floatingCursorControl = (controlMode & 2) ? 2 : 1;
+
+ gui_toggleFightButtons(true);
+
+ int start = 74;
+ int end = 83;
+ int swtch = 76;
+
+ if (_flags.isTalkie) {
+ start = 76;
+ end = 85;
+ swtch = 78;
+ }
+
+ for (int i = start; i < end; i++)
+ gui_toggleButtonDisplayMode(i, ((controlMode & 2) && (i > swtch)) ? 2 : 3);
+
+ return 1;
+}
+
+void LoLEngine::gui_toggleButtonDisplayMode(int shapeIndex, int mode) {
+ static const int16 buttonX[] = { 0x0056, 0x0128, 0x000C, 0x0021, 0x0122, 0x000C, 0x0021, 0x0036, 0x000C, 0x0021, 0x0036 };
+ static const int16 buttonY[] = { 0x00B4, 0x00B4, 0x00B4, 0x00B4, 0x0020, 0x0084, 0x0084, 0x0084, 0x0096, 0x0096, 0x0096 };
+
+ int swtch = 76;
+ int subst = 72;
+
+ if (_flags.isTalkie) {
+ swtch = 78;
+ subst = 74;
+ }
+
+ if (shapeIndex == swtch && !(_flagsTable[31] & 0x10))
+ return;
+
+ if (_currentControlMode && _needSceneRestore)
+ return;
+
+ if (mode == 0)
+ shapeIndex = _lastButtonShape;
+
+ int pageNum = 0;
+
+ int16 x1 = buttonX[shapeIndex - subst];
+ int16 y1 = buttonY[shapeIndex - subst];
+ int16 x2 = 0;
+ int16 y2 = 0;
+ uint32 t = 0;
+
+ switch (mode) {
+ case 1:
+ mode = 0x100;
+ _lastButtonShape = shapeIndex;
+ break;
+
+ case 0:
+ if (!_lastButtonShape)
+ return;
+
+ t = _system->getMillis();
+ if (_buttonPressTimer > t)
+ delay(_buttonPressTimer - t);
+ // fall through
+ case 2:
+ mode = 0;
+ _lastButtonShape = 0;
+ break;
+
+ case 3:
+ mode = 0;
+ _lastButtonShape = 0;
+ pageNum = 6;
+
+ x2 = x1;
+ y2 = y1;
+ x1 = 0;
+ y1 = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ _screen->drawShape(pageNum, _gameShapes[shapeIndex], x1, y1, 0, mode, _screen->_paletteOverlay1, 1);
+
+ if (!pageNum)
+ _screen->updateScreen();
+
+ if (pageNum == 6) {
+ int cp = _screen->setCurPage(6);
+
+ _screen->drawGridBox(x1, y1, _gameShapes[shapeIndex][3], _gameShapes[shapeIndex][2], 1);
+ _screen->copyRegion(x1, y1, x2, y2, _gameShapes[shapeIndex][3], _gameShapes[shapeIndex][2], pageNum, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ _screen->setCurPage(cp);
+ }
+
+ _buttonPressTimer = _system->getMillis() + 6 * _tickLength;
+}
+
+void LoLEngine::gui_toggleFightButtons(bool disable) {
+ for (int i = 0; i < 3; i++) {
+ if (!(_characters[i].flags & 1))
+ continue;
+
+ if (disable)
+ _characters[i].flags |= 0x2000;
+ else
+ _characters[i].flags &= 0xDFFF;
+
+ if (disable && !textEnabled()) {
+ int u = _selectedCharacter;
+ _selectedCharacter = 99;
+ int f = _updateFlags;
+ _updateFlags &= 0xFFFD;
+
+ gui_drawCharPortraitWithStats(i);
+
+ _updateFlags = f;
+ _selectedCharacter = u;
+ } else {
+ gui_drawCharPortraitWithStats(i);
+ }
+ }
+}
+
+void LoLEngine::gui_updateInput() {
+ // TODO: We need to catch all cases where loading is not possible and
+ // set the "mainLoop" parameter to false for them.
+ int inputFlag = checkInput(_activeButtons, !((_updateFlags & 3) || _weaponsDisabled), 0);
+
+ if (_preserveEvents)
+ _preserveEvents = false;
+ else
+ removeInputTop();
+
+ if (inputFlag && _activeMagicMenu != -1 && !(inputFlag & 0x8800)) {
+ gui_enableDefaultPlayfieldButtons();
+ _characters[_activeMagicMenu].flags &= 0xFFEF;
+ gui_drawCharPortraitWithStats(_activeMagicMenu);
+ gui_triggerEvent(inputFlag);
+ _preserveEvents = false;
+ _activeMagicMenu = -1;
+ inputFlag = 0;
+ }
+
+ if (inputFlag == _keyMap[Common::KEYCODE_SPACE] || inputFlag == _keyMap[Common::KEYCODE_RETURN]) {
+ snd_stopSpeech(true);
+ } else if (inputFlag == _keyMap[Common::KEYCODE_SLASH]) {
+ if (_weaponsDisabled || _availableSpells[1] == -1)
+ return;
+
+ gui_highlightSelectedSpell(false);
+ if (_availableSpells[++_selectedSpell] == -1)
+ _selectedSpell = 0;
+ gui_highlightSelectedSpell(true);
+ gui_drawAllCharPortraitsWithStats();
+ }
+}
+
+void LoLEngine::gui_triggerEvent(int eventType) {
+ Common::Event evt;
+ memset(&evt, 0, sizeof(Common::Event));
+ evt.mouse.x = _mouseX;
+ evt.mouse.y = _mouseY;
+
+ if (eventType == 65 || eventType == 199) {
+ evt.type = Common::EVENT_LBUTTONDOWN;
+ } else if (eventType == 66 || eventType == 201) {
+ evt.type = Common::EVENT_RBUTTONDOWN;
+ } else {
+ evt.type = Common::EVENT_KEYDOWN;
+
+ for (KeyMap::const_iterator c = _keyMap.begin(); c != _keyMap.end(); ++c) {
+ if (c->_value == eventType)
+ evt.kbd.keycode = (Common::KeyCode)c->_key;
+ }
+ }
+
+ removeInputTop();
+ _eventList.push_back(Event(evt, true));
+ _preserveEvents = true;
+}
+
+void LoLEngine::gui_enableDefaultPlayfieldButtons() {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_buttonList1);
+ gui_setFaceFramesControlButtons(7, 44);
+ gui_setFaceFramesControlButtons(11, 44);
+ gui_setFaceFramesControlButtons(17, 0);
+ gui_setFaceFramesControlButtons(29, 0);
+ gui_setFaceFramesControlButtons(25, 33);
+
+ if (_flagsTable[31] & 0x20)
+ gui_initMagicScrollButtons();
+}
+
+void LoLEngine::gui_enableSequenceButtons(int x, int y, int w, int h, int enableFlags) {
+ gui_resetButtonList();
+
+ _sceneWindowButton.x = x;
+ _sceneWindowButton.y = y;
+ _sceneWindowButton.w = w;
+ _sceneWindowButton.h = h;
+
+ gui_initButtonsFromList(_buttonList3);
+
+ if (enableFlags & 1)
+ gui_initButtonsFromList(_buttonList4);
+
+ if (enableFlags & 2)
+ gui_initButtonsFromList(_buttonList5);
+}
+
+void LoLEngine::gui_specialSceneRestoreButtons() {
+ if (!_spsWindowW && !_spsWindowH)
+ return;
+
+ gui_enableDefaultPlayfieldButtons();
+ _spsWindowX = _spsWindowY = _spsWindowW = _spsWindowH = _seqTrigger = 0;
+}
+
+void LoLEngine::gui_enableCharInventoryButtons(int charNum) {
+ gui_resetButtonList();
+ gui_initButtonsFromList(_buttonList2);
+ gui_initCharInventorySpecialButtons(charNum);
+ gui_setFaceFramesControlButtons(21, 0);
+}
+
+void LoLEngine::gui_setFaceFramesControlButtons(int index, int xOffs) {
+ int c = countActiveCharacters();
+ for (int i = 0; i < c; i++)
+ gui_initButton(index + i, _activeCharsXpos[i] + xOffs);
+}
+
+void LoLEngine::gui_initCharInventorySpecialButtons(int charNum) {
+ const uint8 *s = &_charInvDefs[_charInvIndex[_characters[charNum].raceClassSex] * 22];
+
+ for (int i = 0; i < 11; i++) {
+ if (*s != 0xFF)
+ gui_initButton(33 + i, s[0], s[1], i);
+ s += 2;
+ }
+}
+
+void LoLEngine::gui_initMagicScrollButtons() {
+ for (int i = 0; i < 7; i++) {
+ if (_availableSpells[i] == -1)
+ continue;
+ gui_initButton(71 + i, -1, -1, i);
+ }
+}
+
+void LoLEngine::gui_initMagicSubmenu(int charNum) {
+ gui_resetButtonList();
+ _subMenuIndex = charNum;
+ gui_initButtonsFromList(_buttonList7);
+}
+
+void LoLEngine::gui_initButton(int index, int x, int y, int val) {
+ Button *b = 0;
+ int cnt = 1;
+
+ if (_activeButtons) {
+ Button *n = _activeButtons;
+ while (n->nextButton) {
+ ++cnt;
+ n = n->nextButton;
+ }
+
+ ++cnt;
+ b = n->nextButton = &_activeButtonData[cnt];
+ } else {
+ b = &_activeButtonData[0];
+ _activeButtons = b;
+ }
+
+ *b = Button();
+
+ b->nextButton = 0;
+ b->data0Val2 = b->data1Val2 = b->data2Val2 = 0xFE;
+ b->data0Val3 = b->data1Val3 = b->data2Val3 = 0x01;
+
+ b->index = cnt;
+ b->keyCode = _buttonData[index].keyCode;
+ b->keyCode2 = _buttonData[index].keyCode2;
+ b->dimTableIndex = _buttonData[index].screenDim;
+ b->flags = _buttonData[index].buttonflags;
+
+ b->arg = (val != -1) ? (uint8)(val & 0xFF) : _buttonData[index].index;
+
+ if (index == 15) {
+ // magic sub menu
+ b->x = _activeCharsXpos[_subMenuIndex] + 44;
+ b->arg = _subMenuIndex;
+ b->y = _buttonData[index].y;
+ b->width = _buttonData[index].w - 1;
+ b->height = _buttonData[index].h - 1;
+ } else if (index == 64) {
+ // scene window button
+ b->x = _sceneWindowButton.x;
+ b->y = _sceneWindowButton.y;
+ b->width = _sceneWindowButton.w - 1;
+ b->height = _sceneWindowButton.h - 1;
+ } else {
+ b->x = x != -1 ? x : _buttonData[index].x;
+ b->y = y != -1 ? y : _buttonData[index].y;
+ b->width = _buttonData[index].w - 1;
+ b->height = _buttonData[index].h - 1;
+ }
+
+ b->buttonCallback = _buttonCallbacks[index];
+}
+
+int LoLEngine::clickedUpArrow(Button *button) {
+ if (button->arg && !_floatingCursorsEnabled)
+ return 0;
+
+ moveParty(_currentDirection, ((button->flags2 & 0x1080) == 0x1080) ? 1 : 0, 0, _flags.isTalkie ? 80 : 78);
+
+ return 1;
+}
+
+int LoLEngine::clickedDownArrow(Button *button) {
+ if (button->arg && !_floatingCursorsEnabled)
+ return 0;
+
+ moveParty(_currentDirection ^ 2, 0, 1, _flags.isTalkie ? 83 : 81);
+
+ return 1;
+}
+
+int LoLEngine::clickedLeftArrow(Button *button) {
+ if (button->arg && !_floatingCursorsEnabled)
+ return 0;
+
+ moveParty((_currentDirection - 1) & 3, ((button->flags2 & 0x1080) == 0x1080) ? 1 : 0, 2, _flags.isTalkie ? 82 : 80);
+
+ return 1;
+}
+
+int LoLEngine::clickedRightArrow(Button *button) {
+ if (button->arg && !_floatingCursorsEnabled)
+ return 0;
+
+ moveParty((_currentDirection + 1) & 3, ((button->flags2 & 0x1080) == 0x1080) ? 1 : 0, 3, _flags.isTalkie ? 84 : 82);
+
+ return 1;
+}
+
+int LoLEngine::clickedTurnLeftArrow(Button *button) {
+ if (button->arg && !_floatingCursorsEnabled)
+ return 0;
+
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 79 : 77, 1);
+ _currentDirection = (_currentDirection - 1) & 3;
+
+ _sceneDefaultUpdate = 1;
+
+ runLevelScript(_currentBlock, 0x4000);
+ initTextFading(2, 0);
+
+ if (!_sceneDefaultUpdate)
+ gui_drawScene(0);
+ else
+ movePartySmoothScrollTurnLeft(1);
+
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 79 : 77, 0);
+ runLevelScript(_currentBlock, 0x10);
+ return 1;
+}
+
+int LoLEngine::clickedTurnRightArrow(Button *button) {
+ if (button->arg && !_floatingCursorsEnabled)
+ return 0;
+
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 81 : 79, 1);
+ _currentDirection = (_currentDirection + 1) & 3;
+
+ _sceneDefaultUpdate = 1;
+
+ runLevelScript(_currentBlock, 0x4000);
+ initTextFading(2, 0);
+
+ if (!_sceneDefaultUpdate)
+ gui_drawScene(0);
+ else
+ movePartySmoothScrollTurnRight(1);
+
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 81 : 79, 0);
+ runLevelScript(_currentBlock, 0x10);
+
+ return 1;
+}
+
+int LoLEngine::clickedAttackButton(Button *button) {
+ int c = button->arg;
+
+ if (_characters[c].flags & 0x314C)
+ return 1;
+
+ int bl = calcNewBlockPosition(_currentBlock, _currentDirection);
+
+ if (_levelBlockProperties[bl].flags & 0x10) {
+ breakIceWall(0, 0);
+ return 1;
+ }
+
+ uint16 target = getNearestMonsterFromCharacter(c);
+ int s = 0;
+
+ for (int i = 0; i < 4; i++) {
+ if (!_characters[c].items[i])
+ continue;
+
+ runItemScript(c, _characters[c].items[i], 0x400, target, s);
+ runLevelScriptCustom(_currentBlock, 0x400, c, _characters[c].items[i], target, s);
+ s -= 10;
+ }
+
+ if (!s) {
+ runItemScript(c, 0, 0x400, target, s);
+ runLevelScriptCustom(_currentBlock, 0x400, c, 0, target, s);
+ }
+
+ s = _characters[c].weaponHit ? 4 : calcMonsterSkillLevel(c, 8) + 4;
+
+ // check for Zephyr ring
+ if (itemEquipped(c, 230))
+ s >>= 1;
+
+ _characters[c].flags |= 4;
+ gui_highlightPortraitFrame(c);
+
+ setCharacterUpdateEvent(c, 1, s, 1);
+
+ return 1;
+}
+
+int LoLEngine::clickedMagicButton(Button *button) {
+ int c = button->arg;
+
+ if (_characters[c].flags & 0x314C)
+ return 1;
+
+ if (checkMagic(c, _availableSpells[_selectedSpell], 0))
+ return 1;
+
+ _characters[c].flags ^= 0x10;
+
+ gui_drawCharPortraitWithStats(c);
+ gui_initMagicSubmenu(c);
+ _activeMagicMenu = c;
+
+ return 1;
+}
+
+int LoLEngine::clickedMagicSubmenu(Button *button) {
+ int spellLevel = (_mouseY - 144) >> 3;
+ int c = button->arg;
+
+ gui_enableDefaultPlayfieldButtons();
+
+ if (checkMagic(c, _availableSpells[_selectedSpell], spellLevel)) {
+ _characters[c].flags &= 0xFFEF;
+ gui_drawCharPortraitWithStats(c);
+ } else {
+ _characters[c].flags |= 4;
+ _characters[c].flags &= 0xFFEF;
+
+ if (castSpell(c, _availableSpells[_selectedSpell], spellLevel)) {
+ setCharacterUpdateEvent(c, 1, 8, 1);
+ increaseExperience(c, 2, spellLevel * spellLevel);
+ } else {
+ _characters[c].flags &= 0xFFFB;
+ gui_drawCharPortraitWithStats(c);
+ }
+ }
+
+ _activeMagicMenu = -1;
+ return 1;
+}
+
+int LoLEngine::clickedScreen(Button *button) {
+ _characters[_activeMagicMenu].flags &= 0xFFEF;
+ gui_drawCharPortraitWithStats(_activeMagicMenu);
+ _activeMagicMenu = -1;
+
+ if (!(button->flags2 & 0x80)) {
+ if (button->flags2 & 0x100)
+ gui_triggerEvent(65);
+ else
+ gui_triggerEvent(66);
+ }
+
+ gui_enableDefaultPlayfieldButtons();
+
+ return 1;
+}
+
+int LoLEngine::clickedPortraitLeft(Button *button) {
+ disableSysTimer(2);
+
+ if (!_weaponsDisabled) {
+ _screen->copyRegionToBuffer(2, 0, 0, 320, 200, _pageBuffer2);
+ _screen->copyPage(0, 2);
+ _screen->copyRegionToBuffer(2, 0, 0, 320, 200, _pageBuffer1);
+ _updateFlags |= 0x0C;
+ gui_disableControls(1);
+ }
+
+ _selectedCharacter = button->arg;
+ _weaponsDisabled = true;
+
+ if (_flags.use16ColorMode)
+ _screen->fillRect(112, 0, 288, 120, 0, 2);
+
+ gui_displayCharInventory(_selectedCharacter);
+ gui_enableCharInventoryButtons(_selectedCharacter);
+
+ return 1;
+}
+
+int LoLEngine::clickedLiveMagicBarsLeft(Button *button) {
+ gui_highlightPortraitFrame(button->arg);
+ _txt->printMessage(0, getLangString(0x4047), _characters[button->arg].name, _characters[button->arg].hitPointsCur,
+ _characters[button->arg].hitPointsMax, _characters[button->arg].magicPointsCur, _characters[button->arg].magicPointsMax);
+ return 1;
+}
+
+int LoLEngine::clickedPortraitEtcRight(Button *button) {
+ if (!_itemInHand)
+ return 1;
+
+ int flg = _itemProperties[_itemsInPlay[_itemInHand].itemPropertyIndex].flags;
+ int c = button->arg;
+
+ if (flg & 1) {
+ if (!(_characters[c].flags & 8) || (flg & 0x20)) {
+ runItemScript(c, _itemInHand, 0x400, 0, 0);
+ runLevelScriptCustom(_currentBlock, 0x400, c, _itemInHand, 0, 0);
+ } else {
+ _txt->printMessage(2, getLangString(0x402C), _characters[c].name);
+ }
+ return 1;
+ }
+
+ _txt->printMessage(2, "%s", getLangString((flg & 8) ? 0x4029 : ((flg & 0x10) ? 0x402A : 0x402B)));
+ return 1;
+}
+
+int LoLEngine::clickedCharInventorySlot(Button *button) {
+ if (_itemInHand) {
+ uint16 sl = 1 << button->arg;
+ int type = _itemProperties[_itemsInPlay[_itemInHand].itemPropertyIndex].type;
+ if (!(sl & type)) {
+ bool f = false;
+
+ for (int i = 0; i < 11; i++) {
+ if (!(type & (1 << i)))
+ continue;
+
+ _txt->printMessage(0, getLangString(i > 3 ? 0x418A : 0x418B), getLangString(_itemProperties[_itemsInPlay[_itemInHand].itemPropertyIndex].nameStringId), getLangString(_inventorySlotDesc[i]));
+ f = true;
+ }
+
+ if (!f)
+ _txt->printMessage(_itemsInPlay[_itemInHand].itemPropertyIndex == 231 ? 2 : 0, "%s", getLangString(0x418C));
+
+ return 1;
+ }
+ } else {
+ if (!_characters[_selectedCharacter].items[button->arg]) {
+ _txt->printMessage(0, "%s", getLangString(_inventorySlotDesc[button->arg] + 8));
+ return 1;
+ }
+ }
+
+ int ih = _itemInHand;
+
+ setHandItem(_characters[_selectedCharacter].items[button->arg]);
+ _characters[_selectedCharacter].items[button->arg] = ih;
+ gui_drawCharInventoryItem(button->arg);
+
+ recalcCharacterStats(_selectedCharacter);
+
+ if (_itemInHand)
+ runItemScript(_selectedCharacter, _itemInHand, 0x100, 0, 0);
+ if (ih)
+ runItemScript(_selectedCharacter, ih, 0x80, 0, 0);
+
+ gui_drawCharInventoryItem(button->arg);
+ gui_drawCharPortraitWithStats(_selectedCharacter);
+ gui_changeCharacterStats(_selectedCharacter);
+
+ return 1;
+}
+
+int LoLEngine::clickedExitCharInventory(Button *button) {
+ _updateFlags &= 0xFFF3;
+ gui_enableDefaultPlayfieldButtons();
+ _weaponsDisabled = false;
+
+ for (int i = 0; i < 4; i++) {
+ if (_charInventoryUnk & (1 << i))
+ _characters[i].flags &= 0xF1FF;
+ }
+
+ _screen->copyBlockToPage(2, 0, 0, 320, 200, _pageBuffer1);
+
+ int cp = _screen->setCurPage(2);
+ gui_drawAllCharPortraitsWithStats();
+ gui_drawInventory();
+ _screen->setCurPage(cp);
+
+ _screen->copyPage(2, 0);
+ _screen->updateScreen();
+ gui_enableControls();
+ _screen->copyBlockToPage(2, 0, 0, 320, 200, _pageBuffer2);
+
+ _lastCharInventory = -1;
+ updateDrawPage2();
+ enableSysTimer(2);
+
+ return 1;
+}
+
+int LoLEngine::clickedSceneDropItem(Button *button) {
+ static const uint8 offsX[] = { 0x40, 0xC0, 0x40, 0xC0 };
+ static const uint8 offsY[] = { 0x40, 0x40, 0xC0, 0xC0 };
+
+ if ((_updateFlags & 1) || !_itemInHand)
+ return 0;
+
+ uint16 block = _currentBlock;
+ if (button->arg > 1) {
+ block = calcNewBlockPosition(_currentBlock, _currentDirection);
+ int f = _wllWallFlags[_levelBlockProperties[block].walls[_currentDirection ^ 2]];
+ if (!(f & 0x80) || (f & 2))
+ return 1;
+ }
+
+ uint16 x = 0;
+ uint16 y = 0;
+ int i = _dropItemDirIndex[(_currentDirection << 2) + button->arg];
+
+ calcCoordinates(x, y, block, offsX[i], offsY[i]);
+ setItemPosition(_itemInHand, x, y, 0, 1);
+ setHandItem(0);
+
+ return 1;
+}
+
+int LoLEngine::clickedScenePickupItem(Button *button) {
+ static const int8 checkX[] = { 0, 0, 1, 0, -1, -1, 1, 1, -1, 0, 2, 0, -2, -1, 1, 2, 2, 1, -1, -2, -2 };
+ static const int8 checkY[] = { 0, -1, 0, 1, 0, -1, -1, 1, 1, -2, 0, 2, 0, -2, -2, -1, 1, 2, 2, 1, -1 };
+ static const int len = ARRAYSIZE(checkX);
+
+ if ((_updateFlags & 1) || _itemInHand)
+ return 0;
+
+ int cp = _screen->setCurPage(_sceneDrawPage1);
+
+ redrawSceneItem();
+
+ int p = 0;
+ for (int i = 0; i < len; i++) {
+ p = _screen->getPagePixel(_screen->_curPage, CLIP(_mouseX + checkX[i], 0, 320), CLIP(_mouseY + checkY[i], 0, 200));
+ if (p)
+ break;
+ }
+
+ _screen->setCurPage(cp);
+
+ if (!p)
+ return 0;
+
+ uint16 block = (p <= 128) ? calcNewBlockPosition(_currentBlock, _currentDirection) : _currentBlock;
+
+ int found = checkSceneForItems(&_levelBlockProperties[block].drawObjects, p & 0x7F);
+
+ if (found != -1) {
+ removeLevelItem(found, block);
+ setHandItem(found);
+ }
+
+ _sceneUpdateRequired = true;
+
+ return 1;
+}
+
+int LoLEngine::clickedInventorySlot(Button *button) {
+ int slot = _inventoryCurItem + button->arg;
+ if (slot > 47)
+ slot -= 48;
+
+ uint16 slotItem = _inventory[slot];
+ int hItem = _itemInHand;
+
+ if ((_itemsInPlay[hItem].itemPropertyIndex == 281 || _itemsInPlay[slotItem].itemPropertyIndex == 281) &&
+ (_itemsInPlay[hItem].itemPropertyIndex == 220 || _itemsInPlay[slotItem].itemPropertyIndex == 220)) {
+ // merge ruby of truth
+
+ WSAMovie_v2 *wsa = new WSAMovie_v2(this);
+ wsa->open("truth.wsa", 0, 0);
+
+ _screen->hideMouse();
+
+ _inventory[slot] = 0;
+ gui_drawInventoryItem(button->arg);
+ _screen->copyRegion(button->x, button->y - 3, button->x, button->y - 3, 25, 27, 0, 2);
+ KyraEngine_v1::snd_playSoundEffect(99);
+
+ for (int i = 0; i < 25; i++) {
+ uint32 delayTimer = _system->getMillis() + 7 * _tickLength;
+ _screen->copyRegion(button->x, button->y - 3, 0, 0, 25, 27, 2, 2);
+ wsa->displayFrame(i, 2, 0, 0, 0x4000, 0, 0);
+ _screen->copyRegion(0, 0, button->x, button->y - 3, 25, 27, 2, 0);
+ _screen->updateScreen();
+ delayUntil(delayTimer);
+ }
+
+ _screen->showMouse();
+
+ wsa->close();
+ delete wsa;
+
+ deleteItem(slotItem);
+ deleteItem(hItem);
+
+ setHandItem(0);
+ _inventory[slot] = makeItem(280, 0, 0);
+ } else {
+ setHandItem(slotItem);
+ _inventory[slot] = hItem;
+ }
+
+ gui_drawInventoryItem(button->arg);
+
+ return 1;
+}
+
+int LoLEngine::clickedInventoryScroll(Button *button) {
+ int8 inc = (int8)button->arg;
+ int shp = (inc == 1) ? 75 : 74;
+ if (!_flags.isTalkie)
+ shp -= 2;
+
+ if (button->flags2 & 0x1000)
+ inc *= 9;
+
+ _inventoryCurItem += inc;
+
+ gui_toggleButtonDisplayMode(shp, 1);
+
+ if (_inventoryCurItem < 0)
+ _inventoryCurItem += 48;
+ if (_inventoryCurItem > 47)
+ _inventoryCurItem -= 48;
+
+ gui_drawInventory();
+ gui_toggleButtonDisplayMode(shp, 0);
+
+ return 1;
+}
+
+int LoLEngine::clickedWall(Button *button) {
+ int block = calcNewBlockPosition(_currentBlock, _currentDirection);
+ int dir = _currentDirection ^ 2;
+ uint8 type = _specialWallTypes[_levelBlockProperties[block].walls[dir]];
+
+ int res = 0;
+ switch (type) {
+ case 1:
+ res = clickedWallShape(block, dir);
+ break;
+
+ case 2:
+ res = clickedLeverOn(block, dir);
+ break;
+
+ case 3:
+ res = clickedLeverOff(block, dir);
+ break;
+
+ case 4:
+ res = clickedWallOnlyScript(block);
+ break;
+
+ case 5:
+ res = clickedDoorSwitch(block, dir);
+ break;
+
+ case 6:
+ res = clickedNiche(block, dir);
+ break;
+
+ default:
+ break;
+ }
+
+ return res;
+}
+
+int LoLEngine::clickedSequenceWindow(Button *button) {
+ runLevelScript(calcNewBlockPosition(_currentBlock, _currentDirection), 0x40);
+ if (!_seqTrigger || !posWithinRect(_mouseX, _mouseY, _seqWindowX1, _seqWindowY1, _seqWindowX2, _seqWindowY2)) {
+ _seqTrigger = 0;
+ removeInputTop();
+ }
+ return 1;
+}
+
+int LoLEngine::clickedScroll(Button *button) {
+ if (_selectedSpell == button->arg)
+ return 1;
+
+ gui_highlightSelectedSpell(false);
+ _selectedSpell = button->arg;
+ gui_highlightSelectedSpell(true);
+ gui_drawAllCharPortraitsWithStats();
+
+ return 1;
+}
+
+int LoLEngine::clickedSpellTargetCharacter(Button *button) {
+ int t = button->arg;
+ _txt->printMessage(0, "%s.\r", _characters[t].name);
+
+ if ((_spellProperties[_activeSpell.spell].flags & 0xFF) == 1) {
+ _activeSpell.target = t;
+ castHealOnSingleCharacter(&_activeSpell);
+ }
+
+ gui_enableDefaultPlayfieldButtons();
+ return 1;
+}
+
+int LoLEngine::clickedSpellTargetScene(Button *button) {
+ LoLCharacter *c = &_characters[_activeSpell.charNum];
+ _txt->printMessage(0, "%s", getLangString(0x4041));
+
+ c->magicPointsCur += _activeSpell.p->mpRequired[_activeSpell.level];
+ if (c->magicPointsCur > c->magicPointsMax)
+ c->magicPointsCur = c->magicPointsMax;
+
+ c->hitPointsCur += _activeSpell.p->hpRequired[_activeSpell.level];
+ if (c->hitPointsCur > c->hitPointsMax)
+ c->hitPointsCur = c->hitPointsMax;
+
+ gui_drawCharPortraitWithStats(_activeSpell.charNum);
+ gui_enableDefaultPlayfieldButtons();
+
+ return 1;
+}
+
+int LoLEngine::clickedSceneThrowItem(Button *button) {
+ if (_updateFlags & 1)
+ return 0;
+
+ uint16 block = calcNewBlockPosition(_currentBlock, _currentDirection);
+ if ((_wllWallFlags[_levelBlockProperties[block].walls[_currentDirection ^ 2]] & 2) || !_itemInHand)
+ return 0;
+
+ uint16 x = 0;
+ uint16 y = 0;
+ calcCoordinates(x, y, _currentBlock, 0x80, 0x80);
+
+ if (launchObject(0, _itemInHand, x, y, 12, _currentDirection << 1, 6, _selectedCharacter, 0x3F)) {
+ snd_playSoundEffect(18, -1);
+ setHandItem(0);
+ }
+
+ _sceneUpdateRequired = true;
+ return 1;
+}
+
+int LoLEngine::clickedOptions(Button *button) {
+ removeInputTop();
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 76 : 74, 1);
+
+ _updateFlags |= 4;
+
+ Button b;
+ b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE;
+ b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01;
+
+ if (_weaponsDisabled)
+ clickedExitCharInventory(&b);
+
+ initTextFading(0, 1);
+ stopPortraitSpeechAnim();
+ setLampMode(true);
+ setMouseCursorToIcon(0);
+ disableSysTimer(2);
+
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 76 : 74, 0);
+
+ bool speechWasEnabled = speechEnabled();
+ if (_flags.isTalkie && getVolume(kVolumeSpeech) == 2)
+ _configVoice |= (textEnabled() ? 2 : 1);
+
+ _gui->runMenu(_gui->_mainMenu);
+
+ _updateFlags &= 0xFFFB;
+ setMouseCursorToItemInHand();
+ resetLampStatus();
+ gui_enableDefaultPlayfieldButtons();
+ enableSysTimer(2);
+ updateDrawPage2();
+
+ gui_drawPlayField();
+
+ if (getVolume(kVolumeSpeech) == 2)
+ _configVoice &= (textEnabled() ? ~2 : ~1);
+
+ if (speechWasEnabled && !textEnabled() && !speechEnabled())
+ _configVoice = 0;
+
+ writeSettings();
+
+ return 1;
+}
+
+int LoLEngine::clickedRestParty(Button *button) {
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 77 : 75, 1);
+
+ Button b;
+ b.data0Val2 = b.data1Val2 = b.data2Val2 = 0xFE;
+ b.data0Val3 = b.data1Val3 = b.data2Val3 = 0x01;
+
+ if (_weaponsDisabled)
+ clickedExitCharInventory(&b);
+
+ int tHp = -1;
+ int tMp = -1;
+ int tHa = -1;
+ int needPoisoningFlags = 0;
+ int needHealingFlags = 0;
+ int needMagicGainFlags = 0;
+
+ for (int i = 0; i < 4; i++) {
+ LoLCharacter *c = &_characters[i];
+ if (!(c->flags & 1) || (c->flags & 8))
+ continue;
+
+ if (c->hitPointsMax > tHp)
+ tHp = c->hitPointsMax;
+
+ if (c->magicPointsMax > tMp)
+ tMp = c->magicPointsMax;
+
+ if (c->flags & 0x80) {
+ needPoisoningFlags |= (1 << i);
+ if (c->hitPointsCur > tHa)
+ tHa = c->hitPointsCur;
+ } else {
+ if (c->hitPointsCur < c->hitPointsMax)
+ needHealingFlags |= (1 << i);
+ }
+
+ if (c->magicPointsCur < c->magicPointsMax)
+ needMagicGainFlags |= (1 << i);
+
+ c->flags |= 0x1000;
+ }
+
+ removeInputTop();
+
+ if (needHealingFlags || needMagicGainFlags) {
+ _screen->fillRect(112, 0, 288, 120, _flags.use16ColorMode ? 0x44 : 1);
+ gui_drawAllCharPortraitsWithStats();
+
+ _txt->printMessage(0x8000, "%s", getLangString(0x4057));
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 77 : 75, 0);
+
+ int h = 600 / tHp;
+ if (h > 30)
+ h = 30;
+
+ int m = 600 / tMp;
+ if (m > 30)
+ m = 30;
+
+ int a = 600 / tHa;
+ if (a > 15)
+ a = 15;
+
+ uint32 delay1 = _system->getMillis() + h * _tickLength;
+ uint32 delay2 = _system->getMillis() + m * _tickLength;
+ uint32 delay3 = _system->getMillis() + a * _tickLength;
+
+ _partyAwake = false;
+ _updateFlags |= 1;
+
+ for (int i = 0, im = _smoothScrollModeNormal ? 32 : 16; i < im; i++) {
+ timerProcessMonsters(0);
+ timerProcessMonsters(1);
+ timerProcessDoors(0);
+ timerProcessFlyingObjects(0);
+
+ if (_partyAwake)
+ break;
+ }
+
+ resetBlockProperties();
+
+ do {
+ for (int i = 0, im = _smoothScrollModeNormal ? 8 : 4; i < im; i++) {
+ timerProcessMonsters(0);
+ timerProcessMonsters(1);
+ timerProcessDoors(0);
+ timerProcessFlyingObjects(0);
+
+ if (_partyAwake)
+ break;
+ }
+
+ int f = checkInput(0);
+ removeInputTop();
+
+ if (f & 0x800) {
+ gui_notifyButtonListChanged();
+ } else if (f) {
+ gui_triggerEvent(f);
+ break;
+ }
+
+ if (!_partyAwake) {
+ if (_system->getMillis() > delay3) {
+ for (int i = 0; i < 4; i++) {
+ if (!(needPoisoningFlags & (1 << i)))
+ continue;
+ inflictDamage(i, 1, 0x8000, 1, 0x80);
+ if (_characters[i].flags & 8)
+ needPoisoningFlags &= ~(1 << i);
+ }
+ delay3 = _system->getMillis() + a * _tickLength;
+ }
+
+ if (_system->getMillis() > delay1) {
+ for (int i = 0; i < 4; i++) {
+ if (!(needHealingFlags & (1 << i)))
+ continue;
+ increaseCharacterHitpoints(i, 1, false);
+ gui_drawCharPortraitWithStats(i);
+ if (_characters[i].hitPointsCur == _characters[i].hitPointsMax)
+ needHealingFlags &= ~(1 << i);
+ }
+ delay1 = _system->getMillis() + h * _tickLength;
+ }
+
+ if (_system->getMillis() > delay2) {
+ for (int i = 0; i < 4; i++) {
+ if (!(needMagicGainFlags & (1 << i)))
+ continue;
+ _characters[i].magicPointsCur++;
+ gui_drawCharPortraitWithStats(i);
+ if (_characters[i].magicPointsCur == _characters[i].magicPointsMax)
+ needMagicGainFlags &= ~(1 << i);
+ }
+ delay2 = _system->getMillis() + m * _tickLength;
+ }
+ _screen->updateScreen();
+ }
+
+ } while (!_partyAwake && (needHealingFlags || needMagicGainFlags));
+
+ for (int i = 0; i < 4; i++) {
+ int frm = 0;
+ int upd = 0;
+ bool setframe = true;
+
+ if (_characters[i].flags & 0x1000) {
+ _characters[i].flags &= 0xEFFF;
+
+ if (_partyAwake) {
+ if (_characters[i].damageSuffered) {
+ frm = 5;
+ snd_playSoundEffect(_characters[i].screamSfx, -1);
+ } else {
+ frm = 4;
+ }
+ upd = 6;
+ }
+
+ } else {
+ if (_characters[i].damageSuffered)
+ setframe = false;
+ else
+ frm = 4;
+ }
+
+ if (setframe)
+ setTemporaryFaceFrame(i, frm, upd, 1);
+ }
+
+ _updateFlags &= 0xFFFE;
+ _partyAwake = true;
+ updateDrawPage2();
+ gui_drawScene(0);
+ _txt->printMessage(0x8000, "%s", getLangString(0x4059));
+ _screen->fadeToPalette1(40);
+
+ } else {
+ for (int i = 0; i < 4; i++)
+ _characters[i].flags &= 0xEFFF;
+
+ if (needPoisoningFlags) {
+ setTemporaryFaceFrameForAllCharacters(0, 0, 0);
+ for (int i = 0; i < 4; i++) {
+ if (needPoisoningFlags & (1 << i))
+ setTemporaryFaceFrame(i, 3, 8, 0);
+ }
+ _txt->printMessage(0x8000, "%s", getLangString(0x405A));
+ gui_drawAllCharPortraitsWithStats();
+
+ } else {
+ setTemporaryFaceFrameForAllCharacters(2, 4, 1);
+ _txt->printMessage(0x8000, "%s", getLangString(0x4058));
+ }
+ gui_toggleButtonDisplayMode(_flags.isTalkie ? 77 : 75, 0);
+ }
+
+ return 1;
+}
+
+int LoLEngine::clickedMoneyBox(Button *button) {
+ _txt->printMessage(0, getLangString(_credits == 1 ? 0x402D : 0x402E), _credits);
+ return 1;
+}
+
+int LoLEngine::clickedCompass(Button *button) {
+ if (!(_flagsTable[31] & 0x40))
+ return 0;
+
+ if (_compassBroken) {
+ if (characterSays(0x425B, -1, true))
+ _txt->printMessage(4, "%s", getLangString(0x425B));
+ } else {
+ _txt->printMessage(0, "%s", getLangString(0x402F + _currentDirection));
+ }
+
+ return 1;
+}
+
+int LoLEngine::clickedAutomap(Button *button) {
+ if (!(_flagsTable[31] & 0x10))
+ return 0;
+
+ removeInputTop();
+ displayAutomap();
+
+ gui_drawPlayField();
+ setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
+ return 1;
+}
+
+int LoLEngine::clickedLamp(Button *button) {
+ if (!(_flagsTable[31] & 0x08))
+ return 0;
+
+ if (_itemsInPlay[_itemInHand].itemPropertyIndex == 248) {
+ if (_lampOilStatus >= 100) {
+ _txt->printMessage(0, "%s", getLangString(0x4061));
+ return 1;
+ }
+
+ _txt->printMessage(0, "%s", getLangString(0x4062));
+
+ deleteItem(_itemInHand);
+ snd_playSoundEffect(181, -1);
+ setHandItem(0);
+
+ _lampOilStatus += 100;
+
+ } else {
+ uint16 s = (_lampOilStatus >= 100) ? 0x4060 : ((!_lampOilStatus) ? 0x405C : (_lampOilStatus / 33) + 0x405D);
+ _txt->printMessage(0, getLangString(0x405B), getLangString(s));
+ }
+
+ if (_brightness)
+ setPaletteBrightness(_screen->getPalette(0), _brightness, _lampEffect);
+
+ return 1;
+}
+
+int LoLEngine::clickedStatusIcon(Button *button) {
+ int t = _mouseX - 220;
+ if (t < 0)
+ t = 0;
+
+ t /= 14;
+ if (t > 2)
+ t = 2;
+
+ uint16 str = _charStatusFlags[t] + 1;
+ if (str == 0 || str > 3)
+ return 1;
+
+ _txt->printMessage(0x8002, "%s", getLangString(str == 1 ? 0x424C : (str == 2 ? 0x424E : 0x424D)));
+ return 1;
+}
+
+GUI_LoL::GUI_LoL(LoLEngine *vm) : GUI_v1(vm), _vm(vm), _screen(vm->_screen) {
+ _scrollUpFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::scrollUp);
+ _scrollDownFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::scrollDown);
+
+ _redrawButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawButtonCallback);
+ _redrawShadedButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawShadedButtonCallback);
+
+ _specialProcessButton = _backUpButtonList = 0;
+ _flagsModifier = 0;
+ _sliderSfx = 11;
+}
+
+void GUI_LoL::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 = 0;
+ 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->arg;
+ 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->hideMouse();
+ _screen->drawShape(_screen->_curPage, dataPtr, x, y, button->dimTableIndex, 0x10);
+ _screen->showMouse();
+ break;
+
+ case 1:
+ _screen->hideMouse();
+ _screen->printText((const char *)dataPtr, x, y, val2, val3);
+ _screen->showMouse();
+ break;
+
+ case 3:
+ if (callback)
+ (*callback)(button);
+ break;
+
+ case 4:
+ _screen->hideMouse();
+ _screen->drawBox(x, y, x2, y2, val2);
+ _screen->showMouse();
+ break;
+
+ case 5:
+ _screen->hideMouse();
+ _screen->fillRect(x, y, x2, y2, val2, -1, true);
+ _screen->showMouse();
+ break;
+
+ default:
+ break;
+ }
+
+ _screen->updateScreen();
+}
+
+int GUI_LoL::processButtonList(Button *buttonList, uint16 inputFlag, int8 mouseWheel) {
+ if (!buttonList)
+ return inputFlag & 0x7FFF;
+
+ if (_backUpButtonList != buttonList || _vm->_buttonListChanged) {
+ _specialProcessButton = 0;
+
+ _flagsModifier = 0;
+ if (_vm->_mouseClick == 1)
+ _flagsModifier |= 0x200;
+ if (_vm->_mouseClick == 2)
+ _flagsModifier |= 0x2000;
+ _vm->_mouseClick = 0;
+
+ _backUpButtonList = buttonList;
+ _vm->_buttonListChanged = false;
+
+ while (buttonList) {
+ processButton(buttonList);
+ buttonList = buttonList->nextButton;
+ }
+ }
+
+ int mouseX = _vm->_mouseX;
+ int mouseY = _vm->_mouseY;
+
+ 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 = 0x100;
+ if (inFlags == 201 || inFlags == 202)
+ temp = 0x1000;
+
+ 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 = 0;
+ }
+
+ 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 = 0;
+ if (!progress || (buttonList->flags & 4))
+ buttonList->flags2 &= ~6;
+ }
+
+ if (!progress && buttonList == _specialProcessButton && !(buttonList->flags & 0x40))
+ _specialProcessButton = 0;
+
+ 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;
+}
+
+int GUI_LoL::redrawButtonCallback(Button *button) {
+ if (!_displayMenu || _vm->gameFlags().use16ColorMode)
+ return 0;
+
+ _screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 225);
+ return 0;
+}
+
+int GUI_LoL::redrawShadedButtonCallback(Button *button) {
+ if (!_displayMenu || _vm->gameFlags().use16ColorMode)
+ return 0;
+
+ _screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 223, 227);
+ return 0;
+}
+
+int GUI_LoL::runMenu(Menu &menu) {
+ _currentMenu = &menu;
+ _lastMenu = _currentMenu;
+ _newMenu = 0;
+ _displayMenu = true;
+ _menuResult = 1;
+ _savegameOffset = 0;
+ backupPage0();
+
+ const ScreenDim *d = _screen->getScreenDim(8);
+ uint32 textCursorTimer = 0;
+ uint8 textCursorStatus = 1;
+ Screen::FontId of = _screen->setFont(Screen::FID_9_FNT);
+ int wW = _screen->getCharWidth('W');
+ _screen->setFont(of);
+
+ int fW = (d->w << 3) - wW;
+ int fC = 0;
+
+ // LoL doesn't have default higlighted items. No item should be
+ // highlighted when entering a new menu.
+ // Instead, the respevtive struct entry is used to determine whether
+ // a menu has scroll buttons or slider bars.
+ uint8 hasSpecialButtons = 0;
+ _saveSlotsListUpdateNeeded = true;
+
+ while (_displayMenu) {
+ _vm->_mouseX = _vm->_mouseY = 0;
+
+ if (_currentMenu == &_loadMenu || _currentMenu == &_saveMenu || _currentMenu == &_deleteMenu) {
+ updateSaveSlotsList(_vm->_targetName);
+ setupSaveMenuSlots(*_currentMenu, 4);
+ }
+
+ hasSpecialButtons = _currentMenu->highlightedItem;
+ _currentMenu->highlightedItem = 255;
+
+ if (_currentMenu == &_gameOptions) {
+ char *s = (char *)_vm->_tempBuffer5120;
+ Common::strlcpy(s, _vm->getLangString(0x406F + _vm->_monsterDifficulty), 30);
+ _currentMenu->item[_vm->gameFlags().isTalkie ? 0 : 2].itemString = s;
+ s += (strlen(s) + 1);
+
+ Common::strlcpy(s, _vm->getLangString(_vm->_smoothScrollingEnabled ? 0x4068 : 0x4069), 30);
+ _currentMenu->item[_vm->gameFlags().isTalkie ? 1 : 3].itemString = s;
+ s += (strlen(s) + 1);
+
+ Common::strlcpy(s, _vm->getLangString(_vm->_floatingCursorsEnabled ? 0x4068 : 0x4069), 30);
+ _currentMenu->item[_vm->gameFlags().isTalkie ? 2 : 4].itemString = s;
+ s += (strlen(s) + 1);
+
+ if (_vm->gameFlags().isTalkie) {
+ Common::strlcpy(s, _vm->getLangString(0x42D6 + _vm->_lang), 30);
+ _currentMenu->item[3].itemString = s;
+ s += (strlen(s) + 1);
+
+ Common::strlcpy(s, _vm->getLangString(_vm->textEnabled() ? 0x4068 : 0x4069), 30);
+ _currentMenu->item[4].itemString = s;
+ s += (strlen(s) + 1);
+ } else {
+ Common::strlcpy(s, _vm->getLangString(_vm->_configMusic ? 0x4068 : 0x4069), 30);
+ _currentMenu->item[0].itemString = s;
+ s += (strlen(s) + 1);
+
+ Common::strlcpy(s, _vm->getLangString(_vm->_configSounds ? 0x4068 : 0x4069), 30);
+ _currentMenu->item[1].itemString = s;
+ s += (strlen(s) + 1);
+ }
+ }
+
+ if (hasSpecialButtons == 1) {
+ if (_savegameOffset == 0) {
+ _scrollUpButton.data0ShapePtr = _scrollUpButton.data1ShapePtr = _scrollUpButton.data2ShapePtr = 0;
+ } else {
+ _scrollUpButton.data0ShapePtr = _vm->_gameShapes[17];
+ _scrollUpButton.data1ShapePtr = _scrollUpButton.data2ShapePtr = _vm->_gameShapes[19];
+ }
+
+ int slotOffs = (_currentMenu == &_saveMenu) ? 1 : 0;
+
+ if (((uint)_savegameOffset == _saveSlots.size() - (4 - slotOffs)) || _saveSlots.size() < (uint)(5 - slotOffs)) {
+ _scrollDownButton.data0ShapePtr = _scrollDownButton.data1ShapePtr = _scrollDownButton.data2ShapePtr = 0;
+ } else {
+ _scrollDownButton.data0ShapePtr = _vm->_gameShapes[18];
+ _scrollDownButton.data1ShapePtr = _scrollDownButton.data2ShapePtr = _vm->_gameShapes[20];
+ }
+ }
+
+ for (uint i = 0; i < _currentMenu->numberOfItems; ++i) {
+ _menuButtons[i].data0Val1 = _menuButtons[i].data1Val1 = _menuButtons[i].data2Val1 = 4;
+ _menuButtons[i].data0Callback = _redrawShadedButtonFunctor;
+ _menuButtons[i].data1Callback = _menuButtons[i].data2Callback = _redrawButtonFunctor;
+ _menuButtons[i].flags = 0x4487;
+ _menuButtons[i].flags2 = 0;
+ }
+
+ initMenu(*_currentMenu);
+
+ if (_currentMenu == &_loadMenu || _currentMenu == &_deleteMenu) {
+ if (_saveSlots.begin() == _saveSlots.end())
+ // "no savegames to load" message
+ _screen->fprintString("%s", _currentMenu->x + _currentMenu->width / 2, _currentMenu->y + 42, 204, 0, 9, _vm->getLangString(0x4009));
+ }
+
+ if (hasSpecialButtons == 2) {
+ static const uint8 oX[] = { 0, 10, 124 };
+ static const uint8 oW[] = { 10, 114, 10 };
+
+ for (int i = 1; i < 4; ++i) {
+ int tX = _currentMenu->x + _currentMenu->item[i].x;
+ int tY = _currentMenu->y + _currentMenu->item[i].y;
+
+ for (int ii = 0; ii < 3; ++ii) {
+ Button *b = getButtonListData() + 1 + (i - 1) * 3 + ii;
+ b->nextButton = 0;
+ b->data0Val2 = b->data1Val2 = b->data2Val2 = 0xFE;
+ b->data0Val3 = b->data1Val3 = b->data2Val3 = 0x01;
+
+ b->index = ii;
+ b->keyCode = b->keyCode2 = 0;
+
+ b->x = tX + oX[ii];
+ b->y = tY;
+ b->width = oW[ii];
+ b->height = _currentMenu->item[i].height;
+
+ b->data0Val1 = b->data1Val1 = b->data2Val1 = 0;
+ b->flags = (ii == 1) ? 0x6606 : 0x4406;
+
+ b->dimTableIndex = 0;
+
+ b->buttonCallback = _currentMenu->item[i].callback;
+ b->arg = _currentMenu->item[i].itemId;
+
+ _menuButtonList = addButtonToList(_menuButtonList, b);
+
+ processButton(b);
+ updateButton(b);
+ }
+
+ _currentMenu->item[i].labelX = _currentMenu->item[i].x - 5;
+ _currentMenu->item[i].labelY = _currentMenu->item[i].y + 3;
+
+ printMenuText(getMenuItemLabel(_currentMenu->item[i]), _currentMenu->x + _currentMenu->item[i].labelX, _currentMenu->y + _currentMenu->item[i].labelY, _currentMenu->item[i].textColor, 0, 10);
+
+ int volume = _vm->getVolume((KyraEngine_v1::kVolumeEntry)(i - 1));
+ _screen->drawShape(_screen->_curPage, _vm->_gameShapes[85], tX , tY, 0, 0x10);
+ _screen->drawShape(_screen->_curPage, _vm->_gameShapes[87], tX + 2 + oX[1], tY, 0, 0x10);
+ _screen->drawShape(_screen->_curPage, _vm->_gameShapes[86], tX + oX[1] + volume, tY, 0, 0x10);
+ }
+
+ _screen->updateScreen();
+ }
+
+ if (_currentMenu == &_mainMenu && !_vm->gameFlags().use16ColorMode) {
+ Screen::FontId f = _screen->setFont(Screen::FID_6_FNT);
+ _screen->fprintString("%s", menu.x + 8, menu.y + menu.height - 12, 204, 0, 8, gScummVMVersion);
+ _screen->setFont(f);
+ _screen->updateScreen();
+ }
+
+ if (_currentMenu == &_savenameMenu) {
+ int mx = (d->sx << 3) - 1;
+ int my = d->sy - 1;
+ int mw = (d->w << 3) + 1;
+ int mh = d->h + 1;
+ if (_vm->gameFlags().use16ColorMode) {
+ _screen->drawShadedBox(mx, my, mx + mw + 1, my + mh + 1, 0xDD, 0xFF);
+ _screen->drawLine(true, mx + mw + 1, my, mh + 1, 0xCC);
+ _screen->drawLine(false, mx, my + mh + 1, mw + 2, 0xCC);
+ } else {
+ _screen->drawShadedBox(mx, my, mx + mw, my + mh, 227, 223);
+ }
+
+ int pg = _screen->setCurPage(0);
+ _vm->_txt->clearDim(8);
+ textCursorTimer = 0;
+ textCursorStatus = 0;
+
+ Screen::FontId f = _screen->setFont(Screen::FID_9_FNT);
+ fC = _screen->getTextWidth(_saveDescription);
+ while (fC >= fW) {
+ _saveDescription[strlen(_saveDescription) - 1] = 0;
+ fC = _screen->getTextWidth(_saveDescription);
+ }
+
+ _screen->fprintString("%s", (d->sx << 3), d->sy + 2, d->unk8, d->unkA, 0, _saveDescription);
+ f = _screen->setFont(f);
+ _screen->fillRect((d->sx << 3) + fC, d->sy, (d->sx << 3) + fC + wW, d->sy + d->h - (_vm->gameFlags().use16ColorMode ? 2 : 1), d->unk8, 0);
+ _screen->setCurPage(pg);
+ }
+
+ while (!_newMenu && _displayMenu) {
+ processHighlights(*_currentMenu);
+
+ if (_currentMenu == &_savenameMenu) {
+ if (textCursorTimer <= _vm->_system->getMillis()) {
+ Screen::FontId f = _screen->setFont(Screen::FID_9_FNT);
+ fC = _screen->getTextWidth(_saveDescription);
+ textCursorStatus ^= 1;
+ textCursorTimer = _vm->_system->getMillis() + 20 * _vm->_tickLength;
+ _screen->fillRect((d->sx << 3) + fC, d->sy, (d->sx << 3) + fC + wW, d->sy + d->h - (_vm->gameFlags().use16ColorMode ? 2 : 1), textCursorStatus ? d->unk8 : d->unkA, 0);
+ _screen->updateScreen();
+ f = _screen->setFont(f);
+ }
+ }
+
+ if (getInput()) {
+ if (!_newMenu) {
+ if (_currentMenu == &_savenameMenu) {
+ Screen::FontId f = _screen->setFont(Screen::FID_9_FNT);
+ _screen->fillRect((d->sx << 3) + fC, d->sy, (d->sx << 3) + fC + wW, d->sy + d->h - (_vm->gameFlags().use16ColorMode ? 2 : 1), d->unkA, 0);
+ fC = _screen->getTextWidth(_saveDescription);
+ while (fC >= fW) {
+ _saveDescription[strlen(_saveDescription) - 1] = 0;
+ fC = _screen->getTextWidth(_saveDescription);
+ }
+ _screen->fprintString("%s", (d->sx << 3), d->sy + 2, d->unk8, d->unkA, 0, _saveDescription);
+ _screen->fillRect((d->sx << 3) + fC, d->sy, (d->sx << 3) + fC + wW, d->sy + d->h - (_vm->gameFlags().use16ColorMode ? 2 : 1), textCursorStatus ? d->unk8 : d->unkA, 0);
+ f = _screen->setFont(f);
+ textCursorTimer = 0;
+ textCursorStatus = 0;
+ } else {
+ _newMenu = (_currentMenu != &_audioOptions) ? _currentMenu : 0;
+ }
+ } else {
+ _lastMenu = _menuResult == -1 ? _lastMenu : _currentMenu;
+ }
+ }
+
+ if (!_menuResult)
+ _displayMenu = false;
+ }
+
+ if (_newMenu != _currentMenu || !_displayMenu)
+ restorePage0();
+
+ _currentMenu->highlightedItem = hasSpecialButtons;
+
+ if (_newMenu)
+ _currentMenu = _newMenu;
+
+ _newMenu = 0;
+ }
+
+ if (_savegameList) {
+ for (int i = 0; i < _savegameListSize; i++)
+ delete[] _savegameList[i];
+ delete[] _savegameList;
+ _savegameList = 0;
+ }
+
+ return _menuResult;
+}
+
+void GUI_LoL::createScreenThumbnail(Graphics::Surface &dst) {
+ uint8 *screenPal = new uint8[768];
+ _screen->getRealPalette(1, screenPal);
+
+ if (_vm->gameFlags().platform == Common::kPlatformPC98) {
+ uint8 *screen = new uint8[Screen::SCREEN_W * Screen::SCREEN_H];
+ assert(screen);
+
+ _screen->copyRegionToBuffer(7, 0, 0, 320, 200, screen);
+ Screen_LoL::convertPC98Gfx(screen, Screen::SCREEN_W, Screen::SCREEN_H, Screen::SCREEN_W);
+ ::createThumbnail(&dst, screen, Screen::SCREEN_W, Screen::SCREEN_H, screenPal);
+ delete[] screen;
+ } else {
+ ::createThumbnail(&dst, _screen->getCPagePtr(7), Screen::SCREEN_W, Screen::SCREEN_H, screenPal);
+ }
+
+ delete[] screenPal;
+}
+
+void GUI_LoL::backupPage0() {
+ _screen->copyPage(0, 7);
+}
+
+void GUI_LoL::restorePage0() {
+ _screen->copyPage(7, 0);
+ _screen->updateScreen();
+}
+
+void GUI_LoL::setupSaveMenuSlots(Menu &menu, int num) {
+ char *s = (char *)_vm->_tempBuffer5120;
+
+ for (int i = 0; i < num; ++i) {
+ menu.item[i].saveSlot = -1;
+ menu.item[i].enabled = false;
+ }
+
+ int startSlot = 0;
+ int slotOffs = 0;
+
+ if (&menu == &_saveMenu) {
+ if (_savegameOffset == 0)
+ startSlot = 1;
+ slotOffs = 1;
+ }
+
+ int saveSlotMaxLen = ((_screen->getScreenDim(8))->w << 3) - _screen->getCharWidth('W');
+
+ for (int i = startSlot; i < num && _savegameOffset + i - slotOffs < _savegameListSize; ++i) {
+ if (_savegameList[i + _savegameOffset - slotOffs]) {
+ Common::strlcpy(s, _savegameList[i + _savegameOffset - slotOffs], 80);
+
+ // Trim long GMM save descriptions to fit our save slots
+ int fC = _screen->getTextWidth(s);
+ while (s[0] && fC >= saveSlotMaxLen) {
+ s[strlen(s) - 1] = 0;
+ fC = _screen->getTextWidth(s);
+ }
+
+ menu.item[i].itemString = s;
+ s += (strlen(s) + 1);
+ menu.item[i].saveSlot = _saveSlots[i + _savegameOffset - slotOffs];
+ menu.item[i].enabled = true;
+ }
+ }
+
+ if (_savegameOffset == 0) {
+ if (&menu == &_saveMenu) {
+ strcpy(s, _vm->getLangString(0x4010));
+ menu.item[0].itemString = s;
+ menu.item[0].saveSlot = -3;
+ menu.item[0].enabled = true;
+ }
+ }
+}
+
+void GUI_LoL::sortSaveSlots() {
+ Common::sort(_saveSlots.begin(), _saveSlots.end(), Common::Greater<int>());
+}
+
+void GUI_LoL::printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 flags) {
+ _screen->fprintString("%s", x, y, c0, c1, _vm->gameFlags().use16ColorMode ? (flags & 3) : flags , str);
+}
+
+int GUI_LoL::getMenuCenterStringX(const char *str, int x1, int x2) {
+ if (!str)
+ return 0;
+
+ int strWidth = _screen->getTextWidth(str);
+ int w = x2 - x1 + 1;
+ return x1 + (w - strWidth) / 2;
+}
+
+int GUI_LoL::getInput() {
+ if (!_displayMenu)
+ return 0;
+
+#ifdef ENABLE_KEYMAPPER
+ Common::Keymapper *const keymapper = _vm->getEventManager()->getKeymapper();
+ keymapper->pushKeymap(Common::kGlobalKeymapName);
+#endif
+
+ Common::Point p = _vm->getMousePos();
+ _vm->_mouseX = p.x;
+ _vm->_mouseY = p.y;
+
+ if (_currentMenu == &_savenameMenu) {
+ _vm->updateInput();
+
+ for (Common::List<LoLEngine::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
+ if (evt->event.type == Common::EVENT_KEYDOWN)
+ _keyPressed = evt->event.kbd;
+ }
+ }
+
+ int inputFlag = _vm->checkInput(_menuButtonList);
+
+ if (_currentMenu == &_savenameMenu && _keyPressed.ascii) {
+ char inputKey = _keyPressed.ascii;
+ Util::convertISOToDOS(inputKey);
+
+ if ((uint8)inputKey > 31 && (uint8)inputKey < (_vm->gameFlags().lang == Common::JA_JPN ? 128 : 226)) {
+ _saveDescription[strlen(_saveDescription) + 1] = 0;
+ _saveDescription[strlen(_saveDescription)] = inputKey;
+ inputFlag |= 0x8000;
+ } else if (_keyPressed.keycode == Common::KEYCODE_BACKSPACE && strlen(_saveDescription)) {
+ _saveDescription[strlen(_saveDescription) - 1] = 0;
+ inputFlag |= 0x8000;
+ }
+ }
+
+ _vm->removeInputTop();
+ _keyPressed.reset();
+
+ if (_vm->shouldQuit())
+ _displayMenu = false;
+
+ _vm->delay(8);
+
+#ifdef ENABLE_KEYMAPPER
+ keymapper->popKeymap(Common::kGlobalKeymapName);
+#endif
+
+ return inputFlag & 0x8000 ? 1 : 0;
+}
+
+int GUI_LoL::clickedMainMenu(Button *button) {
+ updateMenuButton(button);
+ switch (button->arg) {
+ case 0x4001:
+ _savegameOffset = 0;
+ _newMenu = &_loadMenu;
+ break;
+ case 0x4002:
+ _savegameOffset = 0;
+ _newMenu = &_saveMenu;
+ break;
+ case 0x4003:
+ _savegameOffset = 0;
+ _newMenu = &_deleteMenu;
+ break;
+ case 0x4004:
+ _newMenu = &_gameOptions;
+ break;
+ case 0x42D9:
+ _newMenu = &_audioOptions;
+ break;
+ case 0x4006:
+ _choiceMenu.menuNameId = 0x400A;
+ _newMenu = &_choiceMenu;
+ break;
+ case 0x4005:
+ _displayMenu = false;
+ break;
+ }
+ return 1;
+}
+
+int GUI_LoL::clickedLoadMenu(Button *button) {
+ updateMenuButton(button);
+
+ if (button->arg == 0x4011) {
+ if (_currentMenu != _lastMenu)
+ _newMenu = _lastMenu;
+ else
+ _menuResult = 0;
+ return 1;
+ }
+
+ int16 s = (int16)button->arg;
+ _vm->_gameToLoad = _loadMenu.item[-s - 2].saveSlot;
+ _displayMenu = false;
+
+ return 1;
+}
+
+int GUI_LoL::clickedSaveMenu(Button *button) {
+ updateMenuButton(button);
+
+ if (button->arg == 0x4011) {
+ _newMenu = &_mainMenu;
+ return 1;
+ }
+
+ _newMenu = &_savenameMenu;
+ int16 s = (int16)button->arg;
+ _menuResult = _saveMenu.item[-s - 2].saveSlot + 1;
+ _saveDescription = (char *)_vm->_tempBuffer5120 + 1000;
+ _saveDescription[0] = 0;
+ if (_saveMenu.item[-s - 2].saveSlot != -3)
+ strcpy(_saveDescription, _saveMenu.item[-s - 2].itemString);
+
+ return 1;
+}
+
+int GUI_LoL::clickedDeleteMenu(Button *button) {
+ updateMenuButton(button);
+
+ if (button->arg == 0x4011) {
+ _newMenu = &_mainMenu;
+ return 1;
+ }
+
+ _choiceMenu.menuNameId = 0x400B;
+ _newMenu = &_choiceMenu;
+ int16 s = (int16)button->arg;
+ _menuResult = _deleteMenu.item[-s - 2].saveSlot + 1;
+
+ return 1;
+}
+
+int GUI_LoL::clickedOptionsMenu(Button *button) {
+ updateMenuButton(button);
+
+ switch (button->arg) {
+ case 0xFFF9:
+ _vm->_configMusic ^= 1;
+ _vm->sound()->enableMusic(_vm->_configMusic);
+
+ if (_vm->_configMusic)
+ _vm->snd_playTrack(_vm->_curMusicTheme);
+ else
+ _vm->_sound->beginFadeOut();
+ break;
+ case 0xFFF8:
+ _vm->_configSounds ^= true;
+ _vm->sound()->enableSFX(_vm->_configSounds);
+ break;
+ case 0xFFF7:
+ _vm->_monsterDifficulty = (_vm->_monsterDifficulty + 1) % 3;
+ break;
+ case 0xFFF6:
+ _vm->_smoothScrollingEnabled ^= true;
+ break;
+ case 0xFFF5:
+ _vm->_floatingCursorsEnabled ^= true;
+ break;
+ case 0xFFF4:
+ _vm->_lang = (_vm->_lang + 1) % 3;
+ break;
+ case 0xFFF3:
+ _vm->_configVoice ^= 3;
+ break;
+ case 0x4072: {
+ Common::String filename;
+ filename = Common::String::format("LEVEL%02d.%s", _vm->_currentLevel, _vm->_languageExt[_vm->_lang]);
+ delete[] _vm->_levelLangFile;
+ _vm->_levelLangFile = _vm->resource()->fileData(filename.c_str(), 0);
+ filename = Common::String::format("LANDS.%s", _vm->_languageExt[_vm->_lang]);
+ delete[] _vm->_landsFile;
+ _vm->_landsFile = _vm->resource()->fileData(filename.c_str(), 0);
+ _newMenu = _lastMenu;
+ } break;
+ default:
+ // TODO: Is there anything we should do if we hit this case?
+ break;
+ }
+
+ return 1;
+}
+
+int GUI_LoL::clickedAudioMenu(Button *button) {
+ updateMenuButton(button);
+
+ if (button->arg == 0x4072) {
+ _newMenu = _lastMenu;
+ return 1;
+ }
+
+ int tX = button->x;
+ const int oldVolume = _vm->getVolume((KyraEngine_v1::kVolumeEntry)(button->arg - 3));
+ int newVolume = oldVolume;
+
+ if (button->index == 0) {
+ newVolume -= 10;
+ tX += 10;
+ } else if (button->index == 1) {
+ newVolume = _vm->_mouseX - (tX + 7);
+ } else if (button->index == 2) {
+ newVolume += 10;
+ tX -= 114;
+ }
+
+ newVolume = CLIP(newVolume, 2, 102);
+
+ if (newVolume == oldVolume) {
+ _screen->updateScreen();
+ return 0;
+ }
+
+ _screen->drawShape(0, _vm->_gameShapes[87], tX + oldVolume, button->y, 0, 0x10);
+ // Temporary HACK
+ const int volumeDrawX = _vm->convertVolumeFromMixer(_vm->convertVolumeToMixer(newVolume));
+ _screen->drawShape(0, _vm->_gameShapes[86], tX + volumeDrawX, button->y, 0, 0x10);
+ _screen->updateScreen();
+
+ _vm->snd_stopSpeech(0);
+
+ _vm->setVolume((KyraEngine_v1::kVolumeEntry)(button->arg - 3), newVolume);
+
+ if (newVolume) {
+ if (button->arg == 4) {
+ _vm->snd_playSoundEffect(_sliderSfx, -1);
+ int16 vocIndex = (int16)READ_LE_UINT16(&_vm->_ingameSoundIndex[_sliderSfx * 2]);
+ do {
+ ++_sliderSfx;
+ if (_sliderSfx < 47)
+ _sliderSfx++;
+ if (vocIndex == 199)
+ _sliderSfx = 11;
+ vocIndex = (int16)READ_LE_UINT16(&_vm->_ingameSoundIndex[_sliderSfx * 2]);
+ if (vocIndex == -1)
+ continue;
+ if (!scumm_stricmp(_vm->_ingameSoundList[vocIndex], "EMPTY"))
+ continue;
+ break;
+ } while (1);
+ } else if (button->arg == 5) {
+ _vm->_lastSpeechId = -1;
+ _vm->snd_playCharacterSpeech(0x42E0, 0, 0);
+ }
+ }
+
+ return 1;
+}
+
+int GUI_LoL::clickedDeathMenu(Button *button) {
+ updateMenuButton(button);
+ if (button->arg == _deathMenu.item[0].itemId) {
+ _vm->quitGame();
+ } else if (button->arg == _deathMenu.item[1].itemId) {
+ _newMenu = &_loadMenu;
+ }
+ return 1;
+}
+
+int GUI_LoL::clickedSavenameMenu(Button *button) {
+ updateMenuButton(button);
+ if (button->arg == _savenameMenu.item[0].itemId) {
+
+ Util::convertDOSToISO(_saveDescription);
+
+ int slot = _menuResult == -2 ? getNextSavegameSlot() : _menuResult - 1;
+ Graphics::Surface thumb;
+ createScreenThumbnail(thumb);
+ _vm->saveGameStateIntern(slot, _saveDescription, &thumb);
+ thumb.free();
+
+ _displayMenu = false;
+
+ } else if (button->arg == _savenameMenu.item[1].itemId) {
+ _newMenu = &_saveMenu;
+ }
+
+ return 1;
+}
+
+int GUI_LoL::clickedChoiceMenu(Button *button) {
+ updateMenuButton(button);
+ if (button->arg == _choiceMenu.item[0].itemId) {
+ if (_lastMenu == &_mainMenu) {
+ _vm->quitGame();
+ } else if (_lastMenu == &_deleteMenu) {
+ _vm->_saveFileMan->removeSavefile(_vm->getSavegameFilename(_menuResult - 1));
+ Common::Array<int>::iterator i = Common::find(_saveSlots.begin(), _saveSlots.end(), _menuResult - 1);
+ while (i != _saveSlots.begin()) {
+ --i;
+ // not rename quicksave slot filenames
+ if (*i >= 990)
+ break;
+ Common::String oldName = _vm->getSavegameFilename(*i);
+ Common::String newName = _vm->getSavegameFilename(*i - 1);
+ _vm->_saveFileMan->renameSavefile(oldName, newName);
+ }
+ _newMenu = &_mainMenu;
+ _saveSlotsListUpdateNeeded = true;
+ }
+ } else if (button->arg == _choiceMenu.item[1].itemId) {
+ _newMenu = &_mainMenu;
+ }
+ return 1;
+}
+
+int GUI_LoL::scrollUp(Button *button) {
+ if (!_scrollUpButton.data0ShapePtr)
+ return 0;
+
+ updateButton(button);
+ if (_savegameOffset > 0) {
+ _savegameOffset--;
+ _newMenu = _currentMenu;
+ _menuResult = -1;
+ }
+ return 1;
+}
+
+int GUI_LoL::scrollDown(Button *button) {
+ if (!_scrollDownButton.data0ShapePtr)
+ return 0;
+
+ updateButton(button);
+ if ((uint)_savegameOffset < _saveSlots.size() - (_currentMenu == &_saveMenu ? 3 : 4)) {
+ _savegameOffset++;
+ _newMenu = _currentMenu;
+ _menuResult = -1;
+ }
+ return 1;
+}
+
+const char *GUI_LoL::getMenuTitle(const Menu &menu) {
+ if (!menu.menuNameId)
+ return 0;
+ return _vm->getLangString(menu.menuNameId);
+}
+
+const char *GUI_LoL::getMenuItemTitle(const MenuItem &menuItem) {
+ if (menuItem.itemId & 0x8000 && menuItem.itemString)
+ return menuItem.itemString;
+ else if (menuItem.itemId & 0x8000 || !menuItem.itemId)
+ return 0;
+ return _vm->getLangString(menuItem.itemId);
+}
+
+const char *GUI_LoL::getMenuItemLabel(const MenuItem &menuItem) {
+ if (menuItem.labelId & 0x8000 && menuItem.labelString)
+ return menuItem.labelString;
+ else if (menuItem.labelId & 0x8000 || !menuItem.labelId)
+ return 0;
+ return _vm->getLangString(menuItem.labelId);
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_LOL
diff --git a/engines/kyra/gui/gui_lol.h b/engines/kyra/gui/gui_lol.h
new file mode 100644
index 0000000000..9bad5b1196
--- /dev/null
+++ b/engines/kyra/gui/gui_lol.h
@@ -0,0 +1,179 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 = 0; \
+ 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);
+ int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel);
+
+ int redrawShadedButtonCallback(Button *button);
+ int redrawButtonCallback(Button *button);
+
+ int runMenu(Menu &menu);
+
+ // utilities for thumbnail creation
+ void createScreenThumbnail(Graphics::Surface &dst);
+
+private:
+ void backupPage0();
+ void restorePage0();
+
+ void setupSaveMenuSlots(Menu &menu, int num);
+
+ void printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 flags);
+ int getMenuCenterStringX(const char *str, int x1, int x2);
+
+ 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() { return _menuButtons; }
+ Button *getScrollUpButton() { return &_scrollUpButton; }
+ Button *getScrollDownButton() { return &_scrollDownButton; }
+
+
+ Button::Callback getScrollUpButtonHandler() const { return _scrollUpFunctor; }
+ Button::Callback getScrollDownButtonHandler() const { return _scrollDownFunctor; }
+
+ uint8 defaultColor1() const { return 0xFE; }
+ uint8 defaultColor2() const { return 0x00; }
+
+ const char *getMenuTitle(const Menu &menu);
+ const char *getMenuItemTitle(const MenuItem &menuItem);
+ const char *getMenuItemLabel(const MenuItem &menuItem);
+
+ 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;
+
+ virtual void sortSaveSlots();
+};
+
+} // End of namespace Kyra
+
+#endif
+
+#endif // ENABLE_LOL
diff --git a/engines/kyra/gui/gui_mr.cpp b/engines/kyra/gui/gui_mr.cpp
new file mode 100644
index 0000000000..dc335f1126
--- /dev/null
+++ b/engines/kyra/gui/gui_mr.cpp
@@ -0,0 +1,1584 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/gui/gui_mr.h"
+#include "kyra/engine/kyra_mr.h"
+#include "kyra/text/text_mr.h"
+#include "kyra/resource/resource.h"
+#include "kyra/engine/timer.h"
+#include "kyra/sound/sound_digital.h"
+
+#include "common/system.h"
+
+#include "graphics/scaler.h"
+
+namespace Kyra {
+
+void KyraEngine_MR::loadButtonShapes() {
+ _res->exists("BUTTONS.SHP", true);
+ uint8 *data = _res->fileData("BUTTONS.SHP", 0);
+ assert(data);
+ for (int i = 0; i <= 10; ++i)
+ addShapeToPool(data, 0x1C7+i, i);
+ delete[] data;
+
+ Button::Callback callback1 = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::callbackButton1);
+ Button::Callback callback2 = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::callbackButton2);
+ Button::Callback callback3 = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::callbackButton3);
+
+ _gui->getScrollUpButton()->data0Callback = callback1;
+ _gui->getScrollUpButton()->data1Callback = callback2;
+ _gui->getScrollUpButton()->data2Callback = callback3;
+ _gui->getScrollDownButton()->data0Callback = callback1;
+ _gui->getScrollDownButton()->data1Callback = callback2;
+ _gui->getScrollDownButton()->data2Callback = callback3;
+
+ _mainButtonData[0].data0Callback = callback1;
+ _mainButtonData[0].data1Callback = callback2;
+ _mainButtonData[0].data2Callback = callback3;
+}
+
+int KyraEngine_MR::callbackButton1(Button *button) {
+ const uint8 *shapePtr = 0;
+ if (button->index == 1)
+ shapePtr = getShapePtr(0x1CD);
+ else if (button->index == 22)
+ shapePtr = getShapePtr(0x1C7);
+ else if (button->index == 23)
+ shapePtr = getShapePtr(0x1CA);
+
+ if (shapePtr)
+ _screen->drawShape(0, shapePtr, button->x, button->y, 0, 0, 0);
+
+ return 0;
+}
+
+int KyraEngine_MR::callbackButton2(Button *button) {
+ const uint8 *shapePtr = 0;
+ if (button->index == 1)
+ shapePtr = getShapePtr(0x1CE);
+ else if (button->index == 22)
+ shapePtr = getShapePtr(0x1C9);
+ else if (button->index == 23)
+ shapePtr = getShapePtr(0x1CC);
+
+ if (shapePtr)
+ _screen->drawShape(0, shapePtr, button->x, button->y, 0, 0, 0);
+
+ return 0;
+}
+
+int KyraEngine_MR::callbackButton3(Button *button) {
+ const uint8 *shapePtr = 0;
+ if (button->index == 1)
+ shapePtr = getShapePtr(0x1CE);
+ else if (button->index == 22)
+ shapePtr = getShapePtr(0x1C8);
+ else if (button->index == 23)
+ shapePtr = getShapePtr(0x1CB);
+
+ if (shapePtr)
+ _screen->drawShape(0, shapePtr, button->x, button->y, 0, 0, 0);
+
+ return 0;
+}
+
+void KyraEngine_MR::showMessage(const char *string, uint8 c0, uint8 c1) {
+ _shownMessage = string;
+
+ restoreCommandLine();
+ _restoreCommandLine = false;
+
+ if (string) {
+ int x = _text->getCenterStringX(string, 0, 320);
+ int pageBackUp = _screen->_curPage;
+ _screen->_curPage = 0;
+ _text->printText(string, x, _commandLineY, c0, c1, 0);
+ _screen->_curPage = pageBackUp;
+ _screen->updateScreen();
+ setCommandLineRestoreTimer(7);
+ }
+}
+
+void KyraEngine_MR::showMessageFromCCode(int string, uint8 c0, int) {
+ showMessage((const char *)getTableEntry(_cCodeFile, string), c0, 0xF0);
+}
+
+void KyraEngine_MR::updateItemCommand(Item item, int str, uint8 c0) {
+ char buffer[100];
+ char *src = (char *)getTableEntry(_itemFile, item);
+
+ while (*src != ' ')
+ ++src;
+ ++src;
+
+ *src = toupper(*src);
+
+ strcpy(buffer, src);
+ strcat(buffer, " ");
+ strcat(buffer, (const char *)getTableEntry(_cCodeFile, str));
+
+ showMessage(buffer, c0, 0xF0);
+}
+
+void KyraEngine_MR::updateCommandLine() {
+ if (_restoreCommandLine) {
+ restoreCommandLine();
+ _restoreCommandLine = false;
+ }
+}
+
+void KyraEngine_MR::restoreCommandLine() {
+ int y = _inventoryState ? 144 : 188;
+ _screen->copyBlockToPage(0, 0, y, 320, 12, _interfaceCommandLine);
+}
+
+void KyraEngine_MR::updateCLState() {
+ if (_inventoryState)
+ _commandLineY = 145;
+ else
+ _commandLineY = 189;
+}
+
+void KyraEngine_MR::showInventory() {
+ if (!_screen->isMouseVisible())
+ return;
+ if (queryGameFlag(3))
+ return;
+
+ _screen->copyBlockToPage(3, 0, 0, 320, 56, _interface);
+ drawMalcolmsMoodText();
+
+ _inventoryState = true;
+ updateCLState();
+
+ redrawInventory(30);
+ drawMalcolmsMoodPointer(-1, 30);
+ drawScore(30, 215, 191);
+
+ if (queryGameFlag(0x97))
+ drawJestersStaff(1, 30);
+
+ _screen->hideMouse();
+
+ if (_itemInHand < 0) {
+ _mouseState = -1;
+ _screen->setMouseCursor(0, 0, getShapePtr(0));
+ }
+
+ _screen->copyRegion(0, 188, 0, 0, 320, 12, 0, 2, Screen::CR_NO_P_CHECK);
+
+ if (_inventoryScrollSpeed == -1) {
+ uint32 endTime = _system->getMillis() + _tickLength * 15;
+ int times = 0;
+ while (_system->getMillis() < endTime) {
+ _screen->copyRegion(0, 188, 0, 0, 320, 12, 0, 2, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(0, 188, 0, 0, 320, 12, 0, 2, Screen::CR_NO_P_CHECK);
+ ++times;
+ }
+
+ times = MAX(times, 1);
+
+ int speed = 60 / times;
+ if (speed <= 1)
+ _inventoryScrollSpeed = 1;
+ else if (speed >= 8)
+ _inventoryScrollSpeed = 8;
+ else
+ _inventoryScrollSpeed = speed;
+ }
+
+ int height = 12;
+ int y = 188;
+ int times = 0;
+ uint32 waitTill = _system->getMillis() + _tickLength;
+
+ while (y > 144) {
+ _screen->copyRegion(0, 0, 0, y, 320, height, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ ++times;
+ if (_inventoryScrollSpeed == 1 && times == 3) {
+ while (waitTill > _system->getMillis())
+ _system->delayMillis(10);
+ times = 0;
+ waitTill = _system->getMillis() + _tickLength;
+ }
+
+ height += _inventoryScrollSpeed;
+ y -= _inventoryScrollSpeed;
+ }
+
+ _screen->copyRegion(0, 0, 0, 144, 320, 56, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ initMainButtonList(false);
+
+ restorePage3();
+ _screen->showMouse();
+}
+
+void KyraEngine_MR::hideInventory() {
+ if (queryGameFlag(3))
+ return;
+
+ _inventoryState = false;
+ updateCLState();
+ initMainButtonList(true);
+
+ _screen->copyBlockToPage(3, 0, 0, 320, 56, _interface);
+ _screen->hideMouse();
+
+ restorePage3();
+ flagAnimObjsForRefresh();
+ drawAnimObjects();
+ _screen->copyRegion(0, 144, 0, 0, 320, 56, 0, 2, Screen::CR_NO_P_CHECK);
+
+ if (_inventoryScrollSpeed == -1) {
+ uint32 endTime = _system->getMillis() + _tickLength * 15;
+ int times = 0;
+ while (_system->getMillis() < endTime) {
+ _screen->copyRegion(0, 144, 0, 0, 320, 12, 0, 2, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(0, 144, 0, 0, 320, 12, 0, 2, Screen::CR_NO_P_CHECK);
+ ++times;
+ }
+
+ times = MAX(times, 1);
+
+ int speed = 60 / times;
+ if (speed <= 1)
+ _inventoryScrollSpeed = 1;
+ else if (speed >= 8)
+ _inventoryScrollSpeed = 8;
+ else
+ _inventoryScrollSpeed = speed;
+ }
+
+ int y = 144;
+ int y2 = 144 + _inventoryScrollSpeed;
+ uint32 waitTill = _system->getMillis() + _tickLength;
+ int times = 0;
+
+ while (y2 < 188) {
+ _screen->copyRegion(0, 0, 0, y2, 320, 56, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(0, y, 0, y, 320, _inventoryScrollSpeed, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+
+ ++times;
+ if (_inventoryScrollSpeed == 1 && times == 3) {
+ while (waitTill > _system->getMillis())
+ _system->delayMillis(10);
+ times = 0;
+ waitTill = _system->getMillis() + _tickLength;
+ }
+
+ y += _inventoryScrollSpeed;
+ y2 += _inventoryScrollSpeed;
+ }
+
+ _screen->copyRegion(0, 0, 0, 188, 320, 56, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->copyRegion(0, y, 0, y, 320, 188-y, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->showMouse();
+}
+
+void KyraEngine_MR::drawMalcolmsMoodText() {
+ static const int stringId[] = { 0x32, 0x37, 0x3C };
+
+ if (queryGameFlag(0x219))
+ return;
+
+ const char *string = (const char *)getTableEntry(_cCodeFile, stringId[_malcolmsMood]);
+
+ Screen::FontId oldFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+
+ int width = _screen->getTextWidth(string);
+
+ _screen->_charWidth = 0;
+ _screen->setFont(oldFont);
+
+ int pageBackUp = _screen->_curPage;
+ const int x = 280 - (width / 2);
+ int y = 0;
+ if (_inventoryState) {
+ y = 189;
+ _screen->_curPage = 0;
+ } else {
+ y = 45;
+ _screen->_curPage = 2;
+ }
+
+ _screen->drawShape(_screen->_curPage, getShapePtr(432), 244, 189, 0, 0);
+ _text->printText(string, x, y+1, 0xFF, 0xF0, 0x00);
+ _screen->_curPage = pageBackUp;
+}
+
+void KyraEngine_MR::drawMalcolmsMoodPointer(int frame, int page) {
+ static const uint8 stateTable[] = {
+ 1, 6, 11
+ };
+
+ if (frame == -1)
+ frame = stateTable[_malcolmsMood];
+ if (queryGameFlag(0x219))
+ frame = 13;
+
+ if (page == 0) {
+ _invWsa->displayFrame(frame, 0, 0, 0, 0, 0, 0);
+ _screen->updateScreen();
+ } else if (page == 30) {
+ _invWsa->displayFrame(frame, 2, 0, -144, 0, 0, 0);
+ }
+
+ _invWsaFrame = frame;
+}
+
+void KyraEngine_MR::drawJestersStaff(int type, int page) {
+ int y = 155;
+ if (page == 30) {
+ page = 2;
+ y -= 144;
+ }
+
+ int shape = (type != 0) ? 454 : 453;
+ _screen->drawShape(page, getShapePtr(shape), 217, y, 0, 0);
+}
+
+void KyraEngine_MR::drawScore(int page, int x, int y) {
+ if (page == 30) {
+ page = 2;
+ y -= 144;
+ }
+
+ int shape1 = _score / 100;
+ int shape2 = (_score - shape1*100) / 10;
+ int shape3 = _score % 10;
+
+ _screen->drawShape(page, getShapePtr(shape1+433), x, y, 0, 0);
+ x += 8;
+ _screen->drawShape(page, getShapePtr(shape2+433), x, y, 0, 0);
+ x += 8;
+ _screen->drawShape(page, getShapePtr(shape3+433), x, y, 0, 0);
+}
+
+void KyraEngine_MR::drawScoreCounting(int oldScore, int newScore, int drawOld, const int x) {
+ int y = 189;
+ if (_inventoryState)
+ y -= 44;
+
+ int old100 = oldScore / 100;
+ int old010 = (oldScore - old100*100) / 10;
+ int old001 = oldScore % 10;
+
+ int new100 = newScore / 100;
+ int new010 = (newScore - new100*100) / 10;
+ int new001 = newScore % 10;
+
+ if (drawOld) {
+ _screen->drawShape(0, getShapePtr(old100+433), x + 0, y, 0, 0);
+ _screen->drawShape(0, getShapePtr(old010+433), x + 8, y, 0, 0);
+ _screen->drawShape(0, getShapePtr(old001+433), x + 16, y, 0, 0);
+ }
+
+ if (old100 != new100)
+ _screen->drawShape(0, getShapePtr(old100+443), x + 0, y, 0, 0);
+
+ if (old010 != new010)
+ _screen->drawShape(0, getShapePtr(old010+443), x + 8, y, 0, 0);
+
+ _screen->drawShape(0, getShapePtr(old001+443), x + 16, y, 0, 0);
+
+ _screen->updateScreen();
+
+ _screen->drawShape(0, getShapePtr(new100+433), x + 0, y, 0, 0);
+ _screen->drawShape(0, getShapePtr(new010+433), x + 8, y, 0, 0);
+ _screen->drawShape(0, getShapePtr(new001+433), x + 16, y, 0, 0);
+}
+
+int KyraEngine_MR::getScoreX(const char *str) {
+ Screen::FontId oldFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+
+ int width = _screen->getTextWidth(str);
+ int x = 160 + (width / 2) - 32;
+
+ _screen->setFont(oldFont);
+ _screen->_charWidth = 0;
+ return x;
+}
+
+void KyraEngine_MR::redrawInventory(int page) {
+ int yOffset = 0;
+
+ if (page == 30) {
+ page = 2;
+ yOffset = -144;
+ }
+
+ int pageBackUp = _screen->_curPage;
+ _screen->_curPage = page;
+
+ for (int i = 0; i < 10; ++i) {
+ clearInventorySlot(i, page);
+ if (_mainCharacter.inventory[i] != kItemNone) {
+ _screen->drawShape(page, getShapePtr(_mainCharacter.inventory[i]+248), _inventoryX[i], _inventoryY[i] + yOffset, 0, 0);
+ drawInventorySlot(page, _mainCharacter.inventory[i], i);
+ }
+ }
+
+ _screen->_curPage = pageBackUp;
+
+ if (page == 0 || page == 1)
+ _screen->updateScreen();
+}
+
+void KyraEngine_MR::clearInventorySlot(int slot, int page) {
+ int yOffset = 0;
+ if (page == 30) {
+ page = 2;
+ yOffset = -144;
+ }
+
+ _screen->drawShape(page, getShapePtr(slot+422), _inventoryX[slot], _inventoryY[slot] + yOffset, 0, 0);
+}
+
+void KyraEngine_MR::drawInventorySlot(int page, Item item, int slot) {
+ int yOffset = 0;
+ if (page == 30) {
+ page = 2;
+ yOffset = -144;
+ }
+
+ _screen->drawShape(page, getShapePtr(item+248), _inventoryX[slot], _inventoryY[slot] + yOffset, 0, 0);
+}
+
+int KyraEngine_MR::buttonInventory(Button *button) {
+ setNextIdleAnimTimer();
+ if (!_enableInventory || !_inventoryState || !_screen->isMouseVisible())
+ return 0;
+
+ const int slot = button->index - 5;
+ const Item slotItem = _mainCharacter.inventory[slot];
+ if (_itemInHand == kItemNone) {
+ if (slotItem == kItemNone)
+ return 0;
+
+ clearInventorySlot(slot, 0);
+ snd_playSoundEffect(0x0B, 0xC8);
+ setMouseCursor(slotItem);
+ updateItemCommand(slotItem, (_lang == 1) ? getItemCommandStringPickUp(slotItem) : 0, 0xFF);
+ _itemInHand = slotItem;
+ _mainCharacter.inventory[slot] = kItemNone;
+ } else if (_itemInHand == 27) {
+ if (_chatText)
+ return 0;
+ return buttonJesterStaff(&_mainButtonData[3]);
+ } else {
+ if (slotItem >= 0) {
+ if (itemInventoryMagic(_itemInHand, slot))
+ return 0;
+
+ snd_playSoundEffect(0x0B, 0xC8);
+
+ clearInventorySlot(slot, 0);
+ drawInventorySlot(0, _itemInHand, slot);
+ setMouseCursor(slotItem);
+ updateItemCommand(slotItem, (_lang == 1) ? getItemCommandStringPickUp(slotItem) : 0, 0xFF);
+ _mainCharacter.inventory[slot] = _itemInHand;
+ _itemInHand = slotItem;
+ } else {
+ snd_playSoundEffect(0x0C, 0xC8);
+ drawInventorySlot(0, _itemInHand, slot);
+ _screen->setMouseCursor(0, 0, getShapePtr(0));
+ updateItemCommand(_itemInHand, (_lang == 1) ? getItemCommandStringInv(_itemInHand) : 2, 0xFF);
+ _mainCharacter.inventory[slot] = _itemInHand;
+ _itemInHand = kItemNone;
+ }
+ }
+
+ return 0;
+}
+
+int KyraEngine_MR::buttonMoodChange(Button *button) {
+ if (queryGameFlag(0x219)) {
+ snd_playSoundEffect(0x0D, 0xC8);
+ return 0;
+ }
+
+ static const uint8 frameTable[] = { 1, 6, 11 };
+
+ if (_mouseX >= 245 && _mouseX <= 267 && _mouseY >= 159 && _mouseY <= 198)
+ _malcolmsMood = 0;
+ else if (_mouseX >= 268 && _mouseX <= 289 && _mouseY >= 159 && _mouseY <= 198)
+ _malcolmsMood = 1;
+ else if (_mouseX >= 290 && _mouseX <= 312 && _mouseY >= 159 && _mouseY <= 198)
+ _malcolmsMood = 2;
+
+ int direction = (_invWsaFrame > frameTable[_malcolmsMood]) ? -1 : 1;
+
+ if (_invWsaFrame != frameTable[_malcolmsMood]) {
+ _screen->hideMouse();
+ setGameFlag(3);
+
+ snd_playSoundEffect(0x2E, 0xC8);
+
+ while (_invWsaFrame != frameTable[_malcolmsMood]) {
+ uint32 endTime = _system->getMillis() + 2 * _tickLength;
+ _invWsaFrame += direction;
+
+ drawMalcolmsMoodPointer(_invWsaFrame, 0);
+ _screen->updateScreen();
+
+ while (endTime > _system->getMillis()) {
+ update();
+ _system->delayMillis(10);
+ }
+ }
+
+ resetGameFlag(3);
+ _screen->showMouse();
+
+ drawMalcolmsMoodText();
+ updateDlgIndex();
+
+ EMCData data;
+ EMCState state;
+ memset(&data, 0, sizeof(data));
+ memset(&state, 0, sizeof(state));
+
+ _res->exists("_ACTOR.EMC", true);
+ _emc->load("_ACTOR.EMC", &data, &_opcodes);
+ _emc->init(&state, &data);
+ _emc->start(&state, 1);
+
+ int vocHigh = _vocHigh;
+ _vocHigh = 200;
+ _useActorBuffer = true;
+
+ while (_emc->isValid(&state))
+ _emc->run(&state);
+
+ _useActorBuffer = false;
+ _vocHigh = vocHigh;
+ _emc->unload(&data);
+ }
+
+ return 0;
+}
+
+int KyraEngine_MR::buttonShowScore(Button *button) {
+ strcpy(_stringBuffer, (const char *)getTableEntry(_cCodeFile, 18));
+
+ char *buffer = _stringBuffer;
+
+ while (*buffer != '%')
+ ++buffer;
+
+ buffer[0] = (_score / 100) + '0';
+ buffer[1] = ((_score % 100) / 10) + '0';
+ buffer[2] = (_score % 10) + '0';
+
+ while (*buffer != '%')
+ ++buffer;
+
+ buffer[0] = (_scoreMax / 100) + '0';
+ buffer[1] = ((_scoreMax % 100) / 10) + '0';
+ buffer[2] = (_scoreMax % 10) + '0';
+
+ showMessage(_stringBuffer, 0xFF, 0xF0);
+ return 0;
+}
+
+int KyraEngine_MR::buttonJesterStaff(Button *button) {
+ makeCharFacingMouse();
+ if (_itemInHand == 27) {
+ removeHandItem();
+ snd_playSoundEffect(0x0C, 0xC8);
+ drawJestersStaff(1, 0);
+ updateItemCommand(27, 2, 0xFF);
+ setGameFlag(0x97);
+ } else if (_itemInHand == kItemNone) {
+ if (queryGameFlag(0x97)) {
+ snd_playSoundEffect(0x0B, 0xC8);
+ setHandItem(27);
+ drawJestersStaff(0, 0);
+ updateItemCommand(27, 0, 0xFF);
+ resetGameFlag(0x97);
+ } else {
+ if (queryGameFlag(0x2F))
+ objectChat((const char *)getTableEntry(_cCodeFile, 20), 0, 204, 20);
+ else
+ objectChat((const char *)getTableEntry(_cCodeFile, 25), 0, 204, 25);
+ }
+ } else {
+ objectChat((const char *)getTableEntry(_cCodeFile, 30), 0, 204, 30);
+ }
+ return 0;
+}
+
+void KyraEngine_MR::showAlbum() {
+ if (!_screen->isMouseVisible() || queryGameFlag(4) || _mouseState != -1)
+ return;
+
+ if (!loadLanguageFile("ALBUM.", _album.file))
+ error("Couldn't load ALBUM");
+
+ if (!queryGameFlag(0x8B))
+ _album.wsa->open("ALBMGNTH.WSA", 1, 0);
+ _album.backUpRect = new uint8[3100];
+ assert(_album.backUpRect);
+ _album.backUpPage = new uint8[64000];
+ assert(_album.backUpPage);
+ _album.nextPage = _album.curPage;
+
+ _screen->copyRegionToBuffer(0, 0, 0, 320, 200, _screenBuffer);
+ _screen->copyRegionToBuffer(4, 0, 0, 320, 200, _album.backUpPage);
+
+ _screen->copyPalette(1, 0);
+ _screen->fadeToBlack(9);
+
+ int itemInHand = _itemInHand;
+ removeHandItem();
+
+ _screen->loadPalette("ALBUM.COL", _screen->getPalette(0));
+ loadAlbumPage();
+ loadAlbumPageWSA();
+
+ if (_album.leftPage.wsa->opened())
+ _album.leftPage.wsa->displayFrame(_album.leftPage.curFrame, 2, _albumWSAX[_album.nextPage+0], _albumWSAY[_album.nextPage+0], 0x4000, 0, 0);
+
+ if (_album.rightPage.wsa->opened())
+ _album.rightPage.wsa->displayFrame(_album.rightPage.curFrame, 2, _albumWSAX[_album.nextPage+1], _albumWSAY[_album.nextPage+1], 0x4000, 0, 0);
+
+ printAlbumPageText();
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ _screen->fadePalette(_screen->getPalette(0), 9);
+
+ processAlbum();
+
+ _screen->fadeToBlack(9);
+ _album.wsa->close();
+
+ setHandItem(itemInHand);
+ updateMouse();
+
+ restorePage3();
+ _screen->copyBlockToPage(0, 0, 0, 320, 200, _screenBuffer);
+ _screen->copyBlockToPage(4, 0, 0, 320, 200, _album.backUpPage);
+
+ _screen->copyPalette(0, 1);
+ _screen->fadePalette(_screen->getPalette(0), 9);
+
+ delete[] _album.backUpRect;
+ _album.backUpRect = 0;
+ delete[] _album.backUpPage;
+ _album.backUpPage = 0;
+ delete[] _album.file;
+ _album.file = 0;
+
+ _eventList.clear();
+}
+
+void KyraEngine_MR::loadAlbumPage() {
+ Common::String filename;
+ int num = _album.curPage / 2;
+
+ if (num == 0) {
+ filename = "ALBUM0.CPS";
+ } else if (num >= 1 && num <= 6) {
+ --num;
+ num %= 2;
+ filename = Common::String::format("ALBUM%d.CPS", num+1);
+ } else {
+ filename = "ALBUM3.CPS";
+ }
+
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 4, Screen::CR_NO_P_CHECK);
+ _screen->loadBitmap(filename.c_str(), 3, 3, 0);
+}
+
+void KyraEngine_MR::loadAlbumPageWSA() {
+ Common::String filename;
+
+ _album.leftPage.curFrame = 0;
+ _album.leftPage.maxFrame = 0;
+ _album.leftPage.wsa->close();
+
+ _album.rightPage.curFrame = 0;
+ _album.rightPage.maxFrame = 0;
+ _album.rightPage.wsa->close();
+
+ if (_album.curPage) {
+ filename = Common::String::format("PAGE%x.WSA", _album.curPage);
+ _album.leftPage.wsa->open(filename.c_str(), 1, 0);
+ _album.leftPage.maxFrame = _album.leftPage.wsa->frames()-1;
+ }
+
+ if (_album.curPage != 14) {
+ filename = Common::String::format("PAGE%x.WSA", _album.curPage+1);
+ _album.rightPage.wsa->open(filename.c_str(), 1, 0);
+ _album.rightPage.maxFrame = _album.rightPage.wsa->frames()-1;
+ }
+}
+
+void KyraEngine_MR::printAlbumPageText() {
+ static const uint8 posY[] = {
+ 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x5A, 0x5A,
+ 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x3C
+ };
+
+ const int leftY = posY[_album.curPage];
+ const int rightY = posY[_album.curPage+1];
+
+ for (int i = 0; i < 5; ++i) {
+ const char *str = (const char *)getTableEntry(_album.file, _album.curPage*5+i);
+ int y = i * 10 + leftY + 20;
+ printAlbumText(2, str, 20, y, 10);
+ }
+
+ for (int i = 0; i < 5; ++i) {
+ const char *str = (const char *)getTableEntry(_album.file, (_album.curPage+1)*5+i);
+ int y = i * 10 + rightY + 20;
+ printAlbumText(2, str, 176, y, 10);
+ }
+
+ albumBackUpRect();
+}
+
+void KyraEngine_MR::printAlbumText(int page, const char *str, int x, int y, uint8 c0) {
+ int oldPage = _screen->_curPage;
+ _screen->_curPage = page;
+
+ static const uint8 colorMap[] = { 0, 0x87, 0xA3, 0 };
+ _screen->setTextColor(colorMap, 0, 3);
+
+ Screen::FontId oldFont = _screen->setFont(Screen::FID_BOOKFONT_FNT);
+ _screen->_charWidth = -2;
+
+ _screen->printText(str, x, y, c0, 0);
+
+ _screen->_charWidth = 0;
+ _screen->setFont(oldFont);
+ _screen->_curPage = oldPage;
+}
+
+void KyraEngine_MR::processAlbum() {
+ Button albumButtons[5];
+
+ GUI_V2_BUTTON(albumButtons[0], 36, 0, 0, 1, 1, 1, 0x4487, 0, 130, 190, 10, 10, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF0, 0);
+ albumButtons[0].buttonCallback = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::albumPrevPage);
+ GUI_V2_BUTTON(albumButtons[1], 37, 0, 0, 1, 1, 1, 0x4487, 0, 177, 190, 10, 10, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF0, 0);
+ albumButtons[1].buttonCallback = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::albumNextPage);
+ GUI_V2_BUTTON(albumButtons[2], 38, 0, 0, 1, 1, 1, 0x4487, 0, 0, 0, 320, 8, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF0, 0);
+ albumButtons[2].buttonCallback = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::albumClose);
+ GUI_V2_BUTTON(albumButtons[3], 39, 0, 0, 1, 1, 1, 0x4487, 0, 8, 8, 144, 180, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF0, 0);
+ albumButtons[3].buttonCallback = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::albumPrevPage);
+ GUI_V2_BUTTON(albumButtons[4], 40, 0, 0, 1, 1, 1, 0x4487, 0, 170, 8, 142, 180, 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF0, 0);
+ albumButtons[4].buttonCallback = BUTTON_FUNCTOR(KyraEngine_MR, this, &KyraEngine_MR::albumNextPage);
+
+ Button *buttonList = 0;
+ for (int i = 0; i < 5; ++i)
+ buttonList = _gui->addButtonToList(buttonList, &albumButtons[i]);
+
+ _album.leftPage.timer = _album.rightPage.timer = _system->getMillis();
+ albumNewPage();
+ _album.running = true;
+
+ while (_album.running && !shouldQuit()) {
+ updateInput();
+ checkInput(buttonList);
+ removeInputTop();
+
+ if (_album.curPage != _album.nextPage) {
+ int oldPage = _album.curPage;
+ _album.curPage = _album.nextPage;
+
+ _album.leftPage.wsa->close();
+ _album.rightPage.wsa->close();
+
+ loadAlbumPage();
+ loadAlbumPageWSA();
+
+ if (_album.leftPage.wsa->opened())
+ _album.leftPage.wsa->displayFrame(_album.leftPage.curFrame, 2, _albumWSAX[_album.nextPage+0], _albumWSAY[_album.nextPage+0], 0x4000, 0, 0);
+
+ if (_album.rightPage.wsa->opened())
+ _album.rightPage.wsa->displayFrame(_album.rightPage.curFrame, 2, _albumWSAX[_album.nextPage+1], _albumWSAY[_album.nextPage+1], 0x4000, 0, 0);
+
+ printAlbumPageText();
+
+ snd_playSoundEffect(0x85, 0x80);
+ albumSwitchPages(oldPage, _album.nextPage, 4);
+
+ _album.leftPage.timer = _album.rightPage.timer = 0;
+ albumNewPage();
+
+ _eventList.clear();
+ }
+
+ albumUpdateAnims();
+ _system->delayMillis(10);
+ }
+
+ _album.leftPage.wsa->close();
+ _album.rightPage.wsa->close();
+}
+
+void KyraEngine_MR::albumNewPage() {
+ int page = _album.nextPage / 2;
+ if (!queryGameFlag(0x84+page)) {
+ albumAnim1();
+ delayWithTicks(8);
+
+ int id = _album.curPage / 2 + 100;
+ albumChat((const char *)getTableEntry(_album.file, id), 205, id);
+
+ if (id == 107) {
+ _screen->copyRegion(76, 100, 76, 100, 244, 100, 2, 0, Screen::CR_NO_P_CHECK);
+ albumChat((const char *)getTableEntry(_album.file, 108), 205, 108);
+ _screen->copyRegion(76, 100, 76, 100, 244, 100, 2, 0, Screen::CR_NO_P_CHECK);
+ albumChat((const char *)getTableEntry(_album.file, 109), 205, 109);
+ }
+
+ delayWithTicks(5);
+ albumAnim2();
+
+ setGameFlag(0x84+page);
+ _album.isPage14 = (_album.nextPage == 14);
+ }
+}
+
+void KyraEngine_MR::albumUpdateAnims() {
+ if (_album.nextPage == 14 && !_album.isPage14)
+ return;
+
+ uint32 nextRun = 0;
+
+ nextRun = _album.leftPage.timer + 5 * _tickLength;
+ if (nextRun < _system->getMillis() && _album.leftPage.wsa->opened()) {
+ _album.leftPage.wsa->displayFrame(_album.leftPage.curFrame, 2, _albumWSAX[_album.nextPage+0], _albumWSAY[_album.nextPage+0], 0x4000, 0, 0);
+ _screen->copyRegion(40, 17, 40, 17, 87, 73, 2, 0, Screen::CR_NO_P_CHECK);
+
+ ++_album.leftPage.curFrame;
+ _album.leftPage.timer = _system->getMillis();
+
+ if (_album.leftPage.curFrame > _album.leftPage.maxFrame) {
+ _album.leftPage.curFrame = 0;
+ if (_album.nextPage == 14) {
+ _album.isPage14 = false;
+ _album.leftPage.timer += 100000 * _tickLength;
+ } else {
+ _album.leftPage.timer += 180 * _tickLength;
+ }
+ }
+ }
+
+ nextRun = _album.rightPage.timer + 5 * _tickLength;
+ if (nextRun < _system->getMillis() && _album.rightPage.wsa->opened()) {
+ _album.rightPage.wsa->displayFrame(_album.rightPage.curFrame, 2, _albumWSAX[_album.nextPage+1], _albumWSAY[_album.nextPage+1], 0x4000, 0, 0);
+ _screen->copyRegion(194, 20, 194, 20, 85, 69, 2, 0, Screen::CR_NO_P_CHECK);
+
+ ++_album.rightPage.curFrame;
+ _album.rightPage.timer = _system->getMillis();
+
+ if (_album.rightPage.curFrame > _album.rightPage.maxFrame) {
+ _album.rightPage.curFrame = 0;
+ _album.rightPage.timer += 180 * _tickLength;
+ }
+ }
+
+ _screen->updateScreen();
+}
+
+void KyraEngine_MR::albumAnim1() {
+ for (int i = 6; i >= 3; --i) {
+ albumRestoreRect();
+ _album.wsa->displayFrame(i, 2, -100, 90, 0x4000, 0, 0);
+ albumUpdateRect();
+ delayWithTicks(1);
+ }
+
+ albumRestoreRect();
+ _album.wsa->displayFrame(14, 2, -100, 90, 0x4000, 0, 0);
+ albumUpdateRect();
+ delayWithTicks(1);
+}
+
+void KyraEngine_MR::albumAnim2() {
+ for (int i = 3; i <= 6; ++i) {
+ albumRestoreRect();
+ _album.wsa->displayFrame(i, 2, -100, 90, 0x4000, 0, 0);
+ albumUpdateRect();
+ delayWithTicks(1);
+ }
+
+ albumRestoreRect();
+ _screen->copyRegion(0, 100, 0, 100, 320, 100, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+}
+
+void KyraEngine_MR::albumBackUpRect() {
+ _screen->copyRegionToBuffer(2, 0, 146, 62, 50, _album.backUpRect);
+}
+
+void KyraEngine_MR::albumRestoreRect() {
+ _screen->copyBlockToPage(2, 0, 146, 62, 50, _album.backUpRect);
+}
+
+void KyraEngine_MR::albumUpdateRect() {
+ _screen->copyRegion(0, 146, 0, 146, 62, 50, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+}
+
+void KyraEngine_MR::albumSwitchPages(int oldPage, int newPage, int srcPage) {
+ if (newPage > oldPage) {
+ _screen->wsaFrameAnimationStep(0xA0, 0x07, 0xA0, 0x07, 0x96, 0xBA, 0x64, 0xBA, srcPage, 0, 2);
+
+ _screen->copyRegion(260, 7, 260, 7, 50, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->wsaFrameAnimationStep(0xA0, 0x07, 0xA0, 0x07, 0x96, 0xBA, 0x32, 0xBA, srcPage, 0, 2);
+
+ _screen->copyRegion(210, 7, 210, 7, 50, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->copyRegion(160, 7, 160, 7, 50, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->wsaFrameAnimationStep(0x10, 0x07, 0x6E, 0x07, 0x96, 0xBA, 0x32, 0xBA, 2, 0, 2);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->wsaFrameAnimationStep(0x10, 0x07, 0x3C, 0x07, 0x96, 0xBA, 0x64, 0xBA, 2, 0, 2);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->copyRegion(10, 7, 10, 7, 150, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ } else {
+ _screen->wsaFrameAnimationStep(0x0A, 0x07, 0x3C, 0x07, 0x96, 0xBA, 0x64, 0xBA, srcPage, 0, 2);
+
+ _screen->copyRegion(10, 7, 10, 7, 50, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->wsaFrameAnimationStep(0x0A, 0x07, 0x6E, 0x07, 0x96, 0xBA, 0x32, 0xBA, srcPage, 0, 2);
+
+ _screen->copyRegion(60, 7, 60, 7, 50, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->copyRegion(110, 7, 110, 7, 50, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->wsaFrameAnimationStep(0xA0, 0x07, 0xA0, 0x07, 0x96, 0xBA, 0x32, 0xBA, 2, 0, 2);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->wsaFrameAnimationStep(0xA0, 0x07, 0xA0, 0x07, 0x96, 0xBA, 0x64, 0xBA, 2, 0, 2);
+ _screen->updateScreen();
+ delayWithTicks(2);
+
+ _screen->copyRegion(160, 7, 160, 7, 150, 186, 2, 0, Screen::CR_NO_P_CHECK);
+ _screen->updateScreen();
+ }
+}
+
+int KyraEngine_MR::albumNextPage(Button *caller) {
+ _album.nextPage = _album.curPage + 2;
+ if (_album.nextPage >= 16) {
+ _album.nextPage -= 2;
+ _album.running = false;
+ }
+ return 0;
+}
+
+int KyraEngine_MR::albumPrevPage(Button *caller) {
+ _album.nextPage = _album.curPage - 2;
+ if (_album.nextPage < 0) {
+ _album.nextPage = 0;
+ _album.running = false;
+ }
+ return 0;
+}
+
+int KyraEngine_MR::albumClose(Button *caller) {
+ _album.running = false;
+ return 0;
+}
+
+#pragma mark -
+
+GUI_MR::GUI_MR(KyraEngine_MR *vm) : GUI_v2(vm), _vm(vm), _screen(vm->_screen) {
+}
+
+void GUI_MR::createScreenThumbnail(Graphics::Surface &dst) {
+ uint8 screenPal[768];
+ _screen->getRealPalette(0, screenPal);
+ ::createThumbnail(&dst, _vm->_screenBuffer, Screen::SCREEN_W, Screen::SCREEN_H, screenPal);
+}
+
+void GUI_MR::flagButtonEnable(Button *button) {
+ if (!button)
+ return;
+
+ if (button->flags & 8) {
+ button->flags &= ~8;
+ processButton(button);
+ }
+}
+
+void GUI_MR::flagButtonDisable(Button *button) {
+ if (!button)
+ return;
+
+ if (!(button->flags & 8)) {
+ button->flags |= 8;
+ processButton(button);
+ }
+}
+
+const char *GUI_MR::getMenuTitle(const Menu &menu) {
+ if (!menu.menuNameId)
+ return 0;
+
+ return (const char *)_vm->getTableEntry(_vm->_optionsFile, menu.menuNameId);
+}
+
+const char *GUI_MR::getMenuItemTitle(const MenuItem &menuItem) {
+ if (!menuItem.itemId)
+ return 0;
+
+ return (const char *)_vm->getTableEntry(_vm->_optionsFile, menuItem.itemId);
+}
+
+const char *GUI_MR::getMenuItemLabel(const MenuItem &menuItem) {
+ if (!menuItem.labelId)
+ return 0;
+
+ return (const char *)_vm->getTableEntry(_vm->_optionsFile, menuItem.labelId);
+}
+
+char *GUI_MR::getTableString(int id) {
+ return (char *)_vm->getTableEntry(_vm->_optionsFile, id);
+}
+
+int GUI_MR::redrawButtonCallback(Button *button) {
+ if (!_displayMenu)
+ return 0;
+
+ _screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 0xD0);
+
+ return 0;
+}
+
+int GUI_MR::redrawShadedButtonCallback(Button *button) {
+ if (!_displayMenu)
+ return 0;
+
+ _screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 0xD1, 0xCF);
+
+ return 0;
+}
+void GUI_MR::resetState(int item) {
+ _vm->_timer->resetNextRun();
+ _vm->setNextIdleAnimTimer();
+ _isDeathMenu = false;
+ if (!_loadedSave) {
+ _vm->_itemInHand = kItemNone;
+ _vm->setHandItem(item);
+ } else {
+ _vm->setHandItem(_vm->_itemInHand);
+ _vm->setCommandLineRestoreTimer(7);
+ _vm->_shownMessage = " ";
+ _vm->_restoreCommandLine = false;
+ }
+ _buttonListChanged = true;
+}
+
+int GUI_MR::quitGame(Button *caller) {
+ updateMenuButton(caller);
+ if (choiceDialog(0x0F, 1)) {
+ _displayMenu = false;
+ _vm->_runFlag = false;
+ _vm->fadeOutMusic(60);
+ _screen->fadeToBlack(60);
+ _screen->clearCurPage();
+ }
+
+ if (_vm->_runFlag) {
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ }
+
+ return 0;
+}
+
+int GUI_MR::optionsButton(Button *button) {
+ PauseTimer pause(*_vm->_timer);
+
+ updateButton(&_vm->_mainButtonData[0]);
+
+ if (!_vm->_inventoryState && button && !_vm->_menuDirectlyToLoad)
+ return 0;
+
+ _restartGame = false;
+ _reloadTemporarySave = false;
+
+ if (!_screen->isMouseVisible() && button && !_vm->_menuDirectlyToLoad)
+ return 0;
+
+ _vm->showMessage(0, 0xF0, 0xF0);
+
+ if (_vm->_mouseState < -1) {
+ _vm->_mouseState = -1;
+ _screen->setMouseCursor(1, 1, _vm->getShapePtr(0));
+ return 0;
+ }
+
+ int oldHandItem = _vm->_itemInHand;
+ _screen->setMouseCursor(0, 0, _vm->getShapePtr(0));
+
+ _displayMenu = true;
+ for (int i = 0; i < 4; ++i) {
+ if (_vm->_musicSoundChannel != i)
+ _vm->_soundDigital->stopSound(i);
+ }
+
+ for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i) {
+ _menuButtons[i].data0Val1 = _menuButtons[i].data1Val1 = _menuButtons[i].data2Val1 = 4;
+ _menuButtons[i].data0Callback = _redrawShadedButtonFunctor;
+ _menuButtons[i].data1Callback = _menuButtons[i].data2Callback = _redrawButtonFunctor;
+ }
+
+ initMenuLayout(_mainMenu);
+ initMenuLayout(_gameOptions);
+ initMenuLayout(_audioOptions);
+ initMenuLayout(_choiceMenu);
+ _loadMenu.numberOfItems = 6;
+ initMenuLayout(_loadMenu);
+ initMenuLayout(_saveMenu);
+ initMenuLayout(_savenameMenu);
+ initMenuLayout(_deathMenu);
+
+ _currentMenu = &_mainMenu;
+
+ if (_vm->_menuDirectlyToLoad) {
+ backUpPage1(_vm->_screenBuffer);
+
+ _loadedSave = false;
+
+ --_loadMenu.numberOfItems;
+ loadMenu(0);
+ ++_loadMenu.numberOfItems;
+
+ if (_loadedSave) {
+ if (_restartGame)
+ _vm->_itemInHand = kItemNone;
+ } else {
+ restorePage1(_vm->_screenBuffer);
+ }
+
+ resetState(-1);
+ _vm->_menuDirectlyToLoad = false;
+ return 0;
+ }
+
+ if (!button) {
+ _currentMenu = &_deathMenu;
+ _isDeathMenu = true;
+ } else {
+ _isDeathMenu = false;
+ }
+
+ backUpPage1(_vm->_screenBuffer);
+ initMenu(*_currentMenu);
+ _madeSave = false;
+ _loadedSave = false;
+ updateAllMenuButtons();
+
+ if (_isDeathMenu) {
+ while (!_screen->isMouseVisible())
+ _screen->showMouse();
+ }
+
+ while (_displayMenu) {
+ processHighlights(*_currentMenu);
+ getInput();
+ }
+
+ if (_vm->_runFlag && !_loadedSave && !_madeSave) {
+ restorePalette();
+ restorePage1(_vm->_screenBuffer);
+ }
+
+ if (_vm->_runFlag)
+ updateMenuButton(&_vm->_mainButtonData[0]);
+
+ resetState(oldHandItem);
+
+ if (!_loadedSave && _reloadTemporarySave) {
+ _vm->_unkSceneScreenFlag1 = true;
+ _vm->loadGameStateCheck(999);
+ //_vm->_saveFileMan->removeSavefile(_vm->getSavegameFilename(999));
+ _vm->_unkSceneScreenFlag1 = false;
+ }
+
+ return 0;
+}
+
+int GUI_MR::loadMenu(Button *caller) {
+ updateSaveFileList(_vm->_targetName);
+
+ if (!_vm->_menuDirectlyToLoad) {
+ updateMenuButton(caller);
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ }
+
+ _savegameOffset = 0;
+ setupSavegameNames(_loadMenu, 5);
+ initMenu(_loadMenu);
+ _isLoadMenu = true;
+ _noLoadProcess = false;
+ _vm->_gameToLoad = -1;
+ updateAllMenuButtons();
+
+ _screen->updateScreen();
+ while (_isLoadMenu) {
+ processHighlights(_loadMenu);
+ getInput();
+ }
+
+ if (_noLoadProcess) {
+ if (!_vm->_menuDirectlyToLoad) {
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ }
+ } else if (_vm->_gameToLoad >= 0) {
+ restorePage1(_vm->_screenBuffer);
+ restorePalette();
+ _vm->_menuDirectlyToLoad = false;
+ _vm->loadGameStateCheck(_vm->_gameToLoad);
+ if (_vm->_gameToLoad == 0) {
+ _restartGame = true;
+ _vm->runStartupScript(1, 1);
+ }
+ _displayMenu = false;
+ _loadedSave = true;
+ }
+
+ return 0;
+}
+
+int GUI_MR::loadSecondChance(Button *button) {
+ updateMenuButton(button);
+
+ _vm->_gameToLoad = 999;
+ restorePage1(_vm->_screenBuffer);
+ _vm->loadGameStateCheck(_vm->_gameToLoad);
+ _displayMenu = false;
+ _loadedSave = true;
+ return 0;
+}
+
+int GUI_MR::gameOptions(Button *caller) {
+ updateMenuButton(caller);
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ bool textEnabled = _vm->textEnabled();
+ int lang = _vm->_lang;
+
+ setupOptionsButtons();
+ initMenu(_gameOptions);
+ _isOptionsMenu = true;
+
+ while (_isOptionsMenu) {
+ processHighlights(_gameOptions);
+ getInput();
+ }
+
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+
+ if (textEnabled && !_vm->textEnabled() && !_vm->speechEnabled()) {
+ _vm->_configVoice = 1;
+ _vm->setVolume(KyraEngine_v1::kVolumeSpeech, 75);
+ choiceDialog(0x1E, 0);
+ }
+
+ if (_vm->_lang != lang) {
+ _reloadTemporarySave = true;
+
+ Graphics::Surface thumb;
+ createScreenThumbnail(thumb);
+ _vm->saveGameStateIntern(999, "Autosave", &thumb);
+ thumb.free();
+
+ _vm->_lastAutosave = _vm->_system->getMillis();
+
+ if (!_vm->loadLanguageFile("ITEMS.", _vm->_itemFile))
+ error("Couldn't load ITEMS");
+ if (!_vm->loadLanguageFile("SCORE.", _vm->_scoreFile))
+ error("Couldn't load SCORE");
+ if (!_vm->loadLanguageFile("C_CODE.", _vm->_cCodeFile))
+ error("Couldn't load C_CODE");
+ if (!_vm->loadLanguageFile("SCENES.", _vm->_scenesFile))
+ error("Couldn't load SCENES");
+ if (!_vm->loadLanguageFile("OPTIONS.", _vm->_optionsFile))
+ error("Couldn't load OPTIONS");
+ if (!_vm->loadLanguageFile("_ACTOR.", _vm->_actorFile))
+ error("couldn't load _ACTOR");
+ }
+
+ _vm->writeSettings();
+
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ return 0;
+}
+
+void GUI_MR::setupOptionsButtons() {
+ if (_vm->_configWalkspeed == 3)
+ _gameOptions.item[0].itemId = 28;
+ else
+ _gameOptions.item[0].itemId = 27;
+
+ if (_vm->textEnabled())
+ _gameOptions.item[4].itemId = 18;
+ else
+ _gameOptions.item[4].itemId = 17;
+
+ switch (_vm->_lang) {
+ case 0:
+ _gameOptions.item[1].itemId = 31;
+ break;
+
+ case 1:
+ _gameOptions.item[1].itemId = 32;
+ break;
+
+ case 2:
+ _gameOptions.item[1].itemId = 33;
+ break;
+
+ default:
+ break;
+ }
+
+ if (_vm->_configStudio)
+ _gameOptions.item[2].itemId = 18;
+ else
+ _gameOptions.item[2].itemId = 17;
+
+ if (_vm->_configSkip)
+ _gameOptions.item[3].itemId = 18;
+ else
+ _gameOptions.item[3].itemId = 17;
+}
+
+int GUI_MR::changeLanguage(Button *caller) {
+ updateMenuButton(caller);
+ if (!_vm->queryGameFlag(0x1B2)) {
+ ++_vm->_lang;
+ _vm->_lang %= 3;
+ setupOptionsButtons();
+ renewHighlight(_gameOptions);
+ }
+ return 0;
+}
+
+int GUI_MR::toggleStudioSFX(Button *caller) {
+ updateMenuButton(caller);
+ _vm->_configStudio ^= 1;
+ setupOptionsButtons();
+ renewHighlight(_gameOptions);
+ return 0;
+}
+
+int GUI_MR::toggleSkipSupport(Button *caller) {
+ updateMenuButton(caller);
+ _vm->_configSkip ^= 1;
+ setupOptionsButtons();
+ renewHighlight(_gameOptions);
+ return 0;
+}
+
+int GUI_MR::toggleHeliumMode(Button *caller) {
+ updateMenuButton(caller);
+ _vm->_configHelium ^= 1;
+ if (_vm->_configHelium)
+ _audioOptions.item[3].itemId = 18;
+ else
+ _audioOptions.item[3].itemId = 17;
+ renewHighlight(_audioOptions);
+ return 0;
+}
+
+int GUI_MR::audioOptions(Button *caller) {
+ updateMenuButton(caller);
+
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+
+ if (_vm->_configHelium)
+ _audioOptions.item[3].itemId = 18;
+ else
+ _audioOptions.item[3].itemId = 17;
+
+ initMenu(_audioOptions);
+
+ const int menuX = _audioOptions.x;
+ const int menuY = _audioOptions.y;
+
+ const int maxButton = 3; // 2 if voc is disabled
+
+ for (int i = 0; i < maxButton; ++i) {
+ int x = menuX + _sliderBarsPosition[i*2+0];
+ int y = menuY + _sliderBarsPosition[i*2+1];
+ _screen->drawShape(0, _vm->getShapePtr(0x1CF), x, y, 0, 0);
+ drawSliderBar(i, _vm->getShapePtr(0x1D0));
+ _sliderButtons[0][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[0][i].x = x;
+ _sliderButtons[0][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[0][i]);
+ _sliderButtons[2][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[2][i].x = x + 10;
+ _sliderButtons[2][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[2][i]);
+ _sliderButtons[1][i].buttonCallback = _sliderHandlerFunctor;
+ _sliderButtons[1][i].x = x + 120;
+ _sliderButtons[1][i].y = y;
+ _menuButtonList = addButtonToList(_menuButtonList, &_sliderButtons[1][i]);
+ }
+
+ _isOptionsMenu = true;
+ updateAllMenuButtons();
+ bool speechEnabled = _vm->speechEnabled();
+ while (_isOptionsMenu) {
+ processHighlights(_audioOptions);
+ getInput();
+ }
+
+ restorePage1(_vm->_screenBuffer);
+ backUpPage1(_vm->_screenBuffer);
+ if (speechEnabled && !_vm->textEnabled() && (!_vm->speechEnabled() || _vm->getVolume(KyraEngine_v1::kVolumeSpeech) == 2)) {
+ _vm->_configVoice = 0;
+ choiceDialog(0x1D, 0);
+ }
+
+ _vm->writeSettings();
+
+ initMenu(*_currentMenu);
+ updateAllMenuButtons();
+ return 0;
+}
+
+int GUI_MR::sliderHandler(Button *caller) {
+ int button = 0;
+ if (caller->index >= 24 && caller->index <= 27)
+ button = caller->index - 24;
+ else if (caller->index >= 28 && caller->index <= 31)
+ button = caller->index - 28;
+ else
+ button = caller->index - 32;
+
+ assert(button >= 0 && button <= 3);
+
+ const int oldVolume = _vm->getVolume(KyraEngine_v1::kVolumeEntry(button));
+ int newVolume = oldVolume;
+
+ if (caller->index >= 24 && caller->index <= 27)
+ newVolume -= 10;
+ else if (caller->index >= 28 && caller->index <= 31)
+ newVolume += 10;
+ else
+ newVolume = _vm->_mouseX - caller->x - 7;
+
+ newVolume = CLIP(newVolume, 2, 97);
+
+ if (newVolume == oldVolume)
+ return 0;
+
+ int lastMusicCommand = -1;
+ bool playSoundEffect = false;
+
+ drawSliderBar(button, _vm->getShapePtr(0x1D1));
+
+ if (button == 2) {
+ if (_vm->textEnabled())
+ _vm->_configVoice = 2;
+ else
+ _vm->_configVoice = 1;
+ }
+
+ _vm->setVolume(KyraEngine_v1::kVolumeEntry(button), newVolume);
+
+ switch (button) {
+ case 0:
+ lastMusicCommand = _vm->_lastMusicCommand;
+ break;
+
+ case 1:
+ playSoundEffect = true;
+ break;
+
+ case 2:
+ if (_vm->_voiceSoundChannel != _vm->_musicSoundChannel)
+ _vm->_soundDigital->stopSound(_vm->_voiceSoundChannel);
+ _vm->playVoice(200, 943);
+ break;
+
+ default:
+ return 0;
+ }
+
+ drawSliderBar(button, _vm->getShapePtr(0x1D0));
+ if (playSoundEffect)
+ _vm->snd_playSoundEffect(0x18, 0xC8);
+ else if (lastMusicCommand >= 0)
+ _vm->snd_playWanderScoreViaMap(lastMusicCommand, 0);
+
+ _screen->updateScreen();
+ return 0;
+}
+
+void GUI_MR::drawSliderBar(int slider, const uint8 *shape) {
+ const int menuX = _audioOptions.x;
+ const int menuY = _audioOptions.y;
+ int x = menuX + _sliderBarsPosition[slider*2+0] + 10;
+ int y = menuY + _sliderBarsPosition[slider*2+1];
+
+ int position = _vm->getVolume(KyraEngine_v1::kVolumeEntry(slider));
+
+ position = CLIP(position, 2, 97);
+ _screen->drawShape(0, shape, x+position, y, 0, 0);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_mr.h b/engines/kyra/gui/gui_mr.h
new file mode 100644
index 0000000000..91e0143b7f
--- /dev/null
+++ b/engines/kyra/gui/gui_mr.h
@@ -0,0 +1,87 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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();
+
+ void flagButtonEnable(Button *button);
+ void flagButtonDisable(Button *button);
+
+ int redrawShadedButtonCallback(Button *button);
+ int redrawButtonCallback(Button *button);
+
+ int optionsButton(Button *button);
+
+ void createScreenThumbnail(Graphics::Surface &dst);
+private:
+ const char *getMenuTitle(const Menu &menu);
+ const char *getMenuItemTitle(const MenuItem &menuItem);
+ const char *getMenuItemLabel(const MenuItem &menuItem);
+ char *getTableString(int id);
+
+ uint8 textFieldColor1() const { return 0xFF; }
+ uint8 textFieldColor2() const { return 0xCF; }
+ uint8 textFieldColor3() const { return 0xBA; }
+
+ uint8 defaultColor1() const { return 0xF0; }
+ uint8 defaultColor2() const { return 0xD0; }
+
+ void resetState(int item);
+
+ int quitGame(Button *button);
+ int loadMenu(Button *button);
+ int loadSecondChance(Button *button);
+
+ int gameOptions(Button *button);
+ void setupOptionsButtons();
+
+ int audioOptions(Button *button);
+
+ int sliderHandler(Button *caller);
+ 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
diff --git a/engines/kyra/gui/gui_rpg.cpp b/engines/kyra/gui/gui_rpg.cpp
new file mode 100644
index 0000000000..a55dd290e2
--- /dev/null
+++ b/engines/kyra/gui/gui_rpg.cpp
@@ -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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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)
diff --git a/engines/kyra/gui/gui_v1.cpp b/engines/kyra/gui/gui_v1.cpp
new file mode 100644
index 0000000000..50a5628f98
--- /dev/null
+++ b/engines/kyra/gui/gui_v1.cpp
@@ -0,0 +1,620 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 = 0;
+
+ _redrawButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawButtonCallback);
+ _redrawShadedButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawShadedButtonCallback);
+}
+
+Button *GUI_v1::addButtonToList(Button *list, Button *newButton) {
+ if (!newButton)
+ return list;
+
+ newButton->nextButton = 0;
+
+ 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 = 0;
+
+ 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);
+ }
+
+ 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;
+
+ if (i < 7) {
+ Button *menuButtonData = getButtonListData() + i;
+ menuButtonData->nextButton = 0;
+ 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])) {
+ if (menu.item[i].titleX != -1)
+ textX = x1 + menu.item[i].titleX + 3;
+ else
+ textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
+
+ textY = y1 + 2;
+ if (_vm->game() == GI_LOL) {
+ textY++;
+ 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(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])) {
+ 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, menu.item[i].textColor, 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 = 0;
+ 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 = 0;
+ 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 + 2;
+ if (_vm->game() == GI_LOL) {
+ textY++;
+ 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(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 + 2;
+
+ if (_vm->game() == GI_LOL) {
+ textY++;
+ 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(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);
+ _screen->updateScreen();
+}
+
+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;
+
+ _vm->_system->updateScreen();
+ _lastScreenUpdate = now;
+ } break;
+
+ default:
+ break;
+ }
+ }
+
+ if (now - _lastScreenUpdate > 50) {
+ _vm->_system->updateScreen();
+ _lastScreenUpdate = now;
+ }
+
+ processButtonList(_menuButtonList, keys | 0x8000, 0);
+ _vm->_system->delayMillis(3);
+}
+
+void GUI_v1::printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
+ _text->printText(str, x, y, c0, c1, c2);
+}
+
+int GUI_v1::getMenuCenterStringX(const char *str, int x1, int x2) {
+ return _text->getCenterStringX(str, x1, x2);
+}
+
+#pragma mark -
+
+MainMenu::MainMenu(KyraEngine_v1 *vm) : _vm(vm), _screen(0) {
+ _screen = _vm->screen();
+ _nextUpdate = 0;
+ _system = g_system;
+}
+
+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, 0, 0);
+ _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)
+ _system->updateScreen();
+ 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->_charWidth;
+
+ if (_vm->game() != GI_LOL)
+ _screen->_charWidth = -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;
+
+ 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();
+ if (_vm->gameFlags().lang == Common::JA_JPN)
+ fh++;
+
+ 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->_charWidth = charWidthBackUp;
+ _screen->setFont(oldFont);
+
+ return command;
+}
+
+void MainMenu::draw(int select) {
+ int top = _screen->_curDim->sy;
+ top += _static.menuTable[1];
+ int fh = _screen->getFontHeight();
+ if (_vm->gameFlags().lang == Common::JA_JPN)
+ fh++;
+
+ 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);
+
+ if (flags & 1)
+ x -= _screen->getTextWidth(string.c_str()) >> 1;
+
+ if (flags & 2)
+ x -= _screen->getTextWidth(string.c_str());
+
+ if (_vm->gameFlags().use16ColorMode)
+ flags &= 3;
+
+ if (flags & 4) {
+ _screen->printText(string.c_str(), x - 1, y, _static.altColor, col2);
+ _screen->printText(string.c_str(), x, y + 1, _static.altColor, col2);
+ }
+
+ if (flags & 8) {
+ _screen->printText(string.c_str(), x - 1, y, 227, col2);
+ _screen->printText(string.c_str(), x, y + 1, 227, col2);
+ }
+
+ _screen->printText(string.c_str(), x, y, col1, col2);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_v1.h b/engines/kyra/gui/gui_v1.h
new file mode 100644
index 0000000000..476aa54834
--- /dev/null
+++ b/engines/kyra/gui/gui_v1.h
@@ -0,0 +1,197 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_GUI_V1_H
+#define KYRA_GUI_V1_H
+
+#include "kyra/gui/gui.h"
+
+namespace Kyra {
+
+struct MenuItem {
+ bool enabled;
+
+ const char *itemString;
+ uint16 itemId;
+
+ 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);
+ virtual ~GUI_v1() {}
+
+ // button specific
+ virtual Button *addButtonToList(Button *list, Button *newButton);
+
+ virtual void processButton(Button *button) = 0;
+ virtual int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) = 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
+ virtual void createScreenThumbnail(Graphics::Surface &dst) = 0;
+
+protected:
+ TextDisplayer *_text;
+
+ Button *_menuButtonList;
+ bool _displayMenu;
+ bool _displaySubMenu;
+ bool _cancelSubMenu;
+
+ virtual void printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
+ virtual int getMenuCenterStringX(const char *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 const char *getMenuTitle(const Menu &menu) = 0;
+ virtual const char *getMenuItemTitle(const MenuItem &menuItem) = 0;
+ virtual const char *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();
+};
+
+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];
+
+ Screen::FontId font;
+ 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
diff --git a/engines/kyra/gui/gui_v2.cpp b/engines/kyra/gui/gui_v2.cpp
new file mode 100644
index 0000000000..b0a8bc18c8
--- /dev/null
+++ b/engines/kyra/gui/gui_v2.cpp
@@ -0,0 +1,879 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 = 0;
+ _buttonListChanged = false;
+ _lastScreenUpdate = 0;
+ _flagsModifier = 0;
+
+ _currentMenu = 0;
+ _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;
+}
+
+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 = 0;
+ 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 = 0;
+ //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 = 0;
+ }
+
+ 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 = 0;
+ if (!progress || (buttonList->flags & 4))
+ buttonList->flags2 &= ~6;
+ }
+
+ if (!progress && buttonList == _specialProcessButton && !(buttonList->flags & 0x40))
+ _specialProcessButton = 0;
+
+ 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) {
+ strcpy(getTableString(menu.item[i].itemId), "");
+ 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)) != 0) {
+ char *s = getTableString(menu.item[i].itemId);
+ Common::strlcpy(s, header.description.c_str(), 80);
+ Util::convertISOToDOS(s);
+
+ // Trim long GMM save descriptions to fit our save slots
+ _screen->_charWidth = -2;
+ int fC = _screen->getTextWidth(s);
+ while (s[0] && fC > 240) {
+ s[strlen(s) - 1] = 0;
+ fC = _screen->getTextWidth(s);
+ }
+ _screen->_charWidth = 0;
+
+ menu.item[i].saveSlot = _saveSlots[i + _savegameOffset];
+ menu.item[i].enabled = true;
+ delete in;
+ }
+ }
+
+ if (_savegameOffset == 0) {
+ if (_isSaveMenu) {
+ char *dst = getTableString(menu.item[0].itemId);
+ const char *src = getTableString(_vm->gameFlags().isTalkie ? 10 : 18);
+ strcpy(dst, src);
+ menu.item[0].saveSlot = -2;
+ menu.item[0].enabled = true;
+ } else {
+ char *dst = getTableString(menu.item[0].itemId);
+ const char *src = getTableString(_vm->gameFlags().isTalkie ? 34 : 42);
+ strcpy(dst, src);
+ }
+ }
+}
+
+int GUI_v2::scrollUpButton(Button *button) {
+ updateMenuButton(button);
+
+ if (_savegameOffset == (_isDeleteMenu ? 1 : 0))
+ return 0;
+
+ --_savegameOffset;
+ if (_isLoadMenu) {
+ setupSavegameNames(_loadMenu, 5);
+ // original calls something different here...
+ initMenu(_loadMenu);
+ } else if (_isSaveMenu || _isDeleteMenu) {
+ setupSavegameNames(_saveMenu, 5);
+ // original calls something different here...
+ initMenu(_saveMenu);
+ }
+
+ return 0;
+}
+
+int GUI_v2::scrollDownButton(Button *button) {
+ updateMenuButton(button);
+ ++_savegameOffset;
+
+ if (uint(_savegameOffset + 5) >= _saveSlots.size())
+ _savegameOffset = MAX<int>(_saveSlots.size() - 5, _isDeleteMenu ? 1 : 0);
+
+ if (_isLoadMenu) {
+ setupSavegameNames(_loadMenu, 5);
+ // original calls something different here...
+ initMenu(_loadMenu);
+ } else if (_isSaveMenu || _isDeleteMenu) {
+ setupSavegameNames(_saveMenu, 5);
+ // original calls something different here...
+ initMenu(_saveMenu);
+ }
+
+ return 0;
+}
+
+int GUI_v2::resumeGame(Button *caller) {
+ updateMenuButton(caller);
+ _displayMenu = false;
+ 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, 5);
+ 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);
+ Util::convertDOSToISO(_saveDescription);
+ _vm->saveGameStateIntern(_saveSlot, _saveDescription, &thumb);
+ thumb.free();
+
+ _displayMenu = false;
+ _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;
+ strcpy(_saveDescription, getTableString(item.itemId));
+ }
+ } 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, 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, 5);
+ 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);
+
+ int x2 = x, y2 = y;
+ Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
+ _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(Screen::FID_8_FNT);
+ 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 0;
+ } 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 < (_vm->gameFlags().lang == Common::JA_JPN ? 128 : 226) && curPos < bufferSize) {
+ of = _screen->setFont(Screen::FID_8_FNT);
+ if (x2 + getCharWidth(inputKey) + 7 < 0x11F) {
+ buffer[curPos] = inputKey;
+ const char text[2] = { buffer[curPos], 0 };
+ _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] != 0x20)
+ return true;
+ else if (buffer[i] == 0x00)
+ return false;
+ }
+ return false;
+}
+
+int GUI_v2::getCharWidth(uint8 c) {
+ Screen::FontId old = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ int width = _screen->getCharWidth(c);
+ _screen->_charWidth = 0;
+ _screen->setFont(old);
+ return width;
+}
+
+void GUI_v2::drawTextfieldBlock(int x, int y, uint8 c) {
+ _screen->fillRect(x + 1, y + 1, x + 7, y + 8, 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
diff --git a/engines/kyra/gui/gui_v2.h b/engines/kyra/gui/gui_v2.h
new file mode 100644
index 0000000000..22cdf18a75
--- /dev/null
+++ b/engines/kyra/gui/gui_v2.h
@@ -0,0 +1,233 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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.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);
+
+ void processButton(Button *button);
+ int processButtonList(Button *button, uint16 inputFlag, int8 mouseWheel);
+
+protected:
+ void updateButton(Button *button);
+
+ KyraEngine_v2 *_vm;
+ Screen_v2 *_screen;
+
+ bool _buttonListChanged;
+ Button *_backUpButtonList;
+ Button *_specialProcessButton;
+ uint16 _flagsModifier;
+
+protected:
+ virtual void setupPalette() {}
+ virtual void restorePalette() {}
+
+ virtual char *getTableString(int id) = 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() { return _menuButtons; }
+
+ Button *getScrollUpButton() { return &_scrollUpButton; }
+ Button *getScrollDownButton() { return &_scrollDownButton; }
+
+ int scrollUpButton(Button *button);
+ int scrollDownButton(Button *button);
+ Button::Callback _scrollUpFunctor;
+ Button::Callback _scrollDownFunctor;
+ Button::Callback getScrollUpButtonHandler() const { return _scrollUpFunctor; }
+ Button::Callback getScrollDownButtonHandler() const { 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);
+
+ // 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);
+
+ // choice menu
+ bool _choice;
+
+ bool choiceDialog(int name, bool type);
+ int choiceYes(Button *caller);
+ int choiceNo(Button *caller);
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/gui/saveload.cpp b/engines/kyra/gui/saveload.cpp
new file mode 100644
index 0000000000..b59f950c94
--- /dev/null
+++ b/engines/kyra/gui/saveload.cpp
@@ -0,0 +1,278 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 18
+
+#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::convertDOSToISO(descriptionBuffer);
+
+ 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 >= 2)
+ header.flags = in->readUint32BE();
+
+ if (header.version >= 14) {
+ if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
+ if (!skipThumbnail)
+ return kRSHEIoError;
+ }
+ } else {
+ header.thumbnail = 0;
+ }
+
+ return ((in->err() || in->eos()) ? kRSHEIoError : kRSHENoError);
+}
+
+Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header, bool checkID) {
+ Common::SeekableReadStream *in = 0;
+ if (!(in = _saveFileMan->openForLoading(filename)))
+ return 0;
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+ }
+
+ if (header.version < 2) {
+ warning("Make sure your savefile was from this version! (too old savefile version to detect that)");
+ } else {
+ 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 0;
+ } 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 0;
+ } 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 0;
+ }
+ }
+ }
+
+ return in;
+}
+
+Common::OutSaveFile *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const {
+ if (shouldQuit())
+ return 0;
+
+ Common::WriteStream *out = 0;
+ if (!(out = _saveFileMan->openForSaving(filename))) {
+ warning("Can't create file '%s', game not saved", filename);
+ return 0;
+ }
+
+ // 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 0;
+ }
+
+ Graphics::Surface *genThumbnail = 0;
+ if (!thumbnail)
+ thumbnail = genThumbnail = generateSaveThumbnail();
+
+ if (thumbnail)
+ Graphics::saveThumbnail(*out, *thumbnail);
+ else
+ Graphics::saveThumbnail(*out);
+
+ if (genThumbnail) {
+ genThumbnail->free();
+ delete genThumbnail;
+ }
+
+ 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::checkAutosave() {
+ if (shouldPerformAutoSave(_lastAutosave)) {
+ saveGameStateIntern(999, "Autosave", 0);
+ _lastAutosave = _system->getMillis();
+ }
+}
+
+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
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
new file mode 100644
index 0000000000..8f2c6e1838
--- /dev/null
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -0,0 +1,1301 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/resource/resource.h"
+#include "kyra/script/script_eob.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/substream.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+
+#include "gui/message.h"
+
+namespace Kyra {
+
+Common::Error EoBCoreEngine::loadGameState(int slot) {
+ // Special slot id -1 for EOB1 party transfer
+ const char *fileName = (slot == -1) ? _savegameFilename.c_str() : getSavegameFilename(slot);
+ setHandItem(-1);
+
+ SaveHeader header;
+ Common::InSaveFile *saveFile = openSaveForReading(fileName, header, (slot != -1));
+ if (!saveFile)
+ return Common::Error(Common::kReadingFailed);
+
+ Common::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !header.originalSave, DisposeAfterUse::YES);
+ _loading = true;
+
+ if (slot != -1)
+ _screen->fadeToBlack(10);
+
+ for (int i = 0; i < 6; i++) {
+ EoBCharacter *c = &_characters[i];
+ c->id = in.readByte();
+ c->flags = in.readByte();
+ in.read(c->name, (header.version < 18) ? 11 : 21);
+ c->strengthCur = in.readSByte();
+ c->strengthMax = in.readSByte();
+ c->strengthExtCur = in.readSByte();
+ c->strengthExtMax = in.readSByte();
+ c->intelligenceCur = in.readSByte();
+ c->intelligenceMax = in.readSByte();
+ c->wisdomCur = in.readSByte();
+ c->wisdomMax = in.readSByte();
+ c->dexterityCur = in.readSByte();
+ c->dexterityMax = in.readSByte();
+ c->constitutionCur = in.readSByte();
+ c->constitutionMax = in.readSByte();
+ c->charismaCur = in.readSByte();
+ c->charismaMax = in.readSByte();
+ c->hitPointsCur = in.readSint16BE();
+ c->hitPointsMax = in.readSint16BE();
+ c->armorClass = in.readSByte();
+ c->disabledSlots = in.readByte();
+ c->raceSex = in.readByte();
+ c->cClass = in.readByte();
+ c->alignment = in.readByte();
+ c->portrait = in.readSByte();
+ if (slot == -1 && c->portrait < 0)
+ c->portrait = -c->portrait + 43;
+ c->food = in.readByte();
+ in.read(c->level, 3);
+ for (int ii = 0; ii < 3; ii++)
+ c->experience[ii] = in.readUint32BE();
+ delete[] c->faceShape;
+ c->faceShape = 0;
+ in.read(c->mageSpells, 80);
+ in.read(c->clericSpells, 80);
+ c->mageSpellsAvailableFlags = in.readUint32BE();
+ for (int ii = 0; ii < 27; ii++)
+ c->inventory[ii] = in.readSint16BE();
+ uint32 ct = _system->getMillis();
+ for (int ii = 0; ii < 10; ii++) {
+ c->timers[ii] = in.readUint32BE();
+ if (c->timers[ii])
+ c->timers[ii] += ct;
+ }
+ in.read(c->events, 10);
+ in.read(c->effectsRemainder, 4);
+ c->effectFlags = in.readUint32BE();
+ c->damageTaken = in.readByte();
+ in.read(c->slotStatus, 5);
+ }
+
+ setupCharacterTimers();
+
+ _screen->loadShapeSetBitmap("CHARGENA", 3, 3);
+ for (int i = 0; i < 6; i++) {
+ EoBCharacter *c = &_characters[i];
+ if (!c->flags || c->portrait < 0)
+ continue;
+ c->faceShape = _screen->encodeShape((c->portrait % 10) << 2, (c->portrait / 10) << 5, 4, 32, true, _cgaMappingDefault);
+ }
+
+ _screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3);
+ for (int i = 0; i < 6; i++) {
+ EoBCharacter *c = &_characters[i];
+ if (!c->flags || c->portrait >= 0)
+ continue;
+ c->faceShape = _screen->encodeShape((-(c->portrait + 1)) << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true, _cgaMappingDefault);
+ }
+ _screen->_curPage = 0;
+
+ if (slot == -1) {
+ // Skip all settings which aren't necessary for party transfer.
+ // Jump directly to the items list.
+ in.skip(108);
+ } else {
+ _currentLevel = in.readByte();
+ _currentSub = in.readSByte();
+ _currentBlock = in.readUint16BE();
+ _currentDirection = in.readUint16BE();
+ _itemInHand = in.readSint16BE();
+ _hasTempDataFlags = in.readUint32BE();
+ _partyEffectFlags = in.readUint32BE();
+
+ _updateFlags = in.readUint16BE();
+ _compassDirection = in.readUint16BE();
+ _currentControlMode = in.readUint16BE();
+ _updateCharNum = in.readSint16BE();
+ _openBookSpellLevel = in.readSByte();
+ _openBookSpellSelectedItem = in.readSByte();
+ _openBookSpellListOffset = in.readSByte();
+ _openBookChar = in.readByte();
+ _openBookType = in.readByte();
+ _openBookCharBackup = in.readByte();
+ _openBookTypeBackup = in.readByte();
+ _activeSpellCharId = in.readByte();
+ _activeSpellCharacterPos = in.readByte();
+ _activeSpell = in.readByte();
+ _returnAfterSpellCallback = in.readByte() ? true : false;
+
+ _inf->loadState(in);
+ }
+
+ for (int i = 0; i < 600; i++) {
+ EoBItem *t = &_items[i];
+ t->nameUnid = in.readByte();
+ t->nameId = in.readByte();
+ t->flags = in.readByte();
+ t->icon = in.readSByte();
+ t->type = in.readSByte();
+ t->pos = in.readSByte();
+ t->block = in.readSint16BE();
+ t->next = in.readSint16BE();
+ t->prev = in.readSint16BE();
+ t->level = in.readByte();
+ t->value = in.readSByte();
+ }
+
+ // No more data needed for party transfer
+ if (slot == -1) {
+ _loading = false;
+ return Common::kNoError;
+ }
+
+ for (int i = 51; i < 65; i++) {
+ EoBItemType *t = &_itemTypes[i];
+ t->invFlags = in.readUint16BE();
+ t->handFlags = in.readUint16BE();
+ t->armorClass = in.readSByte();
+ t->allowedClasses = in.readSByte();
+ t->requiredHands = in.readSByte();
+ t->dmgNumDiceS = in.readSByte();
+ t->dmgNumPipsS = in.readSByte();
+ t->dmgIncS = in.readSByte();
+ t->dmgNumDiceL = in.readSByte();
+ t->dmgNumPipsL = in.readSByte();
+ t->dmgIncL = in.readSByte();
+ t->unk1 = in.readByte();
+ t->extraProperties = in.readUint16BE();
+ }
+
+ for (int i = 0; i < 18; i++) {
+ if (!(_hasTempDataFlags & (1 << i)))
+ 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;
+ LevelTempData *l = _lvlTempData[i];
+ l->wallsXorData = new uint8[4096];
+ l->flags = new uint16[1024];
+ EoBMonsterInPlay *lm = new EoBMonsterInPlay[30];
+ l->monsters = lm;
+ EoBFlyingObject *lf = new EoBFlyingObject[_numFlyingObjects];
+ l->flyingObjects = lf;
+ WallOfForce *lw = new WallOfForce[5];
+ l->wallsOfForce = lw;
+
+ in.read(l->wallsXorData, 4096);
+ for (int ii = 0; ii < 1024; ii++)
+ l->flags[ii] = in.readByte();
+
+ for (int ii = 0; ii < 30; ii++) {
+ EoBMonsterInPlay *m = &lm[ii];
+ m->type = in.readByte();
+ m->unit = in.readByte();
+ m->block = in.readUint16BE();
+ m->pos = in.readByte();
+ m->dir = in.readSByte();
+ m->animStep = in.readByte();
+ m->shpIndex = in.readByte();
+ m->mode = in.readSByte();
+ m->f_9 = in.readSByte();
+ m->curAttackFrame = in.readSByte();
+ m->spellStatusLeft = in.readSByte();
+ m->hitPointsMax = in.readSint16BE();
+ m->hitPointsCur = in.readSint16BE();
+ m->dest = in.readUint16BE();
+ m->randItem = in.readUint16BE();
+ m->fixedItem = in.readUint16BE();
+ m->flags = in.readByte();
+ m->idleAnimState = in.readByte();
+ m->curRemoteWeapon = in.readByte();
+ m->numRemoteAttacks = in.readByte();
+ m->palette = in.readSByte();
+ m->directionChanged = in.readByte();
+ m->stepsTillRemoteAttack = in.readByte();
+ m->sub = in.readByte();
+ }
+
+ for (int ii = 0; ii < _numFlyingObjects; ii++) {
+ EoBFlyingObject *m = &lf[ii];
+ m->enable = in.readByte();
+ m->objectType = in.readByte();
+ m->attackerId = in.readSint16BE();
+ m->item = in.readSint16BE();
+ m->curBlock = in.readUint16BE();
+ m->starting = in.readUint16BE();
+ m->u1 = in.readByte();
+ m->direction = in.readByte();
+ m->distance = in.readByte();
+ m->callBackIndex = in.readSByte();
+ m->curPos = in.readByte();
+ m->flags = in.readByte();
+ m->unused = in.readByte();
+ }
+
+ for (int ii = 0; ii < 5; ii++) {
+ WallOfForce *w = &lw[ii];
+ w->block = in.readUint16BE();
+ w->duration = in.readUint32BE();
+ }
+ }
+
+ loadLevel(_currentLevel, _currentSub);
+ if (_flags.platform == Common::kPlatformFMTowns && _gameToLoad != -1)
+ _screen->setScreenPalette(_screen->getPalette(0));
+
+ _sceneUpdateRequired = true;
+ _screen->setFont(Screen::FID_6_FNT);
+
+ for (int i = 0; i < 6; i++) {
+ for (int ii = 0; ii < 10; ii++) {
+ if (_characters[i].events[ii] == -57)
+ spellCallback_start_trueSeeing();
+ }
+ }
+
+ if (!_updateFlags)
+ _screen->fillRect(64, 121, 175, 176, 0, 2);
+
+ _screen->setCurPage(0);
+ gui_drawPlayField(false);
+
+ if (_currentControlMode)
+ _screen->copyRegion(176, 0, 0, 0, 144, 168, 0, 5, Screen::CR_NO_P_CHECK);
+
+ _screen->setCurPage(0);
+ gui_drawAllCharPortraitsWithStats();
+ drawScene(1);
+
+ if (_updateFlags) {
+ _updateFlags = 0;
+ useMagicBookOrSymbol(_openBookChar, _openBookType);
+ }
+
+ _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK);
+
+ gui_toggleButtons();
+ setHandItem(_itemInHand);
+
+ while (!_screen->isMouseVisible())
+ _screen->showMouse();
+
+ _loading = false;
+ _screen->fadeFromBlack(20);
+ removeInputTop();
+
+ return Common::kNoError;
+}
+
+Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
+ Common::String saveNameTmp;
+ const char *fileName = 0;
+ setHandItem(-1);
+
+ // Special slot id -1 to create final save for party transfer
+ if (slot == -1) {
+ _savegameFilename = _targetName + Common::String(".fin");
+ fileName = _savegameFilename.c_str();
+ saveNameTmp = _targetName + Common::String(" final");
+ saveNameTmp.toUppercase();
+ saveName = saveNameTmp.c_str();
+ } else {
+ fileName = getSavegameFilename(slot);
+ }
+
+ Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
+ if (!out)
+ return _saveFileMan->getError();
+
+ completeDoorOperations();
+ generateTempData();
+ advanceTimers(_restPartyElapsedTime);
+ _restPartyElapsedTime = 0;
+
+ for (int i = 0; i < 6; i++)
+ timerSpecialCharacterUpdate(0x30 + i);
+
+ for (int i = 0; i < 6; i++) {
+ EoBCharacter *c = &_characters[i];
+
+ out->writeByte(c->id);
+ out->writeByte(c->flags);
+ out->write(c->name, 21);
+ out->writeSByte(c->strengthCur);
+ out->writeSByte(c->strengthMax);
+ out->writeSByte(c->strengthExtCur);
+ out->writeSByte(c->strengthExtMax);
+ out->writeSByte(c->intelligenceCur);
+ out->writeSByte(c->intelligenceMax);
+ out->writeSByte(c->wisdomCur);
+ out->writeSByte(c->wisdomMax);
+ out->writeSByte(c->dexterityCur);
+ out->writeSByte(c->dexterityMax);
+ out->writeSByte(c->constitutionCur);
+ out->writeSByte(c->constitutionMax);
+ out->writeSByte(c->charismaCur);
+ out->writeSByte(c->charismaMax);
+ out->writeSint16BE(c->hitPointsCur);
+ out->writeSint16BE(c->hitPointsMax);
+ out->writeSByte(c->armorClass);
+ out->writeByte(c->disabledSlots);
+ out->writeByte(c->raceSex);
+ out->writeByte(c->cClass);
+ out->writeByte(c->alignment);
+ out->writeByte(c->portrait);
+ out->writeByte(c->food);
+ out->write(c->level, 3);
+ for (int ii = 0; ii < 3; ii++)
+ out->writeUint32BE(c->experience[ii]);
+ out->write(c->mageSpells, 80);
+ out->write(c->clericSpells, 80);
+ out->writeUint32BE(c->mageSpellsAvailableFlags);
+ for (int ii = 0; ii < 27; ii++)
+ out->writeSint16BE(c->inventory[ii]);
+ uint32 ct = _system->getMillis();
+ for (int ii = 0; ii < 10; ii++)
+ out->writeUint32BE((c->timers[ii] && c->timers[ii] > ct) ? c->timers[ii] - ct : 0);
+
+ out->write(c->events, 10);
+ out->write(c->effectsRemainder, 4);
+ out->writeUint32BE(c->effectFlags);
+ out->writeByte(c->damageTaken);
+ out->write(c->slotStatus, 5);
+ }
+
+ out->writeByte(_currentLevel);
+ out->writeSByte(_currentSub);
+ out->writeUint16BE(_currentBlock);
+ out->writeUint16BE(_currentDirection);
+ out->writeSint16BE(_itemInHand);
+ out->writeUint32BE(_hasTempDataFlags);
+ out->writeUint32BE(_partyEffectFlags);
+
+ out->writeUint16BE(_updateFlags);
+ out->writeUint16BE(_compassDirection);
+ out->writeUint16BE(_currentControlMode);
+ out->writeSint16BE(_updateCharNum);
+ out->writeSByte(_openBookSpellLevel);
+ out->writeSByte(_openBookSpellSelectedItem);
+ out->writeSByte(_openBookSpellListOffset);
+ out->writeByte(_openBookChar);
+ out->writeByte(_openBookType);
+ out->writeByte(_openBookCharBackup);
+ out->writeByte(_openBookTypeBackup);
+ out->writeByte(_activeSpellCharId);
+ out->writeByte(_activeSpellCharacterPos);
+ out->writeByte(_activeSpell);
+ out->writeByte(_returnAfterSpellCallback ? 1 : 0);
+
+ _inf->saveState(out);
+
+ for (int i = 0; i < 600; i++) {
+ EoBItem *t = &_items[i];
+ out->writeByte(t->nameUnid);
+ out->writeByte(t->nameId);
+ out->writeByte(t->flags);
+ out->writeSByte(t->icon);
+ out->writeSByte(t->type);
+ out->writeSByte(t->pos);
+ out->writeSint16BE(t->block);
+ out->writeSint16BE(t->next);
+ out->writeSint16BE(t->prev);
+ out->writeByte(t->level);
+ out->writeSByte(t->value);
+ }
+
+ for (int i = 51; i < 65; i++) {
+ EoBItemType *t = &_itemTypes[i];
+ out->writeUint16BE(t->invFlags);
+ out->writeUint16BE(t->handFlags);
+ out->writeSByte(t->armorClass);
+ out->writeSByte(t->allowedClasses);
+ out->writeSByte(t->requiredHands);
+ out->writeSByte(t->dmgNumDiceS);
+ out->writeSByte(t->dmgNumPipsS);
+ out->writeSByte(t->dmgIncS);
+ out->writeSByte(t->dmgNumDiceL);
+ out->writeSByte(t->dmgNumPipsL);
+ out->writeSByte(t->dmgIncL);
+ out->writeByte(t->unk1);
+ out->writeUint16BE(t->extraProperties);
+ }
+
+ for (int i = 0; i < 18; 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);
+
+ EoBMonsterInPlay *lm = (EoBMonsterInPlay *)_lvlTempData[i]->monsters;
+ EoBFlyingObject *lf = (EoBFlyingObject *)_lvlTempData[i]->flyingObjects;
+ WallOfForce *lw = (WallOfForce *)_lvlTempData[i]->wallsOfForce;
+
+ for (int ii = 0; ii < 30; ii++) {
+ EoBMonsterInPlay *m = &lm[ii];
+ out->writeByte(m->type);
+ out->writeByte(m->unit);
+ out->writeUint16BE(m->block);
+ out->writeByte(m->pos);
+ out->writeSByte(m->dir);
+ out->writeByte(m->animStep);
+ out->writeByte(m->shpIndex);
+ out->writeSByte(m->mode);
+ out->writeSByte(m->f_9);
+ out->writeSByte(m->curAttackFrame);
+ out->writeSByte(m->spellStatusLeft);
+ out->writeSint16BE(m->hitPointsMax);
+ out->writeSint16BE(m->hitPointsCur);
+ out->writeUint16BE(m->dest);
+ out->writeUint16BE(m->randItem);
+ out->writeUint16BE(m->fixedItem);
+ out->writeByte(m->flags);
+ out->writeByte(m->idleAnimState);
+ out->writeByte(m->curRemoteWeapon);
+ out->writeByte(m->numRemoteAttacks);
+ out->writeSByte(m->palette);
+ out->writeByte(m->directionChanged);
+ out->writeByte(m->stepsTillRemoteAttack);
+ out->writeByte(m->sub);
+ }
+
+ for (int ii = 0; ii < _numFlyingObjects; ii++) {
+ EoBFlyingObject *m = &lf[ii];
+ out->writeByte(m->enable);
+ out->writeByte(m->objectType);
+ out->writeSint16BE(m->attackerId);
+ out->writeSint16BE(m->item);
+ out->writeUint16BE(m->curBlock);
+ out->writeUint16BE(m->starting);
+ out->writeByte(m->u1);
+ out->writeByte(m->direction);
+ out->writeByte(m->distance);
+ out->writeSByte(m->callBackIndex);
+ out->writeByte(m->curPos);
+ out->writeByte(m->flags);
+ out->writeByte(m->unused);
+ }
+
+ for (int ii = 0; ii < 5; ii++) {
+ WallOfForce *w = &lw[ii];
+ out->writeUint16BE(w->block);
+ out->writeUint32BE(w->duration);
+ }
+ }
+
+ 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;
+
+ _gui->notifyUpdateSaveSlotsList();
+
+ setHandItem(_itemInHand);
+
+ return Common::kNoError;
+}
+
+bool EoBCoreEngine::importOriginalSaveFile(int destSlot, const char *sourceFile) {
+ Common::Array<Common::String> origFiles;
+ Common::Array<int> newSlots;
+
+ if (sourceFile) {
+ // If a source file is specified via the console command we just check whether it exists.
+ if (Common::File::exists(sourceFile))
+ origFiles.push_back(sourceFile);
+ else
+ return false;
+ } else {
+ // Check for original save files in the game path (usually at least the "Quick Start Party" file will be present).
+ int numMax = (_flags.gameID == GI_EOB1) ? 1 : 6;
+ const char *pattern = (_flags.gameID == GI_EOB1) ? "EOBDATA.SAV" : "EOBDATA%d.SAV";
+ for (int i = 0; i < numMax; ++i) {
+ Common::String temp = Common::String::format(pattern, i);
+ Common::SeekableReadStream *fs = _res->createReadStream(temp);
+ if (fs) {
+ Common::String dsc;
+ if (_flags.gameID == GI_EOB2) {
+ char descStr[20];
+ fs->read(descStr, 20);
+ dsc = Common::String::format("(\"%s\")", descStr).c_str();
+ }
+
+ delete fs;
+ ::GUI::MessageDialog dialog(Common::String::format(_("The following original saved game file has been found in your game path:\n\n%s %s\n\nDo you wish to use this saved game file with ScummVM?\n\n"), temp.c_str(), dsc.c_str()), _("Yes"), _("No"));
+ if (dialog.runModal())
+ origFiles.push_back(temp);
+ }
+ }
+ }
+
+ int numFilesFound = origFiles.size();
+ if (!numFilesFound)
+ return false;
+
+ _gui->updateSaveSlotsList(_targetName, true);
+
+ // Find free save slots for the original save files
+ if (destSlot == -1) {
+ int assignedSlots = 0;
+ for (int testSlot = 0; testSlot < 990 && assignedSlots < numFilesFound; testSlot++) {
+ if (Common::find(_gui->_saveSlots.begin(), _gui->_saveSlots.end(), testSlot) == _gui->_saveSlots.end()) {
+ newSlots.push_back(testSlot);
+ assignedSlots++;
+ }
+ }
+
+ // This will probably never happen, since we do have 990 save slots
+ if (assignedSlots != numFilesFound)
+ warning("%d original save files could not be converted due to missing saved game slots", numFilesFound - assignedSlots);
+
+ } else {
+ newSlots.push_back(destSlot);
+ }
+
+ if (destSlot != -1) {
+ if (Common::find(_gui->_saveSlots.begin(), _gui->_saveSlots.end(), destSlot) != _gui->_saveSlots.end()) {
+ ::GUI::MessageDialog dialog(Common::String::format(_("A saved game file was found in the specified slot %d. Overwrite?\n\n"), destSlot), _("Yes"), _("No"));
+ if (!dialog.runModal())
+ return false;
+ }
+ }
+
+ int importedCount = 0;
+ for (int i = 0; i < numFilesFound; i++) {
+ Common::String desc = readOriginalSaveFile(origFiles[i]);
+ if (desc.empty()) {
+ warning("Unable to import original save file '%s'", origFiles[i].c_str());
+ } else {
+ // We can't make thumbnails here, since we do not want to load all the level data, monsters, etc. for each save we convert.
+ // Instead, we use an empty surface to avoid that createThumbnailFromScreen() makes a completely pointless thumbnail from
+ // whatever screen that is currently shown when this function is called.
+ Graphics::Surface dummy;
+ saveGameStateIntern(newSlots[i], desc.c_str(), &dummy);
+ warning("Imported original save file '%s' ('%s')", origFiles[i].c_str(), desc.c_str());
+ importedCount++;
+ }
+ }
+
+ _currentLevel = 0;
+ _currentSub = 0;
+ _currentBlock = 0;
+ _currentDirection = 0;
+ _itemInHand = 0;
+ _hasTempDataFlags = 0;
+ _partyEffectFlags = 0;
+ memset(_characters, 0, sizeof(EoBCharacter) * 6);
+ _inf->reset();
+
+ if (destSlot == -1 && importedCount) {
+ ::GUI::MessageDialog dialog(Common::String::format(_("%d original saved games have been successfully imported into\nScummVM. If you want to manually import original saved game later you will\nneed to open the ScummVM debug console and use the command 'import_savefile'.\n\n"), importedCount));
+ dialog.runModal();
+ }
+
+ return true;
+}
+
+Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) {
+ Common::String desc;
+
+ Common::SeekableReadStream *fs = _res->createReadStream(file);
+ if (!fs)
+ return desc;
+
+ Common::SeekableSubReadStreamEndian in(fs, 0, fs->size(), _flags.platform == Common::kPlatformAmiga, DisposeAfterUse::YES);
+
+ // detect source platform
+ Common::Platform sourcePlatform = Common::kPlatformDOS;
+ in.seek(32);
+ uint16 testSJIS = in.readByte();
+ in.seek(53);
+ int8 testStr = in.readSByte();
+ in.seek(66);
+ int8 testChr = in.readSByte();
+ in.seek(0);
+ if (testStr >= 0 && testStr <= 25 && testChr >= 0 && testChr <= 25) {
+ if (testSJIS >= 0xE0 || (testSJIS > 0x80 && testSJIS < 0xA0))
+ sourcePlatform = Common::kPlatformFMTowns;
+ }
+
+ if (_flags.gameID == GI_EOB1) {
+ // Nothing to read here for EOB 1. Original EOB 1 has
+ // only one save slot without save file description.
+ desc = "<IMPORTED GAME>";
+ } else {
+ char tempStr[30];
+ in.read(tempStr, sourcePlatform == Common::kPlatformFMTowns ? 30 : 20);
+ desc = tempStr;
+ }
+
+ for (int i = 0; i < 6; i++) {
+ EoBCharacter *c = &_characters[i];
+ c->id = in.readByte();
+ c->flags = in.readByte();
+ in.read(c->name, sourcePlatform == Common::kPlatformFMTowns ? 21 : 11);
+ if (_flags.platform != sourcePlatform)
+ c->name[10] = '\0';
+ c->strengthCur = in.readSByte();
+ c->strengthMax = in.readSByte();
+ c->strengthExtCur = in.readSByte();
+ c->strengthExtMax = in.readSByte();
+ c->intelligenceCur = in.readSByte();
+ c->intelligenceMax = in.readSByte();
+ c->wisdomCur = in.readSByte();
+ c->wisdomMax = in.readSByte();
+ c->dexterityCur = in.readSByte();
+ c->dexterityMax = in.readSByte();
+ c->constitutionCur = in.readSByte();
+ c->constitutionMax = in.readSByte();
+ c->charismaCur = in.readSByte();
+ c->charismaMax = in.readSByte();
+ c->hitPointsCur = (_flags.gameID == GI_EOB1) ? in.readSByte() : in.readSint16();
+ c->hitPointsMax = (_flags.gameID == GI_EOB1) ? in.readSByte() : in.readSint16();
+ c->armorClass = in.readSByte();
+ c->disabledSlots = in.readByte();
+ c->raceSex = in.readByte();
+ c->cClass = in.readByte();
+ c->alignment = in.readByte();
+ c->portrait = in.readSByte();
+ c->food = in.readByte();
+ in.read(c->level, 3);
+ for (int ii = 0; ii < 3; ii++)
+ c->experience[ii] = in.readUint32();
+ in.skip(4);
+ delete[] c->faceShape;
+ c->faceShape = 0;
+ in.read(c->mageSpells, (_flags.gameID == GI_EOB1) ? 30 : 80);
+ in.read(c->clericSpells, (_flags.gameID == GI_EOB1) ? 30 : 80);
+ c->mageSpellsAvailableFlags = in.readUint32();
+ for (int ii = 0; ii < 27; ii++)
+ c->inventory[ii] = in.readSint16();
+ uint32 ct = _system->getMillis();
+ for (int ii = 0; ii < 10; ii++) {
+ c->timers[ii] = in.readUint32() * _tickLength;
+ if (c->timers[ii])
+ c->timers[ii] += ct;
+ }
+ in.read(c->events, 10);
+ in.read(c->effectsRemainder, 4);
+ c->effectFlags = in.readUint32();
+ if (c->effectFlags && _flags.gameID == GI_EOB1) {
+ // Spell effect flags are completely different in EOB I. We only use EOB II style flags in ScummVM.
+ // Doesn't matter much, since these are only temporary spell effects.
+ warning("EoBCoreEngine::readOriginalSaveFile(): Unhandled character effect flags encountered in original EOB1 save file '%s' ('%s')", file.c_str(), desc.c_str());
+ c->effectFlags = 0;
+ }
+ c->damageTaken = in.readByte();
+ in.read(c->slotStatus, 5);
+ in.skip(6);
+ }
+
+ setupCharacterTimers();
+
+ _currentLevel = in.readUint16();
+ _currentSub = (_flags.gameID == GI_EOB1) ? 0 : in.readSint16();
+ _currentBlock = in.readUint16();
+ _currentDirection = in.readUint16();
+ _itemInHand = in.readSint16();
+ _hasTempDataFlags = (_flags.gameID == GI_EOB1) ? in.readUint16() : in.readUint32();
+ _partyEffectFlags = (_flags.gameID == GI_EOB1) ? in.readUint16() : in.readUint32();
+ if (_partyEffectFlags && _flags.gameID == GI_EOB1) {
+ // Spell effect flags are completely different in EOB I. We only use EOB II style flags in ScummVM.
+ // Doesn't matter much, since these are only temporary spell effects.
+ warning("EoBCoreEngine::readOriginalSaveFile(): Unhandled party effect flags encountered in original EOB1 save file '%s' ('%s')", file.c_str(), desc.c_str());
+ _partyEffectFlags = 0;
+ }
+ if (_flags.gameID == GI_EOB2)
+ in.skip(1);
+
+ _inf->loadState(in, true);
+
+ int numItems = (_flags.gameID == GI_EOB1) ? 500 : 600;
+ for (int i = 0; i < numItems; i++) {
+ EoBItem *t = &_items[i];
+ t->nameUnid = in.readByte();
+ t->nameId = in.readByte();
+ t->flags = in.readByte();
+ t->icon = in.readSByte();
+ t->type = in.readSByte();
+ t->pos = in.readSByte();
+ t->block = in.readSint16();
+ t->next = in.readSint16();
+ t->prev = in.readSint16();
+ t->level = in.readByte();
+ t->value = in.readSByte();
+ }
+
+ int numParts = (_flags.gameID == GI_EOB1) ? 12 : 17;
+ int partSize = (sourcePlatform == Common::kPlatformFMTowns) ? 5030 : (_flags.gameID == GI_EOB1 ? 2040 : 2130);
+ uint32 nextPart = in.pos();
+ uint8 *cmpData = new uint8[1200];
+
+ for (int i = 0; i < numParts; i++) {
+ in.seek(nextPart);
+ nextPart += partSize;
+
+ if (!(_hasTempDataFlags & (1 << i)))
+ 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;
+ LevelTempData *l = _lvlTempData[i];
+ l->wallsXorData = new uint8[4096];
+ l->flags = new uint16[1024];
+ memset(l->flags, 0, 1024 * sizeof(uint16));
+ EoBMonsterInPlay *lm = new EoBMonsterInPlay[30];
+ memset(lm, 0, 30 * sizeof(EoBMonsterInPlay));
+ l->monsters = lm;
+ EoBFlyingObject *lf = new EoBFlyingObject[_numFlyingObjects];
+ memset(lf, 0, _numFlyingObjects * sizeof(EoBFlyingObject));
+ l->flyingObjects = lf;
+ WallOfForce *lw = new WallOfForce[5];
+ memset(lw, 0, 5 * sizeof(WallOfForce));
+ l->wallsOfForce = lw;
+
+ if (sourcePlatform == Common::kPlatformFMTowns) {
+ in.read(l->wallsXorData, 4096);
+ } else {
+ in.read(cmpData, 1200);
+ _screen->decodeFrame4(cmpData, l->wallsXorData, 4096);
+ }
+ _curBlockFile = getBlockFileName(i + 1, 0);
+ const uint8 *p = getBlockFileData();
+ uint16 len = READ_LE_UINT16(p + 4);
+ p += 6;
+
+ uint8 *d = l->wallsXorData;
+ for (int ii = 0; ii < 1024; ii++) {
+ for (int iii = 0; iii < 4; iii++)
+ *d++ ^= p[ii * len + iii];
+ }
+
+ if (sourcePlatform == Common::kPlatformFMTowns)
+ in.skip(4);
+
+ for (int ii = 0; ii < 30; ii++) {
+ EoBMonsterInPlay *m = &lm[ii];
+ m->type = in.readByte();
+ m->unit = in.readByte();
+ m->block = in.readUint16();
+ m->pos = in.readByte();
+ m->dir = in.readSByte();
+ m->animStep = in.readByte();
+ m->shpIndex = in.readByte();
+ m->mode = in.readSByte();
+ m->f_9 = in.readSByte();
+ m->curAttackFrame = in.readSByte();
+ m->spellStatusLeft = in.readSByte();
+ m->hitPointsMax = in.readSint16();
+ m->hitPointsCur = in.readSint16();
+ m->dest = in.readUint16();
+ m->randItem = in.readUint16();
+ m->fixedItem = in.readUint16();
+ m->flags = in.readByte();
+ m->idleAnimState = in.readByte();
+
+ if (_flags.gameID == GI_EOB1)
+ m->stepsTillRemoteAttack = in.readByte();
+ else
+ m->curRemoteWeapon = in.readByte();
+
+ m->numRemoteAttacks = in.readByte();
+ m->palette = in.readSByte();
+
+ if (_flags.gameID == GI_EOB1) {
+ in.skip(1);
+ } else {
+ m->directionChanged = in.readByte();
+ m->stepsTillRemoteAttack = in.readByte();
+ m->sub = in.readByte();
+ }
+
+ l->flags[m->block]++;
+ }
+
+ if (_flags.gameID == GI_EOB1)
+ continue;
+
+ for (int ii = 0; ii < 5; ii++) {
+ WallOfForce *w = &lw[ii];
+ w->block = in.readUint16();
+ w->duration = in.readUint32() * _tickLength;
+ }
+ }
+
+ delete[] cmpData;
+
+ restoreBlockTempData(_currentLevel);
+
+ in.skip(3);
+
+ delete[] _itemTypes;
+ _itemTypes = new EoBItemType[65];
+ memset(_itemTypes, 0, sizeof(EoBItemType) * 65);
+
+ for (int i = 51; i < 57; i++) {
+ EoBItemType *t = &_itemTypes[i];
+ t->invFlags = in.readUint16();
+ t->handFlags = in.readUint16();
+ t->armorClass = in.readSByte();
+ t->allowedClasses = in.readSByte();
+ t->requiredHands = in.readSByte();
+ t->dmgNumDiceS = in.readSByte();
+ t->dmgNumPipsS = in.readSByte();
+ t->dmgIncS = in.readSByte();
+ t->dmgNumDiceL = in.readSByte();
+ t->dmgNumPipsL = in.readSByte();
+ t->dmgIncL = in.readSByte();
+ t->unk1 = in.readByte();
+ t->extraProperties = in.readUint16();
+ }
+
+ return in.err() ? Common::String() : desc;
+}
+
+static uint32 encodeFrame4(const uint8 *src, uint8 *dst, uint32 insize) {
+ const uint8 *end = src + insize;
+
+ bool updateCounter = true;
+ const uint8 *in = src;
+
+ uint8 *out = dst;
+ uint8 *cntPtr = dst;
+
+ *dst++ = 0x81;
+ *dst++ = *src++;
+
+ while (src < end) {
+ const uint8 *src2 = in;
+ const uint8 *src3 = 0;
+ uint16 len = 1;
+
+ for (bool loop = true; loop; ) {
+ uint16 count = 0;
+ uint16 scansize = end - src - 1;
+ if (scansize > 64) {
+ if (src[0] == src[64]) {
+ for (uint16 i = 0; i < scansize && src[0] == src[i]; ++i)
+ count++;
+ if (count > 64) {
+ updateCounter = false;
+ *dst++ = 0xFE;
+ WRITE_LE_UINT16(dst, count);
+ dst += 2;
+ *dst++ = src[0];
+ src += count;
+ loop = true;
+ continue;
+ }
+ }
+ }
+
+ const uint8 *tmp = src2;
+
+ do {
+ count = src - tmp;
+ loop = false;
+ if (count == 0)
+ break;
+
+ while (count--) {
+ if (*src == *tmp++) {
+ loop = true;
+ break;
+ }
+ }
+ if (!loop)
+ break;
+ } while (*(src + len - 1) != *(tmp + len - 2));
+
+ if (!loop)
+ break;
+
+ src2 = tmp--;
+ const uint8 *tsrc = src;
+ count = end - src;
+ bool nmatch = false;
+
+ while (count--) {
+ if (*tsrc++ != *tmp++) {
+ nmatch = true;
+ break;
+ }
+ }
+ if (!nmatch)
+ tmp++;
+
+ count = tmp - src2;
+ if (count >= len) {
+ len = count;
+ src3 = src2 - 1;
+ }
+ }
+
+ if (len <= 2) {
+ for (bool forceLoop = !updateCounter; forceLoop || *cntPtr == 0xBF; forceLoop = false) {
+ cntPtr = dst;
+ *dst++ = 0x80;
+ }
+ (*cntPtr)++;
+ *dst++ = *src++;
+ updateCounter = true;
+ continue;
+
+ } else if (len > 10 || (src - src3) > 4095) {
+ if (len <= 64) {
+ *dst++ = (len - 3) | 0xC0;
+ } else {
+ *dst++ = 0xFF;
+ WRITE_LE_UINT16(dst, len);
+ dst += 2;
+ }
+ WRITE_LE_UINT16(dst, src3 - in);
+ } else {
+ WRITE_BE_UINT16(dst, (src - src3) + ((len - 3) << 12));
+ }
+
+ dst += 2;
+ src += len;
+ updateCounter = false;
+ }
+
+ *dst++ = 0x80;
+
+ return dst - out;
+}
+
+bool EoBCoreEngine::saveAsOriginalSaveFile(int slot) {
+ if (_flags.gameID == GI_EOB2 && (slot < 0 || slot > 5))
+ return false;
+
+ Common::String dir = ConfMan.get("savepath");
+ if (dir == "None")
+ dir.clear();
+
+ Common::FSNode nd(dir);
+ if (!nd.isDirectory())
+ return false;
+
+ Common::FSNode nf = nd.getChild(_flags.gameID == GI_EOB1 ? "EOBDATA.SAV" : Common::String::format("EOBDATA%d.SAV", slot));
+ Common::OutSaveFile *out = new Common::OutSaveFile(nf.createWriteStream());
+
+ if (_flags.gameID == GI_EOB2) {
+ static const char tempStr[31] = "SCUMMVM EXPORT ";
+ out->write(tempStr, (_flags.platform == Common::kPlatformFMTowns) ? 30 : 20);
+ }
+
+ completeDoorOperations();
+ generateTempData();
+ advanceTimers(_restPartyElapsedTime);
+ _restPartyElapsedTime = 0;
+
+ for (int i = 0; i < 6; i++)
+ timerSpecialCharacterUpdate(0x30 + i);
+
+ for (int i = 0; i < 6; i++) {
+ EoBCharacter *c = &_characters[i];
+ out->writeByte(c->id);
+ out->writeByte(c->flags);
+ out->write(c->name, (_flags.platform == Common::kPlatformFMTowns) ? 21 : 11);
+ out->writeSByte(c->strengthCur);
+ out->writeSByte(c->strengthMax);
+ out->writeSByte(c->strengthExtCur);
+ out->writeSByte(c->strengthExtMax);
+ out->writeSByte(c->intelligenceCur);
+ out->writeSByte(c->intelligenceMax);
+ out->writeSByte(c->wisdomCur);
+ out->writeSByte(c->wisdomMax);
+ out->writeSByte(c->dexterityCur);
+ out->writeSByte(c->dexterityMax);
+ out->writeSByte(c->constitutionCur);
+ out->writeSByte(c->constitutionMax);
+ out->writeSByte(c->charismaCur);
+ out->writeSByte(c->charismaMax);
+ if (_flags.gameID == GI_EOB1) {
+ out->writeSByte(c->hitPointsCur);
+ out->writeSByte(c->hitPointsMax);
+ } else {
+ out->writeSint16LE(c->hitPointsCur);
+ out->writeSint16LE(c->hitPointsMax);
+ }
+ out->writeSByte(c->armorClass);
+ out->writeByte(c->disabledSlots);
+ out->writeByte(c->raceSex);
+ out->writeByte(c->cClass);
+ out->writeByte(c->alignment);
+ out->writeSByte(c->portrait);
+ out->writeByte(c->food);
+ out->write(c->level, 3);
+ for (int ii = 0; ii < 3; ii++)
+ out->writeUint32LE(c->experience[ii]);
+ out->writeUint32LE(0);
+ out->write(c->mageSpells, (_flags.gameID == GI_EOB1) ? 30 : 80);
+ out->write(c->clericSpells, (_flags.gameID == GI_EOB1) ? 30 : 80);
+ out->writeUint32LE(c->mageSpellsAvailableFlags);
+ for (int ii = 0; ii < 27; ii++)
+ out->writeSint16LE(c->inventory[ii]);
+ uint32 ct = _system->getMillis();
+ for (int ii = 0; ii < 10; ii++)
+ out->writeUint32LE((c->timers[ii] && c->timers[ii] > ct) ? (c->timers[ii] - ct) / _tickLength : 0);
+ out->write(c->events, 10);
+ out->write(c->effectsRemainder, 4);
+
+ if (c->effectFlags && _flags.gameID == GI_EOB1) {
+ // Spell effect flags are completely different in original EOB I. We only use EOB II style flags in ScummVM.
+ // This doesn't matter much here, since these flags only apply to the temporary spell effects (things like prayer, haste, etc.) anyway.
+ warning("EoBCoreEngine::saveAsOriginalFile(): Character effect flags lost while exporting original EOB1 save file");
+ out->writeUint32LE(0);
+ } else {
+ out->writeUint32LE(c->effectFlags);
+ }
+ out->writeByte(c->damageTaken);
+ out->write(c->slotStatus, 5);
+ for (int ii = 0; ii < 6; ii++)
+ out->writeByte(0);
+ }
+
+ out->writeUint16LE(_currentLevel);
+ if (_flags.gameID == GI_EOB2)
+ out->writeSint16LE(_currentSub);
+ out->writeUint16LE(_currentBlock);
+ out->writeUint16LE(_currentDirection);
+ out->writeSint16LE(_itemInHand);
+ if (_flags.gameID == GI_EOB1) {
+ out->writeUint16LE(_hasTempDataFlags);
+ out->writeUint16LE(0);
+ if (_partyEffectFlags)
+ // Spell effect flags are completely different in original EOB I. We only use EOB II style flags in ScummVM.
+ // This doesn't matter much here, since these flags only apply to the temporary spell effects (things like prayer, haste, etc.) anyway.
+ warning("EoBCoreEngine::saveAsOriginalFile(): Party effect flags lost while exporting original EOB1 save file");
+ } else {
+ out->writeUint32LE(_hasTempDataFlags);
+ out->writeUint32LE(_partyEffectFlags);
+ }
+ if (_flags.gameID == GI_EOB2)
+ out->writeByte(0);
+ _inf->saveState(out, true);
+
+ int numItems = (_flags.gameID == GI_EOB1) ? 500 : 600;
+ for (int i = 0; i < numItems; i++) {
+ EoBItem *t = &_items[i];
+ out->writeByte(t->nameUnid);
+ out->writeByte(t->nameId);
+ out->writeByte(t->flags);
+ out->writeSByte(t->icon);
+ out->writeSByte(t->type);
+ out->writeSByte(t->pos);
+ out->writeSint16LE(t->block);
+ out->writeSint16LE(t->next);
+ out->writeSint16LE(t->prev);
+ out->writeByte(t->level);
+ out->writeSByte(t->value);
+ }
+
+ int numParts = (_flags.gameID == GI_EOB1) ? 12 : 17;
+ int partSize = (_flags.platform == Common::kPlatformFMTowns) ? 5030 :(_flags.gameID == GI_EOB1) ? 2040 : 2130;
+
+ uint8 *tempData = new uint8[5030];
+ uint8 *cmpData = new uint8[1200];
+
+ for (int i = 0; i < numParts; i++) {
+ LevelTempData *l = _lvlTempData[i];
+ memset(tempData, 0, 5030);
+ memset(cmpData, 0, 1200);
+
+ if (!l || !(_hasTempDataFlags & (1 << i))) {
+ out->write(tempData, partSize);
+ continue;
+ }
+
+ _curBlockFile = getBlockFileName(i + 1, 0);
+ const uint8 *p = getBlockFileData();
+ uint16 len = READ_LE_UINT16(p + 4);
+ p += 6;
+
+ uint8 *d = tempData;
+ for (int ii = 0; ii < 1024; ii++) {
+ for (int iii = 0; iii < 4; iii++)
+ *d++ = l->wallsXorData[ii * len + iii] ^ p[ii * len + iii];
+ }
+
+ if (_flags.platform == Common::kPlatformFMTowns) {
+ out->write(tempData, 4096);
+ } else {
+ uint32 outsize = encodeFrame4(tempData, cmpData, 4096);
+ if (outsize > 1200)
+ error("Map compression failure: size of map = %d", outsize);
+
+ out->write(cmpData, 1200);
+ }
+
+ if (_flags.platform == Common::kPlatformFMTowns)
+ out->writeUint32BE(0);
+
+ for (int ii = 0; ii < 30; ii++) {
+ EoBMonsterInPlay *m = &((EoBMonsterInPlay*)l->monsters)[ii];
+ out->writeByte(m->type);
+ out->writeByte(m->unit);
+ out->writeUint16LE(m->block);
+ out->writeByte(m->pos);
+ out->writeSByte(m->dir);
+ out->writeByte(m->animStep);
+ out->writeByte(m->shpIndex);
+ out->writeSByte(m->mode);
+ out->writeSByte(m->f_9);
+ out->writeSByte(m->curAttackFrame);
+ out->writeSByte(m->spellStatusLeft);
+ out->writeSint16LE(m->hitPointsMax);
+ out->writeSint16LE(m->hitPointsCur);
+ out->writeUint16LE(m->dest);
+ out->writeUint16LE(m->randItem);
+ out->writeUint16LE(m->fixedItem);
+ out->writeByte(m->flags);
+ out->writeByte(m->idleAnimState);
+
+ if (_flags.gameID == GI_EOB1)
+ out->writeByte(m->stepsTillRemoteAttack);
+ else
+ out->writeByte(m->curRemoteWeapon);
+
+ out->writeByte(m->numRemoteAttacks);
+ out->writeSByte(m->palette);
+
+ if (_flags.gameID == GI_EOB1) {
+ out->writeByte(0);
+ } else {
+ out->writeByte(m->directionChanged);
+ out->writeByte(m->stepsTillRemoteAttack);
+ out->writeByte(m->sub);
+ }
+ }
+
+ if (_flags.gameID == GI_EOB1)
+ continue;
+
+ for (int ii = 0; ii < 5; ii++) {
+ WallOfForce *w= &((WallOfForce*)l->wallsOfForce)[ii];
+ out->writeUint16LE(w->block);
+ out->writeUint32LE(w->duration / _tickLength);
+ }
+ }
+
+ delete[] cmpData;
+ delete[] tempData;
+
+ out->writeByte(_configMusic ? 1 : 0);
+ out->writeByte(_configMusic ? 1 : 0);
+ out->writeByte(_configHpBarGraphs ? 1 : 0);
+
+ for (int i = 51; i < 57; i++) {
+ EoBItemType *t = &_itemTypes[i];
+ out->writeUint16LE(t->invFlags);
+ out->writeUint16LE(t->handFlags);
+ out->writeSByte(t->armorClass);
+ out->writeSByte(t->allowedClasses);
+ out->writeSByte(t->requiredHands);
+ out->writeSByte(t->dmgNumDiceS);
+ out->writeSByte(t->dmgNumPipsS);
+ out->writeSByte(t->dmgIncS);
+ out->writeSByte(t->dmgNumDiceL);
+ out->writeSByte(t->dmgNumPipsL);
+ out->writeSByte(t->dmgIncL);
+ out->writeByte(t->unk1);
+ out->writeUint16LE(t->extraProperties);
+ }
+
+ out->finalize();
+ delete out;
+
+ return true;
+}
+
+void *EoBCoreEngine::generateMonsterTempData(LevelTempData *tmp) {
+ EoBMonsterInPlay *m = new EoBMonsterInPlay[30];
+ memcpy(m, _monsters, sizeof(EoBMonsterInPlay) * 30);
+ return m;
+}
+
+void EoBCoreEngine::restoreMonsterTempData(LevelTempData *tmp) {
+ memcpy(_monsters, tmp->monsters, sizeof(EoBMonsterInPlay) * 30);
+}
+
+void EoBCoreEngine::releaseMonsterTempData(LevelTempData *tmp) {
+ EoBMonsterInPlay *p = (EoBMonsterInPlay *)tmp->monsters;
+ delete[] p;
+}
+
+void *EoBCoreEngine::generateWallOfForceTempData(LevelTempData *tmp) {
+ WallOfForce *w = new WallOfForce[5];
+ memcpy(w, _wallsOfForce, sizeof(WallOfForce) * 5);
+ uint32 ct = _system->getMillis();
+ for (int i = 0; i < 5; i++)
+ w[i].duration = (w[i].duration > ct) ? w[i].duration - ct : _tickLength;
+ return w;
+}
+
+void EoBCoreEngine::restoreWallOfForceTempData(LevelTempData *tmp) {
+ memcpy(_wallsOfForce, tmp->wallsOfForce, sizeof(WallOfForce) * 5);
+ uint32 ct = _system->getMillis();
+ for (int i = 0; i < 5; i++)
+ _wallsOfForce[i].duration += ct;
+}
+
+void EoBCoreEngine::releaseWallOfForceTempData(LevelTempData *tmp) {
+ WallOfForce *p = (WallOfForce *)tmp->wallsOfForce;
+ delete[] p;
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/gui/saveload_hof.cpp b/engines/kyra/gui/saveload_hof.cpp
new file mode 100644
index 0000000000..ebfbffe1f8
--- /dev/null
+++ b/engines/kyra/gui/saveload_hof.cpp
@@ -0,0 +1,331 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !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);
+ }
+
+ // 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, 0);
+ 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, 0);
+ 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;
+
+ 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
diff --git a/engines/kyra/gui/saveload_lok.cpp b/engines/kyra/gui/saveload_lok.cpp
new file mode 100644
index 0000000000..493fcd029d
--- /dev/null
+++ b/engines/kyra/gui/saveload_lok.cpp
@@ -0,0 +1,319 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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) {
+ char file[32];
+ assert(_currentRoom < _roomTableSize);
+ int tableId = _roomTable[_currentRoom].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+
+ 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();
+ }
+ }
+ if (header.version >= 3) {
+ _lastMusicCommand = in->readSint16BE();
+ if (_lastMusicCommand != -1)
+ snd_playWanderScoreViaMap(_lastMusicCommand, 1);
+ }
+
+ // 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, 0);
+ 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 an savegame might
+ // result in no shapes being drawn at all. See bug report
+ // #2868581 "KYRA1: Invisible Brandon" for an example of this.
+ _animator->_noDrawShapesFlag = 0;
+
+ enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 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
diff --git a/engines/kyra/gui/saveload_lol.cpp b/engines/kyra/gui/saveload_lol.cpp
new file mode 100644
index 0000000000..0412dc6bdf
--- /dev/null
+++ b/engines/kyra/gui/saveload_lol.cpp
@@ -0,0 +1,583 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !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();
+ 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
diff --git a/engines/kyra/gui/saveload_mr.cpp b/engines/kyra/gui/saveload_mr.cpp
new file mode 100644
index 0000000000..c3c2d9bdf8
--- /dev/null
+++ b/engines/kyra/gui/saveload_mr.cpp
@@ -0,0 +1,329 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !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;
+
+ 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
diff --git a/engines/kyra/gui/saveload_rpg.cpp b/engines/kyra/gui/saveload_rpg.cpp
new file mode 100644
index 0000000000..7e60f001c5
--- /dev/null
+++ b/engines/kyra/gui/saveload_rpg.cpp
@@ -0,0 +1,127 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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