diff options
Diffstat (limited to 'engines/zvision')
84 files changed, 3533 insertions, 2964 deletions
diff --git a/engines/zvision/configure.engine b/engines/zvision/configure.engine index 02e31943af..226870c3fd 100644 --- a/engines/zvision/configure.engine +++ b/engines/zvision/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine zvision "ZVision" no "" "" "freetype2 16bit" +add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit" diff --git a/engines/zvision/core/clock.h b/engines/zvision/core/clock.h index cbf52be560..ae8c968111 100644 --- a/engines/zvision/core/clock.h +++ b/engines/zvision/core/clock.h @@ -67,14 +67,14 @@ public: } /** - * Pause the clock. Any future delta times will take this pause into account. - * Has no effect if the clock is already paused. - */ + * Un-pause the clock. + * Has no effect if the clock is already un-paused. + */ void start(); /** - * Un-pause the clock. - * Has no effect if the clock is already un-paused. + * Pause the clock. Any future delta times will take this pause into account. + * Has no effect if the clock is already paused. */ void stop(); }; diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp index 4dd10d6f40..336541d82a 100644 --- a/engines/zvision/core/console.cpp +++ b/engines/zvision/core/console.cpp @@ -52,6 +52,10 @@ Console::Console(ZVision *engine) : GUI::Debugger(), _engine(engine) { registerCmd("setpanoramascale", WRAP_METHOD(Console, cmdSetPanoramaScale)); registerCmd("location", WRAP_METHOD(Console, cmdLocation)); registerCmd("dumpfile", WRAP_METHOD(Console, cmdDumpFile)); + registerCmd("dumpfiles", WRAP_METHOD(Console, cmdDumpFiles)); + registerCmd("dumpimage", WRAP_METHOD(Console, cmdDumpImage)); + registerCmd("statevalue", WRAP_METHOD(Console, cmdStateValue)); + registerCmd("stateflag", WRAP_METHOD(Console, cmdStateFlag)); } bool Console::cmdLoadVideo(int argc, const char **argv) { @@ -78,12 +82,14 @@ bool Console::cmdLoadSound(int argc, const char **argv) { Audio::AudioStream *soundStream = makeRawZorkStream(argv[1], _engine); Audio::SoundHandle handle; _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); - } else if (argc == 4) { int isStereo = atoi(argv[3]); Common::File *file = new Common::File(); - file->open(argv[1]); + if (!_engine->getSearchManager()->openFile(*file, argv[1])) { + warning("File not found: %s", argv[1]); + return true; + } Audio::AudioStream *soundStream = makeRawZorkStream(file, atoi(argv[2]), isStereo == 0 ? false : true); Audio::SoundHandle handle; @@ -103,8 +109,10 @@ bool Console::cmdRawToWav(int argc, const char **argv) { } Common::File file; - if (!file.open(argv[1])) + if (!_engine->getSearchManager()->openFile(file, argv[1])) { + warning("File not found: %s", argv[1]); return true; + } Audio::AudioStream *audioStream = makeRawZorkStream(argv[1], _engine); @@ -133,6 +141,10 @@ bool Console::cmdRawToWav(int argc, const char **argv) { output.writeUint32LE(file.size() * 2); int16 *buffer = new int16[file.size()]; audioStream->readBuffer(buffer, file.size()); +#ifndef SCUMM_LITTLE_ENDIAN + for (int i = 0; i < file.size(); ++i) + buffer[i] = TO_LE_16(buffer[i]); +#endif output.write(buffer, file.size() * 2); delete[] buffer; @@ -194,7 +206,7 @@ bool Console::cmdLocation(int argc, const char **argv) { Common::String scrFile = Common::String::format("%c%c%c%c.scr", curLocation.world, curLocation.room, curLocation.node, curLocation.view); debugPrintf("Current location: world '%c', room '%c', node '%c', view '%c', offset %d, script %s\n", curLocation.world, curLocation.room, curLocation.node, curLocation.view, curLocation.offset, scrFile.c_str()); - + if (argc != 6) { debugPrintf("Use %s <char: world> <char: room> <char:node> <char:view> <int: x offset> to change your location\n", argv[0]); return true; @@ -205,6 +217,20 @@ bool Console::cmdLocation(int argc, const char **argv) { return true; } +void dumpFile(Common::SeekableReadStream *s, const char *outName) { + byte *buffer = new byte[s->size()]; + s->read(buffer, s->size()); + + Common::DumpFile dumpFile; + dumpFile.open(outName); + + dumpFile.write(buffer, s->size()); + dumpFile.flush(); + dumpFile.close(); + + delete[] buffer; +} + bool Console::cmdDumpFile(int argc, const char **argv) { if (argc != 2) { debugPrintf("Use %s <fileName> to dump a file\n", argv[0]); @@ -217,17 +243,126 @@ bool Console::cmdDumpFile(int argc, const char **argv) { return true; } - byte *buffer = new byte[f.size()]; - f.read(buffer, f.size()); + dumpFile(&f, argv[1]); - Common::DumpFile dumpFile; - dumpFile.open(argv[1]); + return true; +} - dumpFile.write(buffer, f.size()); - dumpFile.flush(); - dumpFile.close(); +bool Console::cmdDumpFiles(int argc, const char **argv) { + Common::String fileName; + Common::SeekableReadStream *in; - delete[] buffer; + if (argc != 2) { + debugPrintf("Use %s <file extension> to dump all files with a specific extension\n", argv[0]); + return true; + } + + SearchManager::MatchList fileList; + _engine->getSearchManager()->listMembersWithExtension(fileList, argv[1]); + + for (SearchManager::MatchList::iterator iter = fileList.begin(); iter != fileList.end(); ++iter) { + fileName = iter->_value.name; + debugPrintf("Dumping %s\n", fileName.c_str()); + + in = iter->_value.arch->createReadStreamForMember(iter->_value.name); + if (in) + dumpFile(in, fileName.c_str()); + delete in; + } + + return true; +} + +bool Console::cmdDumpImage(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Use %s <TGA/TGZ name> to dump a Z-Vision TGA/TGZ image into a regular BMP image\n", argv[0]); + return true; + } + + Common::String fileName = argv[1]; + if (!fileName.hasSuffix(".tga")) { + debugPrintf("%s is not an image file", argv[1]); + } + + Common::File f; + if (!_engine->getSearchManager()->openFile(f, argv[1])) { + warning("File not found: %s", argv[1]); + return true; + } + + Graphics::Surface surface; + _engine->getRenderManager()->readImageToSurface(argv[1], surface, false); + + // Open file + Common::DumpFile out; + + fileName.setChar('b', fileName.size() - 3); + fileName.setChar('m', fileName.size() - 2); + fileName.setChar('p', fileName.size() - 1); + + out.open(fileName); + + // Write BMP header + out.writeByte('B'); + out.writeByte('M'); + out.writeUint32LE(surface.h * surface.pitch + 54); + out.writeUint32LE(0); + out.writeUint32LE(54); + out.writeUint32LE(40); + out.writeUint32LE(surface.w); + out.writeUint32LE(surface.h); + out.writeUint16LE(1); + out.writeUint16LE(16); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + out.writeUint32LE(0); + + // Write pixel data to BMP + out.write(surface.getPixels(), surface.pitch * surface.h); + + out.flush(); + out.close(); + + surface.free(); + + return true; +} + +bool Console::cmdStateValue(int argc, const char **argv) { + if (argc < 2) { + debugPrintf("Use %s <valuenum> to show the value of a state variable\n", argv[0]); + debugPrintf("Use %s <valuenum> <newvalue> to set the value of a state variable\n", argv[0]); + return true; + } + + int valueNum = atoi(argv[1]); + int newValue = (argc > 2) ? atoi(argv[2]) : -1; + + if (argc == 2) + debugPrintf("[%d] = %d\n", valueNum, _engine->getScriptManager()->getStateValue(valueNum)); + else if (argc == 3) + _engine->getScriptManager()->setStateValue(valueNum, newValue); + + return true; +} + +bool Console::cmdStateFlag(int argc, const char **argv) { + if (argc < 2) { + debugPrintf("Use %s <flagnum> to show the value of a state flag\n", argv[0]); + debugPrintf("Use %s <flagnum> <newvalue> to set the value of a state flag\n", argv[0]); + return true; + } + + int valueNum = atoi(argv[1]); + int newValue = (argc > 2) ? atoi(argv[2]) : -1; + + if (argc == 2) + debugPrintf("[%d] = %d\n", valueNum, _engine->getScriptManager()->getStateFlag(valueNum)); + else if (argc == 3) + _engine->getScriptManager()->setStateFlag(valueNum, newValue); return true; } diff --git a/engines/zvision/core/console.h b/engines/zvision/core/console.h index 299bd6127f..ac834185a0 100644 --- a/engines/zvision/core/console.h +++ b/engines/zvision/core/console.h @@ -46,6 +46,10 @@ private: bool cmdSetPanoramaScale(int argc, const char **argv); bool cmdLocation(int argc, const char **argv); bool cmdDumpFile(int argc, const char **argv); + bool cmdDumpFiles(int argc, const char **argv); + bool cmdDumpImage(int argc, const char **argv); + bool cmdStateValue(int argc, const char **argv); + bool cmdStateFlag(int argc, const char **argv); }; } // End of namespace ZVision diff --git a/engines/zvision/core/events.cpp b/engines/zvision/core/events.cpp index c66e61a61a..cc1c00b6d0 100644 --- a/engines/zvision/core/events.cpp +++ b/engines/zvision/core/events.cpp @@ -28,8 +28,9 @@ #include "zvision/graphics/cursors/cursor_manager.h" #include "zvision/graphics/render_manager.h" #include "zvision/scripting/script_manager.h" -#include "zvision/core/menu.h" +#include "zvision/scripting/menu.h" #include "zvision/sound/zork_raw.h" +#include "zvision/text/string_manager.h" #include "common/events.h" #include "common/system.h" @@ -39,23 +40,50 @@ namespace ZVision { +void ZVision::pushKeyToCheatBuf(uint8 key) { + for (int i = 0; i < KEYBUF_SIZE - 1; i++) + _cheatBuffer[i] = _cheatBuffer[i + 1]; + + _cheatBuffer[KEYBUF_SIZE - 1] = key; +} + +bool ZVision::checkCode(const char *code) { + int codeLen = strlen(code); + + if (codeLen > KEYBUF_SIZE) + return false; + + for (int i = 0; i < codeLen; i++) + if (code[i] != _cheatBuffer[KEYBUF_SIZE - codeLen + i] && code[i] != '?') + return false; + + return true; +} + +uint8 ZVision::getBufferedKey(uint8 pos) { + if (pos >= KEYBUF_SIZE) + return 0; + else + return _cheatBuffer[KEYBUF_SIZE - pos - 1]; +} + void ZVision::shortKeys(Common::Event event) { if (event.kbd.hasFlags(Common::KBD_CTRL)) { switch (event.kbd.keycode) { case Common::KEYCODE_s: - if (getMenuBarEnable() & menuBar_Save) + if (_menu->getEnable() & kMenubarSave) _scriptManager->changeLocation('g', 'j', 's', 'e', 0); break; case Common::KEYCODE_r: - if (getMenuBarEnable() & menuBar_Restore) + if (_menu->getEnable() & kMenubarRestore) _scriptManager->changeLocation('g', 'j', 'r', 'e', 0); break; case Common::KEYCODE_p: - if (getMenuBarEnable() & menuBar_Settings) + if (_menu->getEnable() & kMenubarSettings) _scriptManager->changeLocation('g', 'j', 'p', 'e', 0); break; case Common::KEYCODE_q: - if (getMenuBarEnable() & menuBar_Exit) + if (_menu->getEnable() & kMenubarExit) ifQuit(); break; default: @@ -65,16 +93,21 @@ void ZVision::shortKeys(Common::Event event) { } void ZVision::cheatCodes(uint8 key) { + Location loc = _scriptManager->getCurrentLocation(); + // Do not process cheat codes while in the game menus + if (loc.world == 'g' && loc.room == 'j') + return; + pushKeyToCheatBuf(key); if (getGameId() == GID_GRANDINQUISITOR) { if (checkCode("IMNOTDEAF")) { // Unknown cheat - showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented")); + _renderManager->showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented")); } if (checkCode("3100OPB")) { - showDebugMsg(Common::String::format("Current location: %c%c%c%c", + _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c", _scriptManager->getStateValue(StateKey_World), _scriptManager->getStateValue(StateKey_Room), _scriptManager->getStateValue(StateKey_Node), @@ -91,9 +124,9 @@ void ZVision::cheatCodes(uint8 key) { } // There are 3 more cheats in script files: - // - "EAT ME": gjcr.scr - // - "WHOAMI": hp1e.scr - // - "HUISOK": uh1f.scr + // - "WHOAMI": gjcr.scr + // - "HUISOK": hp1e.scr + // - "EAT ME": uh1f.scr } else if (getGameId() == GID_NEMESIS) { if (checkCode("CHLOE")) { _scriptManager->changeLocation('t', 'm', '2', 'g', 0); @@ -101,7 +134,7 @@ void ZVision::cheatCodes(uint8 key) { } if (checkCode("77MASSAVE")) { - showDebugMsg(Common::String::format("Current location: %c%c%c%c", + _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c", _scriptManager->getStateValue(StateKey_World), _scriptManager->getStateValue(StateKey_Room), _scriptManager->getStateValue(StateKey_Node), @@ -118,9 +151,8 @@ void ZVision::cheatCodes(uint8 key) { } if (checkCode("HELLOSAILOR")) { - Location loc = _scriptManager->getCurrentLocation(); Audio::AudioStream *soundStream; - if (loc.world == 'v' && loc.room == 'b' && loc.node == '1' && loc.view == '0') { + if (loc == "vb10") { soundStream = makeRawZorkStream("v000hpta.raw", this); } else { soundStream = makeRawZorkStream("v000hnta.raw", this); @@ -130,21 +162,29 @@ void ZVision::cheatCodes(uint8 key) { } } - if (checkCode("FRAME")) - showDebugMsg(Common::String::format("FPS: ???, not implemented")); + if (checkCode("FRAME")) { + Common::String fpsStr = Common::String::format("FPS: %d", getFPS()); + _renderManager->showDebugMsg(fpsStr); + } + + if (checkCode("COMPUTERARCH")) + _renderManager->showDebugMsg("COMPUTERARCH: var-viewer not implemented"); + // This cheat essentially toggles the GOxxxx cheat below if (checkCode("XYZZY")) _scriptManager->setStateValue(StateKey_DebugCheats, 1 - _scriptManager->getStateValue(StateKey_DebugCheats)); - if (checkCode("COMPUTERARCH")) - showDebugMsg(Common::String::format("COMPUTERARCH: var-viewer not implemented")); - if (_scriptManager->getStateValue(StateKey_DebugCheats) == 1) if (checkCode("GO????")) _scriptManager->changeLocation(getBufferedKey(3), getBufferedKey(2), getBufferedKey(1), getBufferedKey(0), 0); + + // Show the Venus screen when "?" or "/" is pressed while inside the temple world + if (_scriptManager->getStateValue(StateKey_VenusEnable) == 1) + if (getBufferedKey(0) == 0xBF && _scriptManager->getStateValue(StateKey_World) == 't') + _scriptManager->changeLocation('g', 'j', 'h', 'e', 0); } void ZVision::processEvents() { @@ -194,19 +234,24 @@ void ZVision::processEvents() { case Common::KEYCODE_LEFT: case Common::KEYCODE_RIGHT: if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA) - _kbdVelocity = (_event.kbd.keycode == Common::KEYCODE_LEFT ? - -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) : - _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; + _keyboardVelocity = (_event.kbd.keycode == Common::KEYCODE_LEFT ? + -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) : + _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; break; case Common::KEYCODE_UP: case Common::KEYCODE_DOWN: if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT) - _kbdVelocity = (_event.kbd.keycode == Common::KEYCODE_UP ? - -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) : - _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; + _keyboardVelocity = (_event.kbd.keycode == Common::KEYCODE_UP ? + -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) : + _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; break; + case Common::KEYCODE_F10: { + Common::String fpsStr = Common::String::format("FPS: %d", getFPS()); + _renderManager->showDebugMsg(fpsStr); + } + break; default: break; } @@ -226,12 +271,12 @@ void ZVision::processEvents() { case Common::KEYCODE_LEFT: case Common::KEYCODE_RIGHT: if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA) - _kbdVelocity = 0; + _keyboardVelocity = 0; break; case Common::KEYCODE_UP: case Common::KEYCODE_DOWN: if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT) - _kbdVelocity = 0; + _keyboardVelocity = 0; break; default: break; @@ -279,26 +324,33 @@ void ZVision::onMouseMove(const Common::Point &pos) { // | // ^ - if (_workingWindow.contains(pos)) { - cursorWasChanged = _scriptManager->onMouseMove(pos, imageCoord); + // Clip the horizontal mouse position to the working window + Common::Point clippedPos = pos; + clippedPos.x = CLIP<int16>(pos.x, _workingWindow.left + 1, _workingWindow.right - 1); + + if (_workingWindow.contains(clippedPos)) { + cursorWasChanged = _scriptManager->onMouseMove(clippedPos, imageCoord); RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); if (renderState == RenderTable::PANORAMA) { - if (pos.x >= _workingWindow.left && pos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) { + if (clippedPos.x >= _workingWindow.left && clippedPos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((pos.x - (ROTATION_SCREEN_EDGE_OFFSET + _workingWindow.left)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.left)) - mspeed).toInt(), -1); + _cursorManager->changeCursor(CursorIndex_Left); cursorWasChanged = true; - } else if (pos.x <= _workingWindow.right && pos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) { + } else if (clippedPos.x <= _workingWindow.right && clippedPos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((pos.x - (_workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MAX((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1); _cursorManager->changeCursor(CursorIndex_Right); cursorWasChanged = true; @@ -306,21 +358,23 @@ void ZVision::onMouseMove(const Common::Point &pos) { _mouseVelocity = 0; } } else if (renderState == RenderTable::TILT) { - if (pos.y >= _workingWindow.top && pos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) { + if (clippedPos.y >= _workingWindow.top && clippedPos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((pos.y - (_workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - mspeed).toInt(), -1); _cursorManager->changeCursor(CursorIndex_UpArr); cursorWasChanged = true; - } else if (pos.y <= _workingWindow.bottom && pos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) { + } else if (clippedPos.y <= _workingWindow.bottom && clippedPos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((pos.y - (_workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MAX((Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1); _cursorManager->changeCursor(CursorIndex_DownArr); cursorWasChanged = true; @@ -441,4 +495,12 @@ uint8 ZVision::getZvisionKey(Common::KeyCode scummKeyCode) { return 0; } +bool ZVision::ifQuit() { + if (_renderManager->askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) { + quitGame(); + return true; + } + return false; +} + } // End of namespace ZVision diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp index ebf5bdcfdd..f44e653c2a 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -24,9 +24,10 @@ #include "base/plugins.h" +#include "engines/advancedDetector.h" + #include "zvision/zvision.h" -#include "zvision/detection.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/script_manager.h" #include "common/translation.h" @@ -36,143 +37,43 @@ namespace ZVision { -uint32 ZVision::getFeatures() const { - return _gameDescription->desc.flags; -} +struct ZVisionGameDescription { + ADGameDescription desc; + ZVisionGameId gameId; +}; +ZVisionGameId ZVision::getGameId() const { + return _gameDescription->gameId; +} Common::Language ZVision::getLanguage() const { return _gameDescription->desc.language; } +uint32 ZVision::getFeatures() const { + return _gameDescription->desc.flags; +} } // End of namespace ZVision -static const PlainGameDescriptor zVisionGames[] = { - {"zvision", "ZVision Game"}, - {"znemesis", "Zork Nemesis: The Forbidden Lands"}, - {"zgi", "Zork: Grand Inquisitor"}, - {0, 0} -}; - -namespace ZVision { - -static const ZVisionGameDescription gameDescriptions[] = { - - { - // Zork Nemesis English version - { - "znemesis", - 0, - AD_ENTRY1s("CSCR.ZFS", "88226e51a205d2e50c67a5237f3bd5f2", 2397741), - Common::EN_ANY, - Common::kPlatformDOS, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) - }, - GID_NEMESIS - }, - - { - // Zork Nemesis English demo version - { - "znemesis", - "Demo", - AD_ENTRY1s("SCRIPTS.ZFS", "64f1e881394e9462305104f99513c833", 380539), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_DEMO, - GUIO1(GUIO_NONE) - }, - GID_NEMESIS - }, - - { - // Zork Grand Inquisitor English CD version - { - "zgi", - "CD", - AD_ENTRY1s("SCRIPTS.ZFS", "81efd40ecc3d22531e211368b779f17f", 8336944), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) - }, - GID_GRANDINQUISITOR - }, - - { - // Zork Grand Inquisitor English demo version - { - "zgi", - "Demo", - AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_DEMO, - GUIO1(GUIO_NONE) - }, - GID_GRANDINQUISITOR - }, - - { - // Zork Grand Inquisitor English DVD version - { - "zgi", - "DVD", - AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326), - Common::EN_ANY, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) - }, - GID_GRANDINQUISITOR - }, - - { - AD_TABLE_END_MARKER, - GID_NONE - } -}; - -} // End of namespace ZVision - -static const char *directoryGlobs[] = { - "znemscr", - 0 -}; - -static const ExtraGuiOption ZVisionExtraGuiOption = { - _s("Use original save/load screens"), - _s("Use the original save/load screens, instead of the ScummVM ones"), - "originalsaveload", - false -}; - -static const ExtraGuiOption ZVisionExtraGuiOption2 = { - _s("Double FPS"), - _s("Halve the update delay"), - "doublefps", - false -}; +#include "zvision/detection_tables.h" class ZVisionMetaEngine : public AdvancedMetaEngine { public: - ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), zVisionGames) { + ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), ZVision::zVisionGames, ZVision::optionsList) { _maxScanDepth = 2; - _directoryGlobs = directoryGlobs; + _directoryGlobs = ZVision::directoryGlobs; _singleid = "zvision"; } virtual const char *getName() const { - return "ZVision"; + return "Z-Vision"; } virtual const char *getOriginalCopyright() const { - return "ZVision Activision (C) 1996"; + return "Z-Vision (C) 1996 Activision"; } virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const; void removeSaveState(const char *target, int slot) const; @@ -202,7 +103,7 @@ Common::Error ZVision::ZVision::loadGameState(int slot) { } Common::Error ZVision::ZVision::saveGameState(int slot, const Common::String &desc) { - _saveManager->saveGame(slot, desc); + _saveManager->saveGame(slot, desc, false); return Common::kNoError; } @@ -223,13 +124,6 @@ bool ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADG return gd != 0; } -const ExtraGuiOptions ZVisionMetaEngine::getExtraGuiOptions(const Common::String &target) const { - ExtraGuiOptions options; - options.push_back(ZVisionExtraGuiOption); - options.push_back(ZVisionExtraGuiOption2); - return options; -} - SaveStateList ZVisionMetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); ZVision::SaveGameHeader header; @@ -308,6 +202,11 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in if (successfulRead) { SaveStateDescriptor desc(slot, header.saveName); + // Do not allow save slot 0 (used for auto-saving) to be deleted or + // overwritten. + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + desc.setThumbnail(header.thumbnail); if (header.version > 0) { diff --git a/engines/zvision/detection.h b/engines/zvision/detection.h deleted file mode 100644 index f80cac79ec..0000000000 --- a/engines/zvision/detection.h +++ /dev/null @@ -1,43 +0,0 @@ -/* 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 ZVISION_DETECTION_H -#define ZVISION_DETECTION_H - -#include "engines/advancedDetector.h" - -namespace ZVision { - -enum ZVisionGameId { - GID_NONE = 0, - GID_NEMESIS = 1, - GID_GRANDINQUISITOR = 2 -}; - -struct ZVisionGameDescription { - ADGameDescription desc; - ZVisionGameId gameId; -}; - -} - -#endif diff --git a/engines/zvision/detection_tables.h b/engines/zvision/detection_tables.h new file mode 100644 index 0000000000..06bc58ee7f --- /dev/null +++ b/engines/zvision/detection_tables.h @@ -0,0 +1,277 @@ +/* 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 ZVISION_DETECTION_TABLES_H +#define ZVISION_DETECTION_TABLES_H + +namespace ZVision { + +static const PlainGameDescriptor zVisionGames[] = { + { "zvision", "Z-Vision Game" }, + { "znemesis", "Zork Nemesis: The Forbidden Lands" }, + { "zgi", "Zork: Grand Inquisitor" }, + { 0, 0 } +}; + +static const char *directoryGlobs[] = { + "znemscr", + 0 +}; + +#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1 +#define GAMEOPTION_DOUBLE_FPS GUIO_GAMEOPTIONS2 +#define GAMEOPTION_ENABLE_VENUS GUIO_GAMEOPTIONS3 +#define GAMEOPTION_DISABLE_ANIM_WHILE_TURNING GUIO_GAMEOPTIONS4 +#define GAMEOPTION_USE_HIRES_MPEG_MOVIES GUIO_GAMEOPTIONS5 + +static const ADExtraGuiOptionsMap optionsList[] = { + + { + GAMEOPTION_ORIGINAL_SAVELOAD, + { + _s("Use original save/load screens"), + _s("Use the original save/load screens instead of the ScummVM interface"), + "originalsaveload", + false + } + }, + + { + GAMEOPTION_DOUBLE_FPS, + { + _s("Double FPS"), + _s("Increase framerate from 30 to 60 FPS"), + "doublefps", + false + } + }, + + { + GAMEOPTION_ENABLE_VENUS, + { + _s("Enable Venus"), + _s("Enable the Venus help system"), + "venusenabled", + true + } + }, + + { + GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, + { + _s("Disable animation while turning"), + _s("Disable animation while turning in panorama mode"), + "noanimwhileturning", + false + } + }, + + { + GAMEOPTION_USE_HIRES_MPEG_MOVIES, + { + _s("Use high resolution MPEG video"), + _s("Use MPEG video from the DVD version, instead of lower resolution AVI"), + "mpegmovies", + true + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + +static const ZVisionGameDescription gameDescriptions[] = { + + { + // Zork Nemesis English version + { + "znemesis", + 0, + AD_ENTRY1s("CSCR.ZFS", "88226e51a205d2e50c67a5237f3bd5f2", 2397741), + Common::EN_ANY, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { + // Zork Nemesis French version + { + "znemesis", + 0, + { + { "CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873 }, + { "NEMESIS.STR", 0, "333bcb17bbb7f57cae742fbbe44f56f3", 9219 }, + AD_LISTEND + }, + Common::FR_FRA, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { + // Zork Nemesis German version + { + "znemesis", + 0, + { + { "CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873 }, + { "NEMESIS.STR", 0, "3d1a12b907751653866cffc6d4dfb331", 9505 }, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { + // Zork Nemesis Italian version + { + "znemesis", + 0, + { + { "CSCR.ZFS", 0, "f04113357b4748c13efcb58b4629887c", 2577873 }, + { "NEMESIS.STR", 0, "7c568feca8d9f9ae855c47183612c305", 9061 }, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { + // Zork Nemesis English demo version + { + "znemesis", + "Demo", + AD_ENTRY1s("SCRIPTS.ZFS", "64f1e881394e9462305104f99513c833", 380539), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_NEMESIS + }, + + { + // Zork Grand Inquisitor English CD version + { + "zgi", + "CD", + AD_ENTRY1s("SCRIPTS.ZFS", "81efd40ecc3d22531e211368b779f17f", 8336944), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_GRANDINQUISITOR + }, + + { + // Zork Grand Inquisitor French CD version, reported by ulrichh on IRC + { + "zgi", + "CD", + AD_ENTRY1s("SCRIPTS.ZFS", "4d1ec4ade7ecc9ee9ec591d43ca3d213", 8338133), + Common::FR_FRA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_GRANDINQUISITOR + }, + + { + // Zork Grand Inquisitor German CD version, reported by breit in bug #6760 + { + "zgi", + "CD", + AD_ENTRY1s("SCRIPTS.ZFS", "b7ac7e331b9b7f884590b0b325b560c8", 8338133), + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_GRANDINQUISITOR + }, + + { + // Zork Grand Inquisitor Spanish CD version, reported by dianiu in bug #6764 + { + "zgi", + "CD", + AD_ENTRY1s("SCRIPTS.ZFS", "5cdc4b99c1134053af135aae71326fd1", 8338141), + Common::ES_ESP, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_GRANDINQUISITOR + }, + + { + // Zork Grand Inquisitor English DVD version + { + "zgi", + "DVD", + AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, GAMEOPTION_USE_HIRES_MPEG_MOVIES) + }, + GID_GRANDINQUISITOR + }, + + { + // Zork Grand Inquisitor English demo version + { + "zgi", + "Demo", + AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) + }, + GID_GRANDINQUISITOR + }, + + { + AD_TABLE_END_MARKER, + GID_NONE + } +}; + +} // End of namespace ZVision + +#endif diff --git a/engines/zvision/file/lzss_read_stream.cpp b/engines/zvision/file/lzss_read_stream.cpp index 6f27eaa765..ca10af7d72 100644 --- a/engines/zvision/file/lzss_read_stream.cpp +++ b/engines/zvision/file/lzss_read_stream.cpp @@ -31,8 +31,9 @@ LzssReadStream::LzssReadStream(Common::SeekableReadStream *source) // It's convention to set the starting cursor position to blockSize - 16 _windowCursor(0x0FEE), _eosFlag(false) { - // Clear the window to null - memset(_window, 0, BLOCK_SIZE); + // All values up to _windowCursor inits by 0x20 + memset(_window, 0x20, _windowCursor); + memset(_window + _windowCursor, 0, BLOCK_SIZE - _windowCursor); } uint32 LzssReadStream::decompressBytes(byte *destination, uint32 numberOfBytes) { diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/file/save_manager.cpp index 20bd39fde5..d169679e28 100644 --- a/engines/zvision/core/save_manager.cpp +++ b/engines/zvision/file/save_manager.cpp @@ -22,8 +22,8 @@ #include "common/scummsys.h" -#include "zvision/core/save_manager.h" #include "zvision/zvision.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" @@ -69,7 +69,7 @@ bool SaveManager::scummVMSaveLoadDialog(bool isSave) { return false; if (isSave) { - saveGame(slot, desc); + saveGame(slot, desc, false); return true; } else { Common::ErrorCode result = loadGame(slot).getCode(); @@ -77,54 +77,34 @@ bool SaveManager::scummVMSaveLoadDialog(bool isSave) { } } -void SaveManager::saveGame(uint slot, const Common::String &saveName) { - // The games only support 20 slots - //assert(slot <= 1 && slot <= 20); +void SaveManager::saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer) { + if (!_tempSave && useSaveBuffer) + return; Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - writeSaveGameHeader(file, saveName); + writeSaveGameHeader(file, saveName, useSaveBuffer); - _engine->getScriptManager()->serialize(file); + if (useSaveBuffer) + file->write(_tempSave->getData(), _tempSave->size()); + else + _engine->getScriptManager()->serialize(file); file->finalize(); delete file; -} - -void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream) { - Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); - Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - - writeSaveGameHeader(file, saveName); - - file->write(stream->getData(), stream->size()); - file->finalize(); - delete file; -} - -void SaveManager::saveGameBuffered(uint slot, const Common::String &saveName) { - if (_tempSave) { - saveGame(slot, saveName, _tempSave); + if (useSaveBuffer) flushSaveBuffer(); - } + + _lastSaveTime = g_system->getMillis(); } void SaveManager::autoSave() { - Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); - - writeSaveGameHeader(file, "auto"); - - _engine->getScriptManager()->serialize(file); - - // Cleanup - file->finalize(); - delete file; + saveGame(0, "Auto save", false); } -void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName) { - +void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer) { file->writeUint32BE(SAVEGAME_ID); // Write version @@ -134,8 +114,11 @@ void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::S file->writeString(saveName); file->writeByte(0); - // Create a thumbnail and save it - Graphics::saveThumbnail(*file); + // Save the game thumbnail + if (useSaveBuffer) + file->write(_tempThumbnail->getData(), _tempThumbnail->size()); + else + Graphics::saveThumbnail(*file); // Write out the save date/time TimeDate td; @@ -147,15 +130,27 @@ void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::S file->writeSint16LE(td.tm_min); } -Common::Error SaveManager::loadGame(uint slot) { - // The games only support 20 slots - //assert(slot <= 1 && slot <= 20); +Common::Error SaveManager::loadGame(int slot) { + Common::SeekableReadStream *saveFile = NULL; - Common::SeekableReadStream *saveFile = getSlotFile(slot); - if (saveFile == 0) { - return Common::kPathDoesNotExist; + if (slot >= 0) { + saveFile = getSlotFile(slot); + } else { + saveFile = _engine->getSearchManager()->openFile("r.svr"); + if (!saveFile) { + Common::File *restoreFile = new Common::File(); + if (!restoreFile->open("r.svr")) { + delete restoreFile; + return Common::kPathDoesNotExist; + } + + saveFile = restoreFile; + } } + if (!saveFile) + return Common::kPathDoesNotExist; + // Read the header SaveGameHeader header; if (!readSaveGameHeader(saveFile, header)) { @@ -170,33 +165,27 @@ Common::Error SaveManager::loadGame(uint slot) { if (header.thumbnail) delete header.thumbnail; - return Common::kNoError; -} - -Common::Error SaveManager::loadGame(const Common::String &saveName) { - Common::File *saveFile = _engine->getSearchManager()->openFile(saveName); - if (saveFile == NULL) { - saveFile = new Common::File; - if (!saveFile->open(saveName)) { - delete saveFile; - return Common::kPathDoesNotExist; + if (_engine->getGameId() == GID_NEMESIS && scriptManager->getCurrentLocation() == "tv2f") { + // WORKAROUND for script bug #6793: location tv2f (stairs) has two states: + // one at the top of the stairs, and one at the bottom. When the player + // goes to the bottom of the stairs, the screen changes, and hotspot + // 4652 (exit opposite the stairs) is enabled. However, the variable that + // controls the state (2408) is reset when the player goes down the stairs. + // Furthermore, the room's initialization script disables the stair exit + // control (4652). This leads to an impossible situation, where all the + // exit controls are disabled, and the player can't more anywhere. Thus, + // when loading a game in that room, we check for that impossible + // situation, which only occurs after the player has moved down the stairs, + // and fix it here by setting the correct background, and enabling the + // stair exit hotspot. + if ((scriptManager->getStateFlag(2411) & Puzzle::DISABLED) && + (scriptManager->getStateFlag(2408) & Puzzle::DISABLED) && + (scriptManager->getStateFlag(4652) & Puzzle::DISABLED)) { + _engine->getRenderManager()->setBackgroundImage("tv2fb21c.tga"); + scriptManager->unsetStateFlag(4652, Puzzle::DISABLED); } } - // Read the header - SaveGameHeader header; - if (!readSaveGameHeader(saveFile, header)) { - return Common::kUnknownError; - } - - ScriptManager *scriptManager = _engine->getScriptManager(); - // Update the state table values - scriptManager->deserialize(saveFile); - - delete saveFile; - if (header.thumbnail) - delete header.thumbnail; - return Common::kNoError; } @@ -216,7 +205,7 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea return true; } if (tag != SAVEGAME_ID) { - warning("File is not a ZVision save file. Aborting load"); + warning("File is not a Z-Vision save file. Aborting load"); return false; } @@ -226,7 +215,13 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea // Check that the save version isn't newer than this binary if (header.version > SAVE_VERSION) { uint tempVersion = header.version; - GUI::MessageDialog dialog(Common::String::format("This save file uses version %u, but this engine only supports up to version %d. You will need an updated version of the engine to use this save file.", tempVersion, SAVE_VERSION), "OK"); + GUI::MessageDialog dialog( + Common::String::format( + "This save file uses version %u, but this engine only " + "supports up to version %d. You will need an updated version " + "of the engine to use this save file.", tempVersion, SAVE_VERSION + ), + "OK"); dialog.runModal(); } @@ -277,18 +272,20 @@ Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) { } void SaveManager::prepareSaveBuffer() { - if (_tempSave) - delete _tempSave; + delete _tempThumbnail; + _tempThumbnail = new Common::MemoryWriteStreamDynamic; + Graphics::saveThumbnail(*_tempThumbnail); + delete _tempSave; _tempSave = new Common::MemoryWriteStreamDynamic; - _engine->getScriptManager()->serialize(_tempSave); } void SaveManager::flushSaveBuffer() { - if (_tempSave) - delete _tempSave; + delete _tempThumbnail; + _tempThumbnail = NULL; + delete _tempSave; _tempSave = NULL; } diff --git a/engines/zvision/core/save_manager.h b/engines/zvision/file/save_manager.h index 75841331e7..9e816373ea 100644 --- a/engines/zvision/core/save_manager.h +++ b/engines/zvision/file/save_manager.h @@ -48,13 +48,18 @@ struct SaveGameHeader { class SaveManager { public: - SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL) {} + SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _tempThumbnail(NULL), _lastSaveTime(0) {} ~SaveManager() { flushSaveBuffer(); } + uint32 getLastSaveTime() const { + return _lastSaveTime; + } + private: ZVision *_engine; + uint32 _lastSaveTime; static const uint32 SAVEGAME_ID; enum { @@ -62,13 +67,13 @@ private: SAVE_VERSION = 1 }; + Common::MemoryWriteStreamDynamic *_tempThumbnail; Common::MemoryWriteStreamDynamic *_tempSave; public: /** * Called every room change. Saves the state of the room just before - * we switched rooms. Uses ZVision::generateAutoSaveFileName() to - * create the save file name. + * the room changes. */ void autoSave(); /** @@ -79,17 +84,14 @@ public: * @param slot The save slot this save pertains to. Must be [1, 20] * @param saveName The internal name for this save. This is NOT the name of the actual save file. */ - void saveGame(uint slot, const Common::String &saveName); - void saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream); - void saveGameBuffered(uint slot, const Common::String &saveName); + void saveGame(uint slot, const Common::String &saveName, bool useSaveBuffer); /** * Loads the state data from the save file that slot references. Uses * ZVision::generateSaveFileName(slot) to get the save file name. * * @param slot The save slot to load. Must be [1, 20] */ - Common::Error loadGame(uint slot); - Common::Error loadGame(const Common::String &saveName); + Common::Error loadGame(int slot); Common::SeekableReadStream *getSlotFile(uint slot); bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header); @@ -98,7 +100,7 @@ public: void flushSaveBuffer(); bool scummVMSaveLoadDialog(bool isSave); private: - void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName); + void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName, bool useSaveBuffer); }; } // End of namespace ZVision diff --git a/engines/zvision/file/search_manager.cpp b/engines/zvision/file/search_manager.cpp index 30c269c290..821b85b053 100644 --- a/engines/zvision/file/search_manager.cpp +++ b/engines/zvision/file/search_manager.cpp @@ -62,19 +62,6 @@ SearchManager::~SearchManager() { _archList.clear(); } -void SearchManager::addPatch(const Common::String &src, const Common::String &dst) { - Common::String lowerCaseName = dst; - lowerCaseName.toLowercase(); - - SearchManager::MatchList::iterator it = _files.find(lowerCaseName); - - if (it != _files.end()) { - lowerCaseName = src; - lowerCaseName.toLowercase(); - _files[lowerCaseName] = it->_value; - } -} - void SearchManager::addFile(const Common::String &name, Common::Archive *arch) { bool addArch = true; Common::List<Common::Archive *>::iterator it = _archList.begin(); @@ -147,9 +134,10 @@ bool SearchManager::hasFile(const Common::String &name) { return false; } -void SearchManager::loadZix(const Common::String &name) { +bool SearchManager::loadZix(const Common::String &name) { Common::File file; - file.open(name); + if (!file.open(name)) + return false; Common::String line; @@ -160,7 +148,7 @@ void SearchManager::loadZix(const Common::String &name) { } if (file.eos()) - return; + error("Corrupt ZIX file: %s", name.c_str()); Common::Array<Common::Archive *> archives; @@ -169,37 +157,47 @@ void SearchManager::loadZix(const Common::String &name) { line.trim(); if (line.matchString("----------*", true)) break; - else if (line.matchString("DIR:*", true)) { - Common::String path(line.c_str() + 5); + else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true) || line.matchString("CD2:*", true)) { Common::Archive *arc; - char tempPath[128]; - strcpy(tempPath, path.c_str()); + + Common::String path(line.c_str() + 5); for (uint i = 0; i < path.size(); i++) - if (tempPath[i] == '\\') - tempPath[i] = '/'; + if (path[i] == '\\') + path.setChar('/', i); + + // Check if NEMESIS.ZIX/MEDIUM.ZIX refers to the znemesis folder, and + // check the game root folder instead + if (path.hasPrefix("znemesis/")) + path = Common::String(path.c_str() + 9); + + // Check if INQUIS.ZIX refers to the ZGI folder, and check the game + // root folder instead + if (path.hasPrefix("zgi/")) + path = Common::String(path.c_str() + 4); + if (path.hasPrefix("zgi_e/")) + path = Common::String(path.c_str() + 6); - path = Common::String(tempPath); if (path.size() && path[0] == '.') path.deleteChar(0); if (path.size() && path[0] == '/') path.deleteChar(0); - - if (path.matchString("*.zfs", true)) - arc = new ZfsArchive(path); - else { - if (path.size()) { - if (path[path.size() - 1] == '\\' || path[path.size() - 1] == '/') - path.deleteLastChar(); - if (path.size()) - for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it) - if (path.equalsIgnoreCase(*it)) { - path = *it; - break; - } + if (path.size() && path.hasSuffix("/")) + path.deleteLastChar(); + + // Handle paths in case-sensitive file systems (bug #6775) + if (path.size()) { + for (Common::List<Common::String>::iterator it = _dirList.begin(); it != _dirList.end(); ++it) { + if (path.equalsIgnoreCase(*it)) { + path = *it; + break; + } } + } + if (path.matchString("*.zfs", true)) { + arc = new ZfsArchive(path); + } else { path = Common::String::format("%s/%s", _root.c_str(), path.c_str()); - arc = new Common::FSDirectory(path); } archives.push_back(arc); @@ -207,7 +205,7 @@ void SearchManager::loadZix(const Common::String &name) { } if (file.eos()) - return; + error("Corrupt ZIX file: %s", name.c_str()); while (!file.eos()) { line = file.readLine(); @@ -220,6 +218,8 @@ void SearchManager::loadZix(const Common::String &name) { } } } + + return true; } void SearchManager::addDir(const Common::String &name) { @@ -265,7 +265,7 @@ void SearchManager::addDir(const Common::String &name) { void SearchManager::listDirRecursive(Common::List<Common::String> &_list, const Common::FSNode &fsNode, int depth) { Common::FSList fsList; - if ( fsNode.getChildren(fsList) ) { + if (fsNode.getChildren(fsList)) { _list.push_back(fsNode.getPath()); @@ -275,4 +275,11 @@ void SearchManager::listDirRecursive(Common::List<Common::String> &_list, const } } +void SearchManager::listMembersWithExtension(MatchList &fileList, Common::String extension) { + for (SearchManager::MatchList::iterator it = _files.begin(); it != _files.end(); ++it) { + if (it->_key.hasSuffix(extension)) + fileList[it->_key] = it->_value; + } +} + } // End of namespace ZVision diff --git a/engines/zvision/file/search_manager.h b/engines/zvision/file/search_manager.h index fdd70fd381..0d0ab14d31 100644 --- a/engines/zvision/file/search_manager.h +++ b/engines/zvision/file/search_manager.h @@ -39,33 +39,30 @@ public: void addFile(const Common::String &name, Common::Archive *arch); void addDir(const Common::String &name); - void addPatch(const Common::String &src, const Common::String &dst); Common::File *openFile(const Common::String &name); bool openFile(Common::File &file, const Common::String &name); bool hasFile(const Common::String &name); - void loadZix(const Common::String &name); - -private: - - void listDirRecursive(Common::List<Common::String> &dirList, const Common::FSNode &fsNode, int depth); + bool loadZix(const Common::String &name); struct Node { Common::String name; Common::Archive *arch; }; - Common::List<Common::String> _dirList; - typedef Common::HashMap<Common::String, Node, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> MatchList; - Common::List<Common::Archive *> _archList; - MatchList _files; - - Common::String _root; + void listMembersWithExtension(MatchList &fileList, Common::String extension); private: + + void listDirRecursive(Common::List<Common::String> &dirList, const Common::FSNode &fsNode, int depth); + + Common::List<Common::String> _dirList; + Common::List<Common::Archive *> _archList; + Common::String _root; + MatchList _files; }; } diff --git a/engines/zvision/graphics/cursors/cursor.cpp b/engines/zvision/graphics/cursors/cursor.cpp index 07323b45c4..2c011668ac 100644 --- a/engines/zvision/graphics/cursors/cursor.cpp +++ b/engines/zvision/graphics/cursors/cursor.cpp @@ -36,35 +36,6 @@ ZorkCursor::ZorkCursor() _hotspotY(0) { } -ZorkCursor::ZorkCursor(const Common::String &fileName) - : _width(0), - _height(0), - _hotspotX(0), - _hotspotY(0) { - Common::File file; - if (!file.open(fileName)) - return; - - uint32 magic = file.readUint32BE(); - if (magic != MKTAG('Z', 'C', 'R', '1')) { - warning("%s is not a Zork Cursor file", fileName.c_str()); - return; - } - - _hotspotX = file.readUint16LE(); - _hotspotY = file.readUint16LE(); - _width = file.readUint16LE(); - _height = file.readUint16LE(); - - uint dataSize = _width * _height * sizeof(uint16); - _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); - uint32 bytesRead = file.read(_surface.getPixels(), dataSize); - assert(bytesRead == dataSize); - - // Convert to RGB 565 - _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); -} - ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) : _width(0), _height(0), @@ -72,7 +43,7 @@ ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) _hotspotY(0) { Common::File file; if (!engine->getSearchManager()->openFile(file, fileName)) - return; + error("Cursor file %s does not exist", fileName.c_str()); uint32 magic = file.readUint32BE(); if (magic != MKTAG('Z', 'C', 'R', '1')) { @@ -86,12 +57,15 @@ ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) _height = file.readUint16LE(); uint dataSize = _width * _height * sizeof(uint16); - _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); + _surface.create(_width, _height, engine->_resourcePixelFormat); uint32 bytesRead = file.read(_surface.getPixels(), dataSize); assert(bytesRead == dataSize); - // Convert to RGB 565 - _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); +#ifndef SCUMM_LITTLE_ENDIAN + int16 *buffer = (int16 *)_surface.getPixels(); + for (uint32 i = 0; i < dataSize / 2; ++i) + buffer[i] = FROM_LE_16(buffer[i]); +#endif } ZorkCursor::ZorkCursor(const ZorkCursor &other) { diff --git a/engines/zvision/graphics/cursors/cursor.h b/engines/zvision/graphics/cursors/cursor.h index 0c1e99411f..6e0083520a 100644 --- a/engines/zvision/graphics/cursors/cursor.h +++ b/engines/zvision/graphics/cursors/cursor.h @@ -39,7 +39,6 @@ namespace ZVision { class ZorkCursor { public: ZorkCursor(); - ZorkCursor(const Common::String &fileName); ZorkCursor(ZVision *engine, const Common::String &fileName); ZorkCursor(const ZorkCursor &other); ~ZorkCursor(); diff --git a/engines/zvision/graphics/cursors/cursor_manager.cpp b/engines/zvision/graphics/cursors/cursor_manager.cpp index 92fd461c72..eeab18f4ba 100644 --- a/engines/zvision/graphics/cursors/cursor_manager.cpp +++ b/engines/zvision/graphics/cursors/cursor_manager.cpp @@ -37,7 +37,7 @@ const char *CursorManager::_cursorNames[NUM_CURSORS] = { "active", "arrow", "bac "hright", "hup", "idle", "leftarrow", "rightarrow", "suggest_surround", "suggest_tilt", "turnaround", "zuparrow" }; -const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac001.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", +const char *CursorManager::_zgiCursorFileNames[NUM_CURSORS] = { "g0gbc011.zcr", "g0gac011.zcr", "g0gac021.zcr", "g0gac031.zcr", "g0gac041.zcr", "g0gac051.zcr", "g0gac061.zcr", "g0gac071.zcr", "g0gac081.zcr", "g0gac091.zcr", "g0gac101.zcr", "g0gac011.zcr", "g0gac111.zcr", "g0gac121.zcr", "g0gac131.zcr", "g0gac141.zcr", "g0gac151.zcr", "g0gac161.zcr" }; @@ -45,7 +45,7 @@ const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up" }; -CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat) +CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat pixelFormat) : _engine(engine), _pixelFormat(pixelFormat), _cursorIsPushed(false), @@ -55,6 +55,11 @@ CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat *pixel for (int i = 0; i < NUM_CURSORS; i++) { if (_engine->getGameId() == GID_NEMESIS) { Common::String name; + if (i == 1) { + // Cursors "arrowa.zcr" and "arrowb.zcr" are missing + _cursors[i][0] = _cursors[i][1] = ZorkCursor(); + continue; + } name = Common::String::format("%sa.zcr", _zNemCursorFileNames[i]); _cursors[i][0] = ZorkCursor(_engine, name); // Up cursor name = Common::String::format("%sb.zcr", _zNemCursorFileNames[i]); @@ -106,7 +111,7 @@ void CursorManager::initialize() { } void CursorManager::changeCursor(const ZorkCursor &cursor) { - CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, _pixelFormat); + CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, &_pixelFormat); } void CursorManager::cursorDown(bool pushed) { @@ -119,31 +124,30 @@ void CursorManager::cursorDown(bool pushed) { } void CursorManager::changeCursor(int id) { - int _id = id; - - if (_item && - (_id == CursorIndex_Active || - _id == CursorIndex_Idle || - _id == CursorIndex_HandPu)) { - - if (_id == CursorIndex_Idle) - _id = CursorIndex_ItemIdle; - else - _id = CursorIndex_ItemAct; + if (_item && (id == CursorIndex_Active || + id == CursorIndex_Idle || + id == CursorIndex_HandPu)) { + if (id == CursorIndex_Idle) { + id = CursorIndex_ItemIdle; + } else { + id = CursorIndex_ItemAct; + } } - if (_currentCursor != _id || - ((_id == CursorIndex_ItemAct || _id == CursorIndex_ItemIdle) && _lastitem != _item)) { - _currentCursor = _id; + if (_currentCursor != id || ((id == CursorIndex_ItemAct || id == CursorIndex_ItemIdle) && _lastitem != _item)) { + _currentCursor = id; _lastitem = _item; changeCursor(_cursors[_currentCursor][_cursorIsPushed]); } } int CursorManager::getCursorId(const Common::String &name) { - for (int i = 0; i < NUM_CURSORS; i++) - if (name.equals(_cursorNames[i])) + for (int i = 0; i < NUM_CURSORS; i++) { + if (name.equals(_cursorNames[i])) { return i; + } + } + return CursorIndex_Idle; } diff --git a/engines/zvision/graphics/cursors/cursor_manager.h b/engines/zvision/graphics/cursors/cursor_manager.h index bbfa085c23..35c605baf8 100644 --- a/engines/zvision/graphics/cursors/cursor_manager.h +++ b/engines/zvision/graphics/cursors/cursor_manager.h @@ -58,7 +58,7 @@ enum CursorIndex { */ class CursorManager { public: - CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat); + CursorManager(ZVision *engine, const Graphics::PixelFormat pixelFormat); private: static const int NUM_CURSORS = 18; @@ -67,7 +67,7 @@ private: ZorkCursor _cursors[NUM_CURSORS + 2][2]; ZVision *_engine; - const Graphics::PixelFormat *_pixelFormat; + const Graphics::PixelFormat _pixelFormat; bool _cursorIsPushed; int _item; int _lastitem; diff --git a/engines/zvision/graphics/effects/fog.cpp b/engines/zvision/graphics/effects/fog.cpp index f59e82a4a0..7b65f60f24 100644 --- a/engines/zvision/graphics/effects/fog.cpp +++ b/engines/zvision/graphics/effects/fog.cpp @@ -31,7 +31,7 @@ namespace ZVision { FogFx::FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds): - Effect(engine, key, region, ported) { + GraphicsEffect(engine, key, region, ported) { _map = Map; @@ -79,10 +79,10 @@ const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) { if (it->inEffect) { // Not 100% equivalent, but looks nice and not buggy uint8 sr, sg, sb; - _engine->_pixelFormat.colorToRGB(lineBuf[i], sr, sg, sb); + _engine->_resourcePixelFormat.colorToRGB(lineBuf[i], sr, sg, sb); uint16 fogColor = *(uint16 *)_fog.getBasePtr((i + _pos) % _fog.w, j); uint8 dr, dg, db; - _engine->_pixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db); + _engine->_resourcePixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db); uint16 fr = dr + sr; if (fr > 255) fr = 255; @@ -92,7 +92,7 @@ const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) { uint16 fb = db + sb; if (fb > 255) fb = 255; - lineBuf[i] = _engine->_pixelFormat.RGBToColor(fr, fg, fb); + lineBuf[i] = _engine->_resourcePixelFormat.RGBToColor(fr, fg, fb); } cnt++; if (cnt >= it->count) { @@ -138,14 +138,14 @@ void FogFx::update() { // Not 100% equivalent, but looks nice and not buggy - _colorMap[31] = _engine->_pixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3); + _colorMap[31] = _engine->_resourcePixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3); for (uint8 i = 0; i < 31; i++) { float perc = (float)i / 31.0; - uint8 cr = (float)_r * perc; - uint8 cg = (float)_g * perc; - uint8 cb = (float)_b * perc; - _colorMap[i] = _engine->_pixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3); + uint8 cr = (uint8)((float)_r * perc); + uint8 cg = (uint8)((float)_g * perc); + uint8 cb = (uint8)((float)_b * perc); + _colorMap[i] = _engine->_resourcePixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3); } } diff --git a/engines/zvision/graphics/effects/fog.h b/engines/zvision/graphics/effects/fog.h index 45d6f9596d..498347609e 100644 --- a/engines/zvision/graphics/effects/fog.h +++ b/engines/zvision/graphics/effects/fog.h @@ -23,13 +23,14 @@ #ifndef ZVISION_FOG_H #define ZVISION_FOG_H -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class FogFx : public Effect { +// Used by Zork: Nemesis for the mixing chamber gas effect in the gas puzzle (location tt5e, when the blinds are down) +class FogFx : public GraphicsEffect { public: FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds); diff --git a/engines/zvision/graphics/effects/light.cpp b/engines/zvision/graphics/effects/light.cpp index 00b3811d65..39341687f8 100644 --- a/engines/zvision/graphics/effects/light.cpp +++ b/engines/zvision/graphics/effects/light.cpp @@ -30,7 +30,7 @@ namespace ZVision { LightFx::LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD, int8 maxD): - Effect(engine, key, region, ported) { + GraphicsEffect(engine, key, region, ported) { _map = Map; _delta = delta; _up = true; @@ -59,10 +59,10 @@ const Graphics::Surface *LightFx::draw(const Graphics::Surface &srcSubRect) { if (_pos < 0) { uint8 cc = ((-_pos) & 0x1F) << 3; - dcolor = _engine->_pixelFormat.RGBToColor(cc, cc, cc); + dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc); } else { uint8 cc = (_pos & 0x1F) << 3; - dcolor = _engine->_pixelFormat.RGBToColor(cc, cc, cc); + dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc); } for (uint16 j = 0; j < _surface.h; j++) { diff --git a/engines/zvision/graphics/effects/light.h b/engines/zvision/graphics/effects/light.h index ae87d66cb3..cd73a585ec 100644 --- a/engines/zvision/graphics/effects/light.h +++ b/engines/zvision/graphics/effects/light.h @@ -23,13 +23,13 @@ #ifndef LIGHTFX_H_INCLUDED #define LIGHTFX_H_INCLUDED -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class LightFx : public Effect { +class LightFx : public GraphicsEffect { public: LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD = -127, int8 maxD = 127); diff --git a/engines/zvision/graphics/effects/wave.cpp b/engines/zvision/graphics/effects/wave.cpp index 1b3aa040e8..d2887b3112 100644 --- a/engines/zvision/graphics/effects/wave.cpp +++ b/engines/zvision/graphics/effects/wave.cpp @@ -30,7 +30,7 @@ namespace ZVision { WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd): - Effect(engine, key, region, ported) { + GraphicsEffect(engine, key, region, ported) { _frame = 0; _frameCount = frames; @@ -54,7 +54,7 @@ WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, in int16 dx = (x - quarterWidth); int16 dy = (y - quarterHeight); - _ampls[i][x + y * _halfWidth] = ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase); + _ampls[i][x + y * _halfWidth] = (int8)(ampl * sin(sqrt(dx * dx / (float)centerX + dy * dy / (float)centerY) / (-waveln * 3.1415926) + phase)); } phase += spd; } diff --git a/engines/zvision/graphics/effects/wave.h b/engines/zvision/graphics/effects/wave.h index 2e813ed5b6..8e912372d7 100644 --- a/engines/zvision/graphics/effects/wave.h +++ b/engines/zvision/graphics/effects/wave.h @@ -24,13 +24,13 @@ #define WAVEFX_H_INCLUDED #include "common/array.h" -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class WaveFx : public Effect { +class WaveFx : public GraphicsEffect { public: WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd); diff --git a/engines/zvision/graphics/effect.h b/engines/zvision/graphics/graphics_effect.h index c6653c6037..bfa266b11d 100644 --- a/engines/zvision/graphics/effect.h +++ b/engines/zvision/graphics/graphics_effect.h @@ -20,8 +20,8 @@ * */ -#ifndef EFFECT_H_INCLUDED -#define EFFECT_H_INCLUDED +#ifndef GRAPHICS_EFFECT_H_INCLUDED +#define GRAPHICS_EFFECT_H_INCLUDED #include "common/rect.h" #include "common/list.h" @@ -33,13 +33,13 @@ namespace ZVision { class ZVision; -class Effect { +class GraphicsEffect { public: - Effect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) { - _surface.create(_region.width(), _region.height(), _engine->_pixelFormat); + GraphicsEffect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) { + _surface.create(_region.width(), _region.height(), _engine->_resourcePixelFormat); } - virtual ~Effect() {} + virtual ~GraphicsEffect() {} uint32 getKey() { return _key; @@ -80,4 +80,4 @@ typedef Common::List<EffectMapUnit> EffectMap; } // End of namespace ZVision -#endif // EFFECT_H_INCLUDED +#endif // GRAPHICS_EFFECT_H_INCLUDED diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp index e2ad13a330..f978ef7844 100644 --- a/engines/zvision/graphics/render_manager.cpp +++ b/engines/zvision/graphics/render_manager.cpp @@ -39,71 +39,75 @@ namespace ZVision { -RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat) +RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat, bool doubleFPS) : _engine(engine), _system(engine->_system), - _wrkWidth(workingWindow.width()), - _wrkHeight(workingWindow.height()), - _screenCenterX(_wrkWidth / 2), - _screenCenterY(_wrkHeight / 2), + _screenCenterX(_workingWindow.width() / 2), + _screenCenterY(_workingWindow.height() / 2), _workingWindow(workingWindow), _pixelFormat(pixelFormat), - _bkgWidth(0), - _bkgHeight(0), - _bkgOff(0), - _renderTable(_wrkWidth, _wrkHeight) { + _backgroundWidth(0), + _backgroundHeight(0), + _backgroundOffset(0), + _renderTable(_workingWindow.width(), _workingWindow.height()), + _doubleFPS(doubleFPS), + _subid(0) { - _wrkWnd.create(_wrkWidth, _wrkHeight, _pixelFormat); - _effectWnd.create(_wrkWidth, _wrkHeight, _pixelFormat); - _outWnd.create(_wrkWidth, _wrkHeight, _pixelFormat); - _menuWnd.create(windowWidth, workingWindow.top, _pixelFormat); - _subWnd.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat); + _backgroundSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat); + _effectSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat); + _warpedSceneSurface.create(_workingWindow.width(), _workingWindow.height(), _pixelFormat); + _menuSurface.create(windowWidth, workingWindow.top, _pixelFormat); - _menuWndRect = Common::Rect(0, 0, windowWidth, workingWindow.top); - _subWndRect = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight); + _menuArea = Common::Rect(0, 0, windowWidth, workingWindow.top); - _subid = 0; + initSubArea(windowWidth, windowHeight, workingWindow); } RenderManager::~RenderManager() { - _curBkg.free(); - _wrkWnd.free(); - _effectWnd.free(); - _outWnd.free(); - _menuWnd.free(); - _subWnd.free(); + _currentBackgroundImage.free(); + _backgroundSurface.free(); + _effectSurface.free(); + _warpedSceneSurface.free(); + _menuSurface.free(); + _subtitleSurface.free(); } -void RenderManager::renderBackbufferToScreen() { - Graphics::Surface *out = &_outWnd; - Graphics::Surface *in = &_wrkWnd; +void RenderManager::renderSceneToScreen() { + Graphics::Surface *out = &_warpedSceneSurface; + Graphics::Surface *in = &_backgroundSurface; Common::Rect outWndDirtyRect; + // If we have graphical effects, we apply them using a temporary buffer if (!_effects.empty()) { bool copied = false; - Common::Rect windRect(_wrkWidth, _wrkHeight); - for (effectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { + Common::Rect windowRect(_workingWindow.width(), _workingWindow.height()); + + for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { Common::Rect rect = (*it)->getRegion(); - Common::Rect scrPlace = rect; - if ((*it)->isPort()) - scrPlace = bkgRectToScreen(scrPlace); - if (windRect.intersects(scrPlace)) { + Common::Rect screenSpaceLocation = rect; + + if ((*it)->isPort()) { + screenSpaceLocation = transformBackgroundSpaceRectToScreenSpace(screenSpaceLocation); + } + + if (windowRect.intersects(screenSpaceLocation)) { if (!copied) { copied = true; - _effectWnd.copyFrom(_wrkWnd); - in = &_effectWnd; + _effectSurface.copyFrom(_backgroundSurface); + in = &_effectSurface; } const Graphics::Surface *post; if ((*it)->isPort()) - post = (*it)->draw(_curBkg.getSubArea(rect)); + post = (*it)->draw(_currentBackgroundImage.getSubArea(rect)); else - post = (*it)->draw(_effectWnd.getSubArea(rect)); - blitSurfaceToSurface(*post, _effectWnd, scrPlace.left, scrPlace.top); - scrPlace.clip(windRect); - if (_wrkWndDirtyRect .isEmpty()) { - _wrkWndDirtyRect = scrPlace; + post = (*it)->draw(_effectSurface.getSubArea(rect)); + Common::Rect empty; + blitSurfaceToSurface(*post, empty, _effectSurface, screenSpaceLocation.left, screenSpaceLocation.top); + screenSpaceLocation.clip(windowRect); + if (_backgroundSurfaceDirtyRect .isEmpty()) { + _backgroundSurfaceDirtyRect = screenSpaceLocation; } else { - _wrkWndDirtyRect.extend(scrPlace); + _backgroundSurfaceDirtyRect.extend(screenSpaceLocation); } } } @@ -111,25 +115,40 @@ void RenderManager::renderBackbufferToScreen() { RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::PANORAMA || state == RenderTable::TILT) { - if (!_wrkWndDirtyRect.isEmpty()) { - _renderTable.mutateImage(&_outWnd, in); - out = &_outWnd; - outWndDirtyRect = Common::Rect(_wrkWidth, _wrkHeight); + if (!_backgroundSurfaceDirtyRect.isEmpty()) { + _renderTable.mutateImage(&_warpedSceneSurface, in); + out = &_warpedSceneSurface; + outWndDirtyRect = Common::Rect(_workingWindow.width(), _workingWindow.height()); } } else { out = in; - outWndDirtyRect = _wrkWndDirtyRect; + outWndDirtyRect = _backgroundSurfaceDirtyRect; } if (!outWndDirtyRect.isEmpty()) { - _system->copyRectToScreen(out->getBasePtr(outWndDirtyRect.left, outWndDirtyRect.top), out->pitch, - outWndDirtyRect.left + _workingWindow.left, - outWndDirtyRect.top + _workingWindow.top, - outWndDirtyRect.width(), - outWndDirtyRect.height()); + Common::Rect rect( + outWndDirtyRect.left + _workingWindow.left, + outWndDirtyRect.top + _workingWindow.top, + outWndDirtyRect.left + _workingWindow.left + outWndDirtyRect.width(), + outWndDirtyRect.top + _workingWindow.top + outWndDirtyRect.height() + ); + copyToScreen(*out, rect, outWndDirtyRect.left, outWndDirtyRect.top); } } +void RenderManager::copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop) { + // Convert the surface to RGB565, if needed + Graphics::Surface *outSurface = surface.convertTo(_engine->_screenPixelFormat); + _system->copyRectToScreen(outSurface->getBasePtr(srcLeft, srcTop), + outSurface->pitch, + rect.left, + rect.top, + rect.width(), + rect.height()); + outSurface->free(); + delete outSurface; +} + void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY) { Graphics::Surface surface; readImageToSurface(fileName, surface); @@ -157,97 +176,8 @@ void RenderManager::renderImageToBackground(const Common::String &fileName, int1 } void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { - Common::File file; - - if (!_engine->getSearchManager()->openFile(file, fileName)) { - warning("Could not open file %s", fileName.c_str()); - return; - } - - // Read the magic number - // Some files are true TGA, while others are TGZ - uint32 fileType = file.readUint32BE(); - - uint32 imageWidth; - uint32 imageHeight; - Image::TGADecoder tga; - uint16 *buffer; bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; - // All ZVision images are in RGB 555 - Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - destination.format = pixelFormat555; - - bool isTGZ; - - // Check for TGZ files - if (fileType == MKTAG('T', 'G', 'Z', '\0')) { - isTGZ = true; - - // TGZ files have a header and then Bitmap data that is compressed with LZSS - uint32 decompressedSize = file.readSint32LE(); - imageWidth = file.readSint32LE(); - imageHeight = file.readSint32LE(); - - LzssReadStream lzssStream(&file); - buffer = (uint16 *)(new uint16[decompressedSize]); - lzssStream.read(buffer, decompressedSize); - } else { - isTGZ = false; - - // Reset the cursor - file.seek(0); - - // Decode - if (!tga.loadStream(file)) { - warning("Error while reading TGA image"); - return; - } - - Graphics::Surface tgaSurface = *(tga.getSurface()); - imageWidth = tgaSurface.w; - imageHeight = tgaSurface.h; - - buffer = (uint16 *)tgaSurface.getPixels(); - } - - // Flip the width and height if transposed - if (isTransposed) { - uint16 temp = imageHeight; - imageHeight = imageWidth; - imageWidth = temp; - } - - // If the destination internal buffer is the same size as what we're copying into it, - // there is no need to free() and re-create - if (imageWidth != destination.w || imageHeight != destination.h) { - destination.create(imageWidth, imageHeight, pixelFormat555); - } - - // If transposed, 'un-transpose' the data while copying it to the destination - // Otherwise, just do a simple copy - if (isTransposed) { - uint16 *dest = (uint16 *)destination.getPixels(); - - for (uint32 y = 0; y < imageHeight; ++y) { - uint32 columnIndex = y * imageWidth; - - for (uint32 x = 0; x < imageWidth; ++x) { - dest[columnIndex + x] = buffer[x * imageHeight + y]; - } - } - } else { - memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); - } - - // Cleanup - if (isTGZ) { - delete[] buffer; - } else { - tga.destroy(); - } - - // Convert in place to RGB 565 from RGB 555 - destination.convertToInPlace(_pixelFormat); + readImageToSurface(fileName, destination, isTransposed); } void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed) { @@ -266,9 +196,8 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: uint32 imageHeight; Image::TGADecoder tga; uint16 *buffer; - // All ZVision images are in RGB 555 - Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - destination.format = pixelFormat555; + // All Z-Vision images are in RGB 555 + destination.format = _engine->_resourcePixelFormat; bool isTGZ; @@ -277,13 +206,17 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: isTGZ = true; // TGZ files have a header and then Bitmap data that is compressed with LZSS - uint32 decompressedSize = file.readSint32LE(); + uint32 decompressedSize = file.readSint32LE() / 2; imageWidth = file.readSint32LE(); imageHeight = file.readSint32LE(); LzssReadStream lzssStream(&file); buffer = (uint16 *)(new uint16[decompressedSize]); - lzssStream.read(buffer, decompressedSize); + lzssStream.read(buffer, 2 * decompressedSize); +#ifndef SCUMM_LITTLE_ENDIAN + for (uint32 i = 0; i < decompressedSize; ++i) + buffer[i] = FROM_LE_16(buffer[i]); +#endif } else { isTGZ = false; @@ -313,7 +246,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: // If the destination internal buffer is the same size as what we're copying into it, // there is no need to free() and re-create if (imageWidth != destination.w || imageHeight != destination.h) { - destination.create(imageWidth, imageHeight, pixelFormat555); + destination.create(imageWidth, imageHeight, _engine->_resourcePixelFormat); } // If transposed, 'un-transpose' the data while copying it to the destination @@ -329,7 +262,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: } } } else { - memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); + memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * destination.format.bytesPerPixel); } // Cleanup @@ -338,9 +271,6 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: } else { tga.destroy(); } - - // Convert in place to RGB 565 from RGB 555 - destination.convertToInPlace(_pixelFormat); } const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) { @@ -354,20 +284,20 @@ const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point & } if (state == RenderTable::PANORAMA) { - newPoint += (Common::Point(_bkgOff - _screenCenterX, 0)); + newPoint += (Common::Point(_backgroundOffset - _screenCenterX, 0)); } else if (state == RenderTable::TILT) { - newPoint += (Common::Point(0, _bkgOff - _screenCenterY)); + newPoint += (Common::Point(0, _backgroundOffset - _screenCenterY)); } - if (_bkgWidth) - newPoint.x %= _bkgWidth; - if (_bkgHeight) - newPoint.y %= _bkgHeight; + if (_backgroundWidth) + newPoint.x %= _backgroundWidth; + if (_backgroundHeight) + newPoint.y %= _backgroundHeight; if (newPoint.x < 0) - newPoint.x += _bkgWidth; + newPoint.x += _backgroundWidth; if (newPoint.y < 0) - newPoint.y += _bkgHeight; + newPoint.y += _backgroundHeight; return newPoint; } else { @@ -380,18 +310,18 @@ RenderTable *RenderManager::getRenderTable() { } void RenderManager::setBackgroundImage(const Common::String &fileName) { - readImageToSurface(fileName, _curBkg); - _bkgWidth = _curBkg.w; - _bkgHeight = _curBkg.h; - _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); + readImageToSurface(fileName, _currentBackgroundImage); + _backgroundWidth = _currentBackgroundImage.w; + _backgroundHeight = _currentBackgroundImage.h; + _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight); } void RenderManager::setBackgroundPosition(int offset) { RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::TILT || state == RenderTable::PANORAMA) - if (_bkgOff != offset) - _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); - _bkgOff = offset; + if (_backgroundOffset != offset) + _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight); + _backgroundOffset = offset; _engine->getScriptManager()->setStateValue(StateKey_ViewPos, offset); } @@ -400,9 +330,9 @@ uint32 RenderManager::getCurrentBackgroundOffset() { RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::PANORAMA) { - return _bkgOff; + return _backgroundOffset; } else if (state == RenderTable::TILT) { - return _bkgOff; + return _backgroundOffset; } else { return 0; } @@ -454,10 +384,6 @@ void RenderManager::scaleBuffer(const void *src, void *dst, uint32 srcWidth, uin } void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y) { - - if (src.format != dst.format) - return; - Common::Rect srcRect = _srcRect; if (srcRect.isEmpty()) srcRect = Common::Rect(src.w, src.h); @@ -468,8 +394,10 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (srcRect.isEmpty() || !srcRect.isValidRect()) return; + Graphics::Surface *srcAdapted = src.convertTo(dst.format); + // Copy srcRect from src surface to dst surface - const byte *srcBuffer = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top); int xx = _x; int yy = _y; @@ -479,8 +407,11 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (yy < 0) yy = 0; - if (_x >= dst.w || _y >= dst.h) + if (_x >= dst.w || _y >= dst.h) { + srcAdapted->free(); + delete srcAdapted; return; + } byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); @@ -488,17 +419,16 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com int32 h = srcRect.height(); for (int32 y = 0; y < h; y++) { - memcpy(dstBuffer, srcBuffer, w * src.format.bytesPerPixel); - srcBuffer += src.pitch; + memcpy(dstBuffer, srcBuffer, w * srcAdapted->format.bytesPerPixel); + srcBuffer += srcAdapted->pitch; dstBuffer += dst.pitch; } + + srcAdapted->free(); + delete srcAdapted; } void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey) { - - if (src.format != dst.format) - return; - Common::Rect srcRect = _srcRect; if (srcRect.isEmpty()) srcRect = Common::Rect(src.w, src.h); @@ -509,10 +439,11 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (srcRect.isEmpty() || !srcRect.isValidRect()) return; - uint32 _keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1); + Graphics::Surface *srcAdapted = src.convertTo(dst.format); + uint32 keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1); // Copy srcRect from src surface to dst surface - const byte *srcBuffer = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top); int xx = _x; int yy = _y; @@ -522,8 +453,11 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (yy < 0) yy = 0; - if (_x >= dst.w || _y >= dst.h) + if (_x >= dst.w || _y >= dst.h) { + srcAdapted->free(); + delete srcAdapted; return; + } byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); @@ -531,12 +465,12 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com int32 h = srcRect.height(); for (int32 y = 0; y < h; y++) { - switch (src.format.bytesPerPixel) { + switch (srcAdapted->format.bytesPerPixel) { case 1: { const uint *srcTemp = (const uint *)srcBuffer; uint *dstTemp = (uint *)dstBuffer; for (int32 x = 0; x < w; x++) { - if (*srcTemp != _keycolor) + if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; @@ -548,7 +482,7 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com const uint16 *srcTemp = (const uint16 *)srcBuffer; uint16 *dstTemp = (uint16 *)dstBuffer; for (int32 x = 0; x < w; x++) { - if (*srcTemp != _keycolor) + if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; @@ -560,7 +494,7 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com const uint32 *srcTemp = (const uint32 *)srcBuffer; uint32 *dstTemp = (uint32 *)dstBuffer; for (int32 x = 0; x < w; x++) { - if (*srcTemp != _keycolor) + if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; @@ -571,60 +505,32 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com default: break; } - srcBuffer += src.pitch; + srcBuffer += srcAdapted->pitch; dstBuffer += dst.pitch; } -} -void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, dst, x, y); + srcAdapted->free(); + delete srcAdapted; } -void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y, uint32 colorkey) { +void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey) { Common::Rect empt; - blitSurfaceToSurface(src, empt, dst, x, y, colorkey); -} - -void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, _curBkg, x, y); - Common::Rect dirty(src.w, src.h); - dirty.translate(x, y); - if (_bkgDirtyRect.isEmpty()) - _bkgDirtyRect = dirty; + if (colorkey >= 0) + blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y, colorkey); else - _bkgDirtyRect.extend(dirty); -} - -void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, _curBkg, x, y, colorkey); + blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y); Common::Rect dirty(src.w, src.h); dirty.translate(x, y); - if (_bkgDirtyRect.isEmpty()) - _bkgDirtyRect = dirty; + if (_backgroundDirtyRect.isEmpty()) + _backgroundDirtyRect = dirty; else - _bkgDirtyRect.extend(dirty); -} - -void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect) { - if (src.w == _dstRect.width() && src.h == _dstRect.height()) - blitSurfaceToBkg(src, _dstRect.left, _dstRect.top); - else { - Graphics::Surface *tmp = new Graphics::Surface; - tmp->create(_dstRect.width(), _dstRect.height(), src.format); - scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height()); - blitSurfaceToBkg(*tmp, _dstRect.left, _dstRect.top); - tmp->free(); - delete tmp; - } + _backgroundDirtyRect.extend(dirty); } -void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, uint32 colorkey) { - if (src.w == _dstRect.width() && src.h == _dstRect.height()) +void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey) { + if (src.w == _dstRect.width() && src.h == _dstRect.height()) { blitSurfaceToBkg(src, _dstRect.left, _dstRect.top, colorkey); - else { + } else { Graphics::Surface *tmp = new Graphics::Surface; tmp->create(_dstRect.width(), _dstRect.height(), src.format); scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height()); @@ -634,160 +540,163 @@ void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const C } } -void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y) { +void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey) { Common::Rect empt; - blitSurfaceToSurface(src, empt, _menuWnd, x, y); - Common::Rect dirty(src.w, src.h); - dirty.translate(x, y); - if (_menuWndDirtyRect.isEmpty()) - _menuWndDirtyRect = dirty; + if (colorkey >= 0) + blitSurfaceToSurface(src, empt, _menuSurface, x, y, colorkey); else - _menuWndDirtyRect.extend(dirty); -} - -void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, _menuWnd, x, y, colorkey); + blitSurfaceToSurface(src, empt, _menuSurface, x, y); Common::Rect dirty(src.w, src.h); dirty.translate(x, y); - if (_menuWndDirtyRect.isEmpty()) - _menuWndDirtyRect = dirty; + if (_menuSurfaceDirtyRect.isEmpty()) + _menuSurfaceDirtyRect = dirty; else - _menuWndDirtyRect.extend(dirty); + _menuSurfaceDirtyRect.extend(dirty); } Graphics::Surface *RenderManager::getBkgRect(Common::Rect &rect) { Common::Rect dst = rect; - dst.clip(_bkgWidth, _bkgHeight); + dst.clip(_backgroundWidth, _backgroundHeight); if (dst.isEmpty() || !dst.isValidRect()) return NULL; Graphics::Surface *srf = new Graphics::Surface; - srf->create(dst.width(), dst.height(), _curBkg.format); + srf->create(dst.width(), dst.height(), _currentBackgroundImage.format); - srf->copyRectToSurface(_curBkg, 0, 0, Common::Rect(dst)); + srf->copyRectToSurface(_currentBackgroundImage, 0, 0, Common::Rect(dst)); return srf; } -Graphics::Surface *RenderManager::loadImage(Common::String &file) { +Graphics::Surface *RenderManager::loadImage(Common::String file) { Graphics::Surface *tmp = new Graphics::Surface; readImageToSurface(file, *tmp); return tmp; } -Graphics::Surface *RenderManager::loadImage(const char *file) { - Common::String str = Common::String(file); - return loadImage(str); -} - -Graphics::Surface *RenderManager::loadImage(Common::String &file, bool transposed) { +Graphics::Surface *RenderManager::loadImage(Common::String file, bool transposed) { Graphics::Surface *tmp = new Graphics::Surface; readImageToSurface(file, *tmp, transposed); return tmp; } -Graphics::Surface *RenderManager::loadImage(const char *file, bool transposed) { - Common::String str = Common::String(file); - return loadImage(str, transposed); -} - -void RenderManager::prepareBkg() { - _bkgDirtyRect.clip(_bkgWidth, _bkgHeight); +void RenderManager::prepareBackground() { + _backgroundDirtyRect.clip(_backgroundWidth, _backgroundHeight); RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::PANORAMA) { - Common::Rect viewPort(_wrkWidth, _wrkHeight); - viewPort.translate(-(_screenCenterX - _bkgOff), 0); - Common::Rect drawRect = _bkgDirtyRect; + // Calculate the visible portion of the background + Common::Rect viewPort(_workingWindow.width(), _workingWindow.height()); + viewPort.translate(-(_screenCenterX - _backgroundOffset), 0); + Common::Rect drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); - if (!drawRect.isEmpty()) - blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, _screenCenterX - _bkgOff + drawRect.left, drawRect.top); + // Render the visible portion + if (!drawRect.isEmpty()) { + blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - _backgroundOffset + drawRect.left, drawRect.top); + } - _wrkWndDirtyRect = _bkgDirtyRect; - _wrkWndDirtyRect.translate(_screenCenterX - _bkgOff, 0); + // Mark the dirty portion of the surface + _backgroundSurfaceDirtyRect = _backgroundDirtyRect; + _backgroundSurfaceDirtyRect.translate(_screenCenterX - _backgroundOffset, 0); - if (_bkgOff < _screenCenterX) { - viewPort.moveTo(-(_screenCenterX - (_bkgOff + _bkgWidth)), 0); - drawRect = _bkgDirtyRect; + // Panorama mode allows the user to spin in circles. Therefore, we need to render + // the portion of the image that wrapped to the other side of the screen + if (_backgroundOffset < _screenCenterX) { + viewPort.moveTo(-(_screenCenterX - (_backgroundOffset + _backgroundWidth)), 0); + drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); if (!drawRect.isEmpty()) - blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, _screenCenterX - (_bkgOff + _bkgWidth) + drawRect.left, drawRect.top); + blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - (_backgroundOffset + _backgroundWidth) + drawRect.left, drawRect.top); - Common::Rect tmp = _bkgDirtyRect; - tmp.translate(_screenCenterX - (_bkgOff + _bkgWidth), 0); + Common::Rect tmp = _backgroundDirtyRect; + tmp.translate(_screenCenterX - (_backgroundOffset + _backgroundWidth), 0); if (!tmp.isEmpty()) - _wrkWndDirtyRect.extend(tmp); + _backgroundSurfaceDirtyRect.extend(tmp); - } else if (_bkgWidth - _bkgOff < _screenCenterX) { - viewPort.moveTo(-(_screenCenterX + _bkgWidth - _bkgOff), 0); - drawRect = _bkgDirtyRect; + } else if (_backgroundWidth - _backgroundOffset < _screenCenterX) { + viewPort.moveTo(-(_screenCenterX + _backgroundWidth - _backgroundOffset), 0); + drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); if (!drawRect.isEmpty()) - blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, _screenCenterX + _bkgWidth - _bkgOff + drawRect.left, drawRect.top); + blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX + _backgroundWidth - _backgroundOffset + drawRect.left, drawRect.top); - Common::Rect tmp = _bkgDirtyRect; - tmp.translate(_screenCenterX + _bkgWidth - _bkgOff, 0); + Common::Rect tmp = _backgroundDirtyRect; + tmp.translate(_screenCenterX + _backgroundWidth - _backgroundOffset, 0); if (!tmp.isEmpty()) - _wrkWndDirtyRect.extend(tmp); + _backgroundSurfaceDirtyRect.extend(tmp); } } else if (state == RenderTable::TILT) { - Common::Rect viewPort(_wrkWidth, _wrkHeight); - viewPort.translate(0, -(_screenCenterY - _bkgOff)); - Common::Rect drawRect = _bkgDirtyRect; + // Tilt doesn't allow wrapping, so we just do a simple clip + Common::Rect viewPort(_workingWindow.width(), _workingWindow.height()); + viewPort.translate(0, -(_screenCenterY - _backgroundOffset)); + Common::Rect drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); if (!drawRect.isEmpty()) - blitSurfaceToSurface(_curBkg, drawRect, _wrkWnd, drawRect.left, _screenCenterY - _bkgOff + drawRect.top); + blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, drawRect.left, _screenCenterY - _backgroundOffset + drawRect.top); - _wrkWndDirtyRect = _bkgDirtyRect; - _wrkWndDirtyRect.translate(0, _screenCenterY - _bkgOff); + // Mark the dirty portion of the surface + _backgroundSurfaceDirtyRect = _backgroundDirtyRect; + _backgroundSurfaceDirtyRect.translate(0, _screenCenterY - _backgroundOffset); } else { - if (!_bkgDirtyRect.isEmpty()) - blitSurfaceToSurface(_curBkg, _bkgDirtyRect, _wrkWnd, _bkgDirtyRect.left, _bkgDirtyRect.top); - _wrkWndDirtyRect = _bkgDirtyRect; + if (!_backgroundDirtyRect.isEmpty()) + blitSurfaceToSurface(_currentBackgroundImage, _backgroundDirtyRect, _backgroundSurface, _backgroundDirtyRect.left, _backgroundDirtyRect.top); + _backgroundSurfaceDirtyRect = _backgroundDirtyRect; } - _bkgDirtyRect = Common::Rect(); + // Clear the dirty rect since everything is clean now + _backgroundDirtyRect = Common::Rect(); - _wrkWndDirtyRect.clip(_wrkWidth, _wrkHeight); + _backgroundSurfaceDirtyRect.clip(_workingWindow.width(), _workingWindow.height()); } void RenderManager::clearMenuSurface() { - _menuWndDirtyRect = Common::Rect(0, 0, _menuWnd.w, _menuWnd.h); - _menuWnd.fillRect(_menuWndDirtyRect, 0); + _menuSurfaceDirtyRect = Common::Rect(0, 0, _menuSurface.w, _menuSurface.h); + _menuSurface.fillRect(_menuSurfaceDirtyRect, 0); } void RenderManager::clearMenuSurface(const Common::Rect &r) { - if (_menuWndDirtyRect.isEmpty()) - _menuWndDirtyRect = r; + if (_menuSurfaceDirtyRect.isEmpty()) + _menuSurfaceDirtyRect = r; else - _menuWndDirtyRect.extend(r); - _menuWnd.fillRect(r, 0); + _menuSurfaceDirtyRect.extend(r); + _menuSurface.fillRect(r, 0); } void RenderManager::renderMenuToScreen() { - if (!_menuWndDirtyRect.isEmpty()) { - _menuWndDirtyRect.clip(Common::Rect(_menuWnd.w, _menuWnd.h)); - if (!_menuWndDirtyRect.isEmpty()) - _system->copyRectToScreen(_menuWnd.getBasePtr(_menuWndDirtyRect.left, _menuWndDirtyRect.top), _menuWnd.pitch, - _menuWndDirtyRect.left + _menuWndRect.left, - _menuWndDirtyRect.top + _menuWndRect.top, - _menuWndDirtyRect.width(), - _menuWndDirtyRect.height()); - _menuWndDirtyRect = Common::Rect(); + if (!_menuSurfaceDirtyRect.isEmpty()) { + _menuSurfaceDirtyRect.clip(Common::Rect(_menuSurface.w, _menuSurface.h)); + if (!_menuSurfaceDirtyRect.isEmpty()) { + Common::Rect rect( + _menuSurfaceDirtyRect.left + _menuArea.left, + _menuSurfaceDirtyRect.top + _menuArea.top, + _menuSurfaceDirtyRect.left + _menuArea.left + _menuSurfaceDirtyRect.width(), + _menuSurfaceDirtyRect.top + _menuArea.top + _menuSurfaceDirtyRect.height() + ); + copyToScreen(_menuSurface, rect, _menuSurfaceDirtyRect.left, _menuSurfaceDirtyRect.top); + } + _menuSurfaceDirtyRect = Common::Rect(); } } +void RenderManager::initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow) { + _workingWindow = workingWindow; + + _subtitleSurface.free(); + + _subtitleSurface.create(windowWidth, windowHeight - workingWindow.bottom, _pixelFormat); + _subtitleArea = Common::Rect(0, workingWindow.bottom, windowWidth, windowHeight); +} + uint16 RenderManager::createSubArea(const Common::Rect &area) { _subid++; - oneSub sub; + OneSubtitle sub; sub.redraw = false; sub.timer = -1; sub.todelete = false; @@ -799,18 +708,9 @@ uint16 RenderManager::createSubArea(const Common::Rect &area) { } uint16 RenderManager::createSubArea() { - _subid++; - - oneSub sub; - sub.redraw = false; - sub.timer = -1; - sub.todelete = false; - sub.r = Common::Rect(_subWndRect.left, _subWndRect.top, _subWndRect.right, _subWndRect.bottom); - sub.r.translate(-_workingWindow.left, -_workingWindow.top); - - _subsList[_subid] = sub; - - return _subid; + Common::Rect r(_subtitleArea.left, _subtitleArea.top, _subtitleArea.right, _subtitleArea.bottom); + r.translate(-_workingWindow.left, -_workingWindow.top); + return createSubArea(r); } void RenderManager::deleteSubArea(uint16 id) { @@ -825,7 +725,7 @@ void RenderManager::deleteSubArea(uint16 id, int16 delay) { void RenderManager::updateSubArea(uint16 id, const Common::String &txt) { if (_subsList.contains(id)) { - oneSub *sub = &_subsList[id]; + OneSubtitle *sub = &_subsList[id]; sub->txt = txt; sub->redraw = true; } @@ -833,7 +733,7 @@ void RenderManager::updateSubArea(uint16 id, const Common::String &txt) { void RenderManager::processSubs(uint16 deltatime) { bool redraw = false; - for (subMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) { + for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) { if (it->_value.timer != -1) { it->_value.timer -= deltatime; if (it->_value.timer <= 0) @@ -848,39 +748,41 @@ void RenderManager::processSubs(uint16 deltatime) { } if (redraw) { - _subWnd.fillRect(Common::Rect(_subWnd.w, _subWnd.h), 0); + _subtitleSurface.fillRect(Common::Rect(_subtitleSurface.w, _subtitleSurface.h), 0); - for (subMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) { - oneSub *sub = &it->_value; + for (SubtitleMap::iterator it = _subsList.begin(); it != _subsList.end(); it++) { + OneSubtitle *sub = &it->_value; if (sub->txt.size()) { - Graphics::Surface *rndr = new Graphics::Surface(); - rndr->create(sub->r.width(), sub->r.height(), _pixelFormat); - _engine->getTextRenderer()->drawTxtInOneLine(sub->txt, *rndr); - blitSurfaceToSurface(*rndr, _subWnd, sub->r.left - _subWndRect.left + _workingWindow.left, sub->r.top - _subWndRect.top + _workingWindow.top); - rndr->free(); - delete rndr; + Graphics::Surface subtitleSurface; + subtitleSurface.create(sub->r.width(), sub->r.height(), _engine->_resourcePixelFormat); + _engine->getTextRenderer()->drawTextWithWordWrapping(sub->txt, subtitleSurface); + Common::Rect empty; + blitSurfaceToSurface(subtitleSurface, empty, _subtitleSurface, sub->r.left - _subtitleArea.left + _workingWindow.left, sub->r.top - _subtitleArea.top + _workingWindow.top); + subtitleSurface.free(); } sub->redraw = false; } - _system->copyRectToScreen(_subWnd.getPixels(), _subWnd.pitch, - _subWndRect.left, - _subWndRect.top, - _subWnd.w, - _subWnd.h); + Common::Rect rect( + _subtitleArea.left, + _subtitleArea.top, + _subtitleArea.left + _subtitleSurface.w, + _subtitleArea.top + _subtitleSurface.h + ); + copyToScreen(_subtitleSurface, rect, 0, 0); } } Common::Point RenderManager::getBkgSize() { - return Common::Point(_bkgWidth, _bkgHeight); + return Common::Point(_backgroundWidth, _backgroundHeight); } -void RenderManager::addEffect(Effect *_effect) { +void RenderManager::addEffect(GraphicsEffect *_effect) { _effects.push_back(_effect); } void RenderManager::deleteEffect(uint32 ID) { - for (effectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { + for (EffectsList::iterator it = _effects.begin(); it != _effects.end(); it++) { if ((*it)->getKey() == ID) { delete *it; it = _effects.erase(it); @@ -888,54 +790,54 @@ void RenderManager::deleteEffect(uint32 ID) { } } -Common::Rect RenderManager::bkgRectToScreen(const Common::Rect &src) { +Common::Rect RenderManager::transformBackgroundSpaceRectToScreenSpace(const Common::Rect &src) { Common::Rect tmp = src; RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::PANORAMA) { - if (_bkgOff < _screenCenterX) { - Common::Rect rScreen(_screenCenterX + _bkgOff, _wrkHeight); - Common::Rect lScreen(_wrkWidth - rScreen.width(), _wrkHeight); - lScreen.translate(_bkgWidth - lScreen.width(), 0); + if (_backgroundOffset < _screenCenterX) { + Common::Rect rScreen(_screenCenterX + _backgroundOffset, _workingWindow.height()); + Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height()); + lScreen.translate(_backgroundWidth - lScreen.width(), 0); lScreen.clip(src); rScreen.clip(src); if (rScreen.width() < lScreen.width()) { - tmp.translate(_screenCenterX - _bkgOff - _bkgWidth, 0); + tmp.translate(_screenCenterX - _backgroundOffset - _backgroundWidth, 0); } else { - tmp.translate(_screenCenterX - _bkgOff, 0); + tmp.translate(_screenCenterX - _backgroundOffset, 0); } - } else if (_bkgWidth - _bkgOff < _screenCenterX) { - Common::Rect rScreen(_screenCenterX - (_bkgWidth - _bkgOff), _wrkHeight); - Common::Rect lScreen(_wrkWidth - rScreen.width(), _wrkHeight); - lScreen.translate(_bkgWidth - lScreen.width(), 0); + } else if (_backgroundWidth - _backgroundOffset < _screenCenterX) { + Common::Rect rScreen(_screenCenterX - (_backgroundWidth - _backgroundOffset), _workingWindow.height()); + Common::Rect lScreen(_workingWindow.width() - rScreen.width(), _workingWindow.height()); + lScreen.translate(_backgroundWidth - lScreen.width(), 0); lScreen.clip(src); rScreen.clip(src); if (lScreen.width() < rScreen.width()) { - tmp.translate(_screenCenterX + (_bkgWidth - _bkgOff), 0); + tmp.translate(_screenCenterX + (_backgroundWidth - _backgroundOffset), 0); } else { - tmp.translate(_screenCenterX - _bkgOff, 0); + tmp.translate(_screenCenterX - _backgroundOffset, 0); } } else { - tmp.translate(_screenCenterX - _bkgOff, 0); + tmp.translate(_screenCenterX - _backgroundOffset, 0); } } else if (state == RenderTable::TILT) { - tmp.translate(0, (_screenCenterY - _bkgOff)); + tmp.translate(0, (_screenCenterY - _backgroundOffset)); } return tmp; } EffectMap *RenderManager::makeEffectMap(const Common::Point &xy, int16 depth, const Common::Rect &rect, int8 *_minComp, int8 *_maxComp) { - Common::Rect bkgRect(_bkgWidth, _bkgHeight); + Common::Rect bkgRect(_backgroundWidth, _backgroundHeight); if (!bkgRect.contains(xy)) return NULL; if (!bkgRect.intersects(rect)) return NULL; - uint16 color = *(uint16 *)_curBkg.getBasePtr(xy.x, xy.y); + uint16 color = *(uint16 *)_currentBackgroundImage.getBasePtr(xy.x, xy.y); uint8 stC1, stC2, stC3; - _curBkg.format.colorToRGB(color, stC1, stC2, stC3); + _currentBackgroundImage.format.colorToRGB(color, stC1, stC2, stC3); EffectMap *newMap = new EffectMap; EffectMapUnit unit; @@ -953,11 +855,11 @@ EffectMap *RenderManager::makeEffectMap(const Common::Point &xy, int16 depth, co uint8 depth8 = depth << 3; for (int16 j = 0; j < h; j++) { - uint16 *pix = (uint16 *)_curBkg.getBasePtr(rect.left, rect.top + j); + uint16 *pix = (uint16 *)_currentBackgroundImage.getBasePtr(rect.left, rect.top + j); for (int16 i = 0; i < w; i++) { uint16 curClr = pix[i]; uint8 cC1, cC2, cC3; - _curBkg.format.colorToRGB(curClr, cC1, cC2, cC3); + _currentBackgroundImage.format.colorToRGB(curClr, cC1, cC2, cC3); bool use = false; @@ -1055,12 +957,261 @@ EffectMap *RenderManager::makeEffectMap(const Graphics::Surface &surf, uint16 tr } void RenderManager::markDirty() { - _bkgDirtyRect = Common::Rect(_bkgWidth, _bkgHeight); + _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight); } +#if 0 void RenderManager::bkgFill(uint8 r, uint8 g, uint8 b) { - _curBkg.fillRect(Common::Rect(_curBkg.w, _curBkg.h), _curBkg.format.RGBToColor(r, g, b)); + _currentBackgroundImage.fillRect(Common::Rect(_currentBackgroundImage.w, _currentBackgroundImage.h), _currentBackgroundImage.format.RGBToColor(r, g, b)); markDirty(); } +#endif + +void RenderManager::timedMessage(const Common::String &str, uint16 milsecs) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, str); + deleteSubArea(msgid, milsecs); +} + +bool RenderManager::askQuestion(const Common::String &str) { + Graphics::Surface textSurface; + textSurface.create(_subtitleArea.width(), _subtitleArea.height(), _engine->_resourcePixelFormat); + _engine->getTextRenderer()->drawTextWithWordWrapping(str, textSurface); + copyToScreen(textSurface, _subtitleArea, 0, 0); + + _engine->stopClock(); + + int result = 0; + + while (result == 0) { + Common::Event evnt; + while (_engine->getEventManager()->pollEvent(evnt)) { + if (evnt.type == Common::EVENT_KEYDOWN) { + // English: yes/no + // German: ja/nein + // Spanish: si/no + // French Nemesis: F4/any other key + // French ZGI: oui/non + switch (evnt.kbd.keycode) { + case Common::KEYCODE_y: + if (_engine->getLanguage() == Common::EN_ANY) + result = 2; + break; + case Common::KEYCODE_j: + if (_engine->getLanguage() == Common::DE_DEU) + result = 2; + break; + case Common::KEYCODE_s: + if (_engine->getLanguage() == Common::ES_ESP) + result = 2; + break; + case Common::KEYCODE_o: + if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_GRANDINQUISITOR) + result = 2; + break; + case Common::KEYCODE_F4: + if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS) + result = 2; + break; + case Common::KEYCODE_n: + result = 1; + break; + default: + if (_engine->getLanguage() == Common::FR_FRA && _engine->getGameId() == GID_NEMESIS) + result = 1; + break; + } + } + } + _system->updateScreen(); + if (_doubleFPS) + _system->delayMillis(33); + else + _system->delayMillis(66); + } + + // Draw over the text in order to clear it + textSurface.fillRect(Common::Rect(_subtitleArea.width(), _subtitleArea.height()), 0); + copyToScreen(textSurface, _subtitleArea, 0, 0); + + // Free the surface + textSurface.free(); + + _engine->startClock(); + return result == 2; +} + +void RenderManager::delayedMessage(const Common::String &str, uint16 milsecs) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, str); + processSubs(0); + renderSceneToScreen(); + _engine->stopClock(); + + uint32 stopTime = _system->getMillis() + milsecs; + while (_system->getMillis() < stopTime) { + Common::Event evnt; + while (_engine->getEventManager()->pollEvent(evnt)) { + if (evnt.type == Common::EVENT_KEYDOWN && + (evnt.kbd.keycode == Common::KEYCODE_SPACE || + evnt.kbd.keycode == Common::KEYCODE_RETURN || + evnt.kbd.keycode == Common::KEYCODE_ESCAPE)) + break; + } + _system->updateScreen(); + if (_doubleFPS) + _system->delayMillis(33); + else + _system->delayMillis(66); + } + deleteSubArea(msgid); + _engine->startClock(); +} + +void RenderManager::showDebugMsg(const Common::String &msg, int16 delay) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, msg); + deleteSubArea(msgid, delay); +} + +void RenderManager::updateRotation() { + int16 _velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity(); + ScriptManager *scriptManager = _engine->getScriptManager(); + + if (_doubleFPS) + _velocity /= 2; + + if (_velocity) { + RenderTable::RenderState renderState = _renderTable.getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition + (_renderTable.getPanoramaReverse() ? -_velocity : _velocity); + + int16 zeroPoint = _renderTable.getPanoramaZeroPoint(); + if (startPosition >= zeroPoint && newPosition < zeroPoint) + scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) - 1); + if (startPosition <= zeroPoint && newPosition > zeroPoint) + scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) + 1); + + int16 screenWidth = getBkgSize().x; + if (screenWidth) + newPosition %= screenWidth; + + if (newPosition < 0) + newPosition += screenWidth; + + setBackgroundPosition(newPosition); + } else if (renderState == RenderTable::TILT) { + int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition + _velocity; + + int16 screenHeight = getBkgSize().y; + int16 tiltGap = (int16)_renderTable.getTiltGap(); + + if (newPosition >= (screenHeight - tiltGap)) + newPosition = screenHeight - tiltGap; + if (newPosition <= tiltGap) + newPosition = tiltGap; + + setBackgroundPosition(newPosition); + } + } +} + +void RenderManager::checkBorders() { + RenderTable::RenderState renderState = _renderTable.getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition; + + int16 screenWidth = getBkgSize().x; + + if (screenWidth) + newPosition %= screenWidth; + + if (newPosition < 0) + newPosition += screenWidth; + + if (startPosition != newPosition) + setBackgroundPosition(newPosition); + } else if (renderState == RenderTable::TILT) { + int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition; + + int16 screenHeight = getBkgSize().y; + int16 tiltGap = (int16)_renderTable.getTiltGap(); + + if (newPosition >= (screenHeight - tiltGap)) + newPosition = screenHeight - tiltGap; + if (newPosition <= tiltGap) + newPosition = tiltGap; + + if (startPosition != newPosition) + setBackgroundPosition(newPosition); + } +} + +void RenderManager::rotateTo(int16 _toPos, int16 _time) { + if (_renderTable.getRenderState() != RenderTable::PANORAMA) + return; + + if (_time == 0) + _time = 1; + + int32 maxX = getBkgSize().x; + int32 curX = getCurrentBackgroundOffset(); + int32 dx = 0; + + if (curX == _toPos) + return; + + if (curX > _toPos) { + if (curX - _toPos > maxX / 2) + dx = (_toPos + (maxX - curX)) / _time; + else + dx = -(curX - _toPos) / _time; + } else { + if (_toPos - curX > maxX / 2) + dx = -((maxX - _toPos) + curX) / _time; + else + dx = (_toPos - curX) / _time; + } + + _engine->stopClock(); + + for (int16 i = 0; i <= _time; i++) { + if (i == _time) + curX = _toPos; + else + curX += dx; + + if (curX < 0) + curX = maxX - curX; + else if (curX >= maxX) + curX %= maxX; + + setBackgroundPosition(curX); + + prepareBackground(); + renderSceneToScreen(); + + _system->updateScreen(); + + _system->delayMillis(500 / _time); + } + + _engine->startClock(); +} + +void RenderManager::upscaleRect(Common::Rect &rect) { + rect.top = rect.top * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT; + rect.left = rect.left * HIRES_WINDOW_WIDTH / WINDOW_WIDTH; + rect.bottom = rect.bottom * HIRES_WINDOW_HEIGHT / WINDOW_HEIGHT; + rect.right = rect.right * HIRES_WINDOW_WIDTH / WINDOW_WIDTH; +} } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h index 879a8643ce..33d8a88e78 100644 --- a/engines/zvision/graphics/render_manager.h +++ b/engines/zvision/graphics/render_manager.h @@ -24,14 +24,14 @@ #define ZVISION_RENDER_MANAGER_H #include "zvision/graphics/render_table.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "common/rect.h" #include "common/hashmap.h" #include "graphics/surface.h" -#include "effect.h" +#include "graphics_effect.h" class OSystem; @@ -48,11 +48,11 @@ namespace ZVision { class RenderManager { public: - RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat); + RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat, bool doubleFPS); ~RenderManager(); private: - struct oneSub { + struct OneSubtitle { Common::Rect r; Common::String txt; int16 timer; @@ -60,87 +60,89 @@ private: bool redraw; }; - typedef Common::HashMap<uint16, oneSub> subMap; - typedef Common::List<Effect *> effectsList; + typedef Common::HashMap<uint16, OneSubtitle> SubtitleMap; + typedef Common::List<GraphicsEffect *> EffectsList; private: ZVision *_engine; OSystem *_system; const Graphics::PixelFormat _pixelFormat; - // A buffer for blitting background image to working window - Graphics::Surface _wrkWnd; + /** + * A Rectangle centered inside the actual window. All in-game coordinates + * are given in this coordinate space. Also, all images are clipped to the + * edges of this Rectangle + */ + Common::Rect _workingWindow; + + // Center of the screen in the x direction + const int _screenCenterX; + // Center of the screen in the y direction + const int _screenCenterY; - Common::Rect _wrkWndDirtyRect; + /** A buffer for background image that's being used to create the background */ + Graphics::Surface _currentBackgroundImage; + Common::Rect _backgroundDirtyRect; - // A buffer for mutate image by tilt or panorama renderers - Graphics::Surface _outWnd; + /** + * The x1 or y1 offset of the subRectangle of the background that is currently displayed on the screen + * It will be x1 if PANORAMA, or y1 if TILT + */ + int16 _backgroundOffset; + /** The width of the current background image */ + uint16 _backgroundWidth; + /** The height of the current background image */ + uint16 _backgroundHeight; - Common::Rect _bkgDirtyRect; + // A buffer that holds the portion of the background that is used to render the final image + // If it's a normal scene, the pixels will be blitted directly to the screen + // If it's a panorma / tilt scene, the pixels will be first warped to _warpedSceneSurface + Graphics::Surface _backgroundSurface; + Common::Rect _backgroundSurfaceDirtyRect; // A buffer for subtitles - Graphics::Surface _subWnd; + Graphics::Surface _subtitleSurface; - Common::Rect _subWndDirtyRect; + // Rectangle for subtitles area + Common::Rect _subtitleArea; // A buffer for menu drawing - Graphics::Surface _menuWnd; + Graphics::Surface _menuSurface; + Common::Rect _menuSurfaceDirtyRect; - Common::Rect _menuWndDirtyRect; + // Rectangle for menu area + Common::Rect _menuArea; // A buffer used for apply graphics effects - Graphics::Surface _effectWnd; + Graphics::Surface _effectSurface; - /** Width of the working window. Saved to prevent extraneous calls to _workingWindow.width() */ - const int _wrkWidth; - /** Height of the working window. Saved to prevent extraneous calls to _workingWindow.height() */ - const int _wrkHeight; - /** Center of the screen in the x direction */ - const int _screenCenterX; - /** Center of the screen in the y direction */ - const int _screenCenterY; + // A buffer to store the result of the panorama / tilt warps + Graphics::Surface _warpedSceneSurface; - /** - * A Rectangle centered inside the actual window. All in-game coordinates - * are given in this coordinate space. Also, all images are clipped to the - * edges of this Rectangle - */ - const Common::Rect _workingWindow; - - // Recatangle for subtitles area - Common::Rect _subWndRect; - - // Recatangle for menu area - Common::Rect _menuWndRect; /** Used to warp the background image */ RenderTable _renderTable; - // A buffer for background image - Graphics::Surface _curBkg; - /** The (x1,y1) coordinates of the subRectangle of the background that is currently displayed on the screen */ - int16 _bkgOff; - /** The width of the current background image */ - uint16 _bkgWidth; - /** The height of the current background image */ - uint16 _bkgHeight; - // Internal subtitles counter uint16 _subid; // Subtitle list - subMap _subsList; + SubtitleMap _subsList; // Visual effects list - effectsList _effects; + EffectsList _effects; + + bool _doubleFPS; public: void initialize(); /** - * Renders the current state of the backbuffer to the screen + * Renders the scene to the screen */ - void renderBackbufferToScreen(); + void renderSceneToScreen(); + + void copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop); /** * Blits the image or a portion of the image to the background. @@ -223,23 +225,20 @@ public: // Blitting surface-to-surface methods void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int x, int y); void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey); - void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y); - void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y, uint32 colorkey); // Blitting surface-to-background methods - void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y); - void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey); + void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey = -1); // Blitting surface-to-background methods with scale - void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect); - void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, uint32 colorkey); + void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey = -1); // Blitting surface-to-menu methods - void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y); - void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey); + void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey = -1); // Subtitles methods + void initSubArea(uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow); + // Create subtitle area and return ID uint16 createSubArea(const Common::Rect &area); uint16 createSubArea(); @@ -261,10 +260,8 @@ public: Graphics::Surface *getBkgRect(Common::Rect &rect); // Load image into new surface - Graphics::Surface *loadImage(const char *file); - Graphics::Surface *loadImage(Common::String &file); - Graphics::Surface *loadImage(const char *file, bool transposed); - Graphics::Surface *loadImage(Common::String &file, bool transposed); + Graphics::Surface *loadImage(Common::String file); + Graphics::Surface *loadImage(Common::String file, bool transposed); // Clear whole/area of menu surface void clearMenuSurface(); @@ -274,11 +271,10 @@ public: void renderMenuToScreen(); // Copy needed portion of background surface to workingWindow surface - void prepareBkg(); + void prepareBackground(); /** - * Reads an image file pixel data into a Surface buffer. In the process - * it converts the pixel data from RGB 555 to RGB 565. Also, if the image + * Reads an image file pixel data into a Surface buffer. Also, if the image * is transposed, it will un-transpose the pixel data. The function will * call destination::create() if the dimensions of destination do not match * up with the dimensions of the image. @@ -289,8 +285,7 @@ public: void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination); /** - * Reads an image file pixel data into a Surface buffer. In the process - * it converts the pixel data from RGB 555 to RGB 565. Also, if the image + * Reads an image file pixel data into a Surface buffer. Also, if the image * is transposed, it will un-transpose the pixel data. The function will * call destination::create() if the dimensions of destination do not match * up with the dimensions of the image. @@ -302,7 +297,7 @@ public: void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed); // Add visual effect to effects list - void addEffect(Effect *_effect); + void addEffect(GraphicsEffect *_effect); // Delete effect(s) by ID (ID equal to slot of action:region that create this effect) void deleteEffect(uint32 ID); @@ -319,13 +314,26 @@ public: EffectMap *makeEffectMap(const Graphics::Surface &surf, uint16 transp); // Return background rectangle in screen coordinates - Common::Rect bkgRectToScreen(const Common::Rect &src); + Common::Rect transformBackgroundSpaceRectToScreenSpace(const Common::Rect &src); // Mark whole background surface as dirty void markDirty(); - // Fille background surface by color +#if 0 + // Fill background surface by color void bkgFill(uint8 r, uint8 g, uint8 b); +#endif + + bool askQuestion(const Common::String &str); + void delayedMessage(const Common::String &str, uint16 milsecs); + void timedMessage(const Common::String &str, uint16 milsecs); + void showDebugMsg(const Common::String &msg, int16 delay = 3000); + + void checkBorders(); + void rotateTo(int16 to, int16 time); + void updateRotation(); + + void upscaleRect(Common::Rect &rect); }; } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp index c30e0bd472..df73247344 100644 --- a/engines/zvision/graphics/render_table.cpp +++ b/engines/zvision/graphics/render_table.cpp @@ -81,27 +81,6 @@ const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Poi return newPoint; } -uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) { - assert(percentColorOne < 1.0f); - - float rOne = float((colorOne & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); - float rTwo = float((colorTwo & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); - float gOne = float((colorOne & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); - float gTwo = float((colorTwo & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); - float bOne = float((colorOne & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); - float bTwo = float((colorTwo & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); - - float rFinal = rOne * percentColorOne + rTwo * (1.0f - percentColorOne); - float gFinal = gOne * percentColorOne + gTwo * (1.0f - percentColorOne); - float bFinal = bOne * percentColorOne + bTwo * (1.0f - percentColorOne); - - uint16 returnColor = (byte(rFinal + 0.5f) << Graphics::ColorMasks<555>::kRedShift) | - (byte(gFinal + 0.5f) << Graphics::ColorMasks<555>::kGreenShift) | - (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift); - - return returnColor; -} - void RenderTable::mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect) { uint32 destOffset = 0; diff --git a/engines/zvision/graphics/truetype_font.cpp b/engines/zvision/graphics/truetype_font.cpp deleted file mode 100644 index 2dbd7ca358..0000000000 --- a/engines/zvision/graphics/truetype_font.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* 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/scummsys.h" -#include "common/config-manager.h" -#include "common/debug.h" -#include "common/file.h" -#include "common/system.h" -#include "common/unzip.h" -#include "graphics/font.h" -#include "graphics/fonts/ttf.h" -#include "graphics/surface.h" - -#include "zvision/zvision.h" -#include "zvision/graphics/render_manager.h" -#include "zvision/graphics/truetype_font.h" - -namespace ZVision { - -StyledTTFont::StyledTTFont(ZVision *engine) { - _engine = engine; - _style = 0; - _font = NULL; - _lineHeight = 0; -} - -StyledTTFont::~StyledTTFont() { - if (_font) - delete _font; -} - -bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint style) { - _style = style; - return loadFont(fontName, point); -} - -bool StyledTTFont::loadFont(const Common::String &fontName, int32 point) { - Common::String newFontName; - if (fontName.matchString("*times new roman*", true) || fontName.matchString("*times*", true)) { - if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) - newFontName = "timesbi.ttf"; - else if (_style & STTF_BOLD) - newFontName = "timesbd.ttf"; - else if (_style & STTF_ITALIC) - newFontName = "timesi.ttf"; - else - newFontName = "times.ttf"; - - } else if (fontName.matchString("*courier new*", true) || fontName.matchString("*courier*", true) || fontName.matchString("*ZorkDeath*", true)) { - if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) - newFontName = "courbi.ttf"; - else if (_style & STTF_BOLD) - newFontName = "courbd.ttf"; - else if (_style & STTF_ITALIC) - newFontName = "couri.ttf"; - else - newFontName = "cour.ttf"; - - } else if (fontName.matchString("*century schoolbook*", true)) { - if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) - newFontName = "censcbkbi.ttf"; - else if (_style & STTF_BOLD) - newFontName = "censcbkbd.ttf"; - else if (_style & STTF_ITALIC) - newFontName = "censcbki.ttf"; - else - newFontName = "censcbk.ttf"; - - } else if (fontName.matchString("*garamond*", true)) { - if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) - newFontName = "garabi.ttf"; - else if (_style & STTF_BOLD) - newFontName = "garabd.ttf"; - else if (_style & STTF_ITALIC) - newFontName = "garai.ttf"; - else - newFontName = "gara.ttf"; - - } else if (fontName.matchString("*arial*", true) || fontName.matchString("*ZorkNormal*", true)) { - if ((_style & (STTF_BOLD | STTF_ITALIC)) == (STTF_BOLD | STTF_ITALIC)) - newFontName = "arialbi.ttf"; - else if (_style & STTF_BOLD) - newFontName = "arialbd.ttf"; - else if (_style & STTF_ITALIC) - newFontName = "ariali.ttf"; - else - newFontName = "arial.ttf"; - - } else { - debug("Could not identify font: %s. Reverting to Arial", fontName.c_str()); - newFontName = "arial.ttf"; - } - - bool sharp = (_style & STTF_SHARP) == STTF_SHARP; - - Common::File *file = _engine->getSearchManager()->openFile(newFontName); - - if (!file) { - Common::SeekableReadStream *themeFile = nullptr; - if (ConfMan.hasKey("themepath")) { - Common::FSNode themePath(ConfMan.get("themepath")); - if (themePath.exists()) { - Common::FSNode scummModern = themePath.getChild("scummmodern.zip"); - if (scummModern.exists()) { - themeFile = scummModern.createReadStream(); - } - } - } - if (!themeFile) { // Fallback : Search for ScummModern.zip in SearchMan. - themeFile = SearchMan.createReadStreamForMember("scummmodern.zip"); - } - if (themeFile) { - Common::Archive *themeArchive = Common::makeZipArchive(themeFile); - if (themeArchive->hasFile("FreeSans.ttf")) { - Common::SeekableReadStream *stream = nullptr; - stream = themeArchive->createReadStreamForMember("FreeSans.ttf"); - Graphics::Font *_newFont = Graphics::loadTTFFont(*stream, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display - if (_newFont) { - if (!_font) - delete _font; - _font = _newFont; - } - if (stream) - delete stream; - } - delete themeArchive; - themeArchive = nullptr; - } - } else { - Graphics::Font *_newFont = Graphics::loadTTFFont(*file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display - if (_newFont) { - if (!_font) - delete _font; - _font = _newFont; - } - delete file; - } - - _fntName = fontName; - _lineHeight = point; - - if (_font) - return true; - return false; -} - -void StyledTTFont::setStyle(uint newStyle) { - if ((_style & (STTF_BOLD | STTF_ITALIC | STTF_SHARP)) != (newStyle & (STTF_BOLD | STTF_ITALIC | STTF_SHARP))) { - _style = newStyle; - loadFont(_fntName, _lineHeight); - } else { - _style = newStyle; - } -} - -int StyledTTFont::getFontHeight() { - if (_font) - return _font->getFontHeight(); - return 0; -} - -int StyledTTFont::getMaxCharWidth() { - if (_font) - return _font->getMaxCharWidth(); - return 0; -} - -int StyledTTFont::getCharWidth(byte chr) { - if (_font) - return _font->getCharWidth(chr); - return 0; -} - -int StyledTTFont::getKerningOffset(byte left, byte right) { - if (_font) - return _font->getKerningOffset(left, right); - return 0; -} - -void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) { - if (_font) { - _font->drawChar(dst, chr, x, y, color); - if (_style & STTF_UNDERLINE) { - int16 pos = floor(_font->getFontHeight() * 0.87); - int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); - dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); - } - if (_style & STTF_STRIKEOUT) { - int16 pos = floor(_font->getFontHeight() * 0.60); - int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); - dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); - } - } -} - -void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align) { - if (_font) { - _font->drawString(dst, str, x, y, w, color, align); - if (_style & STTF_UNDERLINE) { - int16 pos = floor(_font->getFontHeight() * 0.87); - int16 wd = MIN(_font->getStringWidth(str), w); - int16 stX = x; - if (align == Graphics::kTextAlignCenter) - stX += (w - wd) / 2; - else if (align == Graphics::kTextAlignRight) - stX += (w - wd); - - int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); - - dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); - } - if (_style & STTF_STRIKEOUT) { - int16 pos = floor(_font->getFontHeight() * 0.60); - int16 wd = MIN(_font->getStringWidth(str), w); - int16 stX = x; - if (align == Graphics::kTextAlignCenter) - stX += (w - wd) / 2; - else if (align == Graphics::kTextAlignRight) - stX += (w - wd); - - int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); - - dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); - } - } -} - -int StyledTTFont::getStringWidth(const Common::String &str) { - if (_font) - return _font->getStringWidth(str); - return 0; -} - -Graphics::Surface *StyledTTFont::renderSolidText(const Common::String &str, uint32 color) { - Graphics::Surface *tmp = new Graphics::Surface; - if (_font) { - int16 w = _font->getStringWidth(str); - if (w && w < 1024) { - tmp->create(w, _font->getFontHeight(), _engine->_pixelFormat); - drawString(tmp, str, 0, 0, w, color); - } - } - return tmp; -} - -} // End of namespace ZVision diff --git a/engines/zvision/module.mk b/engines/zvision/module.mk index 00652f0824..93fba2879d 100644 --- a/engines/zvision/module.mk +++ b/engines/zvision/module.mk @@ -4,10 +4,9 @@ MODULE_OBJS := \ core/console.o \ core/clock.o \ core/events.o \ - core/menu.o \ - core/save_manager.o \ detection.o \ file/lzss_read_stream.o \ + file/save_manager.o \ file/search_manager.o \ file/zfs_archive.o \ graphics/cursors/cursor_manager.o \ @@ -17,8 +16,6 @@ MODULE_OBJS := \ graphics/effects/wave.o \ graphics/render_manager.o \ graphics/render_table.o \ - graphics/subtitles.o \ - graphics/truetype_font.o \ scripting/actions.o \ scripting/control.o \ scripting/controls/fist_control.o \ @@ -32,19 +29,22 @@ MODULE_OBJS := \ scripting/controls/slot_control.o \ scripting/controls/titler_control.o \ scripting/inventory.o \ + scripting/menu.o \ scripting/scr_file_handling.o \ scripting/script_manager.o \ - scripting/sidefx/animation_node.o \ - scripting/sidefx/distort_node.o \ - scripting/sidefx/music_node.o \ - scripting/sidefx/region_node.o \ - scripting/sidefx/syncsound_node.o \ - scripting/sidefx/timer_node.o \ - scripting/sidefx/ttytext_node.o \ + scripting/effects/animation_effect.o \ + scripting/effects/distort_effect.o \ + scripting/effects/music_effect.o \ + scripting/effects/region_effect.o \ + scripting/effects/syncsound_effect.o \ + scripting/effects/timer_effect.o \ + scripting/effects/ttytext_effect.o \ sound/midi.o \ sound/zork_raw.o \ text/string_manager.o \ + text/subtitles.o \ text/text.o \ + text/truetype_font.o \ video/rlf_decoder.o \ video/video.o \ video/zork_avi_decoder.o \ diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index 0422a2c028..e1380b0eb2 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -21,42 +21,44 @@ */ #include "common/scummsys.h" +#include "video/video_decoder.h" #include "zvision/scripting/actions.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" -#include "zvision/sound/zork_raw.h" -#include "zvision/video/zork_avi_decoder.h" -#include "zvision/scripting/sidefx/timer_node.h" -#include "zvision/scripting/sidefx/music_node.h" -#include "zvision/scripting/sidefx/syncsound_node.h" -#include "zvision/scripting/sidefx/animation_node.h" -#include "zvision/scripting/sidefx/distort_node.h" -#include "zvision/scripting/sidefx/ttytext_node.h" -#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/file/save_manager.h" +#include "zvision/scripting/menu.h" +#include "zvision/scripting/effects/timer_effect.h" +#include "zvision/scripting/effects/music_effect.h" +#include "zvision/scripting/effects/syncsound_effect.h" +#include "zvision/scripting/effects/animation_effect.h" +#include "zvision/scripting/effects/distort_effect.h" +#include "zvision/scripting/effects/ttytext_effect.h" +#include "zvision/scripting/effects/region_effect.h" #include "zvision/scripting/controls/titler_control.h" #include "zvision/graphics/render_table.h" -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" #include "zvision/graphics/effects/fog.h" #include "zvision/graphics/effects/light.h" #include "zvision/graphics/effects/wave.h" -#include "zvision/core/save_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "common/file.h" - -#include "audio/decoders/wave.h" - namespace ZVision { +ResultAction::ResultAction(ZVision *engine, int32 slotKey) : + _engine(engine), + _slotKey(slotKey), + _scriptManager(engine->getScriptManager()) { +} + ////////////////////////////////////////////////////////////////////////////// // ActionAdd ////////////////////////////////////////////////////////////////////////////// -ActionAdd::ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionAdd::ActionAdd(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; _value = 0; @@ -64,7 +66,7 @@ ActionAdd::ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line) } bool ActionAdd::execute() { - _engine->getScriptManager()->setStateValue(_key, _engine->getScriptManager()->getStateValue(_key) + _value); + _scriptManager->setStateValue(_key, _scriptManager->getStateValue(_key) + _value); return true; } @@ -72,23 +74,22 @@ bool ActionAdd::execute() { // ActionAssign ////////////////////////////////////////////////////////////////////////////// -ActionAssign::ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionAssign::ActionAssign(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; char buf[64]; memset(buf, 0, 64); sscanf(line.c_str(), "%u, %s", &_key, buf); - _value = new ValueSlot(_engine->getScriptManager(), buf); + _value = new ValueSlot(_scriptManager, buf); } ActionAssign::~ActionAssign() { - if (_value) - delete _value; + delete _value; } bool ActionAssign::execute() { - _engine->getScriptManager()->setStateValue(_key, _value->getValue()); + _scriptManager->setStateValue(_key, _value->getValue()); return true; } @@ -96,8 +97,8 @@ bool ActionAssign::execute() { // ActionAttenuate ////////////////////////////////////////////////////////////////////////////// -ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; _attenuation = 0; @@ -105,10 +106,10 @@ ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::S } bool ActionAttenuate::execute() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_key); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { - MusicNode *mus = (MusicNode *)fx; - mus->setVolume(255 - (abs(_attenuation) >> 7)); + ScriptingEffect *fx = _scriptManager->getSideFX(_key); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; + mus->setVolume(255 * (10000 - abs(_attenuation)) / 10000 ); } return true; } @@ -117,8 +118,8 @@ bool ActionAttenuate::execute() { // ActionChangeLocation ////////////////////////////////////////////////////////////////////////////// -ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _world = 'g'; _room = 'a'; _node = 'r'; @@ -130,7 +131,7 @@ ActionChangeLocation::ActionChangeLocation(ZVision *engine, int32 slotkey, const bool ActionChangeLocation::execute() { // We can't directly call ScriptManager::ChangeLocationIntern() because doing so clears all the Puzzles, and thus would corrupt the current puzzle checking - _engine->getScriptManager()->changeLocation(_world, _room, _node, _view, _offset); + _scriptManager->changeLocation(_world, _room, _node, _view, _offset); // Tell the puzzle system to stop checking any more puzzles return false; } @@ -139,8 +140,8 @@ bool ActionChangeLocation::execute() { // ActionCrossfade ////////////////////////////////////////////////////////////////////////////// -ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _keyOne = 0; _keyTwo = 0; _oneStartVolume = 0; @@ -156,9 +157,9 @@ ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::S bool ActionCrossfade::execute() { if (_keyOne) { - SideFX *fx = _engine->getScriptManager()->getSideFX(_keyOne); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { - MusicNode *mus = (MusicNode *)fx; + ScriptingEffect *fx = _scriptManager->getSideFX(_keyOne); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; if (_oneStartVolume >= 0) mus->setVolume((_oneStartVolume * 255) / 100); @@ -167,9 +168,9 @@ bool ActionCrossfade::execute() { } if (_keyTwo) { - SideFX *fx = _engine->getScriptManager()->getSideFX(_keyTwo); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { - MusicNode *mus = (MusicNode *)fx; + ScriptingEffect *fx = _scriptManager->getSideFX(_keyTwo); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { + MusicNodeBASE *mus = (MusicNodeBASE *)fx; if (_twoStartVolume >= 0) mus->setVolume((_twoStartVolume * 255) / 100); @@ -183,8 +184,8 @@ bool ActionCrossfade::execute() { // ActionCursor ////////////////////////////////////////////////////////////////////////////// -ActionCursor::ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionCursor::ActionCursor(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { Common::String up = line; up.toUppercase(); _action = 0; @@ -215,10 +216,13 @@ bool ActionCursor::execute() { // ActionDelayRender ////////////////////////////////////////////////////////////////////////////// -ActionDelayRender::ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDelayRender::ActionDelayRender(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _framesToDelay = 0; sscanf(line.c_str(), "%u", &_framesToDelay); + // Limit to 10 frames maximum. This fixes the script bug in ZGI scene px10 + // (outside Frobozz Electric building), where this is set to 100 (bug #6791). + _framesToDelay = MIN<uint32>(_framesToDelay, 10); } bool ActionDelayRender::execute() { @@ -230,32 +234,15 @@ bool ActionDelayRender::execute() { // ActionDisableControl ////////////////////////////////////////////////////////////////////////////// -ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDisableControl::ActionDisableControl(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionDisableControl::execute() { - _engine->getScriptManager()->setStateFlag(_key, Puzzle::DISABLED); - return true; -} - -////////////////////////////////////////////////////////////////////////////// -// ActionDisableVenus -////////////////////////////////////////////////////////////////////////////// - -ActionDisableVenus::ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { - _key = 0; - - sscanf(line.c_str(), "%d", &_key); -} - -bool ActionDisableVenus::execute() { - _engine->getScriptManager()->setStateValue(_key, 0); - + _scriptManager->setStateFlag(_key, Puzzle::DISABLED); return true; } @@ -263,8 +250,8 @@ bool ActionDisableVenus::execute() { // ActionDisplayMessage ////////////////////////////////////////////////////////////////////////////// -ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _control = 0; _msgid = 0; @@ -272,7 +259,7 @@ ActionDisplayMessage::ActionDisplayMessage(ZVision *engine, int32 slotkey, const } bool ActionDisplayMessage::execute() { - Control *ctrl = _engine->getScriptManager()->getControl(_control); + Control *ctrl = _scriptManager->getControl(_control); if (ctrl && ctrl->getType() == Control::CONTROL_TITLER) { TitlerControl *titler = (TitlerControl *)ctrl; titler->setString(_msgid); @@ -295,11 +282,11 @@ bool ActionDissolve::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionDistort +// ActionDistort - only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30) ////////////////////////////////////////////////////////////////////////////// -ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionDistort::ActionDistort(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _distSlot = 0; _speed = 0; _startAngle = 60.0; @@ -311,14 +298,14 @@ ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::Strin } ActionDistort::~ActionDistort() { - _engine->getScriptManager()->killSideFx(_distSlot); + _scriptManager->killSideFx(_distSlot); } bool ActionDistort::execute() { - if (_engine->getScriptManager()->getSideFX(_distSlot)) + if (_scriptManager->getSideFX(_distSlot)) return true; - _engine->getScriptManager()->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale)); + _scriptManager->addSideFX(new DistortNode(_engine, _distSlot, _speed, _startAngle, _endAngle, _startLineScale, _endLineScale)); return true; } @@ -327,15 +314,15 @@ bool ActionDistort::execute() { // ActionEnableControl ////////////////////////////////////////////////////////////////////////////// -ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionEnableControl::ActionEnableControl(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionEnableControl::execute() { - _engine->getScriptManager()->unsetStateFlag(_key, Puzzle::DISABLED); + _scriptManager->unsetStateFlag(_key, Puzzle::DISABLED); return true; } @@ -343,13 +330,13 @@ bool ActionEnableControl::execute() { // ActionFlushMouseEvents ////////////////////////////////////////////////////////////////////////////// -ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotkey) : - ResultAction(engine, slotkey) { +ActionFlushMouseEvents::ActionFlushMouseEvents(ZVision *engine, int32 slotKey) : + ResultAction(engine, slotKey) { } bool ActionFlushMouseEvents::execute() { - _engine->getScriptManager()->flushEvent(Common::EVENT_LBUTTONUP); - _engine->getScriptManager()->flushEvent(Common::EVENT_LBUTTONDOWN); + _scriptManager->flushEvent(Common::EVENT_LBUTTONUP); + _scriptManager->flushEvent(Common::EVENT_LBUTTONDOWN); return true; } @@ -357,13 +344,13 @@ bool ActionFlushMouseEvents::execute() { // ActionInventory ////////////////////////////////////////////////////////////////////////////// -ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionInventory::ActionInventory(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _type = -1; _key = 0; char buf[25]; - sscanf(line.c_str(), "%25s %d", buf, &_key); + sscanf(line.c_str(), "%24s %d", buf, &_key); if (strcmp(buf, "add") == 0) { _type = 0; @@ -382,22 +369,22 @@ ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::S bool ActionInventory::execute() { switch (_type) { case 0: // add - _engine->getScriptManager()->inventoryAdd(_key); + _scriptManager->inventoryAdd(_key); break; case 1: // addi - _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(_key)); + _scriptManager->inventoryAdd(_scriptManager->getStateValue(_key)); break; case 2: // drop if (_key >= 0) - _engine->getScriptManager()->inventoryDrop(_key); + _scriptManager->inventoryDrop(_key); else - _engine->getScriptManager()->inventoryDrop(_engine->getScriptManager()->getStateValue(StateKey_InventoryItem)); + _scriptManager->inventoryDrop(_scriptManager->getStateValue(StateKey_InventoryItem)); break; case 3: // dropi - _engine->getScriptManager()->inventoryDrop(_engine->getScriptManager()->getStateValue(_key)); + _scriptManager->inventoryDrop(_scriptManager->getStateValue(_key)); break; case 4: // cycle - _engine->getScriptManager()->inventoryCycle(); + _scriptManager->inventoryCycle(); break; default: break; @@ -406,41 +393,41 @@ bool ActionInventory::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionKill +// ActionKill - only used by ZGI ////////////////////////////////////////////////////////////////////////////// -ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionKill::ActionKill(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; _type = 0; char keytype[25]; - sscanf(line.c_str(), "%25s", keytype); + sscanf(line.c_str(), "%24s", keytype); if (keytype[0] == '"') { if (!scumm_stricmp(keytype, "\"ANIM\"")) - _type = SideFX::SIDEFX_ANIM; + _type = ScriptingEffect::SCRIPTING_EFFECT_ANIM; else if (!scumm_stricmp(keytype, "\"AUDIO\"")) - _type = SideFX::SIDEFX_AUDIO; + _type = ScriptingEffect::SCRIPTING_EFFECT_AUDIO; else if (!scumm_stricmp(keytype, "\"DISTORT\"")) - _type = SideFX::SIDEFX_DISTORT; + _type = ScriptingEffect::SCRIPTING_EFFECT_DISTORT; else if (!scumm_stricmp(keytype, "\"PANTRACK\"")) - _type = SideFX::SIDEFX_PANTRACK; + _type = ScriptingEffect::SCRIPTING_EFFECT_PANTRACK; else if (!scumm_stricmp(keytype, "\"REGION\"")) - _type = SideFX::SIDEFX_REGION; + _type = ScriptingEffect::SCRIPTING_EFFECT_REGION; else if (!scumm_stricmp(keytype, "\"TIMER\"")) - _type = SideFX::SIDEFX_TIMER; + _type = ScriptingEffect::SCRIPTING_EFFECT_TIMER; else if (!scumm_stricmp(keytype, "\"TTYTEXT\"")) - _type = SideFX::SIDEFX_TTYTXT; + _type = ScriptingEffect::SCRIPTING_EFFECT_TTYTXT; else if (!scumm_stricmp(keytype, "\"ALL\"")) - _type = SideFX::SIDEFX_ALL; + _type = ScriptingEffect::SCRIPTING_EFFECT_ALL; } else _key = atoi(keytype); } bool ActionKill::execute() { if (_type) - _engine->getScriptManager()->killSideFxType((SideFX::SideFXType)_type); + _scriptManager->killSideFxType((ScriptingEffect::ScriptingEffectType)_type); else - _engine->getScriptManager()->killSideFx(_key); + _scriptManager->killSideFx(_key); return true; } @@ -448,15 +435,15 @@ bool ActionKill::execute() { // ActionMenuBarEnable ////////////////////////////////////////////////////////////////////////////// -ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _menus = 0xFFFF; sscanf(line.c_str(), "%hu", &_menus); } bool ActionMenuBarEnable::execute() { - _engine->menuBarEnable(_menus); + _engine->getMenuHandler()->setEnable(_menus); return true; } @@ -464,57 +451,76 @@ bool ActionMenuBarEnable::execute() { // ActionMusic ////////////////////////////////////////////////////////////////////////////// -ActionMusic::ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global) : - ResultAction(engine, slotkey), - _volume(255), +ActionMusic::ActionMusic(ZVision *engine, int32 slotKey, const Common::String &line, bool global) : + ResultAction(engine, slotKey), _note(0), _prog(0), _universe(global) { uint type = 0; char fileNameBuffer[25]; uint loop = 0; - uint volume = 255; + char volumeBuffer[15]; - sscanf(line.c_str(), "%u %25s %u %u", &type, fileNameBuffer, &loop, &volume); + // Volume is optional. If it doesn't appear, assume full volume + strcpy(volumeBuffer, "100"); - // type 4 are midi sound effect files + sscanf(line.c_str(), "%u %24s %u %14s", &type, fileNameBuffer, &loop, volumeBuffer); + + // Type 4 actions are MIDI commands, not files. These are only used by + // Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well + // as vr) if (type == 4) { _midi = true; int note; int prog; - sscanf(line.c_str(), "%u %d %d %u", &type, &prog, ¬e, &volume); - _volume = volume; + sscanf(line.c_str(), "%u %d %d %14s", &type, &prog, ¬e, volumeBuffer); + _volume = new ValueSlot(_scriptManager, volumeBuffer); _note = note; _prog = prog; } else { _midi = false; _fileName = Common::String(fileNameBuffer); _loop = loop == 1 ? true : false; - - // Volume is optional. If it doesn't appear, assume full volume - if (volume != 255) { - // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] - _volume = volume * 255 / 100; + if (volumeBuffer[0] != '[' && atoi(volumeBuffer) > 100) { + // I thought I saw a case like this in Zork Nemesis, so + // let's guard against it. + warning("ActionMusic: Adjusting volume for %s from %s to 100", _fileName.c_str(), volumeBuffer); + strcpy(volumeBuffer, "100"); } + _volume = new ValueSlot(_scriptManager, volumeBuffer); } + + // WORKAROUND for a script bug in Zork Nemesis, rooms mq70/mq80. + // Fixes an edge case where the player goes to the dark room with the grue + // without holding a torch, and then quickly runs away before the grue's + // sound effect finishes. Fixes script bug #6794. + if (engine->getGameId() == GID_NEMESIS && _slotKey == 14822 && _scriptManager->getStateValue(_slotKey) == 2) + _scriptManager->setStateValue(_slotKey, 0); + } ActionMusic::~ActionMusic() { if (!_universe) - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); + delete _volume; } bool ActionMusic::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) - return true; + if (_scriptManager->getSideFX(_slotKey)) { + _scriptManager->killSideFx(_slotKey); + _scriptManager->setStateValue(_slotKey, 2); + } + + uint volume = _volume->getValue(); if (_midi) { - _engine->getScriptManager()->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, _volume)); + _scriptManager->addSideFX(new MusicMidiNode(_engine, _slotKey, _prog, _note, volume)); } else { if (!_engine->getSearchManager()->hasFile(_fileName)) return true; - _engine->getScriptManager()->addSideFX(new MusicNode(_engine, _slotKey, _fileName, _loop, _volume)); + // Volume in the script files is mapped to [0, 100], but the ScummVM mixer uses [0, 255] + _scriptManager->addSideFX(new MusicNode(_engine, _slotKey, _fileName, _loop, volume * 255 / 100)); } return true; @@ -524,8 +530,8 @@ bool ActionMusic::execute() { // ActionPanTrack ////////////////////////////////////////////////////////////////////////////// -ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey), +ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey), _pos(0), _musicSlot(0) { @@ -533,14 +539,14 @@ ActionPanTrack::ActionPanTrack(ZVision *engine, int32 slotkey, const Common::Str } ActionPanTrack::~ActionPanTrack() { - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); } bool ActionPanTrack::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - _engine->getScriptManager()->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos)); + _scriptManager->addSideFX(new PanTrackNode(_engine, _slotKey, _musicSlot, _pos)); return true; } @@ -549,8 +555,8 @@ bool ActionPanTrack::execute() { // ActionPreferences ////////////////////////////////////////////////////////////////////////////// -ActionPreferences::ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPreferences::ActionPreferences(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { if (line.compareToIgnoreCase("save") == 0) _save = true; else @@ -570,38 +576,38 @@ bool ActionPreferences::execute() { // ActionPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _mask = 0; _framerate = 0; char fileName[25]; - // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), "%25s %*u %*u %d %d", fileName, &_mask, &_framerate); + // The two %*u are usually 0 and dont seem to have a use + sscanf(line.c_str(), "%24s %*u %*u %d %d", fileName, &_mask, &_framerate); - if (_mask > 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); - _mask = _engine->_pixelFormat.RGBToColor(r, g, b); - } + // Mask 0 means "no transparency" in this case. Since we use a common blitting + // code for images and animations, we set it to -1 to avoid confusion with + // color 0, which is used as a mask in some images + if (_mask == 0) + _mask = -1; _fileName = Common::String(fileName); } ActionPreloadAnimation::~ActionPreloadAnimation() { - _engine->getScriptManager()->deleteSideFx(_slotKey); + _scriptManager->deleteSideFx(_slotKey); } bool ActionPreloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey); if (!nod) { - nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate, false); - _engine->getScriptManager()->addSideFX(nod); + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate, false); + _scriptManager->addSideFX(nod); } else nod->stop(); - _engine->getScriptManager()->setStateValue(_slotKey, 2); + _scriptManager->setStateValue(_slotKey, 2); return true; } @@ -609,18 +615,18 @@ bool ActionPreloadAnimation::execute() { // ActionUnloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionUnloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_key); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_key); - if (nod && nod->getType() == SideFX::SIDEFX_ANIM) - _engine->getScriptManager()->deleteSideFx(_key); + if (nod && nod->getType() == ScriptingEffect::SCRIPTING_EFFECT_ANIM) + _scriptManager->deleteSideFx(_key); return true; } @@ -629,8 +635,8 @@ bool ActionUnloadAnimation::execute() { // ActionPlayAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _x = 0; _y = 0; _x2 = 0; @@ -645,28 +651,35 @@ ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const C // The two %*u are always 0 and dont seem to have a use sscanf(line.c_str(), - "%25s %u %u %u %u %u %u %d %*u %*u %d %d", + "%24s %u %u %u %u %u %u %d %*u %*u %d %d", fileName, &_x, &_y, &_x2, &_y2, &_start, &_end, &_loopCount, &_mask, &_framerate); - if (_mask > 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); - _mask = _engine->_pixelFormat.RGBToColor(r, g, b); - } + // Mask 0 means "no transparency" in this case. Since we use a common blitting + // code for images and animations, we set it to -1 to avoid confusion with + // color 0, which is used as a mask in some images + if (_mask == 0) + _mask = -1; _fileName = Common::String(fileName); + + // WORKAROUND for bug #6769, location me1g.scr (the "Alchemical debacle" + // video in ZGI). We only scale up by 2x, in AnimationEffect::process(), + // but the dimensions of the target frame are off by 2 pixels. We fix that + // here, so that the video can be scaled. + if (_fileName == "me1ga011.avi" && _y2 == 213) + _y2 = 215; } ActionPlayAnimation::~ActionPlayAnimation() { - _engine->getScriptManager()->deleteSideFx(_slotKey); + _scriptManager->deleteSideFx(_slotKey); } bool ActionPlayAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_slotKey); if (!nod) { - nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate); - _engine->getScriptManager()->addSideFX(nod); + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate); + _scriptManager->addSideFX(nod); } else nod->stop(); @@ -680,8 +693,8 @@ bool ActionPlayAnimation::execute() { // ActionPlayPreloadAnimation ////////////////////////////////////////////////////////////////////////////// -ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _controlKey = 0; _x1 = 0; _y1 = 0; @@ -697,7 +710,7 @@ ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 sl } bool ActionPlayPreloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_controlKey); + AnimationEffect *nod = (AnimationEffect *)_scriptManager->getSideFX(_controlKey); if (nod) nod->addPlayNode(_slotKey, _x1, _y1, _x2, _y2, _startFrame, _endFrame, _loopCount); @@ -716,11 +729,11 @@ bool ActionQuit::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionRegion +// ActionRegion - only used by Zork: Nemesis ////////////////////////////////////////////////////////////////////////////// -ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRegion::ActionRegion(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _delay = 0; _type = 0; _unk1 = 0; @@ -738,20 +751,20 @@ ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String } ActionRegion::~ActionRegion() { - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); } bool ActionRegion::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - Effect *effct = NULL; + GraphicsEffect *effect = NULL; switch (_type) { case 0: { uint16 centerX, centerY, frames; double amplitude, waveln, speed; sscanf(_custom.c_str(), "%hu,%hu,%hu,%lf,%lf,%lf,", ¢erX, ¢erY, &frames, &litude, &waveln, &speed); - effct = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); + effect = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); } break; case 1: { @@ -763,7 +776,7 @@ bool ActionRegion::execute() { int8 minD; int8 maxD; EffectMap *_map = _engine->getRenderManager()->makeEffectMap(Common::Point(aX, aY), aD, _rect, &minD, &maxD); - effct = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); + effect = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); } break; case 9: { @@ -779,16 +792,16 @@ bool ActionRegion::execute() { _rect.setHeight(tempMask.h); EffectMap *_map = _engine->getRenderManager()->makeEffectMap(tempMask, 0); - effct = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); + effect = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); } break; default: break; } - if (effct) { - _engine->getScriptManager()->addSideFX(new RegionNode(_engine, _slotKey, effct, _delay)); - _engine->getRenderManager()->addEffect(effct); + if (effect) { + _scriptManager->addSideFX(new RegionNode(_engine, _slotKey, effect, _delay)); + _engine->getRenderManager()->addEffect(effect); } return true; @@ -798,22 +811,21 @@ bool ActionRegion::execute() { // ActionRandom ////////////////////////////////////////////////////////////////////////////// -ActionRandom::ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRandom::ActionRandom(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char maxBuffer[64]; memset(maxBuffer, 0, 64); sscanf(line.c_str(), "%s", maxBuffer); - _max = new ValueSlot(_engine->getScriptManager(), maxBuffer); + _max = new ValueSlot(_scriptManager, maxBuffer); } ActionRandom::~ActionRandom() { - if (_max) - delete _max; + delete _max; } bool ActionRandom::execute() { uint randNumber = _engine->getRandomSource()->getRandomNumber(_max->getValue()); - _engine->getScriptManager()->setStateValue(_slotKey, randNumber); + _scriptManager->setStateValue(_slotKey, randNumber); return true; } @@ -821,15 +833,15 @@ bool ActionRandom::execute() { // ActionRestoreGame ////////////////////////////////////////////////////////////////////////////// -ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRestoreGame::ActionRestoreGame(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char buf[128]; sscanf(line.c_str(), "%s", buf); _fileName = Common::String(buf); } bool ActionRestoreGame::execute() { - _engine->getSaveManager()->loadGame(_fileName); + _engine->getSaveManager()->loadGame(-1); return false; } @@ -837,8 +849,8 @@ bool ActionRestoreGame::execute() { // ActionRotateTo ////////////////////////////////////////////////////////////////////////////// -ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _time = 0; _toPos = 0; @@ -846,7 +858,7 @@ ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::Str } bool ActionRotateTo::execute() { - _engine->rotateTo(_toPos, _time); + _engine->getRenderManager()->rotateTo(_toPos, _time); return true; } @@ -855,27 +867,18 @@ bool ActionRotateTo::execute() { // ActionSetPartialScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _x = 0; _y = 0; char fileName[25]; - int color; - sscanf(line.c_str(), "%u %u %25s %*u %d", &_x, &_y, fileName, &color); + sscanf(line.c_str(), "%u %u %24s %*u %d", &_x, &_y, fileName, &_backgroundColor); _fileName = Common::String(fileName); - if (color >= 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); - _backgroundColor = _engine->_pixelFormat.RGBToColor(r, g, b); - } else { - _backgroundColor = color; - } - - if (color > 65535) { + if (_backgroundColor > 65535) { warning("Background color for ActionSetPartialScreen is bigger than a uint16"); } } @@ -904,10 +907,10 @@ bool ActionSetPartialScreen::execute() { // ActionSetScreen ////////////////////////////////////////////////////////////////////////////// -ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char fileName[25]; - sscanf(line.c_str(), "%25s", fileName); + sscanf(line.c_str(), "%24s", fileName); _fileName = Common::String(fileName); } @@ -919,35 +922,17 @@ bool ActionSetScreen::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionSetVenus -////////////////////////////////////////////////////////////////////////////// - -ActionSetVenus::ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { - _key = 0; - - sscanf(line.c_str(), "%d", &_key); -} - -bool ActionSetVenus::execute() { - if (_engine->getScriptManager()->getStateValue(_key)) - _engine->getScriptManager()->setStateValue(StateKey_Venus, _key); - - return true; -} - -////////////////////////////////////////////////////////////////////////////// // ActionStop ////////////////////////////////////////////////////////////////////////////// -ActionStop::ActionStop(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionStop::ActionStop(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _key = 0; sscanf(line.c_str(), "%u", &_key); } bool ActionStop::execute() { - _engine->getScriptManager()->stopSideFx(_key); + _scriptManager->stopSideFx(_key); return true; } @@ -955,8 +940,8 @@ bool ActionStop::execute() { // ActionStreamVideo ////////////////////////////////////////////////////////////////////////////// -ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _x1 = 0; _x2 = 0; _y1 = 0; @@ -966,42 +951,69 @@ ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Commo char fileName[25]; uint skipline = 0; //skipline - render video with skip every second line, not skippable. - sscanf(line.c_str(), "%25s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); + sscanf(line.c_str(), "%24s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); _fileName = Common::String(fileName); _skippable = true; } bool ActionStreamVideo::execute() { - ZorkAVIDecoder decoder; - Common::File *_file = _engine->getSearchManager()->openFile(_fileName); - - if (_file) { - if (!decoder.loadStream(_file)) { + Video::VideoDecoder *decoder; + Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + Common::String subname = _fileName; + subname.setChar('s', subname.size() - 3); + subname.setChar('u', subname.size() - 2); + subname.setChar('b', subname.size() - 1); + bool subtitleExists = _engine->getSearchManager()->hasFile(subname); + bool switchToHires = false; + +// NOTE: We only show the hires MPEG2 videos when libmpeg2 is compiled in, +// otherwise we fall back to the lowres ones +#ifdef USE_MPEG2 + Common::String hiresFileName = _fileName; + hiresFileName.setChar('d', hiresFileName.size() - 8); + hiresFileName.setChar('v', hiresFileName.size() - 3); + hiresFileName.setChar('o', hiresFileName.size() - 2); + hiresFileName.setChar('b', hiresFileName.size() - 1); + + if (_scriptManager->getStateValue(StateKey_MPEGMovies) == 1 &&_engine->getSearchManager()->hasFile(hiresFileName)) { + // TODO: Enable once AC3 support is implemented + if (!_engine->getSearchManager()->hasFile(_fileName)) // Check for the regular video return true; - } - - _engine->getCursorManager()->showMouse(false); + warning("The hires videos of the DVD version of ZGI aren't supported yet, using lowres"); + //_fileName = hiresFileName; + //switchToHires = true; + } else if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; +#else + if (!_engine->getSearchManager()->hasFile(_fileName)) + return true; +#endif - Common::Rect destRect = Common::Rect(_x1, _y1, _x2 + 1, _y2 + 1); + decoder = _engine->loadAnimation(_fileName); + Subtitle *sub = (subtitleExists) ? new Subtitle(_engine, subname, switchToHires) : NULL; - Common::String subname = _fileName; - subname.setChar('s', subname.size() - 3); - subname.setChar('u', subname.size() - 2); - subname.setChar('b', subname.size() - 1); + _engine->getCursorManager()->showMouse(false); - Subtitle *sub = NULL; + if (switchToHires) { + _engine->initHiresScreen(); + destRect = Common::Rect(40, -40, 760, 440); + Common::Rect workingWindow = _engine->_workingWindow; + workingWindow.translate(0, -40); + _engine->getRenderManager()->initSubArea(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, workingWindow); + } - if (_engine->getSearchManager()->hasFile(subname)) - sub = new Subtitle(_engine, subname); + _engine->playVideo(*decoder, destRect, _skippable, sub); - _engine->playVideo(decoder, destRect, _skippable, sub); + if (switchToHires) { + _engine->initScreen(); + _engine->getRenderManager()->initSubArea(WINDOW_WIDTH, WINDOW_HEIGHT, _engine->_workingWindow); + } - _engine->getCursorManager()->showMouse(true); + _engine->getCursorManager()->showMouse(true); - if (sub) - delete sub; - } + delete decoder; + delete sub; return true; } @@ -1010,31 +1022,27 @@ bool ActionStreamVideo::execute() { // ActionSyncSound ////////////////////////////////////////////////////////////////////////////// -ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _syncto = 0; char fileName[25]; int notUsed = 0; - sscanf(line.c_str(), "%d %d %25s", &_syncto, ¬Used, fileName); + sscanf(line.c_str(), "%d %d %24s", &_syncto, ¬Used, fileName); _fileName = Common::String(fileName); } bool ActionSyncSound::execute() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_syncto); + ScriptingEffect *fx = _scriptManager->getSideFX(_syncto); if (!fx) return true; - if (!(fx->getType() & SideFX::SIDEFX_ANIM)) + if (!(fx->getType() & ScriptingEffect::SCRIPTING_EFFECT_ANIM)) return true; - AnimationNode *animnode = (AnimationNode *)fx; - if (animnode->getFrameDelay() > 200) // Hack for fix incorrect framedelay in some animpreload - animnode->setNewFrameDelay(66); // ~15fps - - _engine->getScriptManager()->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); + _scriptManager->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); return true; } @@ -1042,24 +1050,23 @@ bool ActionSyncSound::execute() { // ActionTimer ////////////////////////////////////////////////////////////////////////////// -ActionTimer::ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionTimer::ActionTimer(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { char timeBuffer[64]; memset(timeBuffer, 0, 64); sscanf(line.c_str(), "%s", timeBuffer); - _time = new ValueSlot(_engine->getScriptManager(), timeBuffer); + _time = new ValueSlot(_scriptManager, timeBuffer); } ActionTimer::~ActionTimer() { - if (_time) - delete _time; - _engine->getScriptManager()->killSideFx(_slotKey); + delete _time; + _scriptManager->killSideFx(_slotKey); } bool ActionTimer::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - _engine->getScriptManager()->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue())); + _scriptManager->addSideFX(new TimerNode(_engine, _slotKey, _time->getValue())); return true; } @@ -1067,25 +1074,25 @@ bool ActionTimer::execute() { // ActionTtyText ////////////////////////////////////////////////////////////////////////////// -ActionTtyText::ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { +ActionTtyText::ActionTtyText(ZVision *engine, int32 slotKey, const Common::String &line) : + ResultAction(engine, slotKey) { _delay = 0; char filename[64]; int32 x1 = 0, y1 = 0, x2 = 0, y2 = 0; - sscanf(line.c_str(), "%d %d %d %d %64s %u", &x1, &y1, &x2, &y2, filename, &_delay); + sscanf(line.c_str(), "%d %d %d %d %63s %u", &x1, &y1, &x2, &y2, filename, &_delay); _r = Common::Rect(x1, y1, x2, y2); _filename = Common::String(filename); } ActionTtyText::~ActionTtyText() { - _engine->getScriptManager()->killSideFx(_slotKey); + _scriptManager->killSideFx(_slotKey); } bool ActionTtyText::execute() { - if (_engine->getScriptManager()->getSideFX(_slotKey)) + if (_scriptManager->getSideFX(_slotKey)) return true; - _engine->getScriptManager()->addSideFX(new ttyTextNode(_engine, _slotKey, _filename, _r, _delay)); + _scriptManager->addSideFX(new ttyTextNode(_engine, _slotKey, _filename, _r, _delay)); return true; } diff --git a/engines/zvision/scripting/actions.h b/engines/zvision/scripting/actions.h index 292e25e19c..bde1baa291 100644 --- a/engines/zvision/scripting/actions.h +++ b/engines/zvision/scripting/actions.h @@ -32,6 +32,7 @@ namespace ZVision { // Forward declaration of ZVision. This file is included before ZVision is declared class ZVision; +class ScriptManager; class ValueSlot; /** @@ -40,7 +41,7 @@ class ValueSlot; */ class ResultAction { public: - ResultAction(ZVision *engine, int32 slotkey) : _engine(engine), _slotKey(slotkey) {} + ResultAction(ZVision *engine, int32 slotkey); virtual ~ResultAction() {} /** * This is called by the script system whenever a Puzzle's criteria are found to be true. @@ -53,6 +54,7 @@ public: virtual bool execute() = 0; protected: ZVision *_engine; + ScriptManager *_scriptManager; int32 _slotKey; }; @@ -124,14 +126,6 @@ private: uint8 _action; }; -class ActionDebug : public ResultAction { -public: - ActionDebug(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: -}; - class ActionDelayRender : public ResultAction { public: ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line); @@ -150,15 +144,6 @@ private: uint32 _key; }; -class ActionDisableVenus : public ResultAction { -public: - ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: - int32 _key; -}; - class ActionDisplayMessage : public ResultAction { public: ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line); @@ -241,7 +226,7 @@ public: private: Common::String _fileName; bool _loop; - byte _volume; + ValueSlot *_volume; bool _universe; bool _midi; int8 _note; @@ -284,7 +269,6 @@ public: bool execute(); private: - uint32 _animationKey; uint32 _controlKey; uint32 _x1; uint32 _y1; @@ -338,7 +322,7 @@ private: uint16 _unk2; }; -// TODO: See if this exists in ZGI. It doesn't in ZNem +// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j) class ActionUnloadAnimation : public ResultAction { public: ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line); @@ -397,15 +381,6 @@ private: Common::String _fileName; }; -class ActionSetVenus : public ResultAction { -public: - ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: - int32 _key; -}; - class ActionStop : public ResultAction { public: ActionStop(ZVision *engine, int32 slotkey, const Common::String &line); diff --git a/engines/zvision/scripting/control.cpp b/engines/zvision/scripting/control.cpp index 127f35ef12..81123eb99b 100644 --- a/engines/zvision/scripting/control.cpp +++ b/engines/zvision/scripting/control.cpp @@ -72,6 +72,7 @@ void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream & renderTable->generateRenderTable(); } +// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view) void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) { RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); renderTable->setRenderState(RenderTable::TILT); diff --git a/engines/zvision/scripting/control.h b/engines/zvision/scripting/control.h index 803d0cf1ce..108b83fd00 100644 --- a/engines/zvision/scripting/control.h +++ b/engines/zvision/scripting/control.h @@ -36,6 +36,11 @@ namespace ZVision { class ZVision; +/** + * The base class for all Controls. + * + * Controls are the things that the user interacts with. Ex: A lever on the door + */ class Control { public: diff --git a/engines/zvision/scripting/controls/fist_control.cpp b/engines/zvision/scripting/controls/fist_control.cpp index 34a64b4298..f79c82dc79 100644 --- a/engines/zvision/scripting/controls/fist_control.cpp +++ b/engines/zvision/scripting/controls/fist_control.cpp @@ -46,10 +46,6 @@ FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream _order = 0; _fistnum = 0; - _frameCur = -1; - _frameEnd = -1; - _frameTime = 0; - _lastRenderedFrame = -1; _animationId = 0; clearFistArray(_fistsUp); @@ -95,41 +91,28 @@ FistControl::~FistControl() { _entries.clear(); } -void FistControl::renderFrame(uint frameNumber) { - if ((int32)frameNumber == _lastRenderedFrame) - return; - - _lastRenderedFrame = frameNumber; - - const Graphics::Surface *frameData; - - if (_animation) { - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _anmRect); - } -} - bool FistControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - if (_frameCur >= 0 && _frameEnd >= 0) - if (_frameCur <= _frameEnd) { - _frameTime -= deltaTimeInMillis; - - if (_frameTime <= 0) { - _frameTime = 1000.0 / _animation->getDuration().framerate(); - - renderFrame(_frameCur); - - _frameCur++; + if (_animation && _animation->isPlaying()) { + if (_animation->endOfVideo()) { + _animation->stop(); + _engine->getScriptManager()->setStateValue(_animationId, 2); + return false; + } - if (_frameCur > _frameEnd) - _engine->getScriptManager()->setStateValue(_animationId, 2); - } + if (_animation->needsUpdate()) { + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + // WORKAROUND: Ignore the target frame dimensions for the finger animations. + // The target dimensions specify an area smaller than expected, thus if we + // scale the finger videos to fit these dimensions, they are not aligned + // correctly. Not scaling these videos yields a result identical to the + // original. Fixes bug #6784. + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _anmRect.left, _anmRect.top); } + } return false; } @@ -160,9 +143,12 @@ bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::P for (int i = 0; i < _numEntries; i++) if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) { - _frameCur = _entries[i]._anmStrt; - _frameEnd = _entries[i]._anmEnd; - _frameTime = 0; + if (_animation) { + _animation->stop(); + _animation->seekToFrame(_entries[i]._anmStrt); + _animation->setEndFrame(_entries[i]._anmEnd); + _animation->start(); + } _engine->getScriptManager()->setStateValue(_animationId, 1); _engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound); diff --git a/engines/zvision/scripting/controls/fist_control.h b/engines/zvision/scripting/controls/fist_control.h index 0a6b977ead..d7cbcb1f71 100644 --- a/engines/zvision/scripting/controls/fist_control.h +++ b/engines/zvision/scripting/controls/fist_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e) class FistControl : public Control { public: FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -63,10 +64,6 @@ private: Video::VideoDecoder *_animation; Common::Rect _anmRect; int32 _soundKey; - int32 _frameCur; - int32 _frameEnd; - int32 _frameTime; - int32 _lastRenderedFrame; int32 _animationId; public: @@ -75,7 +72,6 @@ public: bool process(uint32 deltaTimeInMillis); private: - void renderFrame(uint frameNumber); void readDescFile(const Common::String &fileName); void clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr); uint32 readBits(const char *str); diff --git a/engines/zvision/scripting/controls/hotmov_control.cpp b/engines/zvision/scripting/controls/hotmov_control.cpp index e77272ec73..182447a990 100644 --- a/engines/zvision/scripting/controls/hotmov_control.cpp +++ b/engines/zvision/scripting/controls/hotmov_control.cpp @@ -41,10 +41,7 @@ HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadSt : Control(engine, key, CONTROL_HOTMOV) { _animation = NULL; _cycle = 0; - _curFrame = -1; - _lastRenderedFrame = -1; _frames.clear(); - _frameTime = 0; _cyclesCount = 0; _framesCount = 0; @@ -78,6 +75,7 @@ HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadSt sscanf(values.c_str(), "%s", filename); values = Common::String(filename); _animation = _engine->loadAnimation(values); + _animation->start(); } else if (param.matchString("venus_id", true)) { _venusId = atoi(values.c_str()); } @@ -95,41 +93,26 @@ HotMovControl::~HotMovControl() { _frames.clear(); } -void HotMovControl::renderFrame(uint frameNumber) { - if ((int)frameNumber == _lastRenderedFrame) - return; - - _lastRenderedFrame = frameNumber; - - const Graphics::Surface *frameData; - - if (_animation) { - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); - } -} - bool HotMovControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; if (_cycle < _cyclesCount) { - _frameTime -= deltaTimeInMillis; + if (_animation && _animation->endOfVideo()) { + _cycle++; - if (_frameTime <= 0) { - _curFrame++; - if (_curFrame >= _framesCount) { - _curFrame = 0; - _cycle++; - } - if (_cycle != _cyclesCount) - renderFrame(_curFrame); - else + if (_cycle == _cyclesCount) { _engine->getScriptManager()->setStateValue(_key, 2); + return false; + } + + _animation->rewind(); + } - _frameTime = 1000.0 / _animation->getDuration().framerate(); + if (_animation && _animation->needsUpdate()) { + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); } } @@ -140,8 +123,11 @@ bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Commo if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_animation) + return false; + if (_cycle < _cyclesCount) { - if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) { _engine->getCursorManager()->changeCursor(CursorIndex_Active); return true; } @@ -154,8 +140,11 @@ bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common: if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_animation) + return false; + if (_cycle < _cyclesCount) { - if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) { setVenus(); _engine->getScriptManager()->setStateValue(_key, 1); return true; diff --git a/engines/zvision/scripting/controls/hotmov_control.h b/engines/zvision/scripting/controls/hotmov_control.h index b18d44c7a6..99d1fd0979 100644 --- a/engines/zvision/scripting/controls/hotmov_control.h +++ b/engines/zvision/scripting/controls/hotmov_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g) class HotMovControl : public Control { public: HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -41,9 +42,6 @@ public: private: int32 _framesCount; - int32 _frameTime; - int32 _curFrame; - int32 _lastRenderedFrame; int32 _cycle; int32 _cyclesCount; Video::VideoDecoder *_animation; @@ -55,7 +53,6 @@ public: bool process(uint32 deltaTimeInMillis); private: - void renderFrame(uint frameNumber); void readHsFile(const Common::String &fileName); }; diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp index e75cc15743..9525333ef0 100644 --- a/engines/zvision/scripting/controls/input_control.cpp +++ b/engines/zvision/scripting/controls/input_control.cpp @@ -39,16 +39,14 @@ namespace ZVision { InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream) : Control(engine, key, CONTROL_INPUT), + _background(0), _nextTabstop(0), _focused(false), _textChanged(false), - _cursorOffset(0), _enterPressed(false), _readOnly(false), _txtWidth(0), - _animation(NULL), - _frameDelay(0), - _frame(-1) { + _animation(NULL) { // Loop until we find the closing brace Common::String line = stream.readLine(); _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); @@ -80,13 +78,13 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre sscanf(values.c_str(), "%u", &fontFormatNumber); - _stringInit.readAllStyle(_engine->getStringManager()->getTextLine(fontFormatNumber)); + _stringInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber)); } else if (param.matchString("chooser_init_string", true)) { uint fontFormatNumber; sscanf(values.c_str(), "%u", &fontFormatNumber); - _stringChooserInit.readAllStyle(_engine->getStringManager()->getTextLine(fontFormatNumber)); + _stringChooserInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber)); } else if (param.matchString("next_tabstop", true)) { sscanf(values.c_str(), "%u", &_nextTabstop); } else if (param.matchString("cursor_dimensions", true)) { @@ -96,11 +94,10 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre } else if (param.matchString("cursor_animation", true)) { char fileName[25]; - sscanf(values.c_str(), "%25s %*u", fileName); + sscanf(values.c_str(), "%24s %*u", fileName); _animation = _engine->loadAnimation(fileName); - _frame = -1; - _frameDelay = 0; + _animation->start(); } else if (param.matchString("focus", true)) { _focused = true; _engine->getScriptManager()->setFocusControlKey(_key); @@ -112,6 +109,15 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); getParams(line, param, values); } + + _maxTxtWidth = _textRectangle.width(); + if (_animation) + _maxTxtWidth -= _animation->getWidth(); +} + +InputControl::~InputControl() { + _background->free(); + delete _background; } bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { @@ -194,36 +200,42 @@ bool InputControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_background) { + _background = _engine->getRenderManager()->getBkgRect(_textRectangle); + } + // First see if we need to render the text if (_textChanged) { // Blit the text using the RenderManager Graphics::Surface txt; - txt.create(_textRectangle.width(), _textRectangle.height(), _engine->_pixelFormat); + txt.copyFrom(*_background); + + int32 oldTxtWidth = _txtWidth; if (!_readOnly || !_focused) - _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringInit, txt); + _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringInit, txt); else - _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringChooserInit, txt); + _txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringChooserInit, txt); - _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top); + if (_readOnly || _txtWidth <= _maxTxtWidth) + _engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top); + else { + // Assume the last character caused the overflow. + _currentInputText.deleteLastChar(); + _txtWidth = oldTxtWidth; + } txt.free(); } if (_animation && !_readOnly && _focused) { - bool needDraw = true;// = _textChanged; - _frameDelay -= deltaTimeInMillis; - if (_frameDelay <= 0) { - _frame = (_frame + 1) % _animation->getFrameCount(); - _frameDelay = 1000.0 / _animation->getDuration().framerate(); - needDraw = true; - } + if (_animation->endOfVideo()) + _animation->rewind(); - if (needDraw) { - _animation->seekToFrame(_frame); + if (_animation->needsUpdate()) { const Graphics::Surface *srf = _animation->decodeNextFrame(); - uint32 xx = _textRectangle.left + _txtWidth; + int16 xx = _textRectangle.left + _txtWidth; if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth())) xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth(); _engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top); diff --git a/engines/zvision/scripting/controls/input_control.h b/engines/zvision/scripting/controls/input_control.h index 99f7f5287d..6abdb3c692 100644 --- a/engines/zvision/scripting/controls/input_control.h +++ b/engines/zvision/scripting/controls/input_control.h @@ -38,25 +38,25 @@ namespace ZVision { class InputControl : public Control { public: InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); + ~InputControl(); private: + Graphics::Surface *_background; Common::Rect _textRectangle; Common::Rect _headerRectangle; - cTxtStyle _stringInit; - cTxtStyle _stringChooserInit; + TextStyleState _stringInit; + TextStyleState _stringChooserInit; uint32 _nextTabstop; bool _focused; Common::String _currentInputText; bool _textChanged; - uint _cursorOffset; bool _enterPressed; bool _readOnly; int16 _txtWidth; + int16 _maxTxtWidth; Video::VideoDecoder *_animation; - int32 _frameDelay; - int16 _frame; public: void focus() { diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp index 8faa18357c..0f105b424c 100644 --- a/engines/zvision/scripting/controls/lever_control.cpp +++ b/engines/zvision/scripting/controls/lever_control.cpp @@ -64,12 +64,12 @@ LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStre while (!stream.eos() && !line.contains('}')) { if (param.matchString("descfile", true)) { char levFileName[25]; - sscanf(values.c_str(), "%25s", levFileName); + sscanf(values.c_str(), "%24s", levFileName); parseLevFile(levFileName); } else if (param.matchString("cursor", true)) { char cursorName[25]; - sscanf(values.c_str(), "%25s", cursorName); + sscanf(values.c_str(), "%24s", cursorName); _cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName)); } @@ -232,10 +232,13 @@ bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common if (angle >= (int)iter->angle - ANGLE_DELTA && angle <= (int)iter->angle + ANGLE_DELTA) { _currentFrame = iter->toFrame; renderFrame(_currentFrame); + _engine->getScriptManager()->setStateValue(_key, _currentFrame); break; } } } + _engine->getCursorManager()->changeCursor(_cursor); + cursorWasChanged = true; } else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) { _engine->getCursorManager()->changeCursor(_cursor); cursorWasChanged = true; diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h index fdf4a649dc..8787234c51 100644 --- a/engines/zvision/scripting/controls/lever_control.h +++ b/engines/zvision/scripting/controls/lever_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e) class LeverControl : public Control { public: LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/controls/paint_control.cpp b/engines/zvision/scripting/controls/paint_control.cpp index df06bb814e..62dde3d170 100644 --- a/engines/zvision/scripting/controls/paint_control.cpp +++ b/engines/zvision/scripting/controls/paint_control.cpp @@ -114,12 +114,18 @@ PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStre PaintControl::~PaintControl() { // Clear the state value back to 0 //_engine->getScriptManager()->setStateValue(_key, 0); - if (_paint) + if (_paint) { + _paint->free(); delete _paint; - if (_brush) + } + if (_brush) { + _brush->free(); delete _brush; - if (_bkg) + } + if (_bkg) { + _bkg->free(); delete _bkg; + } } bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { diff --git a/engines/zvision/scripting/controls/paint_control.h b/engines/zvision/scripting/controls/paint_control.h index 8097290ac2..8c01f0e68a 100644 --- a/engines/zvision/scripting/controls/paint_control.h +++ b/engines/zvision/scripting/controls/paint_control.h @@ -32,6 +32,7 @@ namespace ZVision { +// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g) class PaintControl : public Control { public: PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/controls/safe_control.cpp b/engines/zvision/scripting/controls/safe_control.cpp index 71be692431..4d2a91a1ad 100644 --- a/engines/zvision/scripting/controls/safe_control.cpp +++ b/engines/zvision/scripting/controls/safe_control.cpp @@ -49,10 +49,7 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream _outerRadiusSqr = 0; _zeroPointer = 0; _startPointer = 0; - _curFrame = -1; _targetFrame = 0; - _frameTime = 0; - _lastRenderedFrame = -1; // Loop until we find the closing brace Common::String line = stream.readLine(); @@ -64,6 +61,7 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream while (!stream.eos() && !line.contains('}')) { if (param.matchString("animation", true)) { _animation = _engine->loadAnimation(values); + _animation->start(); } else if (param.matchString("rectangle", true)) { int x; int y; @@ -104,7 +102,9 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); getParams(line, param, values); } - renderFrame(_curState); + + if (_animation) + _animation->seekToFrame(_curState); } SafeControl::~SafeControl() { @@ -113,44 +113,22 @@ SafeControl::~SafeControl() { } -void SafeControl::renderFrame(uint frameNumber) { - if (frameNumber == 0) { - _lastRenderedFrame = frameNumber; - } else if ((int16)frameNumber < _lastRenderedFrame) { - _lastRenderedFrame = frameNumber; - frameNumber = (_statesCount * 2) - frameNumber; - } else { - _lastRenderedFrame = frameNumber; - } - - const Graphics::Surface *frameData; - int x = _rectangle.left; - int y = _rectangle.top; - - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkg(*frameData, x, y); -} - bool SafeControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - if (_curFrame != _targetFrame) { - _frameTime -= deltaTimeInMillis; - - if (_frameTime <= 0) { - if (_curFrame < _targetFrame) { - _curFrame++; - renderFrame(_curFrame); - } else if (_curFrame > _targetFrame) { - _curFrame--; - renderFrame(_curFrame); - } - _frameTime = 1000.0 / _animation->getDuration().framerate(); - } + if (_animation && _animation->getCurFrame() != _targetFrame && _animation->needsUpdate()) { + // If we're past the target frame, move back one + if (_animation->getCurFrame() > _targetFrame) + _animation->seekToFrame(_animation->getCurFrame() - 1); + + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (_animation->getCurFrame() == _targetFrame) + _engine->getScriptManager()->setStateValue(_key, _curState); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _rectangle.left, _rectangle.top); } + return false; } @@ -187,13 +165,12 @@ bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::P int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount; - _curFrame = (_curState + _statesCount - _startPointer) % _statesCount; + if (_animation) + _animation->seekToFrame((_curState + _statesCount - _startPointer) % _statesCount); _curState = (_statesCount * 2 + tmp2) % _statesCount; _targetFrame = (_curState + _statesCount - _startPointer) % _statesCount; - - _engine->getScriptManager()->setStateValue(_key, _curState); return true; } } diff --git a/engines/zvision/scripting/controls/safe_control.h b/engines/zvision/scripting/controls/safe_control.h index 6e1095e304..3e8c17635c 100644 --- a/engines/zvision/scripting/controls/safe_control.h +++ b/engines/zvision/scripting/controls/safe_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g) class SafeControl : public Control { public: SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -51,19 +52,12 @@ private: int32 _outerRadiusSqr; int16 _zeroPointer; int16 _startPointer; - int16 _curFrame; int16 _targetFrame; - int32 _frameTime; - - int16 _lastRenderedFrame; public: bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool process(uint32 deltaTimeInMillis); - -private: - void renderFrame(uint frameNumber); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/save_control.cpp b/engines/zvision/scripting/controls/save_control.cpp index b35611feca..2ac77c4776 100644 --- a/engines/zvision/scripting/controls/save_control.cpp +++ b/engines/zvision/scripting/controls/save_control.cpp @@ -29,7 +29,8 @@ #include "zvision/scripting/script_manager.h" #include "zvision/text/string_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" +#include "zvision/graphics/render_manager.h" #include "common/str.h" #include "common/stream.h" @@ -97,18 +98,16 @@ bool SaveControl::process(uint32 deltaTimeInMillis) { if (inp->getText().size() > 0) { bool toSave = true; if (iter->exist) - if (!_engine->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) + if (!_engine->getRenderManager()->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) toSave = false; if (toSave) { - // FIXME: At this point, the screen shows the save control, so the save game thumbnails will always - // show the save control - _engine->getSaveManager()->saveGameBuffered(iter->saveId, inp->getText()); - _engine->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); + _engine->getSaveManager()->saveGame(iter->saveId, inp->getText(), true); + _engine->getRenderManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); _engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation()); } } else { - _engine->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); + _engine->getRenderManager()->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); } } else { _engine->getSaveManager()->loadGame(iter->saveId); diff --git a/engines/zvision/scripting/controls/titler_control.cpp b/engines/zvision/scripting/controls/titler_control.cpp index 10ba0af655..683d6660af 100644 --- a/engines/zvision/scripting/controls/titler_control.cpp +++ b/engines/zvision/scripting/controls/titler_control.cpp @@ -67,20 +67,22 @@ TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadSt if (!_rectangle.isEmpty()) { _surface = new Graphics::Surface; - _surface->create(_rectangle.width(), _rectangle.height(), _engine->_pixelFormat); + _surface->create(_rectangle.width(), _rectangle.height(), _engine->_resourcePixelFormat); _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); } } TitlerControl::~TitlerControl() { - if (_surface) + if (_surface) { + _surface->free(); delete _surface; + } } void TitlerControl::setString(int strLine) { if (strLine != _curString && strLine >= 0 && strLine < (int)_strings.size()) { _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); - _engine->getTextRenderer()->drawTxtInOneLine(_strings[strLine], *_surface); + _engine->getTextRenderer()->drawTextWithWordWrapping(_strings[strLine], *_surface); _engine->getRenderManager()->blitSurfaceToBkg(*_surface, _rectangle.left, _rectangle.top); _curString = strLine; } diff --git a/engines/zvision/scripting/controls/titler_control.h b/engines/zvision/scripting/controls/titler_control.h index 075e47c9e9..dd96e4a846 100644 --- a/engines/zvision/scripting/controls/titler_control.h +++ b/engines/zvision/scripting/controls/titler_control.h @@ -32,6 +32,7 @@ namespace ZVision { +// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons class TitlerControl : public Control { public: TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/sidefx/animation_node.cpp b/engines/zvision/scripting/effects/animation_effect.cpp index 3a21227d1a..511a0db353 100644 --- a/engines/zvision/scripting/sidefx/animation_node.cpp +++ b/engines/zvision/scripting/effects/animation_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/animation_node.h" +#include "zvision/scripting/effects/animation_effect.h" #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" @@ -33,20 +33,27 @@ namespace ZVision { -AnimationNode::AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse) - : SideFX(engine, controlKey, SIDEFX_ANIM), - _DisposeAfterUse(DisposeAfterUse), +AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse) + : ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM), + _disposeAfterUse(disposeAfterUse), _mask(mask), _animation(NULL) { _animation = engine->loadAnimation(fileName); - _frmDelay = 1000.0 / _animation->getDuration().framerate(); - if (frate > 0) - _frmDelay = 1000.0 / frate; + if (frate > 0) { + _frmDelayOverride = (int32)(1000.0 / frate); + + // WORKAROUND: We do not allow the engine to delay more than 66 msec + // per frame (15fps max) + if (_frmDelayOverride > 66) + _frmDelayOverride = 66; + } else { + _frmDelayOverride = 0; + } } -AnimationNode::~AnimationNode() { +AnimationEffect::~AnimationEffect() { if (_animation) delete _animation; @@ -56,60 +63,83 @@ AnimationNode::~AnimationNode() { if (it != _playList.end()) { _engine->getScriptManager()->setStateValue((*it).slot, 2); - if ((*it)._scaled) + if ((*it)._scaled) { + (*it)._scaled->free(); delete(*it)._scaled; + } } _playList.clear(); } -bool AnimationNode::process(uint32 deltaTimeInMillis) { +bool AnimationEffect::process(uint32 deltaTimeInMillis) { + ScriptManager *scriptManager = _engine->getScriptManager(); + RenderManager *renderManager = _engine->getRenderManager(); + RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState(); + bool isPanorama = (renderState == RenderTable::PANORAMA); + int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity(); + + // Do not update animation nodes in panoramic mode while turning, if the user + // has set this option + if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity) + return false; + PlayNodes::iterator it = _playList.begin(); if (it != _playList.end()) { playnode *nod = &(*it); - nod->_delay -= deltaTimeInMillis; - if (nod->_delay <= 0) { - nod->_delay += _frmDelay; - - const Graphics::Surface *frame = NULL; - - if (nod->_curFrame == -1) { // Start of new playlist node - nod->_curFrame = nod->start; - - _animation->seekToFrame(nod->_curFrame); - frame = _animation->decodeNextFrame(); - - nod->_delay = _frmDelay; - if (nod->slot) - _engine->getScriptManager()->setStateValue(nod->slot, 1); - } else { - nod->_curFrame++; - - if (nod->_curFrame > nod->stop) { - nod->loop--; - - if (nod->loop == 0) { - if (nod->slot >= 0) - _engine->getScriptManager()->setStateValue(nod->slot, 2); - if (nod->_scaled) - delete nod->_scaled; - _playList.erase(it); - return _DisposeAfterUse; - } - - nod->_curFrame = nod->start; - _animation->seekToFrame(nod->_curFrame); + if (nod->_curFrame == -1) { + // The node is just beginning playback + nod->_curFrame = nod->start; + + _animation->start(); + _animation->seekToFrame(nod->start); + _animation->setEndFrame(nod->stop); + + nod->_delay = deltaTimeInMillis; // Force the frame to draw + if (nod->slot) + scriptManager->setStateValue(nod->slot, 1); + } else if (_animation->endOfVideo()) { + // The node has reached the end; check if we need to loop + nod->loop--; + + if (nod->loop == 0) { + if (nod->slot >= 0) + scriptManager->setStateValue(nod->slot, 2); + if (nod->_scaled) { + nod->_scaled->free(); + delete nod->_scaled; } + _playList.erase(it); + return _disposeAfterUse; + } - frame = _animation->decodeNextFrame(); + nod->_curFrame = nod->start; + _animation->seekToFrame(nod->start); + } + + // Check if we need to draw a frame + bool needsUpdate = false; + if (_frmDelayOverride == 0) { + // If not overridden, use the VideoDecoder's check + needsUpdate = _animation->needsUpdate(); + } else { + // Otherwise, implement our own timing + nod->_delay -= deltaTimeInMillis; + + if (nod->_delay <= 0) { + nod->_delay += _frmDelayOverride; + needsUpdate = true; } + } - if (frame) { + if (needsUpdate) { + const Graphics::Surface *frame = _animation->decodeNextFrame(); + if (frame) { uint32 dstw; uint32 dsth; - if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + if (isPanorama) { dstw = nod->pos.height(); dsth = nod->pos.width(); } else { @@ -126,6 +156,7 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) { if (nod->_scaled) if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) { + nod->_scaled->free(); delete nod->_scaled; nod->_scaled = NULL; } @@ -135,22 +166,17 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { nod->_scaled->create(dstw, dsth, frame->format); } - _engine->getRenderManager()->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); + renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); frame = nod->_scaled; } - if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + if (isPanorama) { Graphics::Surface *transposed = RenderManager::tranposeSurface(frame); - if (_mask > 0) - _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); - else - _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top); + renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); + transposed->free(); delete transposed; } else { - if (_mask > 0) - _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); - else - _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top); + renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); } } } @@ -159,16 +185,12 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { return false; } -void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { +void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { playnode nod; nod.loop = loops; nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1); nod.start = startFrame; - nod.stop = endFrame; - - if (nod.stop >= (int)_animation->getFrameCount()) - nod.stop = _animation->getFrameCount() - 1; - + nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1); nod.slot = slot; nod._curFrame = -1; nod._delay = 0; @@ -176,12 +198,14 @@ void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int st _playList.push_back(nod); } -bool AnimationNode::stop() { +bool AnimationEffect::stop() { PlayNodes::iterator it = _playList.begin(); if (it != _playList.end()) { _engine->getScriptManager()->setStateValue((*it).slot, 2); - if ((*it)._scaled) + if ((*it)._scaled) { + (*it)._scaled->free(); delete(*it)._scaled; + } } _playList.clear(); @@ -190,21 +214,4 @@ bool AnimationNode::stop() { return false; } -void AnimationNode::setNewFrameDelay(int32 newDelay) { - if (newDelay > 0) { - PlayNodes::iterator it = _playList.begin(); - if (it != _playList.end()) { - playnode *nod = &(*it); - float percent = (float)nod->_delay / (float)_frmDelay; - nod->_delay = percent * newDelay; // Scale to new max - } - - _frmDelay = newDelay; - } -} - -int32 AnimationNode::getFrameDelay() { - return _frmDelay; -} - } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/animation_node.h b/engines/zvision/scripting/effects/animation_effect.h index 3adfd91f32..fd6e24ab8b 100644 --- a/engines/zvision/scripting/sidefx/animation_node.h +++ b/engines/zvision/scripting/effects/animation_effect.h @@ -23,7 +23,7 @@ #ifndef ZVISION_ANIMATION_NODE_H #define ZVISION_ANIMATION_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "common/rect.h" #include "common/list.h" @@ -39,10 +39,10 @@ namespace ZVision { class ZVision; -class AnimationNode : public SideFX { +class AnimationEffect : public ScriptingEffect { public: - AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse = true); - ~AnimationNode(); + AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse = true); + ~AnimationEffect(); struct playnode { Common::Rect pos; @@ -61,10 +61,10 @@ private: PlayNodes _playList; int32 _mask; - bool _DisposeAfterUse; + bool _disposeAfterUse; Video::VideoDecoder *_animation; - int32 _frmDelay; + int32 _frmDelayOverride; public: bool process(uint32 deltaTimeInMillis); @@ -72,9 +72,6 @@ public: void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1); bool stop(); - - void setNewFrameDelay(int32 newDelay); - int32 getFrameDelay(); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/distort_node.cpp b/engines/zvision/scripting/effects/distort_effect.cpp index 0d5c8b1ed5..113b5d048d 100644 --- a/engines/zvision/scripting/sidefx/distort_node.cpp +++ b/engines/zvision/scripting/effects/distort_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/distort_node.h" +#include "zvision/scripting/effects/distort_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -34,7 +34,7 @@ namespace ZVision { DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale) - : SideFX(engine, key, SIDEFX_DISTORT) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_DISTORT) { _angle = _engine->getRenderManager()->getRenderTable()->getAngle(); _linScale = _engine->getRenderManager()->getRenderTable()->getLinscale(); @@ -52,7 +52,7 @@ DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAn _diffLinScale = endLineScale - startLineScale; _frmSpeed = (float)speed / 15.0; - _frames = ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed); + _frames = (int)ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed); if (_frames <= 0) _frames = 1; @@ -65,7 +65,6 @@ DistortNode::~DistortNode() { } bool DistortNode::process(uint32 deltaTimeInMillis) { - float updTime = deltaTimeInMillis / (1000.0 / 60.0); if (_incr) diff --git a/engines/zvision/scripting/sidefx/distort_node.h b/engines/zvision/scripting/effects/distort_effect.h index 787a69bdde..c64f10e6ff 100644 --- a/engines/zvision/scripting/sidefx/distort_node.h +++ b/engines/zvision/scripting/effects/distort_effect.h @@ -23,13 +23,13 @@ #ifndef ZVISION_DISTORT_NODE_H #define ZVISION_DISTORT_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" namespace ZVision { class ZVision; -class DistortNode : public SideFX { +class DistortNode : public ScriptingEffect { public: DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale); ~DistortNode(); diff --git a/engines/zvision/scripting/sidefx/music_node.cpp b/engines/zvision/scripting/effects/music_effect.cpp index 56598189f6..e3fdc96dba 100644 --- a/engines/zvision/scripting/sidefx/music_node.cpp +++ b/engines/zvision/scripting/effects/music_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/music_node.h" +#include "zvision/scripting/effects/music_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -36,16 +36,33 @@ namespace ZVision { -MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, int8 volume) - : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { +static const uint8 dbMapLinear[256] = +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, +2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, +4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, +8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, +14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 21, 21, 22, 23, 24, 25, +26, 27, 28, 29, 30, 31, 32, 33, 34, 36, 37, 38, 40, 41, 43, 45, +46, 48, 50, 52, 53, 55, 57, 60, 62, 64, 67, 69, 72, 74, 77, 80, +83, 86, 89, 92, 96, 99, 103, 107, 111, 115, 119, 123, 128, 133, 137, 143, +148, 153, 159, 165, 171, 177, 184, 191, 198, 205, 212, 220, 228, 237, 245, 255}; + +MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, uint8 volume) + : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) { _loop = loop; _volume = volume; + _deltaVolume = 0; + _balance = 0; _crossfade = false; _crossfadeTarget = 0; _crossfadeTime = 0; - _attenuate = 0; - _pantrack = false; - _pantrackPosition = 0; _sub = NULL; _stereo = false; _loaded = false; @@ -66,9 +83,9 @@ MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool if (_loop) { Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES); - _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, _volume); + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, dbMapLinear[_volume]); } else { - _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, _volume); + _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, dbMapLinear[_volume]); } if (_key != StateKey_NotSet) @@ -88,26 +105,23 @@ MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool } MusicNode::~MusicNode() { - if (!_loaded) + if (_loaded) _engine->_mixer->stopHandle(_handle); if (_key != StateKey_NotSet) _engine->getScriptManager()->setStateValue(_key, 2); if (_sub) delete _sub; - debug(1, "MusicNode: %d destroyed\n", _key); + debug(1, "MusicNode: %d destroyed", _key); } -void MusicNode::setPanTrack(int16 pos) { - if (!_stereo) { - _pantrack = true; - _pantrackPosition = pos; - setVolume(_volume); - } +void MusicNode::setDeltaVolume(uint8 volume) { + _deltaVolume = volume; + setVolume(_volume); } -void MusicNode::unsetPanTrack() { - _pantrack = false; - setVolume(_volume); +void MusicNode::setBalance(int8 balance) { + _balance = balance; + _engine->_mixer->setChannelBalance(_handle, _balance); } void MusicNode::setFade(int32 time, uint8 target) { @@ -126,7 +140,7 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { if (_crossfadeTime > 0) { if ((int32)deltaTimeInMillis > _crossfadeTime) deltaTimeInMillis = _crossfadeTime; - _newvol += floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis; + _newvol += (int)(floor(((float)(_crossfadeTarget - _newvol) / (float)_crossfadeTime)) * (float)deltaTimeInMillis); _crossfadeTime -= deltaTimeInMillis; } else { _crossfade = false; @@ -134,10 +148,10 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { } } - if (_pantrack || _volume != _newvol) + if (_volume != _newvol) setVolume(_newvol); - if (_sub) + if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1) _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); } return false; @@ -146,59 +160,89 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { void MusicNode::setVolume(uint8 newVolume) { if (!_loaded) return; - if (_pantrack) { - int curX = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); - curX -= _pantrackPosition; - int32 _width = _engine->getRenderManager()->getBkgSize().x; - if (curX < (-_width) / 2) - curX += _width; - else if (curX >= _width / 2) - curX -= _width; - - float norm = (float)curX / ((float)_width / 2.0); - float lvl = fabs(norm); - if (lvl > 0.5) - lvl = (lvl - 0.5) * 1.7; - else - lvl = 1.0; - float bal = sin(-norm * 3.1415926) * 127.0; + _volume = newVolume; - if (_engine->_mixer->isSoundHandleActive(_handle)) { - _engine->_mixer->setChannelBalance(_handle, bal); - _engine->_mixer->setChannelVolume(_handle, newVolume * lvl); - } - } else { - if (_engine->_mixer->isSoundHandleActive(_handle)) { - _engine->_mixer->setChannelBalance(_handle, 0); - _engine->_mixer->setChannelVolume(_handle, newVolume); - } - } + if (_deltaVolume >= _volume) + _engine->_mixer->setChannelVolume(_handle, 0); + else + _engine->_mixer->setChannelVolume(_handle, dbMapLinear[_volume - _deltaVolume]); +} - _volume = newVolume; +uint8 MusicNode::getVolume() { + return _volume; } PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos) - : SideFX(engine, key, SIDEFX_PANTRACK) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_PANTRACK) { _slot = slot; + _position = pos; - SideFX *fx = _engine->getScriptManager()->getSideFX(slot); - if (fx && fx->getType() == SIDEFX_AUDIO) { - MusicNodeBASE *mus = (MusicNodeBASE *)fx; - mus->setPanTrack(pos); - } + // Try to set pan value for music node immediately + process(0); } PanTrackNode::~PanTrackNode() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_slot); - if (fx && fx->getType() == SIDEFX_AUDIO) { +} + +bool PanTrackNode::process(uint32 deltaTimeInMillis) { + ScriptManager * scriptManager = _engine->getScriptManager(); + ScriptingEffect *fx = scriptManager->getSideFX(_slot); + if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) { MusicNodeBASE *mus = (MusicNodeBASE *)fx; - mus->unsetPanTrack(); + + int curPos = scriptManager->getStateValue(StateKey_ViewPos); + int16 _width = _engine->getRenderManager()->getBkgSize().x; + int16 _halfWidth = _width / 2; + int16 _quarterWidth = _width / 4; + + int tmp = 0; + if (curPos <= _position) + tmp = _position - curPos; + else + tmp = _position - curPos + _width; + + int balance = 0; + + if (tmp > _halfWidth) + tmp -= _width; + + if (tmp > _quarterWidth) { + balance = 1; + tmp = _halfWidth - tmp; + } else if (tmp < -_quarterWidth) { + balance = -1; + tmp = -_halfWidth - tmp; + } + + // Originally it's value -90...90 but we use -127...127 and therefore 360 replaced by 508 + mus->setBalance( (508 * tmp) / _width ); + + tmp = (360 * tmp) / _width; + + int deltaVol = balance; + + // This value sets how fast volume goes off than sound source back of you + // By this value we can hack some "bugs" have place in originall game engine like beat sound in ZGI-dc10 + int volumeCorrection = 2; + + if (_engine->getGameId() == GID_GRANDINQUISITOR) { + if (scriptManager->getCurrentLocation() == "dc10") + volumeCorrection = 5; + } + + if (deltaVol != 0) + deltaVol = (mus->getVolume() * volumeCorrection) * (90 - tmp * balance) / 90; + if (deltaVol > 255) + deltaVol = 255; + + mus->setDeltaVolume(deltaVol); } + return false; } MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume) - : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { + : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) { _volume = volume; _prog = program; _noteNumber = note; @@ -225,10 +269,10 @@ MusicMidiNode::~MusicMidiNode() { _engine->getScriptManager()->setStateValue(_key, 2); } -void MusicMidiNode::setPanTrack(int16 pos) { +void MusicMidiNode::setDeltaVolume(uint8 volume) { } -void MusicMidiNode::unsetPanTrack() { +void MusicMidiNode::setBalance(int8 balance) { } void MusicMidiNode::setFade(int32 time, uint8 target) { @@ -245,4 +289,8 @@ void MusicMidiNode::setVolume(uint8 newVolume) { _volume = newVolume; } +uint8 MusicMidiNode::getVolume() { + return _volume; +} + } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/music_node.h b/engines/zvision/scripting/effects/music_effect.h index 09bdc3707e..7657be8e09 100644 --- a/engines/zvision/scripting/sidefx/music_node.h +++ b/engines/zvision/scripting/effects/music_effect.h @@ -24,8 +24,8 @@ #define ZVISION_MUSIC_NODE_H #include "audio/mixer.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/text/subtitles.h" namespace Common { class String; @@ -33,9 +33,9 @@ class String; namespace ZVision { -class MusicNodeBASE : public SideFX { +class MusicNodeBASE : public ScriptingEffect { public: - MusicNodeBASE(ZVision *engine, uint32 key, SideFXType type) : SideFX(engine, key, type) {} + MusicNodeBASE(ZVision *engine, uint32 key, ScriptingEffectType type) : ScriptingEffect(engine, key, type) {} ~MusicNodeBASE() {} /** @@ -48,16 +48,16 @@ public: virtual bool process(uint32 deltaTimeInMillis) = 0; virtual void setVolume(uint8 volume) = 0; - - virtual void setPanTrack(int16 pos) = 0; - virtual void unsetPanTrack() = 0; + virtual uint8 getVolume() = 0; + virtual void setDeltaVolume(uint8 volume) = 0; + virtual void setBalance(int8 balance) = 0; virtual void setFade(int32 time, uint8 target) = 0; }; class MusicNode : public MusicNodeBASE { public: - MusicNode(ZVision *engine, uint32 key, Common::String &file, bool loop, int8 volume); + MusicNode(ZVision *engine, uint32 key, Common::String &file, bool loop, uint8 volume); ~MusicNode(); /** @@ -70,17 +70,16 @@ public: bool process(uint32 deltaTimeInMillis); void setVolume(uint8 volume); - - void setPanTrack(int16 pos); - void unsetPanTrack(); + uint8 getVolume(); + void setDeltaVolume(uint8 volume); + void setBalance(int8 balance); void setFade(int32 time, uint8 target); private: - bool _pantrack; - int32 _pantrackPosition; - int32 _attenuate; uint8 _volume; + uint8 _deltaVolume; + int8 _balance; bool _loop; bool _crossfade; uint8 _crossfadeTarget; @@ -91,6 +90,7 @@ private: bool _loaded; }; +// Only used by Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well as vr) class MusicMidiNode : public MusicNodeBASE { public: MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume); @@ -106,9 +106,9 @@ public: bool process(uint32 deltaTimeInMillis); void setVolume(uint8 volume); - - void setPanTrack(int16 pos); - void unsetPanTrack(); + uint8 getVolume(); + void setDeltaVolume(uint8 volume); + void setBalance(int8 balance); void setFade(int32 time, uint8 target); @@ -120,13 +120,16 @@ private: int8 _prog; }; -class PanTrackNode : public SideFX { +class PanTrackNode : public ScriptingEffect { public: PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos); ~PanTrackNode(); + bool process(uint32 deltaTimeInMillis); + private: uint32 _slot; + int16 _position; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/region_node.cpp b/engines/zvision/scripting/effects/region_effect.cpp index de613d8af2..78061cf4de 100644 --- a/engines/zvision/scripting/sidefx/region_node.cpp +++ b/engines/zvision/scripting/effects/region_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/scripting/effects/region_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -30,8 +30,8 @@ namespace ZVision { -RegionNode::RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay) - : SideFX(engine, key, SIDEFX_REGION) { +RegionNode::RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay) + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_REGION) { _effect = effect; _delay = delay; _timeLeft = 0; diff --git a/engines/zvision/scripting/sidefx/region_node.h b/engines/zvision/scripting/effects/region_effect.h index ec716b6e3e..4fc16224ff 100644 --- a/engines/zvision/scripting/sidefx/region_node.h +++ b/engines/zvision/scripting/effects/region_effect.h @@ -25,16 +25,16 @@ #include "graphics/surface.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/effect.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class RegionNode : public SideFX { +class RegionNode : public ScriptingEffect { public: - RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay); + RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay); ~RegionNode(); /** @@ -49,7 +49,7 @@ public: private: int32 _timeLeft; uint32 _delay; - Effect *_effect; + GraphicsEffect *_effect; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/syncsound_node.cpp b/engines/zvision/scripting/effects/syncsound_effect.cpp index c1f139694b..70ba97deb8 100644 --- a/engines/zvision/scripting/sidefx/syncsound_node.cpp +++ b/engines/zvision/scripting/effects/syncsound_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/syncsound_node.h" +#include "zvision/scripting/effects/syncsound_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -36,7 +36,7 @@ namespace ZVision { SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::String &filename, int32 syncto) - : SideFX(engine, key, SIDEFX_AUDIO) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_AUDIO) { _syncto = syncto; _sub = NULL; @@ -76,7 +76,7 @@ bool SyncSoundNode::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getSideFX(_syncto) == NULL) return stop(); - if (_sub) + if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1) _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); } return false; diff --git a/engines/zvision/scripting/sidefx/syncsound_node.h b/engines/zvision/scripting/effects/syncsound_effect.h index 7cd02a8aef..0eabff77a3 100644 --- a/engines/zvision/scripting/sidefx/syncsound_node.h +++ b/engines/zvision/scripting/effects/syncsound_effect.h @@ -24,15 +24,15 @@ #define ZVISION_SYNCSOUND_NODE_H #include "audio/mixer.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/text/subtitles.h" namespace Common { class String; } namespace ZVision { -class SyncSoundNode : public SideFX { +class SyncSoundNode : public ScriptingEffect { public: SyncSoundNode(ZVision *engine, uint32 key, Common::String &file, int32 syncto); ~SyncSoundNode(); diff --git a/engines/zvision/scripting/sidefx/timer_node.cpp b/engines/zvision/scripting/effects/timer_effect.cpp index 170f6e7472..778f9dec6c 100644 --- a/engines/zvision/scripting/sidefx/timer_node.cpp +++ b/engines/zvision/scripting/effects/timer_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/effects/timer_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -32,7 +32,7 @@ namespace ZVision { TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) - : SideFX(engine, key, SIDEFX_TIMER) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_TIMER) { _timeLeft = 0; if (_engine->getGameId() == GID_NEMESIS) diff --git a/engines/zvision/scripting/sidefx/timer_node.h b/engines/zvision/scripting/effects/timer_effect.h index 7a26aff251..5e45d54d7d 100644 --- a/engines/zvision/scripting/sidefx/timer_node.h +++ b/engines/zvision/scripting/effects/timer_effect.h @@ -23,13 +23,13 @@ #ifndef ZVISION_TIMER_NODE_H #define ZVISION_TIMER_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" namespace ZVision { class ZVision; -class TimerNode : public SideFX { +class TimerNode : public ScriptingEffect { public: TimerNode(ZVision *engine, uint32 key, uint timeInSeconds); ~TimerNode(); diff --git a/engines/zvision/scripting/sidefx/ttytext_node.cpp b/engines/zvision/scripting/effects/ttytext_effect.cpp index 9a7fa01649..8d340dae39 100644 --- a/engines/zvision/scripting/sidefx/ttytext_node.cpp +++ b/engines/zvision/scripting/effects/ttytext_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/ttytext_node.h" +#include "zvision/scripting/effects/ttytext_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -35,7 +35,7 @@ namespace ZVision { ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay) : - SideFX(engine, key, SIDEFX_TTYTXT), + ScriptingEffect(engine, key, SCRIPTING_EFFECT_TTYTXT), _fnt(engine) { _delay = delay; _r = r; @@ -56,10 +56,10 @@ ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file delete infile; } - _img.create(_r.width(), _r.height(), _engine->_pixelFormat); - _style.sharp = true; - _style.readAllStyle(_txtbuf); - _style.setFont(_fnt); + _img.create(_r.width(), _r.height(), _engine->_resourcePixelFormat); + _state._sharp = true; + _state.readAllStyles(_txtbuf); + _state.updateFontWithTextState(_fnt); _engine->getScriptManager()->setStateValue(_key, 1); } @@ -74,29 +74,27 @@ bool ttyTextNode::process(uint32 deltaTimeInMillis) { if (_nexttime < 0) { if (_txtpos < _txtbuf.size()) { if (_txtbuf[_txtpos] == '<') { - int32 strt = _txtpos; - int32 endt = 0; + int32 start = _txtpos; + int32 end = 0; int16 ret = 0; while (_txtbuf[_txtpos] != '>' && _txtpos < _txtbuf.size()) _txtpos++; - endt = _txtpos; - if (strt != -1) - if ((endt - strt - 1) > 0) - ret = _style.parseStyle(_txtbuf.c_str() + strt + 1, endt - strt - 1); - - if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { - if (ret & TXT_RET_FNTCHG) - _style.setFont(_fnt); - if (ret & TXT_RET_FNTSTL) - _style.setFontStyle(_fnt); - - if (ret & TXT_RET_NEWLN) - newline(); + end = _txtpos; + if (start != -1) { + if ((end - start - 1) > 0) { + ret = _state.parseStyle(_txtbuf.c_str() + start + 1, end - start - 1); + } + } + + if (ret & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) { + _state.updateFontWithTextState(_fnt); + } else if (ret & TEXT_CHANGE_NEWLINE) { + newline(); } - if (ret & TXT_RET_HASSTBOX) { + if (ret & TEXT_CHANGE_HAS_STATE_BOX) { Common::String buf; - buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_style.statebox)); + buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_state._statebox)); for (uint8 j = 0; j < buf.size(); j++) outchar(buf[j]); @@ -158,7 +156,7 @@ void ttyTextNode::newline() { } void ttyTextNode::outchar(uint16 chr) { - uint32 clr = _engine->_pixelFormat.RGBToColor(_style.red, _style.green, _style.blue); + uint32 clr = _engine->_resourcePixelFormat.RGBToColor(_state._red, _state._green, _state._blue); if (_dx + _fnt.getCharWidth(chr) > _r.width()) newline(); diff --git a/engines/zvision/scripting/sidefx/ttytext_node.h b/engines/zvision/scripting/effects/ttytext_effect.h index b6cbed3e34..18cbbbaee3 100644 --- a/engines/zvision/scripting/sidefx/ttytext_node.h +++ b/engines/zvision/scripting/effects/ttytext_effect.h @@ -26,16 +26,16 @@ #include "common/rect.h" #include "graphics/surface.h" -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "zvision/text/text.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" namespace Common { class String; } namespace ZVision { -class ttyTextNode : public SideFX { +class ttyTextNode : public ScriptingEffect { public: ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay); ~ttyTextNode(); @@ -51,7 +51,7 @@ public: private: Common::Rect _r; - cTxtStyle _style; + TextStyleState _state; StyledTTFont _fnt; Common::String _txtbuf; uint32 _txtpos; diff --git a/engines/zvision/core/menu.cpp b/engines/zvision/scripting/menu.cpp index 31e0d71370..064bd1b57d 100644 --- a/engines/zvision/core/menu.cpp +++ b/engines/zvision/scripting/menu.cpp @@ -20,26 +20,22 @@ * */ -#include "common/scummsys.h" - -#include "zvision/core/menu.h" - #include "zvision/graphics/render_manager.h" +#include "zvision/scripting/menu.h" namespace ZVision { enum { - SLOT_START_SLOT = 151, - SLOT_SPELL_1 = 191, - SLOT_USER_CHOSE_THIS_SPELL = 205, - SLOT_REVERSED_SPELLBOOK = 206 + kMainMenuSave = 0, + kMainMenuLoad = 1, + kMainMenuPrefs = 2, + kMainMenuExit = 3 }; enum { - menu_MAIN_SAVE = 0, - menu_MAIN_REST = 1, - menu_MAIN_PREF = 2, - menu_MAIN_EXIT = 3 + kMenuItem = 0, + kMenuMagic = 1, + kMenuMain = 2 }; MenuHandler::MenuHandler(ZVision *engine) { @@ -50,13 +46,13 @@ MenuHandler::MenuHandler(ZVision *engine) { MenuZGI::MenuZGI(ZVision *engine) : MenuHandler(engine) { menuMouseFocus = -1; - inmenu = false; + inMenu = false; scrolled[0] = false; scrolled[1] = false; scrolled[2] = false; - scrollPos[0] = 0.0; - scrollPos[1] = 0.0; - scrollPos[2] = 0.0; + scrollPos[0] = 0; + scrollPos[1] = 0; + scrollPos[2] = 0; mouseOnItem = -1; redraw = false; clean = false; @@ -64,15 +60,15 @@ MenuZGI::MenuZGI(ZVision *engine) : char buf[24]; for (int i = 1; i < 4; i++) { sprintf(buf, "gmzau%2.2x1.tga", i); - _engine->getRenderManager()->readImageToSurface(buf, menuback[i - 1][0], false); + _engine->getRenderManager()->readImageToSurface(buf, menuBack[i - 1][0], false); sprintf(buf, "gmzau%2.2x1.tga", i + 0x10); - _engine->getRenderManager()->readImageToSurface(buf, menuback[i - 1][1], false); + _engine->getRenderManager()->readImageToSurface(buf, menuBack[i - 1][1], false); } for (int i = 0; i < 4; i++) { sprintf(buf, "gmzmu%2.2x1.tga", i); - _engine->getRenderManager()->readImageToSurface(buf, menubar[i][0], false); + _engine->getRenderManager()->readImageToSurface(buf, menuBar[i][0], false); sprintf(buf, "gmznu%2.2x1.tga", i); - _engine->getRenderManager()->readImageToSurface(buf, menubar[i][1], false); + _engine->getRenderManager()->readImageToSurface(buf, menuBar[i][1], false); } for (int i = 0; i < 50; i++) { @@ -90,12 +86,12 @@ MenuZGI::MenuZGI(ZVision *engine) : MenuZGI::~MenuZGI() { for (int i = 0; i < 3; i++) { - menuback[i][0].free(); - menuback[i][1].free(); + menuBack[i][0].free(); + menuBack[i][1].free(); } for (int i = 0; i < 4; i++) { - menubar[i][0].free(); - menubar[i][1].free(); + menuBar[i][0].free(); + menuBar[i][1].free(); } for (int i = 0; i < 50; i++) { if (items[i][0]) { @@ -122,8 +118,8 @@ MenuZGI::~MenuZGI() { void MenuZGI::onMouseUp(const Common::Point &Pos) { if (Pos.y < 40) { switch (menuMouseFocus) { - case menu_ITEM: - if (menuBarFlag & menuBar_Items) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) { int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); if (itemCount == 0) itemCount = 20; @@ -131,13 +127,13 @@ void MenuZGI::onMouseUp(const Common::Point &Pos) { for (int i = 0; i < itemCount; i++) { int itemspace = (600 - 28) / itemCount; - if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, - scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0, + scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) { int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); if (mouseItem >= 0 && mouseItem < 0xE0) { _engine->getScriptManager()->inventoryDrop(mouseItem); - _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i)); - _engine->getScriptManager()->setStateValue(SLOT_START_SLOT + i, mouseItem); + _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i)); + _engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem); redraw = true; } @@ -146,62 +142,62 @@ void MenuZGI::onMouseUp(const Common::Point &Pos) { } break; - case menu_MAGIC: - if (menuBarFlag & menuBar_Magic) { + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) { for (int i = 0; i < 12; i++) { - uint itemnum = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i); if (itemnum != 0) { - if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1) itemnum = 0xEE + i; else itemnum = 0xE0 + i; } if (itemnum) if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0) - if (Common::Rect(668 + 47 * i - scrollPos[menu_MAGIC], 0, - 668 + 47 * i - scrollPos[menu_MAGIC] + 28, 32).contains(Pos)) - _engine->getScriptManager()->setStateValue(SLOT_USER_CHOSE_THIS_SPELL, itemnum); + if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0, + 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) + _engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum); } } break; - case menu_MAIN: + case kMenuMain: // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(320 + 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->ifQuit(); } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(320 , - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0); } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(320 - 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0); } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(320 - 135 * 2, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 - 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0); } break; @@ -212,12 +208,12 @@ void MenuZGI::onMouseUp(const Common::Point &Pos) { void MenuZGI::onMouseMove(const Common::Point &Pos) { if (Pos.y < 40) { - if (!inmenu) + if (!inMenu) redraw = true; - inmenu = true; + inMenu = true; switch (menuMouseFocus) { - case menu_ITEM: - if (menuBarFlag & menuBar_Items) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) { int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); if (itemCount == 0) itemCount = 20; @@ -231,78 +227,78 @@ void MenuZGI::onMouseMove(const Common::Point &Pos) { for (int i = 0; i < itemCount; i++) { int itemspace = (600 - 28) / itemCount; - if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, - scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0, + scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) { mouseOnItem = i; break; } } if (lastItem != mouseOnItem) - if (_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + mouseOnItem) || - _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + lastItem)) + if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + mouseOnItem) || + _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem)) redraw = true; } break; - case menu_MAGIC: - if (menuBarFlag & menuBar_Magic) { + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) { int lastItem = mouseOnItem; mouseOnItem = -1; for (int i = 0; i < 12; i++) { - if (Common::Rect(668 + 47 * i - scrollPos[menu_MAGIC], 0, - 668 + 47 * i - scrollPos[menu_MAGIC] + 28, 32).contains(Pos)) { + if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0, + 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) { mouseOnItem = i; break; } } if (lastItem != mouseOnItem) - if (_engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + mouseOnItem) || - _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + lastItem)) + if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + mouseOnItem) || + _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem)) redraw = true; } break; - case menu_MAIN: { + case kMenuMain: { int lastItem = mouseOnItem; mouseOnItem = -1; // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(320 + 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_EXIT; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuExit; } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(320 , - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_PREF; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuPrefs; } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(320 - 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_REST; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuLoad; } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(320 - 135 * 2, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 - 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_SAVE; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuSave; } if (lastItem != mouseOnItem) @@ -313,25 +309,25 @@ void MenuZGI::onMouseMove(const Common::Point &Pos) { default: int cur_menu = menuMouseFocus; if (Common::Rect(64, 0, 64 + 512, 8).contains(Pos)) { // Main - menuMouseFocus = menu_MAIN; - scrolled[menu_MAIN] = false; - scrollPos[menu_MAIN] = menuback[menu_MAIN][1].h - menuback[menu_MAIN][0].h; + menuMouseFocus = kMenuMain; + scrolled[kMenuMain] = false; + scrollPos[kMenuMain] = menuBack[kMenuMain][1].h - menuBack[kMenuMain][0].h; _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); } - if (menuBarFlag & menuBar_Magic) + if (menuBarFlag & kMenubarMagic) if (Common::Rect(640 - 28, 0, 640, 32).contains(Pos)) { // Magic - menuMouseFocus = menu_MAGIC; - scrolled[menu_MAGIC] = false; - scrollPos[menu_MAGIC] = 28; + menuMouseFocus = kMenuMagic; + scrolled[kMenuMagic] = false; + scrollPos[kMenuMagic] = 28; _engine->getScriptManager()->setStateValue(StateKey_MenuState, 3); } - if (menuBarFlag & menuBar_Items) + if (menuBarFlag & kMenubarItems) if (Common::Rect(0, 0, 28, 32).contains(Pos)) { // Items - menuMouseFocus = menu_ITEM; - scrolled[menu_ITEM] = false; - scrollPos[menu_ITEM] = 28 - 600; + menuMouseFocus = kMenuItem; + scrolled[kMenuItem] = false; + scrollPos[kMenuItem] = 28 - 600; _engine->getScriptManager()->setStateValue(StateKey_MenuState, 1); } @@ -341,9 +337,9 @@ void MenuZGI::onMouseMove(const Common::Point &Pos) { break; } } else { - if (inmenu) + if (inMenu) clean = true; - inmenu = false; + inMenu = false; if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); menuMouseFocus = -1; @@ -356,24 +352,24 @@ void MenuZGI::process(uint32 deltatime) { clean = false; } switch (menuMouseFocus) { - case menu_ITEM: - if (menuBarFlag & menuBar_Items) - if (!scrolled[menu_ITEM]) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) + if (!scrolled[kMenuItem]) { redraw = true; float scrl = 600.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos [menu_ITEM] += scrl; + scrollPos[kMenuItem] += (int)scrl; - if (scrollPos[menu_ITEM] >= 0) { - scrolled[menu_ITEM] = true; - scrollPos [menu_ITEM] = 0; + if (scrollPos[kMenuItem] >= 0) { + scrolled[kMenuItem] = true; + scrollPos[kMenuItem] = 0; } } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][0], scrollPos[menu_ITEM], 0); + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuItem][0], scrollPos[kMenuItem], 0); int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); if (itemCount == 0) @@ -389,7 +385,7 @@ void MenuZGI::process(uint32 deltatime) { if (mouseOnItem == i) inrect = true; - uint curItemId = _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i); + uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i); if (curItemId != 0) { if (itemId[i] != curItemId) { @@ -402,9 +398,9 @@ void MenuZGI::process(uint32 deltatime) { } if (inrect) - _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[menu_ITEM] + itemspace * i, 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[kMenuItem] + itemspace * i, 0, 0); else - _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[menu_ITEM] + itemspace * i, 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[kMenuItem] + itemspace * i, 0, 0); } else { if (items[i][0]) { @@ -425,24 +421,24 @@ void MenuZGI::process(uint32 deltatime) { } break; - case menu_MAGIC: - if (menuBarFlag & menuBar_Magic) - if (!scrolled[menu_MAGIC]) { + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) + if (!scrolled[kMenuMagic]) { redraw = true; float scrl = 600.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos [menu_MAGIC] += scrl; + scrollPos[kMenuMagic] += (int)scrl; - if (scrollPos[menu_MAGIC] >= 600) { - scrolled[menu_MAGIC] = true; - scrollPos [menu_MAGIC] = 600; + if (scrollPos[kMenuMagic] >= 600) { + scrolled[kMenuMagic] = true; + scrollPos[kMenuMagic] = 600; } } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][0], 640 - scrollPos[menu_MAGIC], 0); + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMagic][0], 640 - scrollPos[kMenuMagic], 0); for (int i = 0; i < 12; i++) { bool inrect = false; @@ -450,9 +446,9 @@ void MenuZGI::process(uint32 deltatime) { if (mouseOnItem == i) inrect = true; - uint curItemId = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i); if (curItemId) { - if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1) curItemId = 0xEE + i; else curItemId = 0xE0 + i; @@ -469,9 +465,9 @@ void MenuZGI::process(uint32 deltatime) { } if (inrect) - _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[menu_MAGIC], 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0); else - _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[menu_MAGIC], 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0); } else { if (magic[i][0]) { @@ -491,61 +487,61 @@ void MenuZGI::process(uint32 deltatime) { } break; - case menu_MAIN: - if (!scrolled[menu_MAIN]) { + case kMenuMain: + if (!scrolled[kMenuMain]) { redraw = true; float scrl = 32.0 * 2.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos [menu_MAIN] += scrl; + scrollPos[kMenuMain] += (int)scrl; - if (scrollPos[menu_MAIN] >= 0) { - scrolled[menu_MAIN] = true; - scrollPos [menu_MAIN] = 0; + if (scrollPos[kMenuMain] >= 0) { + scrolled[kMenuMain] = true; + scrollPos[kMenuMain] = 0; } } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAIN][0], 30, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMain][0], 30, scrollPos[kMenuMain]); - if (menuBarFlag & menuBar_Exit) { - if (mouseOnItem == menu_MAIN_EXIT) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_EXIT][1], 320 + 135, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarExit) { + if (mouseOnItem == kMainMenuExit) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuExit][1], 320 + 135, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_EXIT][0], 320 + 135, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuExit][0], 320 + 135, scrollPos[kMenuMain]); } - if (menuBarFlag & menuBar_Settings) { - if (mouseOnItem == menu_MAIN_PREF) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_PREF][1], 320, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarSettings) { + if (mouseOnItem == kMainMenuPrefs) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuPrefs][1], 320, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_PREF][0], 320, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuPrefs][0], 320, scrollPos[kMenuMain]); } - if (menuBarFlag & menuBar_Restore) { - if (mouseOnItem == menu_MAIN_REST) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_REST][1], 320 - 135, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarRestore) { + if (mouseOnItem == kMainMenuLoad) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuLoad][1], 320 - 135, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_REST][0], 320 - 135, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuLoad][0], 320 - 135, scrollPos[kMenuMain]); } - if (menuBarFlag & menuBar_Save) { - if (mouseOnItem == menu_MAIN_SAVE) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_SAVE][1], 320 - 135 * 2, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarSave) { + if (mouseOnItem == kMainMenuSave) + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuSave][1], 320 - 135 * 2, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_SAVE][0], 320 - 135 * 2, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menuBar[kMainMenuSave][0], 320 - 135 * 2, scrollPos[kMenuMain]); } redraw = false; } break; default: if (redraw) { - if (inmenu) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAIN][1], 30, 0); + if (inMenu) { + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMain][1], 30, 0); - if (menuBarFlag & menuBar_Items) - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][1], 0, 0); + if (menuBarFlag & kMenubarItems) + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuItem][1], 0, 0); - if (menuBarFlag & menuBar_Magic) - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][1], 640 - 28, 0); + if (menuBarFlag & kMenubarMagic) + _engine->getRenderManager()->blitSurfaceToMenu(menuBack[kMenuMagic][1], 640 - 28, 0); } redraw = false; } @@ -555,9 +551,9 @@ void MenuZGI::process(uint32 deltatime) { MenuNemesis::MenuNemesis(ZVision *engine) : MenuHandler(engine) { - inmenu = false; + inMenu = false; scrolled = false; - scrollPos = 0.0; + scrollPos = 0; mouseOnItem = -1; redraw = false; delay = 0; @@ -569,7 +565,7 @@ MenuNemesis::MenuNemesis(ZVision *engine) : _engine->getRenderManager()->readImageToSurface(buf, but[i][j], false); } - _engine->getRenderManager()->readImageToSurface("bar.tga", menubar, false); + _engine->getRenderManager()->readImageToSurface("bar.tga", menuBar, false); frm = 0; } @@ -579,7 +575,7 @@ MenuNemesis::~MenuNemesis() { for (int j = 0; j < 6; j++) but[i][j].free(); - menubar.free(); + menuBar.free(); } static const int16 buts[4][2] = { {120 , 64}, {144, 184}, {128, 328}, {120, 456} }; @@ -587,7 +583,7 @@ static const int16 buts[4][2] = { {120 , 64}, {144, 184}, {128, 328}, {120, 456} void MenuNemesis::onMouseUp(const Common::Point &Pos) { if (Pos.y < 40) { // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(buts[3][1], scrollPos, buts[3][0] + buts[3][1], @@ -598,7 +594,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(buts[2][1], scrollPos, buts[2][0] + buts[2][1], @@ -609,7 +605,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(buts[1][1], scrollPos, buts[1][0] + buts[1][1], @@ -620,7 +616,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(buts[0][1], scrollPos, buts[0][0] + buts[0][1], @@ -635,7 +631,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { void MenuNemesis::onMouseMove(const Common::Point &Pos) { if (Pos.y < 40) { - inmenu = true; + inMenu = true; if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2) _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); @@ -644,39 +640,39 @@ void MenuNemesis::onMouseMove(const Common::Point &Pos) { mouseOnItem = -1; // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(buts[3][1], scrollPos, buts[3][0] + buts[3][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_EXIT; + mouseOnItem = kMainMenuExit; } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(buts[2][1], scrollPos, buts[2][0] + buts[2][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_PREF; + mouseOnItem = kMainMenuPrefs; } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(buts[1][1], scrollPos, buts[1][0] + buts[1][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_REST; + mouseOnItem = kMainMenuLoad; } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(buts[0][1], scrollPos, buts[0][0] + buts[0][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_SAVE; + mouseOnItem = kMainMenuSave; } if (lastItem != mouseOnItem) { @@ -685,7 +681,7 @@ void MenuNemesis::onMouseMove(const Common::Point &Pos) { delay = 200; } } else { - inmenu = false; + inMenu = false; if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0) _engine->getScriptManager()->setStateValue(StateKey_MenuState, 0); mouseOnItem = -1; @@ -693,14 +689,14 @@ void MenuNemesis::onMouseMove(const Common::Point &Pos) { } void MenuNemesis::process(uint32 deltatime) { - if (inmenu) { + if (inMenu) { if (!scrolled) { float scrl = 32.0 * 2.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos += scrl; + scrollPos += (int)scrl; redraw = true; } @@ -719,22 +715,22 @@ void MenuNemesis::process(uint32 deltatime) { } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos); + _engine->getRenderManager()->blitSurfaceToMenu(menuBar, 64, scrollPos); - if (menuBarFlag & menuBar_Exit) - if (mouseOnItem == menu_MAIN_EXIT) + if (menuBarFlag & kMenubarExit) + if (mouseOnItem == kMainMenuExit) _engine->getRenderManager()->blitSurfaceToMenu(but[3][frm], buts[3][1], scrollPos); - if (menuBarFlag & menuBar_Settings) - if (mouseOnItem == menu_MAIN_PREF) + if (menuBarFlag & kMenubarSettings) + if (mouseOnItem == kMainMenuPrefs) _engine->getRenderManager()->blitSurfaceToMenu(but[2][frm], buts[2][1], scrollPos); - if (menuBarFlag & menuBar_Restore) - if (mouseOnItem == menu_MAIN_REST) + if (menuBarFlag & kMenubarRestore) + if (mouseOnItem == kMainMenuLoad) _engine->getRenderManager()->blitSurfaceToMenu(but[1][frm], buts[1][1], scrollPos); - if (menuBarFlag & menuBar_Save) - if (mouseOnItem == menu_MAIN_SAVE) + if (menuBarFlag & kMenubarSave) + if (mouseOnItem == kMainMenuSave) _engine->getRenderManager()->blitSurfaceToMenu(but[0][frm], buts[0][1], scrollPos); redraw = false; @@ -747,16 +743,16 @@ void MenuNemesis::process(uint32 deltatime) { if (scrl == 0) scrl = 1.0; - Common::Rect cl(64, 32 + scrollPos - scrl, 64 + 512, 32 + scrollPos + 1); + Common::Rect cl(64, (int16)(32 + scrollPos - scrl), 64 + 512, 32 + scrollPos + 1); _engine->getRenderManager()->clearMenuSurface(cl); - scrollPos -= scrl; + scrollPos -= (int)scrl; redraw = true; } else scrollPos = -32; if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos); + _engine->getRenderManager()->blitSurfaceToMenu(menuBar, 64, scrollPos); redraw = false; } } diff --git a/engines/zvision/core/menu.h b/engines/zvision/scripting/menu.h index ebe0bb50ac..f6b21b9c97 100644 --- a/engines/zvision/core/menu.h +++ b/engines/zvision/scripting/menu.h @@ -32,12 +32,12 @@ namespace ZVision { enum menuBar { - menuBar_Exit = 0x1, - menuBar_Settings = 0x2, - menuBar_Restore = 0x4, - menuBar_Save = 0x8, - menuBar_Items = 0x100, - menuBar_Magic = 0x200 + kMenubarExit = 0x1, + kMenubarSettings = 0x2, + kMenubarRestore = 0x4, + kMenubarSave = 0x8, + kMenubarItems = 0x100, + kMenubarMagic = 0x200 }; class MenuHandler { @@ -68,8 +68,8 @@ public: void onMouseUp(const Common::Point &Pos); void process(uint32 deltaTimeInMillis); private: - Graphics::Surface menuback[3][2]; - Graphics::Surface menubar[4][2]; + Graphics::Surface menuBack[3][2]; + Graphics::Surface menuBar[4][2]; Graphics::Surface *items[50][2]; uint itemId[50]; @@ -77,19 +77,13 @@ private: uint magicId[12]; int menuMouseFocus; - bool inmenu; + bool inMenu; int mouseOnItem; - bool scrolled[3]; + bool scrolled[3]; int16 scrollPos[3]; - enum { - menu_ITEM = 0, - menu_MAGIC = 1, - menu_MAIN = 2 - }; - bool clean; bool redraw; @@ -104,13 +98,13 @@ public: void process(uint32 deltaTimeInMillis); private: Graphics::Surface but[4][6]; - Graphics::Surface menubar; + Graphics::Surface menuBar; - bool inmenu; + bool inMenu; int mouseOnItem; - bool scrolled; + bool scrolled; int16 scrollPos; bool redraw; @@ -120,6 +114,6 @@ private: }; -} +} // End of namespace ZVision #endif diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp index c117da5ec2..edc1b8622c 100644 --- a/engines/zvision/scripting/scr_file_handling.cpp +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -47,15 +47,13 @@ namespace ZVision { void ScriptManager::parseScrFile(const Common::String &fileName, ScriptScope &scope) { Common::File file; if (!_engine->getSearchManager()->openFile(file, fileName)) { - warning("Script file not found: %s", fileName.c_str()); - return; + error("Script file not found: %s", fileName.c_str()); } while (!file.eos()) { Common::String line = file.readLine(); if (file.err()) { - warning("Error parsing scr file: %s", fileName.c_str()); - return; + error("Error parsing scr file: %s", fileName.c_str()); } trimCommentsAndWhiteSpace(&line); @@ -85,9 +83,18 @@ void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stre while (!stream.eos() && !line.contains('}')) { if (line.matchString("criteria {", true)) { - parseCriteria(stream, puzzle->criteriaList); + parseCriteria(stream, puzzle->criteriaList, puzzle->key); } else if (line.matchString("results {", true)) { parseResults(stream, puzzle->resultActions); + + // WORKAROUND for a script bug in Zork Nemesis, room ve5e (tuning + // fork box closeup). If the player leaves the screen while the + // box is open, puzzle 19398 shows the animation where the box + // closes, but the box state (state variable 19397) is not updated. + // We insert the missing assignment for the box state here. + // Fixes bug #6803. + if (_engine->getGameId() == GID_NEMESIS && puzzle->key == 19398) + puzzle->resultActions.push_back(new ActionAssign(_engine, 11, "19397, 0")); } else if (line.matchString("flags {", true)) { setStateFlag(puzzle->key, parseFlags(stream)); } @@ -99,11 +106,18 @@ void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stre puzzle->addedBySetState = false; } -bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const { +bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const { // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); + // Skip any commented out criteria. If all the criteria are commented out, + // we might end up with an invalid criteria list (bug #6776). + while (line.empty()) { + line = stream.readLine(); + trimCommentsAndWhiteSpace(&line); + } + // Criteria can be empty if (line.contains('}')) { return false; @@ -112,6 +126,21 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li // Create a new List to hold the CriteriaEntries criteriaList.push_back(Common::List<Puzzle::CriteriaEntry>()); + // WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle) + // Since we patch the script that triggers when manipulating the left fist + // (below), we add an additional check for the left fist sound, so that it + // doesn't get killed immediately when the left fist animation starts. + // Together with the workaround below, it fixes bug #6783. + if (_engine->getGameId() == GID_NEMESIS && key == 3594) { + Puzzle::CriteriaEntry entry; + entry.key = 567; + entry.criteriaOperator = Puzzle::NOT_EQUAL_TO; + entry.argumentIsAKey = false; + entry.argument = 1; + + criteriaList.back().push_back(entry); + } + while (!stream.eos() && !line.contains('}')) { Puzzle::CriteriaEntry entry; @@ -123,6 +152,13 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li token = tokenizer.nextToken(); sscanf(token.c_str(), "[%u]", &(entry.key)); + // WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle) + // Check for the state of animation 567 (left fist) when manipulating + // the fingers of the left fist (puzzle slots 3582, 3583). + // Together with the workaround above, it fixes bug #6783. + if (_engine->getGameId() == GID_NEMESIS && (key == 3582 || key == 3583) && entry.key == 568) + entry.key = 567; + // Parse the operator out of the second token token = tokenizer.nextToken(); if (token.c_str()[0] == '=') @@ -134,9 +170,17 @@ bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::Li else if (token.c_str()[0] == '<') entry.criteriaOperator = Puzzle::LESS_THAN; + // There are supposed to be three tokens, but there is no + // guarantee that there will be a space between the second and + // the third one (bug #6774) + if (token.size() == 1) { + token = tokenizer.nextToken(); + } else { + token.deleteChar(0); + } + // First determine if the last token is an id or a value // Then parse it into 'argument' - token = tokenizer.nextToken(); if (token.contains('[')) { sscanf(token.c_str(), "[%u]", &(entry.argument)); entry.argumentIsAKey = true; @@ -216,6 +260,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("animpreload", true)) { actionList.push_back(new ActionPreloadAnimation(_engine, slot, args)); } else if (act.matchString("animunload", true)) { + // Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j) actionList.push_back(new ActionUnloadAnimation(_engine, slot, args)); } else if (act.matchString("attenuate", true)) { actionList.push_back(new ActionAttenuate(_engine, slot, args)); @@ -234,12 +279,13 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("disable_control", true)) { actionList.push_back(new ActionDisableControl(_engine, slot, args)); } else if (act.matchString("disable_venus", true)) { - actionList.push_back(new ActionDisableVenus(_engine, slot, args)); + // Not used. Purposely left empty } else if (act.matchString("display_message", true)) { actionList.push_back(new ActionDisplayMessage(_engine, slot, args)); } else if (act.matchString("dissolve", true)) { actionList.push_back(new ActionDissolve(_engine)); } else if (act.matchString("distort", true)) { + // Only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30) actionList.push_back(new ActionDistort(_engine, slot, args)); } else if (act.matchString("enable_control", true)) { actionList.push_back(new ActionEnableControl(_engine, slot, args)); @@ -248,6 +294,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("inventory", true)) { actionList.push_back(new ActionInventory(_engine, slot, args)); } else if (act.matchString("kill", true)) { + // Only used by ZGI actionList.push_back(new ActionKill(_engine, slot, args)); } else if (act.matchString("menu_bar_enable", true)) { actionList.push_back(new ActionMenuBarEnable(_engine, slot, args)); @@ -264,8 +311,11 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("random", true)) { actionList.push_back(new ActionRandom(_engine, slot, args)); } else if (act.matchString("region", true)) { + // Only used by Zork: Nemesis actionList.push_back(new ActionRegion(_engine, slot, args)); } else if (act.matchString("restore_game", true)) { + // Only used by ZGI to load the restart game slot, r.svr. + // Used by the credits screen. actionList.push_back(new ActionRestoreGame(_engine, slot, args)); } else if (act.matchString("rotate_to", true)) { actionList.push_back(new ActionRotateTo(_engine, slot, args)); @@ -276,7 +326,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("set_screen", true)) { actionList.push_back(new ActionSetScreen(_engine, slot, args)); } else if (act.matchString("set_venus", true)) { - actionList.push_back(new ActionSetVenus(_engine, slot, args)); + // Not used. Purposely left empty } else if (act.matchString("stop", true)) { actionList.push_back(new ActionStop(_engine, slot, args)); } else if (act.matchString("streamvideo", true)) { @@ -337,6 +387,16 @@ Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadS Common::String controlType(controlTypeBuffer); if (controlType.equalsIgnoreCase("push_toggle")) { + // WORKAROUND for a script bug in ZGI: There is an invalid hotspot + // at scene em1h (bottom of tower), which points to a missing + // script em1n. This is a hotspot at the right of the screen. + // In the original, this hotspot doesn't lead anywhere anyway, + // so instead of moving to a missing scene, we just remove the + // hotspot altogether. The alternative would be to just process + // and ignore invalid scenes, but I don't think it's worth the + // effort. Fixes bug #6780. + if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 5653) + return NULL; return new PushToggleControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("flat")) { Control::parseFlatControl(_engine); @@ -345,25 +405,32 @@ Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadS Control::parsePanoramaControl(_engine, stream); return NULL; } else if (controlType.equalsIgnoreCase("tilt")) { + // Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view) Control::parseTiltControl(_engine, stream); return NULL; - } else if (controlType.equalsIgnoreCase("lever")) { - return new LeverControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("slot")) { return new SlotControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("input")) { return new InputControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("save")) { return new SaveControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("lever")) { + // Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e) + return new LeverControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("safe")) { + // Only used in Zork Nemesis, handles the safe in the Asylum (ac4g) return new SafeControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("hotmovie")) { + // Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g) return new HotMovControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("fist")) { + // Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e) return new FistControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("paint")) { + // Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g) return new PaintControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("titler")) { + // Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons (cjde) return new TitlerControl(_engine, key, stream); } return NULL; diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index 4c1e69072d..70eaab2a0a 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -27,9 +27,10 @@ #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/actions.h" -#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/menu.h" +#include "zvision/scripting/effects/timer_effect.h" #include "common/algorithm.h" #include "common/hashmap.h" @@ -71,21 +72,23 @@ void ScriptManager::initialize() { } void ScriptManager::update(uint deltaTimeMillis) { - if (_currentLocation.node != _nextLocation.node || - _currentLocation.room != _nextLocation.room || - _currentLocation.view != _nextLocation.view || - _currentLocation.world != _nextLocation.world) - ChangeLocationReal(); + if (_currentLocation != _nextLocation) { + ChangeLocationReal(false); + } updateNodes(deltaTimeMillis); - if (! execScope(nodeview)) + if (!execScope(nodeview)) { return; - if (! execScope(room)) + } + if (!execScope(room)) { return; - if (! execScope(world)) + } + if (!execScope(world)) { return; - if (! execScope(universe)) + } + if (!execScope(universe)) { return; + } updateControls(deltaTimeMillis); } @@ -96,17 +99,22 @@ bool ScriptManager::execScope(ScriptScope &scope) { scope.scopeQueue = tmp; scope.scopeQueue->clear(); - for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) { (*PuzzleIter)->addedBySetState = false; + } if (scope.procCount < 2 || getStateValue(StateKey_ExecScopeStyle)) { - for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) - if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) + for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) { + if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) { return false; + } + } } else { - for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) - if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) + for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) { + if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) { return false; + } + } } if (scope.procCount < 2) { @@ -118,9 +126,11 @@ bool ScriptManager::execScope(ScriptScope &scope) { void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) { if (_referenceTable.contains(key)) { Common::Array<PuzzleRef> *arr = &_referenceTable[key]; - for (uint32 i = 0; i < arr->size(); i++) - if ((*arr)[i].puz == ref.puz) + for (uint32 i = 0; i < arr->size(); i++) { + if ((*arr)[i].puz == ref.puz) { return; + } + } } _referenceTable[key].push_back(ref); @@ -138,9 +148,11 @@ void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) { referenceTableAddPuzzle(puzzlePtr->key, ref); // Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle - for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) - for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) + for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) { + for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { referenceTableAddPuzzle(entryIter->key, ref); + } + } } } @@ -158,8 +170,9 @@ void ScriptManager::updateNodes(uint deltaTimeMillis) { } void ScriptManager::updateControls(uint deltaTimeMillis) { - if (!_activeControls) + if (!_activeControls) { return; + } // Process only one event if (!_controlEvents.empty()) { @@ -186,21 +199,24 @@ void ScriptManager::updateControls(uint deltaTimeMillis) { _controlEvents.pop_front(); } - for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) - if ((*iter)->process(deltaTimeMillis)) + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) { + if ((*iter)->process(deltaTimeMillis)) { break; + } + } } bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { // Check if the puzzle is already finished // Also check that the puzzle isn't disabled - if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED) == Puzzle::DISABLED) { + if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED)) { return true; } // Check each Criteria - if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) + if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) { return true; + } bool criteriaMet = false; for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) { @@ -209,10 +225,11 @@ bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) { // Get the value to compare against int argumentValue; - if (entryIter->argumentIsAKey) + if (entryIter->argumentIsAKey) { argumentValue = getStateValue(entryIter->argument); - else + } else { argumentValue = entryIter->argument; + } // Do the comparison switch (entryIter->criteriaOperator) { @@ -250,8 +267,9 @@ bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) { setStateValue(puzzle->key, 1); for (Common::List<ResultAction *>::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) { - if (!(*resultIter)->execute()) + if (!(*resultIter)->execute()) { return false; + } } } @@ -274,13 +292,15 @@ void ScriptManager::cleanScriptScope(ScriptScope &scope) { scope.privQueueTwo.clear(); scope.scopeQueue = &scope.privQueueOne; scope.execQueue = &scope.privQueueTwo; - for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) + for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) { delete(*iter); + } scope.puzzles.clear(); - for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) + for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) { delete(*iter); + } scope.controls.clear(); @@ -288,44 +308,49 @@ void ScriptManager::cleanScriptScope(ScriptScope &scope) { } int ScriptManager::getStateValue(uint32 key) { - if (_globalState.contains(key)) + if (_globalState.contains(key)) { return _globalState[key]; - else + } else { return 0; + } } void ScriptManager::queuePuzzles(uint32 key) { if (_referenceTable.contains(key)) { Common::Array<PuzzleRef> *arr = &_referenceTable[key]; - for (int32 i = arr->size() - 1; i >= 0; i--) + for (int32 i = arr->size() - 1; i >= 0; i--) { if (!(*arr)[i].puz->addedBySetState) { (*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz); (*arr)[i].puz->addedBySetState = true; } + } } } void ScriptManager::setStateValue(uint32 key, int value) { - if (value == 0) + if (value == 0) { _globalState.erase(key); - else + } else { _globalState[key] = value; + } queuePuzzles(key); } void ScriptManager::setStateValueSilent(uint32 key, int value) { - if (value == 0) + if (value == 0) { _globalState.erase(key); - else + } else { _globalState[key] = value; + } } uint ScriptManager::getStateFlag(uint32 key) { - if (_globalStateFlags.contains(key)) + if (_globalStateFlags.contains(key)) { return _globalStateFlags[key]; - else + } else { return 0; + } } void ScriptManager::setStateFlag(uint32 key, uint value) { @@ -335,10 +360,11 @@ void ScriptManager::setStateFlag(uint32 key, uint value) { } void ScriptManager::setStateFlagSilent(uint32 key, uint value) { - if (value == 0) + if (value == 0) { _globalStateFlags.erase(key); - else + } else { _globalStateFlags[key] = value; + } } void ScriptManager::unsetStateFlag(uint32 key, uint value) { @@ -347,23 +373,29 @@ void ScriptManager::unsetStateFlag(uint32 key, uint value) { if (_globalStateFlags.contains(key)) { _globalStateFlags[key] &= ~value; - if (_globalStateFlags[key] == 0) + if (_globalStateFlags[key] == 0) { _globalStateFlags.erase(key); + } } } Control *ScriptManager::getControl(uint32 key) { - for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) - if ((*iter)->getKey() == key) + for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { + if ((*iter)->getKey() == key) { return *iter; + } + } + return nullptr; } void ScriptManager::focusControl(uint32 key) { - if (!_activeControls) + if (!_activeControls) { return; - if (_currentlyFocusedControl == key) + } + if (_currentlyFocusedControl == key) { return; + } for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { uint32 controlKey = (*iter)->getKey(); @@ -381,11 +413,11 @@ void ScriptManager::setFocusControlKey(uint32 key) { _currentlyFocusedControl = key; } -void ScriptManager::addSideFX(SideFX *fx) { +void ScriptManager::addSideFX(ScriptingEffect *fx) { _activeSideFx.push_back(fx); } -SideFX *ScriptManager::getSideFX(uint32 key) { +ScriptingEffect *ScriptManager::getSideFX(uint32 key) { for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { if ((*iter)->getKey() == key) { return (*iter); @@ -429,7 +461,7 @@ void ScriptManager::killSideFx(uint32 key) { } } -void ScriptManager::killSideFxType(SideFX::SideFXType type) { +void ScriptManager::killSideFxType(ScriptingEffect::ScriptingEffectType type) { for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) { if ((*iter)->getType() & type) { (*iter)->kill(); @@ -442,50 +474,60 @@ void ScriptManager::killSideFxType(SideFX::SideFXType type) { } void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { - if ((*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos)) + if ((*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos)) { return; + } } } void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { - if ((*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos)) + if ((*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos)) { return; + } } } bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { - if (!_activeControls) + if (!_activeControls) { return false; + } for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) { - if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) + if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) { return true; + } } return false; } void ScriptManager::onKeyDown(Common::KeyState keyState) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { - if ((*iter)->onKeyDown(keyState)) + if ((*iter)->onKeyDown(keyState)) { return; + } } } void ScriptManager::onKeyUp(Common::KeyState keyState) { - if (!_activeControls) + if (!_activeControls) { return; + } for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) { - if ((*iter)->onKeyUp(keyState)) + if ((*iter)->onKeyUp(keyState)) { return; + } } } @@ -499,8 +541,8 @@ void ScriptManager::changeLocation(char _world, char _room, char _node, char _vi _nextLocation.node = _node; _nextLocation.view = _view; _nextLocation.offset = offset; - // If next location 0000 - it's indicate to go to previous location. - if (_nextLocation.world == '0' && _nextLocation.room == '0' && _nextLocation.node == '0' && _nextLocation.view == '0') { + // If next location is 0000, return to the previous location. + if (_nextLocation == "0000") { if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { _nextLocation.world = getStateValue(StateKey_LastWorld); _nextLocation.room = getStateValue(StateKey_LastRoom); @@ -517,36 +559,42 @@ void ScriptManager::changeLocation(char _world, char _room, char _node, char _vi } } -void ScriptManager::ChangeLocationReal() { +void ScriptManager::ChangeLocationReal(bool isLoading) { assert(_nextLocation.world != 0); debug(1, "Changing location to: %c %c %c %c %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset); - if (_nextLocation.world == 'g' && _nextLocation.room == 'j' && !ConfMan.getBool("originalsaveload")) { - if ((_nextLocation.node == 's' || _nextLocation.node == 'r') && _nextLocation.view == 'e') { + const bool enteringMenu = (_nextLocation.world == 'g' && _nextLocation.room == 'j'); + const bool leavingMenu = (_currentLocation.world == 'g' && _currentLocation.room == 'j'); + const bool isSaveScreen = (enteringMenu && _nextLocation.node == 's' && _nextLocation.view == 'e'); + const bool isRestoreScreen = (enteringMenu && _nextLocation.node == 'r' && _nextLocation.view == 'e'); + + if (enteringMenu && !ConfMan.getBool("originalsaveload")) { + if (isSaveScreen || isRestoreScreen) { // Hook up the ScummVM save/restore dialog - bool isSave = (_nextLocation.node == 's'); - bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSave); - if (!gameSavedOrLoaded || isSave) { + bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSaveScreen); + if (!gameSavedOrLoaded || isSaveScreen) { // Reload the current room _nextLocation.world = _currentLocation.world; _nextLocation.room = _currentLocation.room; _nextLocation.node = _currentLocation.node; _nextLocation.view = _currentLocation.view; _nextLocation.offset = _currentLocation.offset; - _currentLocation.world = '0'; + + return; + } else { + _currentLocation.world = 'g'; _currentLocation.room = '0'; _currentLocation.node = '0'; _currentLocation.view = '0'; _currentLocation.offset = 0; - } else - return; + } } } _engine->setRenderDelay(2); - if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') { - if (_nextLocation.world != 'g' || _nextLocation.room != 'j') { + if (!leavingMenu) { + if (!isLoading && !enteringMenu) { setStateValue(StateKey_LastWorld, getStateValue(StateKey_World)); setStateValue(StateKey_LastRoom, getStateValue(StateKey_Room)); setStateValue(StateKey_LastNode, getStateValue(StateKey_Node)); @@ -561,16 +609,13 @@ void ScriptManager::ChangeLocationReal() { } } - if (_nextLocation.world == 'g' && _nextLocation.room == 'j') { - if (_nextLocation.node == 's' && _nextLocation.view == 'e' && - _currentLocation.world != 'g' && _currentLocation.room != 'j') + if (enteringMenu) { + if (isSaveScreen && !leavingMenu) { _engine->getSaveManager()->prepareSaveBuffer(); + } } else { - if (_currentLocation.world == 'g' && _currentLocation.room == 'j') + if (leavingMenu) { _engine->getSaveManager()->flushSaveBuffer(); - else { - // Auto save - //_engine->getSaveManager()->autoSave(); } } @@ -583,7 +628,7 @@ void ScriptManager::ChangeLocationReal() { _referenceTable.clear(); addPuzzlesToReferenceTable(universe); - _engine->menuBarEnable(0xFFFF); + _engine->getMenuHandler()->setEnable(0xFFFF); if (_nextLocation.world != _currentLocation.world) { cleanScriptScope(nodeview); @@ -634,7 +679,7 @@ void ScriptManager::ChangeLocationReal() { // Change the background position _engine->getRenderManager()->setBackgroundPosition(_nextLocation.offset); - if (_currentLocation.world == 0 && _currentLocation.room == 0 && _currentLocation.node == 0 && _currentLocation.view == 0) { + if (_currentLocation == "0000") { _currentLocation = _nextLocation; execScope(world); execScope(room); @@ -652,7 +697,7 @@ void ScriptManager::ChangeLocationReal() { execScope(nodeview); } - _engine->checkBorders(); + _engine->getRenderManager()->checkBorders(); } void ScriptManager::serialize(Common::WriteStream *stream) { @@ -667,26 +712,30 @@ void ScriptManager::serialize(Common::WriteStream *stream) { stream->writeByte(getStateValue(StateKey_View)); stream->writeUint32LE(getStateValue(StateKey_ViewPos)); - for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { (*iter)->serialize(stream); + } stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G')); int32 slots = 20000; - if (_engine->getGameId() == GID_NEMESIS) + if (_engine->getGameId() == GID_NEMESIS) { slots = 30000; + } stream->writeUint32LE(slots * 2); - for (int32 i = 0; i < slots; i++) + for (int32 i = 0; i < slots; i++) { stream->writeUint16LE(getStateFlag(i)); + } stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z')); stream->writeUint32LE(slots * 2); - for (int32 i = 0; i < slots; i++) + for (int32 i = 0; i < slots; i++) { stream->writeSint16LE(getStateValue(i)); + } } void ScriptManager::deserialize(Common::SeekableReadStream *stream) { @@ -703,8 +752,9 @@ void ScriptManager::deserialize(Common::SeekableReadStream *stream) { _currentLocation.room = 0; _currentLocation.view = 0; - for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) { delete(*iter); + } _activeSideFx.clear(); @@ -737,20 +787,23 @@ void ScriptManager::deserialize(Common::SeekableReadStream *stream) { case MKTAG('T', 'I', 'M', 'R'): { uint32 key = stream->readUint32LE(); uint32 time = stream->readUint32LE(); - if (_engine->getGameId() == GID_GRANDINQUISITOR) + if (_engine->getGameId() == GID_GRANDINQUISITOR) { time /= 100; - else if (_engine->getGameId() == GID_NEMESIS) + } else if (_engine->getGameId() == GID_NEMESIS) { time /= 1000; + } addSideFX(new TimerNode(_engine, key, time)); } break; case MKTAG('F', 'L', 'A', 'G'): - for (uint32 i = 0; i < tagSize / 2; i++) + for (uint32 i = 0; i < tagSize / 2; i++) { setStateFlagSilent(i, stream->readUint16LE()); + } break; case MKTAG('P', 'U', 'Z', 'Z'): - for (uint32 i = 0; i < tagSize / 2; i++) + for (uint32 i = 0; i < tagSize / 2; i++) { setStateValueSilent(i, stream->readUint16LE()); + } break; default: stream->seek(tagSize, SEEK_CUR); @@ -759,7 +812,7 @@ void ScriptManager::deserialize(Common::SeekableReadStream *stream) { _nextLocation = nextLocation; - ChangeLocationReal(); + ChangeLocationReal(true); _engine->setRenderDelay(10); setStateValue(StateKey_RestoreFlag, 1); @@ -804,10 +857,11 @@ void ScriptManager::flushEvent(Common::EventType type) { EventList::iterator it = _controlEvents.begin(); while (it != _controlEvents.end()) { - if ((*it).type == type) + if ((*it).type == type) { it = _controlEvents.erase(it); - else + } else { it++; + } } } @@ -836,12 +890,15 @@ ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue): } int16 ValueSlot::getValue() { if (slot) { - if (value >= 0) + if (value >= 0) { return _scriptManager->getStateValue(value); - else + } + else { return 0; - } else + } + } else { return value; + } } } // End of namespace ZVision diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h index 1e308faf0d..7c276bf917 100644 --- a/engines/zvision/scripting/script_manager.h +++ b/engines/zvision/scripting/script_manager.h @@ -25,7 +25,7 @@ #include "zvision/scripting/puzzle.h" #include "zvision/scripting/control.h" -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "common/hashmap.h" #include "common/queue.h" @@ -87,6 +87,7 @@ enum StateKey { StateKey_JapanFonts = 75, StateKey_ExecScopeStyle = 76, StateKey_Brightness = 77, + StateKey_MPEGMovies = 78, StateKey_EF9_R = 91, StateKey_EF9_G = 92, StateKey_EF9_B = 93, @@ -94,7 +95,12 @@ enum StateKey { StateKey_Inv_Cnt_Slot = 100, StateKey_Inv_1_Slot = 101, StateKey_Inv_49_Slot = 149, - StateKey_Inv_TotalSlots = 150 + // ZGI only + StateKey_Inv_TotalSlots = 150, + StateKey_Inv_StartSlot = 151, + StateKey_Spell_1 = 191, + StateKey_Active_Spell = 205, + StateKey_Reversed_Spellbooc = 206 }; struct Location { @@ -107,11 +113,33 @@ struct Location { uint32 offset; }; +inline bool operator==(const Location& lhs, const Location& rhs) { + return ( + lhs.world == rhs.world && + lhs.room == rhs.room && + lhs.node == rhs.node && + lhs.view == rhs.view + ); +} + +inline bool operator==(const Location& lhs, const char* rhs) { + Common::String lhsStr = Common::String::format("%c%c%c%c", lhs.world, lhs.room, lhs.node, lhs.view); + return lhsStr == rhs; +} + +inline bool operator!=(const Location& lhs, const Location& rhs) { + return !(lhs == rhs); +} + +inline bool operator!=(const Location& lhs, const char* rhs) { + return !(lhs == rhs); +} + typedef Common::List<Puzzle *> PuzzleList; typedef Common::Queue<Puzzle *> PuzzleQueue; typedef Common::List<Control *> ControlList; typedef Common::HashMap<uint32, int32> StateMap; -typedef Common::List<SideFX *> SideFXList; +typedef Common::List<ScriptingEffect *> SideFXList; typedef Common::List<Common::Event> EventList; class ScriptManager { @@ -191,12 +219,12 @@ public: // Only change focus control without call focus/unfocus. void setFocusControlKey(uint32 key); - void addSideFX(SideFX *fx); - SideFX *getSideFX(uint32 key); + void addSideFX(ScriptingEffect *fx); + ScriptingEffect *getSideFX(uint32 key); void deleteSideFx(uint32 key); void stopSideFx(uint32 key); void killSideFx(uint32 key); - void killSideFxType(SideFX::SideFXType type); + void killSideFxType(ScriptingEffect::ScriptingEffectType type); void addEvent(Common::Event); void flushEvent(Common::EventType type); @@ -267,7 +295,7 @@ private: bool execScope(ScriptScope &scope); /** Perform change location */ - void ChangeLocationReal(); + void ChangeLocationReal(bool isLoading); int8 inventoryGetCount(); void inventorySetCount(int8 cnt); @@ -282,7 +310,7 @@ public: void inventoryDrop(int16 item); void inventoryCycle(); - // TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it +private: /** * Parses a script file into triggers and events * @@ -291,7 +319,6 @@ public: */ void parseScrFile(const Common::String &fileName, ScriptScope &scope); -private: /** * Parses the stream into a Puzzle object * Helper method for parseScrFile. @@ -307,9 +334,10 @@ private: * * @param criteria Pointer to the Criteria object to fill * @param stream Scr file stream + * @param key Puzzle key (for workarounds) * @return Whether any criteria were read */ - bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList) const; + bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const; /** * Parses the stream into a ResultAction objects diff --git a/engines/zvision/scripting/sidefx.h b/engines/zvision/scripting/scripting_effect.h index 5bb14f0cdd..2a2153204f 100644 --- a/engines/zvision/scripting/sidefx.h +++ b/engines/zvision/scripting/scripting_effect.h @@ -20,8 +20,8 @@ * */ -#ifndef SIDEFX_H_INCLUDED -#define SIDEFX_H_INCLUDED +#ifndef SCRIPTING_EFFECT_H_INCLUDED +#define SCRIPTING_EFFECT_H_INCLUDED namespace Common { class SeekableReadStream; @@ -33,29 +33,39 @@ namespace ZVision { class ZVision; -class SideFX { +/** + * The base class that represents effects created from Actions. + * This class is virtual. + * + * Detailed Description: + * A scene has Controls. By interacting with the controls, the user + * causes Actions to execute. Certain Actions create 'effects', for + * example, a sound or an animation. This is the base class for + * those effects. + */ +class ScriptingEffect { public: - enum SideFXType { - SIDEFX_ANIM = 1, - SIDEFX_AUDIO = 2, - SIDEFX_DISTORT = 4, - SIDEFX_PANTRACK = 8, - SIDEFX_REGION = 16, - SIDEFX_TIMER = 32, - SIDEFX_TTYTXT = 64, - SIDEFX_UNK = 128, - SIDEFX_ALL = 255 + enum ScriptingEffectType { + SCRIPTING_EFFECT_ANIM = 1, + SCRIPTING_EFFECT_AUDIO = 2, + SCRIPTING_EFFECT_DISTORT = 4, + SCRIPTING_EFFECT_PANTRACK = 8, + SCRIPTING_EFFECT_REGION = 16, + SCRIPTING_EFFECT_TIMER = 32, + SCRIPTING_EFFECT_TTYTXT = 64, + SCRIPTING_EFFECT_UNKNOWN = 128, + SCRIPTING_EFFECT_ALL = 255 }; - SideFX() : _engine(0), _key(0), _type(SIDEFX_UNK) {} - SideFX(ZVision *engine, uint32 key, SideFXType type) : _engine(engine), _key(key), _type(type) {} - virtual ~SideFX() {} + ScriptingEffect() : _engine(0), _key(0), _type(SCRIPTING_EFFECT_UNKNOWN) {} + ScriptingEffect(ZVision *engine, uint32 key, ScriptingEffectType type) : _engine(engine), _key(key), _type(type) {} + virtual ~ScriptingEffect() {} uint32 getKey() { return _key; } - SideFXType getType() { + ScriptingEffectType getType() { return _type; } @@ -103,7 +113,7 @@ public: protected: ZVision *_engine; uint32 _key; - SideFXType _type; + ScriptingEffectType _type; // Static member functions public: @@ -111,4 +121,4 @@ public: }; } // End of namespace ZVision -#endif // SIDEFX_H_INCLUDED +#endif // SCRIPTING_EFFECT_H_INCLUDED diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp index 8688039325..124235e0e0 100644 --- a/engines/zvision/sound/zork_raw.cpp +++ b/engines/zvision/sound/zork_raw.cpp @@ -33,7 +33,6 @@ #include "zvision/sound/zork_raw.h" #include "zvision/zvision.h" -#include "zvision/detection.h" namespace ZVision { @@ -73,10 +72,7 @@ RawChunkStream::RawChunk RawChunkStream::readNextChunk(Common::SeekableReadStrea tmp.size = 0; tmp.data = NULL; - if (!stream) - return tmp; - - if (stream && (stream->size() == 0 || stream->eos())) + if (!stream || stream->size() == 0 || stream->eos()) return tmp; tmp.size = (stream->size() - stream->pos()) * 2; @@ -139,7 +135,8 @@ int RawChunkStream::readBuffer(int16 *buffer, Common::SeekableReadStream *stream return bytesRead; } -const SoundParams RawZorkStream::_zNemSoundParamLookupTable[32] = {{'0', 0x1F40, false, false, false}, +const SoundParams RawZorkStream::_zNemSoundParamLookupTable[32] = { + {'0', 0x1F40, false, false, false}, {'1', 0x1F40, true, false, false}, {'2', 0x1F40, false, false, true}, {'3', 0x1F40, true, false, true}, @@ -173,7 +170,8 @@ const SoundParams RawZorkStream::_zNemSoundParamLookupTable[32] = {{'0', 0x1F40, {'x', 0xAC44, true, true, true} }; -const SoundParams RawZorkStream::_zgiSoundParamLookupTable[24] = {{'4', 0x2B11, false, false, false}, +const SoundParams RawZorkStream::_zgiSoundParamLookupTable[24] = { + {'4', 0x2B11, false, false, false}, {'5', 0x2B11, true, false, false}, {'6', 0x2B11, false, false, true}, {'7', 0x2B11, true, false, true}, @@ -216,7 +214,6 @@ RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag dis } int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { - int32 bytesRead = _streamReader.readBuffer(buffer, _stream.get(), numSamples); if (_stream->eos()) @@ -244,19 +241,29 @@ Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stre return new RawZorkStream(rate, stereo, disposeAfterUse, stream); } -Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse) { - return makeRawZorkStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, stereo, DisposeAfterUse::YES); -} - Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { Common::File *file = new Common::File(); - assert(engine->getSearchManager()->openFile(*file, filePath)); + Common::String actualName = filePath; + bool found = engine->getSearchManager()->openFile(*file, actualName); + bool isRaw = actualName.hasSuffix(".raw"); + + if ((!found && isRaw) || (found && isRaw && file->size() < 10)) { + if (found) + file->close(); + + // Check for an audio patch (.src) + actualName.setChar('s', actualName.size() - 3); + actualName.setChar('r', actualName.size() - 2); + actualName.setChar('c', actualName.size() - 1); + + if (!engine->getSearchManager()->openFile(*file, actualName)) + return NULL; + } else if (!found && !isRaw) { + return NULL; + } // Get the file name - Common::StringTokenizer tokenizer(filePath, "/\\"); + Common::StringTokenizer tokenizer(actualName, "/\\"); Common::String fileName; while (!tokenizer.empty()) { fileName = tokenizer.nextToken(); diff --git a/engines/zvision/sound/zork_raw.h b/engines/zvision/sound/zork_raw.h index 0b408d818c..892bad4d5f 100644 --- a/engines/zvision/sound/zork_raw.h +++ b/engines/zvision/sound/zork_raw.h @@ -123,20 +123,6 @@ public: }; /** - * Creates an audio stream, which plays from the given buffer. - * - * @param buffer Buffer to play from. - * @param size Size of the buffer in bytes. - * @param rate Rate of the sound data. - * @param dispose AfterUse Whether to free the buffer after use (with free!). - * @return The new SeekableAudioStream (or 0 on failure). - */ -Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); - -/** * Creates an audio stream, which plays from the given stream. * * @param stream Stream object to play from. diff --git a/engines/zvision/text/string_manager.cpp b/engines/zvision/text/string_manager.cpp index ec10b6220c..c62e18f4b0 100644 --- a/engines/zvision/text/string_manager.cpp +++ b/engines/zvision/text/string_manager.cpp @@ -43,21 +43,17 @@ StringManager::~StringManager() { } void StringManager::initialize(ZVisionGameId gameId) { - if (gameId == GID_NEMESIS) { - // TODO: Check this hardcoded filename against all versions of Nemesis + if (gameId == GID_NEMESIS) loadStrFile("nemesis.str"); - } else if (gameId == GID_GRANDINQUISITOR) { - // TODO: Check this hardcoded filename against all versions of Grand Inquisitor + else if (gameId == GID_GRANDINQUISITOR) loadStrFile("inquis.str"); - } } void StringManager::loadStrFile(const Common::String &fileName) { Common::File file; - if (!_engine->getSearchManager()->openFile(file, fileName)) { - warning("%s does not exist. String parsing failed", fileName.c_str()); - return; - } + if (!_engine->getSearchManager()->openFile(file, fileName)) + error("%s does not exist. String parsing failed", fileName.c_str()); + uint lineNumber = 0; while (!file.eos()) { _lines[lineNumber] = readWideLine(file); diff --git a/engines/zvision/text/string_manager.h b/engines/zvision/text/string_manager.h index b77ad65040..2c31cf7afe 100644 --- a/engines/zvision/text/string_manager.h +++ b/engines/zvision/text/string_manager.h @@ -23,8 +23,7 @@ #ifndef ZVISION_STRING_MANAGER_H #define ZVISION_STRING_MANAGER_H -#include "zvision/detection.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" namespace Graphics { class FontManager; diff --git a/engines/zvision/graphics/subtitles.cpp b/engines/zvision/text/subtitles.cpp index d2c56f0991..ffc9e2b808 100644 --- a/engines/zvision/graphics/subtitles.cpp +++ b/engines/zvision/text/subtitles.cpp @@ -21,13 +21,13 @@ */ #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/text/subtitles.h" #include "zvision/file/search_manager.h" #include "zvision/text/text.h" namespace ZVision { -Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : +Subtitle::Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires) : _engine(engine), _areaId(-1), _subId(-1) { @@ -44,6 +44,8 @@ Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : int32 x1, y1, x2, y2; sscanf(str.c_str(), "%*[^:]:%d %d %d %d", &x1, &y1, &x2, &y2); Common::Rect rct = Common::Rect(x1, y1, x2, y2); + if (upscaleToHires) + _engine->getRenderManager()->upscaleRect(rct); _areaId = _engine->getRenderManager()->createSubArea(rct); } else if (str.matchString("*TextFile*", true)) { char filename[64]; @@ -67,6 +69,11 @@ Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : int32 sb; if (sscanf(str.c_str(), "%*[^:]:(%d,%d)=%d", &st, &en, &sb) == 3) { if (sb <= (int32)_subs.size()) { + if (upscaleToHires) { + // Convert from 15FPS (AVI) to 29.97FPS (VOB) + st = st * 2997 / 1500; + en = en * 2997 / 1500; + } _subs[sb].start = st; _subs[sb].stop = en; } diff --git a/engines/zvision/graphics/subtitles.h b/engines/zvision/text/subtitles.h index c3da6583a4..329339be55 100644 --- a/engines/zvision/graphics/subtitles.h +++ b/engines/zvision/text/subtitles.h @@ -31,7 +31,7 @@ class ZVision; class Subtitle { public: - Subtitle(ZVision *engine, const Common::String &subname); + Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires = false); ~Subtitle(); void process(int32 time); diff --git a/engines/zvision/text/text.cpp b/engines/zvision/text/text.cpp index 406c36e5b0..868ee4f1ae 100644 --- a/engines/zvision/text/text.cpp +++ b/engines/zvision/text/text.cpp @@ -33,33 +33,37 @@ #include "zvision/text/text.h" #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "zvision/scripting/script_manager.h" namespace ZVision { -cTxtStyle::cTxtStyle() { - fontname = "Arial"; - blue = 255; - green = 255; - red = 255; - bold = false; - escapement = 0; - italic = false; - justify = TXT_JUSTIFY_LEFT; - newline = false; - size = 12; - skipcolor = false; - strikeout = false; - underline = false; - statebox = 0; - sharp = false; +TextStyleState::TextStyleState() { + _fontname = "Arial"; + _blue = 255; + _green = 255; + _red = 255; + _bold = false; +#if 0 + _newline = false; + _escapement = 0; +#endif + _italic = false; + _justification = TEXT_JUSTIFY_LEFT; + _size = 12; +#if 0 + _skipcolor = false; +#endif + _strikeout = false; + _underline = false; + _statebox = 0; + _sharp = false; } -txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { - Common::String buf = Common::String(strin.c_str(), ln); +TextChange TextStyleState::parseStyle(const Common::String &str, int16 len) { + Common::String buf = Common::String(str.c_str(), len); - int8 retval = TXT_RET_NOTHING; + uint retval = TEXT_CHANGE_NONE; Common::StringTokenizer tokenizer(buf, " "); Common::String token; @@ -80,73 +84,77 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (_tmp.lastChar() == '"') _tmp.deleteLastChar(); - fontname = _tmp; + _fontname = _tmp; } else { if (!tokenizer.empty()) - fontname = token; + _fontname = token; } - retval |= TXT_RET_FNTCHG; + retval |= TEXT_CHANGE_FONT_TYPE; } else if (token.matchString("blue", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); int32 tmp = atoi(token.c_str()); - if (blue != tmp) { - blue = tmp; - retval |= TXT_RET_FNTSTL; + if (_blue != tmp) { + _blue = tmp; + retval |= TEXT_CHANGE_FONT_STYLE; } } } else if (token.matchString("red", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); int32 tmp = atoi(token.c_str()); - if (red != tmp) { - red = tmp; - retval |= TXT_RET_FNTSTL; + if (_red != tmp) { + _red = tmp; + retval |= TEXT_CHANGE_FONT_STYLE; } } } else if (token.matchString("green", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); int32 tmp = atoi(token.c_str()); - if (green != tmp) { - green = tmp; - retval |= TXT_RET_FNTSTL; + if (_green != tmp) { + _green = tmp; + retval |= TEXT_CHANGE_FONT_STYLE; } } } else if (token.matchString("newline", true)) { +#if 0 if ((retval & TXT_RET_NEWLN) == 0) - newline = 0; + _newline = 0; - newline++; - retval |= TXT_RET_NEWLN; + _newline++; +#endif + retval |= TEXT_CHANGE_NEWLINE; } else if (token.matchString("point", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); int32 tmp = atoi(token.c_str()); - if (size != tmp) { - size = tmp; - retval |= TXT_RET_FNTCHG; + if (_size != tmp) { + _size = tmp; + retval |= TEXT_CHANGE_FONT_TYPE; } } } else if (token.matchString("escapement", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); +#if 0 int32 tmp = atoi(token.c_str()); - escapement = tmp; + _escapement = tmp; +#endif } } else if (token.matchString("italic", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); if (token.matchString("on", true)) { - if (italic != true) { - italic = true; - retval |= TXT_RET_FNTSTL; + if (_italic != true) { + _italic = true; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { - if (italic != false) { - italic = false; - retval |= TXT_RET_FNTSTL; + if (_italic != false) { + _italic = false; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -154,14 +162,14 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); if (token.matchString("on", true)) { - if (underline != true) { - underline = true; - retval |= TXT_RET_FNTSTL; + if (_underline != true) { + _underline = true; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { - if (underline != false) { - underline = false; - retval |= TXT_RET_FNTSTL; + if (_underline != false) { + _underline = false; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -169,14 +177,14 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); if (token.matchString("on", true)) { - if (strikeout != true) { - strikeout = true; - retval |= TXT_RET_FNTSTL; + if (_strikeout != true) { + _strikeout = true; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { - if (strikeout != false) { - strikeout = false; - retval |= TXT_RET_FNTSTL; + if (_strikeout != false) { + _strikeout = false; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -184,50 +192,52 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); if (token.matchString("on", true)) { - if (bold != true) { - bold = true; - retval |= TXT_RET_FNTSTL; + if (_bold != true) { + _bold = true; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { - if (bold != false) { - bold = false; - retval |= TXT_RET_FNTSTL; + if (_bold != false) { + _bold = false; + retval |= TEXT_CHANGE_FONT_STYLE; } } } } else if (token.matchString("skipcolor", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); +#if 0 if (token.matchString("on", true)) { - skipcolor = true; + _skipcolor = true; } else if (token.matchString("off", true)) { - skipcolor = false; + _skipcolor = false; } +#endif } } else if (token.matchString("image", true)) { // Not used } else if (token.matchString("statebox", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); - statebox = atoi(token.c_str()); - retval |= TXT_RET_HASSTBOX; + _statebox = atoi(token.c_str()); + retval |= TEXT_CHANGE_HAS_STATE_BOX; } } else if (token.matchString("justify", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); if (token.matchString("center", true)) - justify = TXT_JUSTIFY_CENTER; + _justification = TEXT_JUSTIFY_CENTER; else if (token.matchString("left", true)) - justify = TXT_JUSTIFY_LEFT; + _justification = TEXT_JUSTIFY_LEFT; else if (token.matchString("right", true)) - justify = TXT_JUSTIFY_RIGHT; + _justification = TEXT_JUSTIFY_RIGHT; } } } - return (txtReturn)retval; + return (TextChange)retval; } -void cTxtStyle::readAllStyle(const Common::String &txt) { +void TextStyleState::readAllStyles(const Common::String &txt) { int16 startTextPosition = -1; int16 endTextPosition = -1; @@ -236,251 +246,273 @@ void cTxtStyle::readAllStyle(const Common::String &txt) { startTextPosition = i; else if (txt[i] == '>') { endTextPosition = i; - if (startTextPosition != -1) - if ((endTextPosition - startTextPosition - 1) > 0) + if (startTextPosition != -1) { + if ((endTextPosition - startTextPosition - 1) > 0) { parseStyle(Common::String(txt.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + } + } } } } -void cTxtStyle::setFontStyle(StyledTTFont &font) { +void TextStyleState::updateFontWithTextState(StyledTTFont &font) { uint tempStyle = 0; - if (bold) - tempStyle |= StyledTTFont::STTF_BOLD; - - if (italic) - tempStyle |= StyledTTFont::STTF_ITALIC; - - if (underline) - tempStyle |= StyledTTFont::STTF_UNDERLINE; - - if (strikeout) - tempStyle |= StyledTTFont::STTF_STRIKEOUT; - - if (sharp) - tempStyle |= StyledTTFont::STTF_SHARP; + if (_bold) { + tempStyle |= StyledTTFont::TTF_STYLE_BOLD; + } + if (_italic) { + tempStyle |= StyledTTFont::TTF_STYLE_ITALIC; + } + if (_underline) { + tempStyle |= StyledTTFont::TTF_STYLE_UNDERLINE; + } + if (_strikeout) { + tempStyle |= StyledTTFont::TTF_STYLE_STRIKETHROUGH; + } + if (_sharp) { + tempStyle |= StyledTTFont::TTF_STYLE_SHARP; + } - font.setStyle(tempStyle); + font.loadFont(_fontname, _size, tempStyle); } -void cTxtStyle::setFont(StyledTTFont &font) { - uint tempStyle = 0; - - if (bold) - tempStyle |= StyledTTFont::STTF_BOLD; +void TextRenderer::drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification justify) { + if (justify == TEXT_JUSTIFY_LEFT) + font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignLeft); + else if (justify == TEXT_JUSTIFY_CENTER) + font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignCenter); + else if (justify == TEXT_JUSTIFY_RIGHT) + font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignRight); +} - if (italic) - tempStyle |= StyledTTFont::STTF_ITALIC; +int32 TextRenderer::drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest) { + StyledTTFont font(_engine); + state.updateFontWithTextState(font); - if (underline) - tempStyle |= StyledTTFont::STTF_UNDERLINE; + uint32 color = _engine->_resourcePixelFormat.RGBToColor(state._red, state._green, state._blue); + drawTextWithJustification(text, font, color, dest, 0, state._justification); - if (strikeout) - tempStyle |= StyledTTFont::STTF_STRIKEOUT; + return font.getStringWidth(text); +} - if (sharp) - tempStyle |= StyledTTFont::STTF_SHARP; +struct TextSurface { + TextSurface(Graphics::Surface *surface, Common::Point surfaceOffset, uint lineNumber) + : _surface(surface), + _surfaceOffset(surfaceOffset), + _lineNumber(lineNumber) { + } - font.loadFont(fontname, size, tempStyle); -} + Graphics::Surface *_surface; + Common::Point _surfaceOffset; + uint _lineNumber; +}; -Graphics::Surface *TextRenderer::render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style) { - style.setFontStyle(fnt); - uint32 clr = _engine->_pixelFormat.RGBToColor(style.red, style.green, style.blue); - return fnt.renderSolidText(txt, clr); -} +void TextRenderer::drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest) { + Common::Array<TextSurface> textSurfaces; + Common::Array<uint> lineWidths; + Common::Array<TextJustification> lineJustifications; -void TextRenderer::drawTxtWithJustify(const Common::String &txt, StyledTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify) { - if (justify == TXT_JUSTIFY_LEFT) - fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignLeft); - else if (justify == TXT_JUSTIFY_CENTER) - fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignCenter); - else if (justify == TXT_JUSTIFY_RIGHT) - fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignRight); -} + // Create the initial text state + TextStyleState currentState; -int32 TextRenderer::drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Graphics::Surface &dst) { + // Create an empty font and bind it to the state StyledTTFont font(_engine); - fontStyle.setFont(font); + currentState.updateFontWithTextState(font); - dst.fillRect(Common::Rect(dst.w, dst.h), 0); + Common::String currentSentence; // Not a true 'grammatical' sentence. Rather, it's just a collection of words + Common::String currentWord; + int sentenceWidth = 0; + int wordWidth = 0; + int lineWidth = 0; + int lineHeight = font.getFontHeight(); - uint32 clr = _engine->_pixelFormat.RGBToColor(fontStyle.red, fontStyle.green, fontStyle.blue); + uint currentLineNumber = 0u; - int16 w; + uint numSpaces = 0u; + int spaceWidth = 0; - w = font.getStringWidth(txt); + // The pixel offset to the currentSentence + Common::Point sentencePixelOffset; - drawTxtWithJustify(txt, font, clr, dst, 0, fontStyle.justify); + uint i = 0u; + uint stringlen = text.size(); - return w; -} + while (i < stringlen) { + if (text[i] == '<') { + // Flush the currentWord to the currentSentence + currentSentence += currentWord; + sentenceWidth += wordWidth; + + // Reset the word variables + currentWord.clear(); + wordWidth = 0; + + // Parse the style tag + uint startTextPosition = i; + while (i < stringlen && text[i] != '>') { + ++i; + } + uint endTextPosition = i; -void TextRenderer::drawTxtInOneLine(const Common::String &text, Graphics::Surface &dst) { - const int16 TXT_CFG_TEXTURES_LINES = 256; // For now I don't want remake it - const int TXT_CFG_TEXTURES_PER_LINE = 6; - cTxtStyle style, style2; - int16 startTextPosition = -1; - int16 endTextPosition = -1; - int16 i = 0; - int16 dx = 0, dy = 0; - int16 textPixelWidth; - int16 textPosition = 0; - Common::String buf; - Common::String buf2; - - Graphics::Surface *TxtSurfaces[TXT_CFG_TEXTURES_LINES][TXT_CFG_TEXTURES_PER_LINE]; - int16 currentline = 0, currentlineitm = 0; - - int TxtJustify[TXT_CFG_TEXTURES_LINES]; - int TxtPoint[TXT_CFG_TEXTURES_LINES]; - - for (int16 k = 0; k < TXT_CFG_TEXTURES_LINES; k++) { - TxtPoint[k] = 0; - for (int j = 0; j < TXT_CFG_TEXTURES_PER_LINE; j++) - TxtSurfaces[k][j] = NULL; - } + uint32 textColor = currentState.getTextColor(_engine); - int16 stringlen = text.size(); + uint stateChanges = 0u; + if ((endTextPosition - startTextPosition - 1) > 0) { + stateChanges = currentState.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + } - StyledTTFont font(_engine); + if (stateChanges & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) { + // Use the last state to render out the current sentence + // Styles apply to the text 'after' them + if (!currentSentence.empty()) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); - style.setFont(font); + lineWidth += sentenceWidth; + sentencePixelOffset.x += sentenceWidth; - int16 prevbufspace = 0, prevtxtspace = 0; + // Reset the sentence variables + currentSentence.clear(); + sentenceWidth = 0; + } - while (i < stringlen) { - TxtJustify[currentline] = style.justify; - if (text[i] == '<') { - int16 ret = 0; + // Update the current state with the style information + currentState.updateFontWithTextState(font); - startTextPosition = i; - while (i < stringlen && text[i] != '>') - i++; - endTextPosition = i; - if (startTextPosition != -1) - if ((endTextPosition - startTextPosition - 1) > 0) { - style2 = style; - ret = style.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + lineHeight = MAX(lineHeight, font.getFontHeight()); + spaceWidth = font.getCharWidth(' '); + } + if (stateChanges & TEXT_CHANGE_NEWLINE) { + // If the current sentence has content, render it out + if (!currentSentence.empty()) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); } + + // Set line width + lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); + + currentSentence.clear(); + sentenceWidth = 0; + + // Update the offsets + sentencePixelOffset.x = 0u; + sentencePixelOffset.y += lineHeight; + + // Reset the line variables + lineHeight = font.getFontHeight(); + lineWidth = 0; + ++currentLineNumber; + lineJustifications.push_back(currentState._justification); + } + if (stateChanges & TEXT_CHANGE_HAS_STATE_BOX) { + Common::String temp = Common::String::format("%d", _engine->getScriptManager()->getStateValue(currentState._statebox)); + wordWidth += font.getStringWidth(temp); - if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { - if (buf.size() > 0) { - textPixelWidth = font.getStringWidth(buf); - - TxtSurfaces[currentline][currentlineitm] = render(font, buf, style2); - TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + // If the word causes the line to overflow, render the sentence and start a new line + if (lineWidth + sentenceWidth + wordWidth > dest.w) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); - currentlineitm++; + // Set line width + lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); - buf.clear(); - prevbufspace = 0; - textPosition = 0; - dx += textPixelWidth; + currentSentence.clear(); + sentenceWidth = 0; - } - if (ret & TXT_RET_FNTCHG) { - style.setFont(font); - } - if (ret & TXT_RET_FNTSTL) - style.setFontStyle(font); + // Update the offsets + sentencePixelOffset.x = 0u; + sentencePixelOffset.y += lineHeight; - if (ret & TXT_RET_NEWLN) { - currentline++; - currentlineitm = 0; - dx = 0; + // Reset the line variables + lineHeight = font.getFontHeight(); + lineWidth = 0; + ++currentLineNumber; + lineJustifications.push_back(currentState._justification); } } - - if (ret & TXT_RET_HASSTBOX) { - Common::String buf3; - buf3 = Common::String::format("%d", _engine->getScriptManager()->getStateValue(style.statebox)); - buf += buf3; - textPosition += buf3.size(); - } - } else { - - buf += text[i]; - textPosition++; + currentWord += text[i]; + wordWidth += font.getCharWidth(text[i]); if (text[i] == ' ') { - prevbufspace = textPosition - 1; - prevtxtspace = i; - } + // When we hit the first space, flush the current word to the sentence + if (!currentWord.empty()) { + currentSentence += currentWord; + sentenceWidth += wordWidth; - if (font.isLoaded()) { - textPixelWidth = font.getStringWidth(buf); - if (textPixelWidth + dx > dst.w) { - if (prevbufspace == 0) { - prevtxtspace = i; - prevbufspace = textPosition - 1; - } - buf2 = Common::String(buf.c_str(), prevbufspace + 1); + currentWord.clear(); + wordWidth = 0; + } - if (buf2.size() > 0) { - TxtSurfaces[currentline][currentlineitm] = render(font, buf2, style); - TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + // We track the number of spaces so we can disregard their width in lineWidth calculations + ++numSpaces; + } else { + // If the word causes the line to overflow, render the sentence and start a new line + if (lineWidth + sentenceWidth + wordWidth > dest.w) { + // Only render out content + if (!currentSentence.empty()) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber)); } - buf.clear(); - i = prevtxtspace; - prevbufspace = 0; - textPosition = 0; - currentline++; - currentlineitm = 0; - dx = 0; + // Set line width + lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); + + currentSentence.clear(); + sentenceWidth = 0; + + // Update the offsets + sentencePixelOffset.x = 0u; + sentencePixelOffset.y += lineHeight; + + // Reset the line variables + lineHeight = font.getFontHeight(); + lineWidth = 0; + ++currentLineNumber; + lineJustifications.push_back(currentState._justification); } + + numSpaces = 0u; } } + i++; } - if (buf.size() > 0) { - TxtSurfaces[currentline][currentlineitm] = render(font, buf, style); - TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + // Render out any remaining words/sentences + if (!currentWord.empty() || !currentSentence.empty()) { + currentSentence += currentWord; + sentenceWidth += wordWidth; + + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber)); } - dy = 0; - for (i = 0; i <= currentline; i++) { - int16 j = 0; - int16 width = 0; - while (TxtSurfaces[i][j] != NULL) { - width += TxtSurfaces[i][j]->w; - j++; - } - dx = 0; - for (int32 jj = 0; jj < j; jj++) { - if (TxtJustify[i] == TXT_JUSTIFY_LEFT) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); - - else if (TxtJustify[i] == TXT_JUSTIFY_CENTER) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, ((dst.w - width) / 2) + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + lineWidths.push_back(lineWidth + sentenceWidth); + lineJustifications.push_back(currentState._justification); - else if (TxtJustify[i] == TXT_JUSTIFY_RIGHT) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, dst.w - width + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + for (Common::Array<TextSurface>::iterator iter = textSurfaces.begin(); iter != textSurfaces.end(); ++iter) { + Common::Rect empty; - dx += TxtSurfaces[i][jj]->w; + if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_LEFT) { + _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); + } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_CENTER) { + _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, ((dest.w - lineWidths[iter->_lineNumber]) / 2) + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); + } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_RIGHT) { + _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, dest.w - lineWidths[iter->_lineNumber] + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); } - dy += TxtPoint[i]; + // Release memory + iter->_surface->free(); + delete iter->_surface; } - - for (i = 0; i < TXT_CFG_TEXTURES_LINES; i++) - for (int32 j = 0; j < TXT_CFG_TEXTURES_PER_LINE; j++) - if (TxtSurfaces[i][j] != NULL) { - TxtSurfaces[i][j]->free(); - delete TxtSurfaces[i][j]; - } } Common::String readWideLine(Common::SeekableReadStream &stream) { Common::String asciiString; - while (!stream.eos()) { + while (true) { uint32 value = stream.readUint16LE(); + if (stream.eos()) + break; // Check for CRLF if (value == 0x0A0D) { // Read in the extra NULL char @@ -495,10 +527,12 @@ Common::String readWideLine(Common::SeekableReadStream &stream) { } else if (value >= 0x80 && value < 0x800) { asciiString += (char)(0xC0 | ((value >> 6) & 0x1F)); asciiString += (char)(0x80 | (value & 0x3F)); - } else if (value >= 0x800 && value < 0x10000) { + } else if (value >= 0x800 && value < 0x10000 && value != 0xCCCC) { asciiString += (char)(0xE0 | ((value >> 12) & 0xF)); asciiString += (char)(0x80 | ((value >> 6) & 0x3F)); asciiString += (char)(0x80 | (value & 0x3F)); + } else if (value == 0xCCCC) { + // Ignore, this character is used as newline sometimes } else if (value >= 0x10000 && value < 0x200000) { asciiString += (char)(0xF0); asciiString += (char)(0x80 | ((value >> 12) & 0x3F)); diff --git a/engines/zvision/text/text.h b/engines/zvision/text/text.h index ecec3ccde6..5dd872a440 100644 --- a/engines/zvision/text/text.h +++ b/engines/zvision/text/text.h @@ -24,63 +24,60 @@ #ifndef ZVISION_TEXT_H #define ZVISION_TEXT_H -#include "zvision/detection.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "zvision/zvision.h" namespace ZVision { class ZVision; -enum txtJustify { - TXT_JUSTIFY_CENTER = 0, - TXT_JUSTIFY_LEFT = 1, - TXT_JUSTIFY_RIGHT = 2 +enum TextJustification { + TEXT_JUSTIFY_CENTER = 0, + TEXT_JUSTIFY_LEFT = 1, + TEXT_JUSTIFY_RIGHT = 2 }; -enum txtReturn { - TXT_RET_NOTHING = 0x0, - TXT_RET_FNTCHG = 0x1, - TXT_RET_FNTSTL = 0x2, - TXT_RET_NEWLN = 0x4, - TXT_RET_HASSTBOX = 0x8 +enum TextChange { + TEXT_CHANGE_NONE = 0x0, + TEXT_CHANGE_FONT_TYPE = 0x1, + TEXT_CHANGE_FONT_STYLE = 0x2, + TEXT_CHANGE_NEWLINE = 0x4, + TEXT_CHANGE_HAS_STATE_BOX = 0x8 }; -class cTxtStyle { +class TextStyleState { public: - cTxtStyle(); - txtReturn parseStyle(const Common::String &strin, int16 len); - void readAllStyle(const Common::String &txt); - void setFontStyle(StyledTTFont &font); - void setFont(StyledTTFont &font); + TextStyleState(); + TextChange parseStyle(const Common::String &str, int16 len); + void readAllStyles(const Common::String &txt); + void updateFontWithTextState(StyledTTFont &font); + + uint32 getTextColor(ZVision *engine) { + return engine->_resourcePixelFormat.RGBToColor(_red, _green, _blue); + } public: - Common::String fontname; - txtJustify justify; // 0 - center, 1-left, 2-right - int16 size; - uint8 red; // 0-255 - uint8 green; // 0-255 - uint8 blue; // 0-255 - int8 newline; - int8 escapement; - bool italic; - bool bold; - bool underline; - bool strikeout; - bool skipcolor; - int32 statebox; - bool sharp; - // char image ?? + Common::String _fontname; + TextJustification _justification; + int16 _size; + uint8 _red; // 0-255 + uint8 _green; // 0-255 + uint8 _blue; // 0-255 + bool _italic; + bool _bold; + bool _underline; + bool _strikeout; + int32 _statebox; + bool _sharp; }; class TextRenderer { public: TextRenderer(ZVision *engine): _engine(engine) {}; - void drawTxtWithJustify(const Common::String &txt, StyledTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify); - int32 drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Graphics::Surface &dst); - Graphics::Surface *render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style); - void drawTxtInOneLine(const Common::String &txt, Graphics::Surface &dst); + void drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification jusification); + int32 drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest); + void drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest); private: ZVision *_engine; diff --git a/engines/zvision/text/truetype_font.cpp b/engines/zvision/text/truetype_font.cpp new file mode 100644 index 0000000000..acb053ea8d --- /dev/null +++ b/engines/zvision/text/truetype_font.cpp @@ -0,0 +1,265 @@ +/* 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/scummsys.h" +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/file.h" +#include "common/system.h" +#include "common/unzip.h" +#include "common/ustr.h" +#include "graphics/font.h" +#include "graphics/fonts/ttf.h" +#include "graphics/surface.h" + +#include "zvision/zvision.h" +#include "zvision/graphics/render_manager.h" +#include "zvision/text/truetype_font.h" + +namespace ZVision { + +const FontStyle systemFonts[] = { + { "*times new roman*", "times", "FreeSerif", "Italic", "LiberationSerif" }, + { "*times*", "times", "FreeSerif", "Italic", "LiberationSerif" }, + { "*century schoolbook*", "censcbk", "FreeSerif", "Italic", "LiberationSerif" }, + { "*garamond*", "gara", "FreeSerif", "Italic", "LiberationSerif" }, + { "*courier new*", "cour", "FreeMono", "Oblique", "LiberationMono" }, + { "*courier*", "cour", "FreeMono", "Oblique", "LiberationMono" }, + { "*ZorkDeath*", "cour", "FreeMono", "Oblique", "LiberationMono" }, + { "*arial*", "arial", "FreeSans", "Oblique", "LiberationSans" }, + { "*ZorkNormal*", "arial", "FreeSans", "Oblique", "LiberationSans" } +}; + +const FontStyle getSystemFont(int fontIndex) { + return systemFonts[fontIndex]; +} + +StyledTTFont::StyledTTFont(ZVision *engine) { + _engine = engine; + _style = 0; + _font = nullptr; + _lineHeight = 0; +} + +StyledTTFont::~StyledTTFont() { + delete _font; +} + +bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint style) { + // Don't re-load the font if we've already loaded it + // We have to check for empty so we can default to Arial + if (!fontName.empty() && _fontName.equalsIgnoreCase(fontName) && _lineHeight == point && _style == style) { + return true; + } + + _style = style; + + Common::String newFontName; + Common::String freeFontName; + Common::String liberationFontName; + + for (int i = 0; i < FONT_COUNT; i++) { + FontStyle curFont = getSystemFont(i); + if (fontName.matchString(curFont.zorkFont, true)) { + newFontName = curFont.fontBase; + freeFontName = curFont.freeFontBase; + liberationFontName = curFont.liberationFontBase; + + if ((_style & TTF_STYLE_BOLD) && (_style & TTF_STYLE_ITALIC)) { + newFontName += "bi"; + freeFontName += "Bold"; + freeFontName += curFont.freeFontItalicName; + liberationFontName += "-BoldItalic"; + } else if (_style & TTF_STYLE_BOLD) { + newFontName += "bd"; + freeFontName += "Bold"; + liberationFontName += "-Bold"; + } else if (_style & TTF_STYLE_ITALIC) { + newFontName += "i"; + freeFontName += curFont.freeFontItalicName; + liberationFontName += "-Italic"; + } else { + liberationFontName += "-Regular"; + } + + newFontName += ".ttf"; + freeFontName += ".ttf"; + liberationFontName += ".ttf"; + break; + } + } + + if (newFontName.empty()) { + debug("Could not identify font: %s. Reverting to Arial", fontName.c_str()); + newFontName = "arial.ttf"; + freeFontName = "FreeSans.ttf"; + liberationFontName = "LiberationSans-Regular.ttf"; + } + + bool sharp = (_style & TTF_STYLE_SHARP) == TTF_STYLE_SHARP; + + Common::File file; + if (!file.open(newFontName) && !_engine->getSearchManager()->openFile(file, newFontName) && + !file.open(liberationFontName) && !_engine->getSearchManager()->openFile(file, liberationFontName) && + !file.open(freeFontName) && !_engine->getSearchManager()->openFile(file, freeFontName)) + error("Unable to open font file %s (Liberation Font alternative: %s, FreeFont alternative: %s)", newFontName.c_str(), liberationFontName.c_str(), freeFontName.c_str()); + + Graphics::Font *newFont = Graphics::loadTTFFont(file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display + if (newFont == nullptr) { + return false; + } + + delete _font; + _font = newFont; + + _fontName = fontName; + _lineHeight = point; + + return true; +} + +int StyledTTFont::getFontHeight() { + if (_font) + return _font->getFontHeight(); + + return 0; +} + +int StyledTTFont::getMaxCharWidth() { + if (_font) + return _font->getMaxCharWidth(); + + return 0; +} + +int StyledTTFont::getCharWidth(byte chr) { + if (_font) + return _font->getCharWidth(chr); + + return 0; +} + +int StyledTTFont::getKerningOffset(byte left, byte right) { + if (_font) + return _font->getKerningOffset(left, right); + + return 0; +} + +Common::U32String StyledTTFont::convertUtf8ToUtf32(const Common::String &str) { + // The String class, and therefore the Font class as well, assume one + // character is one byte, but in this case it's actually an UTF-8 + // string with up to 4 bytes per character. To work around this, + // convert it to an U32String before drawing it, because our Font class + // can handle that. + Common::U32String u32str; + uint i = 0; + while (i < str.size()) { + uint32 chr = 0; + if ((str[i] & 0xF8) == 0xF0) { + chr |= (str[i++] & 0x07) << 18; + chr |= (str[i++] & 0x3F) << 12; + chr |= (str[i++] & 0x3F) << 6; + chr |= (str[i++] & 0x3F); + } else if ((str[i] & 0xF0) == 0xE0) { + chr |= (str[i++] & 0x0F) << 12; + chr |= (str[i++] & 0x3F) << 6; + chr |= (str[i++] & 0x3F); + } else if ((str[i] & 0xE0) == 0xC0) { + chr |= (str[i++] & 0x1F) << 6; + chr |= (str[i++] & 0x3F); + } else { + chr = (str[i++] & 0x7F); + } + u32str += chr; + } + return u32str; +} + +void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) { + if (_font) { + _font->drawChar(dst, chr, x, y, color); + if (_style & TTF_STYLE_UNDERLINE) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.87); + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); + } + if (_style & TTF_STYLE_STRIKETHROUGH) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.60); + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); + } + } +} + +void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align) { + if (_font) { + Common::U32String u32str = convertUtf8ToUtf32(str); + _font->drawString(dst, u32str, x, y, w, color, align); + if (_style & TTF_STYLE_UNDERLINE) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.87); + int16 wd = MIN(_font->getStringWidth(u32str), w); + int16 stX = x; + if (align == Graphics::kTextAlignCenter) + stX += (w - wd) / 2; + else if (align == Graphics::kTextAlignRight) + stX += (w - wd); + + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + + dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); + } + if (_style & TTF_STYLE_STRIKETHROUGH) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.60); + int16 wd = MIN(_font->getStringWidth(u32str), w); + int16 stX = x; + if (align == Graphics::kTextAlignCenter) + stX += (w - wd) / 2; + else if (align == Graphics::kTextAlignRight) + stX += (w - wd); + + int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); + + dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); + } + } +} + +int StyledTTFont::getStringWidth(const Common::String &str) { + if (_font) + return _font->getStringWidth(str); + return 0; +} + +Graphics::Surface *StyledTTFont::renderSolidText(const Common::String &str, uint32 color) { + Graphics::Surface *tmp = new Graphics::Surface; + if (_font) { + int16 w = _font->getStringWidth(str); + if (w && w < 1024) { + tmp->create(w, _font->getFontHeight(), _engine->_resourcePixelFormat); + drawString(tmp, str, 0, 0, w, color); + } + } + return tmp; +} + +} // End of namespace ZVision diff --git a/engines/zvision/graphics/truetype_font.h b/engines/zvision/text/truetype_font.h index b5fac4af8a..6abe05cda6 100644 --- a/engines/zvision/graphics/truetype_font.h +++ b/engines/zvision/text/truetype_font.h @@ -34,6 +34,16 @@ struct Surface; namespace ZVision { +struct FontStyle { + const char *zorkFont; + const char *fontBase; + const char *freeFontBase; + const char *freeFontItalicName; + const char *liberationFontBase; +}; + +#define FONT_COUNT 9 + class ZVision; // Styled TTF @@ -43,11 +53,11 @@ public: ~StyledTTFont(); enum { - STTF_BOLD = 1, - STTF_ITALIC = 2, - STTF_UNDERLINE = 4, - STTF_STRIKEOUT = 8, - STTF_SHARP = 16 + TTF_STYLE_BOLD = 0x01, + TTF_STYLE_ITALIC = 0x02, + TTF_STYLE_UNDERLINE = 0x04, + TTF_STYLE_STRIKETHROUGH = 0x08, + TTF_STYLE_SHARP = 0x10 }; private: @@ -55,18 +65,18 @@ private: Graphics::Font *_font; int _lineHeight; uint _style; - Common::String _fntName; + Common::String _fontName; public: - bool loadFont(const Common::String &fontName, int32 point); bool loadFont(const Common::String &fontName, int32 point, uint style); - void setStyle(uint newStyle); int getFontHeight(); int getMaxCharWidth(); int getCharWidth(byte chr); int getKerningOffset(byte left, byte right); + Common::U32String convertUtf8ToUtf32(const Common::String &str); + void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color); void drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft); diff --git a/engines/zvision/video/rlf_decoder.cpp b/engines/zvision/video/rlf_decoder.cpp index bdb5dc18bc..3bbf22edff 100644 --- a/engines/zvision/video/rlf_decoder.cpp +++ b/engines/zvision/video/rlf_decoder.cpp @@ -30,8 +30,6 @@ #include "common/debug.h" #include "common/endian.h" -#include "graphics/colormasks.h" - namespace ZVision { RLFDecoder::~RLFDecoder() { @@ -41,9 +39,13 @@ RLFDecoder::~RLFDecoder() { bool RLFDecoder::loadStream(Common::SeekableReadStream *stream) { close(); - addTrack(new RLFVideoTrack(stream)); - - return true; + // Check if the stream is valid + if (stream && !stream->err() && stream->readUint32BE() == MKTAG('F', 'E', 'L', 'R')) { + addTrack(new RLFVideoTrack(stream)); + return true; + } else { + return false; + } } RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) @@ -54,7 +56,7 @@ RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) _height(0), _frameTime(0), _frames(0), - _curFrame(0), + _displayedFrame(-1), _frameBufferByteSize(0) { if (!readHeader()) { @@ -62,7 +64,7 @@ RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) return; } - _currentFrameBuffer.create(_width, _height, Graphics::createPixelFormat<565>()); + _currentFrameBuffer.create(_width, _height, getPixelFormat()); _frameBufferByteSize = _width * _height * sizeof(uint16); _frames = new Frame[_frameCount]; @@ -83,10 +85,6 @@ RLFDecoder::RLFVideoTrack::~RLFVideoTrack() { } bool RLFDecoder::RLFVideoTrack::readHeader() { - if (_readStream->readUint32BE() != MKTAG('F', 'E', 'L', 'R')) { - return false; - } - // Read the header _readStream->readUint32LE(); // Size1 _readStream->readUint32LE(); // Unknown1 @@ -161,22 +159,17 @@ RLFDecoder::RLFVideoTrack::Frame RLFDecoder::RLFVideoTrack::readNextFrame() { bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) { uint frame = getFrameAtTime(time); - assert(frame < (int)_frameCount); + assert(frame < _frameCount); - if ((uint)_curFrame == frame) + if ((uint)_displayedFrame == frame) return true; - if (frame < 0) { - _curFrame = 0; - return false; - } - - int closestFrame = _curFrame; - int distance = (int)frame - _curFrame; + int closestFrame = _displayedFrame; + int distance = (int)frame - closestFrame; if (distance < 0) { for (uint i = 0; i < _completeFrames.size(); ++i) { - if ((int)_completeFrames[i] > frame) + if (_completeFrames[i] > frame) break; closestFrame = _completeFrames[i]; } @@ -196,19 +189,18 @@ bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) { applyFrameToCurrent(i); } - _curFrame = frame; + _displayedFrame = frame - 1; return true; } const Graphics::Surface *RLFDecoder::RLFVideoTrack::decodeNextFrame() { - // When an animation ends, rewind - if (_curFrame == (int)_frameCount) - seek(Audio::Timestamp(0, getFrameRate().toInt())); - - applyFrameToCurrent(_curFrame); + if (_displayedFrame >= (int)_frameCount) + return NULL; + + _displayedFrame++; + applyFrameToCurrent(_displayedFrame); - _curFrame++; return &_currentFrameBuffer; } @@ -242,10 +234,7 @@ void RLFDecoder::RLFVideoTrack::decodeMaskedRunLengthEncoding(int8 *source, int8 return; } - byte r, g, b; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 destColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); - WRITE_UINT16(dest + destOffset, destColor); + WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset)); sourceOffset += 2; destOffset += 2; @@ -289,10 +278,7 @@ void RLFDecoder::RLFVideoTrack::decodeSimpleRunLengthEncoding(int8 *source, int8 return; } - byte r, g, b; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 destColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); - WRITE_UINT16(dest + destOffset, destColor); + WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset)); sourceOffset += 2; destOffset += 2; @@ -306,9 +292,7 @@ void RLFDecoder::RLFVideoTrack::decodeSimpleRunLengthEncoding(int8 *source, int8 return; } - byte r, g, b; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 sampleColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); + uint16 sampleColor = READ_LE_UINT16(source + sourceOffset); sourceOffset += 2; numberOfCopy = numberOfSamples + 2; diff --git a/engines/zvision/video/rlf_decoder.h b/engines/zvision/video/rlf_decoder.h index f0f31c2128..8b8cbaecd5 100644 --- a/engines/zvision/video/rlf_decoder.h +++ b/engines/zvision/video/rlf_decoder.h @@ -45,15 +45,15 @@ private: uint16 getWidth() const { return _width; } uint16 getHeight() const { return _height; } - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); /*RGB 565*/ } - int getCurFrame() const { return _curFrame; } + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); /* RGB 555 */ } + int getCurFrame() const { return _displayedFrame; } int getFrameCount() const { return _frameCount; } const Graphics::Surface *decodeNextFrame(); bool isSeekable() const { return true; } bool seek(const Audio::Timestamp &time); protected: - Common::Rational getFrameRate() const { return Common::Rational(60, _frameTime); } + Common::Rational getFrameRate() const { return Common::Rational(1000, _frameTime); } private: enum EncodingType { @@ -121,7 +121,7 @@ private: Frame *_frames; Common::Array<uint> _completeFrames; - int _curFrame; + int _displayedFrame; Graphics::Surface _currentFrameBuffer; uint32 _frameBufferByteSize; diff --git a/engines/zvision/video/video.cpp b/engines/zvision/video/video.cpp index 189fb22194..1cfd0f4197 100644 --- a/engines/zvision/video/video.cpp +++ b/engines/zvision/video/video.cpp @@ -23,13 +23,17 @@ #include "common/scummsys.h" #include "common/system.h" #include "video/video_decoder.h" +#ifdef USE_MPEG2 +#include "video/mpegps_decoder.h" +#endif #include "engines/util.h" #include "graphics/surface.h" #include "zvision/zvision.h" #include "zvision/core/clock.h" #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/script_manager.h" +#include "zvision/text/subtitles.h" #include "zvision/video/rlf_decoder.h" #include "zvision/video/zork_avi_decoder.h" @@ -44,12 +48,21 @@ Video::VideoDecoder *ZVision::loadAnimation(const Common::String &fileName) { animation = new RLFDecoder(); else if (tmpFileName.hasSuffix(".avi")) animation = new ZorkAVIDecoder(); +#ifdef USE_MPEG2 + else if (tmpFileName.hasSuffix(".vob")) + animation = new Video::MPEGPSDecoder(); +#endif else error("Unknown suffix for animation %s", fileName.c_str()); Common::File *_file = getSearchManager()->openFile(tmpFileName); - animation->loadStream(_file); - + if (!_file) + error("Error opening %s", tmpFileName.c_str()); + + bool loaded = animation->loadStream(_file); + if (!loaded) + error("Error loading animation %s", tmpFileName.c_str()); + return animation; } @@ -70,6 +83,7 @@ void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, uint16 y = _workingWindow.top + dst.top; uint16 finalWidth = dst.width() < _workingWindow.width() ? dst.width() : _workingWindow.width(); uint16 finalHeight = dst.height() < _workingWindow.height() ? dst.height() : _workingWindow.height(); + bool showSubs = (_scriptManager->getStateValue(StateKey_Subtitles) == 1); _clock.stop(); vid.start(); @@ -101,7 +115,7 @@ void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, if (vid.needsUpdate()) { const Graphics::Surface *frame = vid.decodeNextFrame(); - if (sub) + if (sub && showSubs) sub->process(vid.getCurFrame()); if (frame) { @@ -109,7 +123,8 @@ void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, _renderManager->scaleBuffer(frame->getPixels(), scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, scaled->w, scaled->h); frame = scaled; } - _system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, x, y, finalWidth, finalHeight); + Common::Rect rect = Common::Rect(x, y, x + finalWidth, y + finalHeight); + _renderManager->copyToScreen(*frame, rect, 0, 0); _renderManager->processSubs(0); } } diff --git a/engines/zvision/video/zork_avi_decoder.cpp b/engines/zvision/video/zork_avi_decoder.cpp index 67fab0a114..cf8505ec82 100644 --- a/engines/zvision/video/zork_avi_decoder.cpp +++ b/engines/zvision/video/zork_avi_decoder.cpp @@ -39,17 +39,34 @@ Video::AVIDecoder::AVIAudioTrack *ZorkAVIDecoder::createAudioTrack(Video::AVIDec } void ZorkAVIDecoder::ZorkAVIAudioTrack::queueSound(Common::SeekableReadStream *stream) { + bool updateCurChunk = true; if (_audStream) { if (_wvInfo.tag == kWaveFormatZorkPCM) { assert(_wvInfo.size == 8); RawChunkStream::RawChunk chunk = decoder->readNextChunk(stream); delete stream; - if (chunk.data) - _audStream->queueBuffer((byte *)chunk.data, chunk.size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO); + if (chunk.data) { + byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO; +#ifdef SCUMM_LITTLE_ENDIAN + // RawChunkStream produces native endianness int16 + flags |= Audio::FLAG_LITTLE_ENDIAN; +#endif + _audStream->queueBuffer((byte *)chunk.data, chunk.size, DisposeAfterUse::YES, flags); + } } else { + updateCurChunk = false; AVIAudioTrack::queueSound(stream); } + } else { + delete stream; + } + + // The superclass always updates _curChunk, whether or not audio has + // been queued, so we should do that too. Unless the superclass already + // has done it for us. + if (updateCurChunk) { + _curChunk++; } } diff --git a/engines/zvision/zvision.cpp b/engines/zvision/zvision.cpp index 4e5307c182..779fdc4464 100644 --- a/engines/zvision/zvision.cpp +++ b/engines/zvision/zvision.cpp @@ -27,13 +27,12 @@ #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/text/string_manager.h" -#include "zvision/detection.h" -#include "zvision/core/menu.h" +#include "zvision/scripting/menu.h" #include "zvision/file/search_manager.h" #include "zvision/text/text.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "zvision/sound/midi.h" #include "zvision/file/zfs_archive.h" @@ -46,42 +45,42 @@ #include "common/system.h" #include "common/file.h" +#include "gui/message.h" #include "engines/util.h" - #include "audio/mixer.h" namespace ZVision { -#define ZVISION_SETTINGS_KEYS_COUNT 17 +#define ZVISION_SETTINGS_KEYS_COUNT 12 struct zvisionIniSettings { const char *name; int16 slot; - int16 deflt; + int16 defaultValue; // -1: use the bool value + bool defaultBoolValue; + bool allowEditing; } settingsKeys[ZVISION_SETTINGS_KEYS_COUNT] = { - {"ZVision_KeyboardTurnSpeed", StateKey_KbdRotateSpeed, 5}, - {"ZVision_PanaRotateSpeed", StateKey_RotateSpeed, 540}, - {"ZVision_QSoundEnabled", StateKey_Qsound, 1}, - {"ZVision_VenusEnabled", StateKey_VenusEnable, 1}, - {"ZVision_HighQuality", StateKey_HighQuality, 1}, - {"ZVision_Platform", StateKey_Platform, 0}, - {"ZVision_InstallLevel", StateKey_InstallLevel, 0}, - {"ZVision_CountryCode", StateKey_CountryCode, 0}, - {"ZVision_CPU", StateKey_CPU, 1}, - {"ZVision_MovieCursor", StateKey_MovieCursor, 1}, - {"ZVision_NoAnimWhileTurning", StateKey_NoTurnAnim, 0}, - {"ZVision_Win958", StateKey_WIN958, 0}, - {"ZVision_ShowErrorDialogs", StateKey_ShowErrorDlg, 0}, - {"ZVision_ShowSubtitles", StateKey_Subtitles, 1}, - {"ZVision_DebugCheats", StateKey_DebugCheats, 0}, - {"ZVision_JapaneseFonts", StateKey_JapanFonts, 0}, - {"ZVision_Brightness", StateKey_Brightness, 0} + // Hardcoded settings + {"countrycode", StateKey_CountryCode, 0, false, false}, // always 0 = US, subtitles are shown for codes 0 - 4, unused + {"lineskipvideo", StateKey_VideoLineSkip, 0, false, false}, // video line skip, 0 = default, 1 = always, 2 = pixel double when possible, unused + {"installlevel", StateKey_InstallLevel, 0, false, false}, // 0 = full, checked by universe.scr + {"highquality", StateKey_HighQuality, -1, true, false}, // high panorama quality, unused + {"qsoundenabled", StateKey_Qsound, -1, true, false}, // 1 = enable QSound - TODO: not supported yet + {"debugcheats", StateKey_DebugCheats, -1, true, false}, // always start with the GOxxxx cheat enabled + // Editable settings + {"keyboardturnspeed", StateKey_KbdRotateSpeed, 5, false, true}, + {"panarotatespeed", StateKey_RotateSpeed, 540, false, true}, // checked by universe.scr + {"noanimwhileturning", StateKey_NoTurnAnim, -1, false, true}, // toggle playing animations during pana rotation + {"venusenabled", StateKey_VenusEnable, -1, true, true}, + {"subtitles", StateKey_Subtitles, -1, true, true}, + {"mpegmovies", StateKey_MPEGMovies, -1, true, true} // Zork: Grand Inquisitor DVD hi-res MPEG movies (0 = normal, 1 = hires, 2 = disable option) }; ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), - _pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /*RGB 565*/ + _resourcePixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), /* RGB 555 */ + _screenPixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /* RGB 565 */ _desiredFrameTime(33), /* ~30 fps */ _clock(_system), _scriptManager(nullptr), @@ -95,25 +94,18 @@ ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) _menu(nullptr), _searchManager(nullptr), _textRenderer(nullptr), - _halveDelay(false), + _doubleFPS(false), _audioId(0), - _rendDelay(2), - _kbdVelocity(0), + _frameRenderDelay(2), + _keyboardVelocity(0), _mouseVelocity(0), - _videoIsPlaying(false) { + _videoIsPlaying(false), + _renderedFrameCount(0), + _fps(0) { debug(1, "ZVision::ZVision"); - uint16 workingWindowWidth = (gameDesc->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_WIDTH : ZGI_WORKING_WINDOW_WIDTH; - uint16 workingWindowHeight = (gameDesc->gameId == GID_NEMESIS) ? ZNM_WORKING_WINDOW_HEIGHT : ZGI_WORKING_WINDOW_HEIGHT; - _workingWindow = Common::Rect( - (WINDOW_WIDTH - workingWindowWidth) / 2, - (WINDOW_HEIGHT - workingWindowHeight) / 2, - ((WINDOW_WIDTH - workingWindowWidth) / 2) + workingWindowWidth, - ((WINDOW_HEIGHT - workingWindowHeight) / 2) + workingWindowHeight - ); - - memset(_cheatBuff, 0, sizeof(_cheatBuff)); + memset(_cheatBuffer, 0, sizeof(_cheatBuffer)); } ZVision::~ZVision() { @@ -124,24 +116,45 @@ ZVision::~ZVision() { delete _cursorManager; delete _stringManager; delete _saveManager; - delete _renderManager; delete _scriptManager; + delete _renderManager; // should be deleted after the script manager delete _rnd; delete _midiManager; + getTimerManager()->removeTimerProc(&fpsTimerCallback); + // Remove all of our debug levels DebugMan.clearAllDebugChannels(); } void ZVision::registerDefaultSettings() { - for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) - ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].deflt); + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) { + if (settingsKeys[i].allowEditing) { + if (settingsKeys[i].defaultValue >= 0) + ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].defaultValue); + else + ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].defaultBoolValue); + } + } + + ConfMan.registerDefault("originalsaveload", false); ConfMan.registerDefault("doublefps", false); } void ZVision::loadSettings() { - for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) - _scriptManager->setStateValue(settingsKeys[i].slot, ConfMan.getInt(settingsKeys[i].name)); + int16 value = 0; + bool boolValue = false; + + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) { + if (settingsKeys[i].defaultValue >= 0) { + value = (settingsKeys[i].allowEditing) ? ConfMan.getInt(settingsKeys[i].name) : settingsKeys[i].defaultValue; + } else { + boolValue = (settingsKeys[i].allowEditing) ? ConfMan.getBool(settingsKeys[i].name) : settingsKeys[i].defaultBoolValue; + value = (boolValue) ? 1 : 0; + } + + _scriptManager->setStateValue(settingsKeys[i].slot, value); + } if (getGameId() == GID_NEMESIS) _scriptManager->setStateValue(StateKey_ExecScopeStyle, 1); @@ -150,8 +163,15 @@ void ZVision::loadSettings() { } void ZVision::saveSettings() { - for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) - ConfMan.setInt(settingsKeys[i].name, _scriptManager->getStateValue(settingsKeys[i].slot)); + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) { + if (settingsKeys[i].allowEditing) { + if (settingsKeys[i].defaultValue >= 0) + ConfMan.setInt(settingsKeys[i].name, _scriptManager->getStateValue(settingsKeys[i].slot)); + else + ConfMan.setBool(settingsKeys[i].name, (_scriptManager->getStateValue(settingsKeys[i].slot) == 1)); + } + } + ConfMan.flushToDisk(); } @@ -163,40 +183,32 @@ void ZVision::initialize() { _searchManager->addDir("FONTS"); _searchManager->addDir("addon"); - if (_gameDescription->gameId == GID_GRANDINQUISITOR) { - _searchManager->loadZix("INQUIS.ZIX"); - _searchManager->addPatch("C000H01Q.RAW", "C000H01Q.SRC"); - _searchManager->addPatch("CM00H01Q.RAW", "CM00H01Q.SRC"); - _searchManager->addPatch("DM00H01Q.RAW", "DM00H01Q.SRC"); - _searchManager->addPatch("E000H01Q.RAW", "E000H01Q.SRC"); - _searchManager->addPatch("EM00H50Q.RAW", "EM00H50Q.SRC"); - _searchManager->addPatch("GJNPH65P.RAW", "GJNPH65P.SRC"); - _searchManager->addPatch("GJNPH72P.RAW", "GJNPH72P.SRC"); - _searchManager->addPatch("H000H01Q.RAW", "H000H01Q.SRC"); - _searchManager->addPatch("M000H01Q.RAW", "M000H01Q.SRC"); - _searchManager->addPatch("P000H01Q.RAW", "P000H01Q.SRC"); - _searchManager->addPatch("Q000H01Q.RAW", "Q000H01Q.SRC"); - _searchManager->addPatch("SW00H01Q.RAW", "SW00H01Q.SRC"); - _searchManager->addPatch("T000H01Q.RAW", "T000H01Q.SRC"); - _searchManager->addPatch("U000H01Q.RAW", "U000H01Q.SRC"); - } else if (_gameDescription->gameId == GID_NEMESIS) - _searchManager->loadZix("NEMESIS.ZIX"); - - initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_pixelFormat); + if (getGameId() == GID_GRANDINQUISITOR) { + if (!_searchManager->loadZix("INQUIS.ZIX")) + error("Unable to load file INQUIS.ZIX"); + } else if (getGameId() == GID_NEMESIS) { + if (!_searchManager->loadZix("NEMESIS.ZIX")) { + // The game might not be installed, try MEDIUM.ZIX instead + if (!_searchManager->loadZix("ZNEMSCR/MEDIUM.ZIX")) + error("Unable to load the file ZNEMSCR/MEDIUM.ZIX"); + } + } + + initScreen(); // Register random source _rnd = new Common::RandomSource("zvision"); // Create managers _scriptManager = new ScriptManager(this); - _renderManager = new RenderManager(this, WINDOW_WIDTH, WINDOW_HEIGHT, _workingWindow, _pixelFormat); + _renderManager = new RenderManager(this, WINDOW_WIDTH, WINDOW_HEIGHT, _workingWindow, _resourcePixelFormat, _doubleFPS); _saveManager = new SaveManager(this); _stringManager = new StringManager(this); - _cursorManager = new CursorManager(this, &_pixelFormat); + _cursorManager = new CursorManager(this, _resourcePixelFormat); _textRenderer = new TextRenderer(this); _midiManager = new MidiManager(); - if (_gameDescription->gameId == GID_GRANDINQUISITOR) + if (getGameId() == GID_GRANDINQUISITOR) _menu = new MenuZGI(this); else _menu = new MenuNemesis(this); @@ -204,17 +216,27 @@ void ZVision::initialize() { // Initialize the managers _cursorManager->initialize(); _scriptManager->initialize(); - _stringManager->initialize(_gameDescription->gameId); + _stringManager->initialize(getGameId()); registerDefaultSettings(); loadSettings(); +#ifndef USE_MPEG2 + // libmpeg2 not loaded, disable the MPEG2 movies option + _scriptManager->setStateValue(StateKey_MPEGMovies, 2); +#endif + // Create debugger console. It requires GFX to be initialized _console = new Console(this); - _halveDelay = ConfMan.getBool("doublefps"); + _doubleFPS = ConfMan.getBool("doublefps"); + + // Initialize FPS timer callback + getTimerManager()->installTimerProc(&fpsTimerCallback, 1000000, this, "zvisionFPS"); } +extern const FontStyle getSystemFont(int fontIndex); + Common::Error ZVision::run() { initialize(); @@ -222,6 +244,68 @@ Common::Error ZVision::run() { if (ConfMan.hasKey("save_slot")) _saveManager->loadGame(ConfMan.getInt("save_slot")); + bool foundAllFonts = true; + + // Before starting, make absolutely sure that the user has copied the needed fonts + for (int i = 0; i < FONT_COUNT; i++) { + FontStyle curFont = getSystemFont(i); + Common::String freeFontBoldItalic = Common::String("Bold") + curFont.freeFontItalicName; + + const char *fontSuffixes[4] = { "", "bd", "i", "bi" }; + const char *freeFontSuffixes[4] = { "", "Bold", curFont.freeFontItalicName, freeFontBoldItalic.c_str() }; + const char *liberationFontSuffixes[4] = { "-Regular", "-Bold", "-Italic", "-BoldItalic" }; + + for (int j = 0; j < 4; j++) { + Common::String fontName = curFont.fontBase; + if (fontName == "censcbk" && j > 0) + fontName = "schlbk"; + fontName += fontSuffixes[j]; + fontName += ".ttf"; + + if (fontName == "schlbkbd.ttf") + fontName = "schlbkb.ttf"; + if (fontName == "garabi.ttf") + continue; + if (fontName == "garai.ttf") + fontName = "garait.ttf"; + + Common::String freeFontName = curFont.freeFontBase; + freeFontName += freeFontSuffixes[j]; + freeFontName += ".ttf"; + + Common::String liberationFontName = curFont.liberationFontBase; + liberationFontName += liberationFontSuffixes[j]; + liberationFontName += ".ttf"; + + if (!Common::File::exists(fontName) && !_searchManager->hasFile(fontName) && + !Common::File::exists(liberationFontName) && !_searchManager->hasFile(liberationFontName) && + !Common::File::exists(freeFontName) && !_searchManager->hasFile(freeFontName)) { + foundAllFonts = false; + break; + } + } + + if (!foundAllFonts) + break; + } + + if (!foundAllFonts) { + GUI::MessageDialog dialog( + "Before playing this game, you'll need to copy the required " + "fonts into ScummVM's extras directory, or into the game directory. " + "On Windows, you'll need the following font files from the Windows " + "font directory: Times New Roman, Century Schoolbook, Garamond, " + "Courier New and Arial. Alternatively, you can download the " + "Liberation Fonts or the GNU FreeFont package. You'll need all the " + "fonts from the font package you choose, i.e., LiberationMono, " + "LiberationSans and LiberationSerif, or FreeMono, FreeSans and " + "FreeSerif respectively." + ); + dialog.runModal(); + quitGame(); + return Common::kUnknownError; + } + // Main loop while (!shouldQuit()) { _clock.update(); @@ -231,106 +315,42 @@ Common::Error ZVision::run() { _cursorManager->setItemID(_scriptManager->getStateValue(StateKey_InventoryItem)); processEvents(); - updateRotation(); + _renderManager->updateRotation(); - // Call _renderManager->update() first so the background renders - // before anything that puzzles/controls will render _scriptManager->update(deltaTime); _menu->process(deltaTime); // Render the backBuffer to the screen - _renderManager->prepareBkg(); + _renderManager->prepareBackground(); _renderManager->renderMenuToScreen(); _renderManager->processSubs(deltaTime); - _renderManager->renderBackbufferToScreen(); + _renderManager->renderSceneToScreen(); // Update the screen - if (_rendDelay <= 0) + if (canRender()) { _system->updateScreen(); - else - _rendDelay--; + _renderedFrameCount++; + } else { + _frameRenderDelay--; + } // Calculate the frame delay based off a desired frame time int delay = _desiredFrameTime - int32(_system->getMillis() - currentTime); // Ensure non-negative delay = delay < 0 ? 0 : delay; - if (_halveDelay) - delay >>= 1; - _system->delayMillis(delay); - } - return Common::kNoError; -} - -bool ZVision::askQuestion(const Common::String &str) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, str); - _renderManager->processSubs(0); - _renderManager->renderBackbufferToScreen(); - _clock.stop(); - - int result = 0; - - while (result == 0) { - Common::Event evnt; - while (_eventMan->pollEvent(evnt)) { - if (evnt.type == Common::EVENT_KEYDOWN) { - switch (evnt.kbd.keycode) { - case Common::KEYCODE_y: - result = 2; - break; - case Common::KEYCODE_n: - result = 1; - break; - default: - break; - } - } + if (_doubleFPS) { + delay >>= 1; } - _system->updateScreen(); - if (_halveDelay) - _system->delayMillis(33); - else - _system->delayMillis(66); - } - _renderManager->deleteSubArea(msgid); - _clock.start(); - return result == 2; -} -void ZVision::delayedMessage(const Common::String &str, uint16 milsecs) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, str); - _renderManager->processSubs(0); - _renderManager->renderBackbufferToScreen(); - _clock.stop(); - - uint32 stopTime = _system->getMillis() + milsecs; - while (_system->getMillis() < stopTime) { - Common::Event evnt; - while (_eventMan->pollEvent(evnt)) { - if (evnt.type == Common::EVENT_KEYDOWN && - (evnt.kbd.keycode == Common::KEYCODE_SPACE || - evnt.kbd.keycode == Common::KEYCODE_RETURN || - evnt.kbd.keycode == Common::KEYCODE_ESCAPE)) - break; + if (canSaveGameStateCurrently() && shouldPerformAutoSave(_saveManager->getLastSaveTime())) { + _saveManager->autoSave(); } - _system->updateScreen(); - if (_halveDelay) - _system->delayMillis(33); - else - _system->delayMillis(66); + + _system->delayMillis(delay); } - _renderManager->deleteSubArea(msgid); - _clock.start(); -} -void ZVision::timedMessage(const Common::String &str, uint16 milsecs) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, str); - _renderManager->processSubs(0); - _renderManager->renderBackbufferToScreen(); - _renderManager->deleteSubArea(msgid, milsecs); + return Common::kNoError; } void ZVision::pauseEngineIntern(bool pause) { @@ -347,200 +367,50 @@ Common::String ZVision::generateSaveFileName(uint slot) { return Common::String::format("%s.%03u", _targetName.c_str(), slot); } -Common::String ZVision::generateAutoSaveFileName() { - return Common::String::format("%s.auto", _targetName.c_str()); -} - void ZVision::setRenderDelay(uint delay) { - _rendDelay = delay; + _frameRenderDelay = delay; } bool ZVision::canRender() { - return _rendDelay <= 0; + return _frameRenderDelay <= 0; } -void ZVision::updateRotation() { - int16 _velocity = _mouseVelocity + _kbdVelocity; - - if (_halveDelay) - _velocity /= 2; - - if (_velocity) { - RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); - if (renderState == RenderTable::PANORAMA) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition + (_renderManager->getRenderTable()->getPanoramaReverse() ? -_velocity : _velocity); - - int16 zeroPoint = _renderManager->getRenderTable()->getPanoramaZeroPoint(); - if (startPosition >= zeroPoint && newPosition < zeroPoint) - _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) - 1); - if (startPosition <= zeroPoint && newPosition > zeroPoint) - _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) + 1); - - int16 screenWidth = _renderManager->getBkgSize().x; - if (screenWidth) - newPosition %= screenWidth; - - if (newPosition < 0) - newPosition += screenWidth; - - _renderManager->setBackgroundPosition(newPosition); - } else if (renderState == RenderTable::TILT) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition + _velocity; - - int16 screenHeight = _renderManager->getBkgSize().y; - int16 tiltGap = _renderManager->getRenderTable()->getTiltGap(); - - if (newPosition >= (screenHeight - tiltGap)) - newPosition = screenHeight - tiltGap; - if (newPosition <= tiltGap) - newPosition = tiltGap; - - _renderManager->setBackgroundPosition(newPosition); - } - } +GUI::Debugger *ZVision::getDebugger() { + return _console; } -void ZVision::checkBorders() { - RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); - if (renderState == RenderTable::PANORAMA) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition; - - int16 screenWidth = _renderManager->getBkgSize().x; - - if (screenWidth) - newPosition %= screenWidth; - - if (newPosition < 0) - newPosition += screenWidth; +void ZVision::syncSoundSettings() { + Engine::syncSoundSettings(); - if (startPosition != newPosition) - _renderManager->setBackgroundPosition(newPosition); - } else if (renderState == RenderTable::TILT) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition; - - int16 screenHeight = _renderManager->getBkgSize().y; - int16 tiltGap = _renderManager->getRenderTable()->getTiltGap(); - - if (newPosition >= (screenHeight - tiltGap)) - newPosition = screenHeight - tiltGap; - if (newPosition <= tiltGap) - newPosition = tiltGap; - - if (startPosition != newPosition) - _renderManager->setBackgroundPosition(newPosition); - } + _scriptManager->setStateValue(StateKey_Subtitles, ConfMan.getBool("subtitles") ? 1 : 0); } -void ZVision::rotateTo(int16 _toPos, int16 _time) { - if (_renderManager->getRenderTable()->getRenderState() != RenderTable::PANORAMA) - return; - - if (_time == 0) - _time = 1; - - int32 maxX = _renderManager->getBkgSize().x; - int32 curX = _renderManager->getCurrentBackgroundOffset(); - int32 dx = 0; - - if (curX == _toPos) - return; - - if (curX > _toPos) { - if (curX - _toPos > maxX / 2) - dx = (_toPos + (maxX - curX)) / _time; - else - dx = -(curX - _toPos) / _time; - } else { - if (_toPos - curX > maxX / 2) - dx = -((maxX - _toPos) + curX) / _time; - else - dx = (_toPos - curX) / _time; - } - - _clock.stop(); - - for (int16 i = 0; i <= _time; i++) { - if (i == _time) - curX = _toPos; - else - curX += dx; - - if (curX < 0) - curX = maxX - curX; - else if (curX >= maxX) - curX %= maxX; - - _renderManager->setBackgroundPosition(curX); - - _renderManager->prepareBkg(); - _renderManager->renderBackbufferToScreen(); - - _system->updateScreen(); - - _system->delayMillis(500 / _time); - } - - _clock.start(); -} - -void ZVision::menuBarEnable(uint16 menus) { - if (_menu) - _menu->setEnable(menus); -} - -uint16 ZVision::getMenuBarEnable() { - if (_menu) - return _menu->getEnable(); - return 0; -} - -bool ZVision::ifQuit() { - if (askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) { - quitGame(); - return true; - } - return false; +void ZVision::fpsTimerCallback(void *refCon) { + ((ZVision *)refCon)->fpsTimer(); } -void ZVision::pushKeyToCheatBuf(uint8 key) { - for (int i = 0; i < KEYBUF_SIZE - 1; i++) - _cheatBuff[i] = _cheatBuff[i + 1]; - - _cheatBuff[KEYBUF_SIZE - 1] = key; +void ZVision::fpsTimer() { + _fps = _renderedFrameCount; + _renderedFrameCount = 0; } -bool ZVision::checkCode(const char *code) { - int codeLen = strlen(code); - - if (codeLen > KEYBUF_SIZE) - return false; - - for (int i = 0; i < codeLen; i++) - if (code[i] != _cheatBuff[KEYBUF_SIZE - codeLen + i] && code[i] != '?') - return false; +void ZVision::initScreen() { + uint16 workingWindowWidth = (getGameId() == GID_NEMESIS) ? ZNM_WORKING_WINDOW_WIDTH : ZGI_WORKING_WINDOW_WIDTH; + uint16 workingWindowHeight = (getGameId() == GID_NEMESIS) ? ZNM_WORKING_WINDOW_HEIGHT : ZGI_WORKING_WINDOW_HEIGHT; + _workingWindow = Common::Rect( + (WINDOW_WIDTH - workingWindowWidth) / 2, + (WINDOW_HEIGHT - workingWindowHeight) / 2, + ((WINDOW_WIDTH - workingWindowWidth) / 2) + workingWindowWidth, + ((WINDOW_HEIGHT - workingWindowHeight) / 2) + workingWindowHeight + ); - return true; + initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_screenPixelFormat); } -uint8 ZVision::getBufferedKey(uint8 pos) { - if (pos >= KEYBUF_SIZE) - return 0; - else - return _cheatBuff[KEYBUF_SIZE - pos - 1]; -} +void ZVision::initHiresScreen() { + _renderManager->upscaleRect(_workingWindow); -void ZVision::showDebugMsg(const Common::String &msg, int16 delay) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, msg); - _renderManager->deleteSubArea(msgid, delay); + initGraphics(HIRES_WINDOW_WIDTH, HIRES_WINDOW_HEIGHT, true, &_screenPixelFormat); } } // End of namespace ZVision diff --git a/engines/zvision/zvision.h b/engines/zvision/zvision.h index 78c1c824a1..4c948faaa4 100644 --- a/engines/zvision/zvision.h +++ b/engines/zvision/zvision.h @@ -24,7 +24,6 @@ #ifndef ZVISION_ZVISION_H #define ZVISION_ZVISION_H -#include "zvision/detection.h" #include "zvision/core/clock.h" #include "zvision/file/search_manager.h" @@ -41,6 +40,16 @@ namespace Video { class VideoDecoder; } +/** + * This is the namespace of the ZVision engine. + * + * Status of this engine: complete + * + * Games using this engine: + * - Zork Nemesis: The Forbidden Lands + * - Zork: Grand Inquisitor + * + */ namespace ZVision { struct ZVisionGameDescription; @@ -56,6 +65,33 @@ class TextRenderer; class Subtitle; class MidiManager; +enum { + WINDOW_WIDTH = 640, + WINDOW_HEIGHT = 480, + + HIRES_WINDOW_WIDTH = 800, + HIRES_WINDOW_HEIGHT = 600, + + // Zork Nemesis working window sizes + ZNM_WORKING_WINDOW_WIDTH = 512, + ZNM_WORKING_WINDOW_HEIGHT = 320, + + // ZGI working window sizes + ZGI_WORKING_WINDOW_WIDTH = 640, + ZGI_WORKING_WINDOW_HEIGHT = 344, + + ROTATION_SCREEN_EDGE_OFFSET = 60, + MAX_ROTATION_SPEED = 400, // Pixels per second + + KEYBUF_SIZE = 20 +}; + +enum ZVisionGameId { + GID_NONE = 0, + GID_NEMESIS = 1, + GID_GRANDINQUISITOR = 2 +}; + class ZVision : public Engine { public: ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc); @@ -68,27 +104,10 @@ public: * edges of this Rectangle */ Common::Rect _workingWindow; - const Graphics::PixelFormat _pixelFormat; + const Graphics::PixelFormat _resourcePixelFormat; + const Graphics::PixelFormat _screenPixelFormat; private: - enum { - WINDOW_WIDTH = 640, - WINDOW_HEIGHT = 480, - - // Zork nemesis working window sizes - ZNM_WORKING_WINDOW_WIDTH = 512, - ZNM_WORKING_WINDOW_HEIGHT = 320, - - // ZGI working window sizes - ZGI_WORKING_WINDOW_WIDTH = 640, - ZGI_WORKING_WINDOW_HEIGHT = 344, - - ROTATION_SCREEN_EDGE_OFFSET = 60, - MAX_ROTATION_SPEED = 400, // Pixels per second - - KEYBUF_SIZE = 20 - }; - Console *_console; const ZVisionGameDescription *_gameDescription; @@ -101,12 +120,12 @@ private: ScriptManager *_scriptManager; RenderManager *_renderManager; CursorManager *_cursorManager; - SaveManager *_saveManager; StringManager *_stringManager; - MenuHandler *_menu; SearchManager *_searchManager; TextRenderer *_textRenderer; MidiManager *_midiManager; + SaveManager *_saveManager; + MenuHandler *_menu; // Clock Clock _clock; @@ -117,19 +136,24 @@ private: // To prevent allocation every time we process events Common::Event _event; - int _rendDelay; + int _frameRenderDelay; + int _renderedFrameCount; + int _fps; int16 _mouseVelocity; - int16 _kbdVelocity; - bool _halveDelay; + int16 _keyboardVelocity; + bool _doubleFPS; bool _videoIsPlaying; - uint8 _cheatBuff[KEYBUF_SIZE]; + uint8 _cheatBuffer[KEYBUF_SIZE]; + public: - uint32 getFeatures() const; - Common::Language getLanguage() const; Common::Error run(); void pauseEngineIntern(bool pause); + ZVisionGameId getGameId() const; + Common::Language getLanguage() const; + uint32 getFeatures() const; + ScriptManager *getScriptManager() const { return _scriptManager; } @@ -154,15 +178,33 @@ public: MidiManager *getMidiManager() const { return _midiManager; } + MenuHandler *getMenuHandler() const { + return _menu; + } + Common::RandomSource *getRandomSource() const { return _rnd; } - ZVisionGameId getGameId() const { - return _gameDescription->gameId; + int16 getKeyboardVelocity() const { + return _keyboardVelocity; + } + int16 getMouseVelocity() const { + return _mouseVelocity; } uint8 getZvisionKey(Common::KeyCode scummKeyCode); + void startClock() { + _clock.start(); + } + + void stopClock() { + _clock.stop(); + } + + void initScreen(); + void initHiresScreen(); + /** * Play a video until it is finished. This is a blocking call. It will call * _clock.stop() when the video starts and _clock.start() when the video finishes. @@ -175,35 +217,31 @@ public: void playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect = Common::Rect(0, 0, 0, 0), bool skippable = true, Subtitle *sub = NULL); Video::VideoDecoder *loadAnimation(const Common::String &fileName); - void rotateTo(int16 to, int16 time); - Common::String generateSaveFileName(uint slot); - Common::String generateAutoSaveFileName(); - - bool askQuestion(const Common::String &str); - void delayedMessage(const Common::String &str, uint16 milsecs); - void timedMessage(const Common::String &str, uint16 milsecs); void setRenderDelay(uint); bool canRender(); + static void fpsTimerCallback(void *refCon); + void fpsTimer(); + int getFPS() const { + return _fps; + } + + GUI::Debugger *getDebugger(); + void syncSoundSettings(); void loadSettings(); void saveSettings(); - void menuBarEnable(uint16 menus); - uint16 getMenuBarEnable(); - bool ifQuit(); - void checkBorders(); - void showDebugMsg(const Common::String &msg, int16 delay = 3000); - // Engine features bool hasFeature(EngineFeature f) const; bool canLoadGameStateCurrently(); bool canSaveGameStateCurrently(); Common::Error loadGameState(int slot); Common::Error saveGameState(int slot, const Common::String &desc); + private: void initialize(); void initFonts(); @@ -214,7 +252,6 @@ private: void processEvents(); void onMouseMove(const Common::Point &pos); - void updateRotation(); void registerDefaultSettings(); void shortKeys(Common::Event); |