/* 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 "audio/audiostream.h" #include "audio/decoders/wave.h" #include "common/config-manager.h" #include "common/file.h" #include "common/system.h" #include "graphics/managed_surface.h" #include "graphics/palette.h" #include "image/bmp.h" #include "image/image_decoder.h" #include "cryomni3d/mouse_boxes.h" #include "cryomni3d/font_manager.h" #include "cryomni3d/versailles/engine.h" namespace CryOmni3D { namespace Versailles { bool CryOmni3DEngine_Versailles::showSubtitles() const { return ConfMan.getBool("subtitles"); } void CryOmni3DEngine_Versailles::drawMenuTitle(Graphics::ManagedSurface *surface, byte color) { int offY; int oldFont = _fontManager.getCurrentFont(); int titleX, titleY, subtitleX, subtitleY; if (getLanguage() == Common::FR_FRA || getLanguage() == Common::ES_ESP || getLanguage() == Common::PT_BRA) { titleX = 144; titleY = 160; subtitleX = 305; subtitleY = 160; } else if (getLanguage() == Common::DE_DEU) { titleX = 122; titleY = 80; subtitleX = 283; subtitleY = 80; } else { titleX = 100; titleY = 80; subtitleX = 261; subtitleY = 80; } _fontManager.setSurface(surface); _fontManager.setForeColor(color); _fontManager.setCurrentFont(1); offY = _fontManager.getFontMaxHeight(); _fontManager.displayStr(titleX, titleY - offY, _messages[23]); _fontManager.setCurrentFont(3); offY = _fontManager.getFontMaxHeight(); _fontManager.displayStr(subtitleX, subtitleY - offY, _messages[24]); if (getLanguage() == Common::FR_FRA || getLanguage() == Common::ES_ESP || getLanguage() == Common::PT_BRA) { surface->vLine(100, 146, 172, color); surface->hLine(100, 172, 168, color); // minus 1 because hLine draws inclusive } _fontManager.setCurrentFont(oldFont); } uint CryOmni3DEngine_Versailles::displayOptions() { Common::Array menuEntries; menuEntries.push_back(26); menuEntries.push_back(27); menuEntries.push_back(28); menuEntries.push_back(29); menuEntries.push_back(48); menuEntries.push_back(30); menuEntries.push_back(32); #if 0 // Music on HDD setting menuEntries.push_back(34); #endif menuEntries.push_back(25); menuEntries.push_back(-42); menuEntries.push_back(43); menuEntries.push_back(40); // 1 is for volume box MouseBoxes boxes(menuEntries.size() + 1); bool end = false; int drawState = 1; uint volumeCursorMiddleY = _sprites.getCursor(102).getHeight() / 2; uint volume = CLIP(ConfMan.getInt("music_volume"), 0, 256); uint soundVolumeY = ((283 * (256 - volume)) >> 8) + 101; byte volumeForeColor = 243; Graphics::ManagedSurface optionsSurface; Image::ImageDecoder *imageDecoder = loadHLZ("option.hlz"); const Graphics::Surface *bgFrame = imageDecoder->getSurface(); optionsSurface.create(bgFrame->w, bgFrame->h, bgFrame->format); setCursor(181); showMouse(true); uint hoveredBox = uint(-1); uint selectedBox = uint(-1); int selectedMsg = 0; uint volumeBox = uint(-1); bool resetScreen = true; bool forceEvents = true; while (!shouldAbort() && !end) { if (resetScreen) { setPalette(imageDecoder->getPalette(), imageDecoder->getPaletteStartIndex(), imageDecoder->getPaletteColorCount()); // _cursorPalette has only 248 colors as 8 last colors are for translucency setPalette(_cursorPalette + 240 * 3, 240, 8); _fontManager.setCurrentFont(3); _fontManager.setTransparentBackground(true); _fontManager.setForeColor(243); _fontManager.setLineHeight(14); _fontManager.setSpaceWidth(0); _fontManager.setCharSpacing(1); _fontManager.setSurface(&optionsSurface); resetScreen = false; } if (drawState > 0) { if (drawState == 1) { optionsSurface.blitFrom(*bgFrame); } drawMenuTitle(&optionsSurface, 243); _fontManager.setForeColor(volumeForeColor); _fontManager.displayStr(550, 407, _messages[39]); optionsSurface.vLine(544, 402, 429, volumeForeColor); optionsSurface.hLine(544, 429, 613, volumeForeColor); // minus 1 because hLine draws inclusive boxes.reset(); uint boxId = 0; uint top = 195; uint bottom; uint width; for (Common::Array::iterator it = menuEntries.begin(); it != menuEntries.end(); it++) { if (*it == 30 && !ConfMan.getBool("subtitles")) { *it = 31; } else if (*it == 32 && (ConfMan.getBool("mute") || ConfMan.getBool("music_mute"))) { *it = 33; } #if 0 else if (*it == 34) { // What to do with music on HDD setting? } #endif else if (*it == 26 && !_isPlaying) { *it = -26; } else if (*it == 29 && !_isPlaying) { *it = -29; } else if (*it == -42 && canVisit()) { *it = 42; } else if (*it == 48) { uint omni3D_speed = ConfMan.getInt("omni3d_speed"); switch (omni3D_speed) { case 1: *it = 51; break; case 2: *it = 52; break; case 3: *it = 49; break; case 4: *it = 50; break; default: // If it is another value, menu text is 48 (normal speed) and there is nothing to do break; } } if (*it > 0) { int msgId = *it; bottom = top; top += 24; // Patch on the fly the text displayed if (_isVisiting) { if (msgId == 26) { msgId = 44; } else if (msgId == 29) { msgId = 45; } } width = _fontManager.getStrWidth(_messages[msgId]); //Common::Rect rct(144, top - 39, width + 144, bottom); //optionsSurface.frameRect(rct, 0); boxes.setupBox(boxId, 144, top - 39, width + 144, bottom); if (boxId == hoveredBox) { _fontManager.setForeColor(240); } else { _fontManager.setForeColor(243); } _fontManager.displayStr(144, top - 39, _messages[msgId]); } boxId++; } volumeBox = boxId; boxes.setupBox(boxId, 525, 101, 570, 401); optionsSurface.transBlitFrom(_sprites.getSurface(102), Common::Point(553, soundVolumeY), _sprites.getKeyColor(102)); g_system->copyRectToScreen(optionsSurface.getPixels(), optionsSurface.pitch, 0, 0, optionsSurface.w, optionsSurface.h); drawState = 0; } g_system->updateScreen(); g_system->delayMillis(10); if (pollEvents() || forceEvents) { // always call pollEvents forceEvents = false; Common::Point mouse = getMousePos(); uint boxId = 0; Common::Array::iterator it; for (it = menuEntries.begin(); it != menuEntries.end(); it++) { if (boxes.hitTest(boxId, mouse)) { if (hoveredBox != boxId) { hoveredBox = boxId; drawState = 2; } // We met a hit, no need to look further break; } boxId++; } if (it != menuEntries.end()) { if (getDragStatus() == 2) { selectedMsg = *it; selectedBox = hoveredBox; } } else { // no menu selected, check volume if (boxes.hitTest(volumeBox, mouse)) { if (volumeForeColor != 240) { volumeForeColor = 240; drawState = 1; } if (getCurrentMouseButton() == 1) { if (soundVolumeY != getMousePos().y - volumeCursorMiddleY) { soundVolumeY = CLIP(getMousePos().y - volumeCursorMiddleY, 101u, 384u); drawState = 1; volume = CLIP(((384 - soundVolumeY) << 8) / 283, 0u, 256u); // Global setting ConfMan.setInt("music_volume", volume); // As we modify speech volume, let's unmute it // Using in-game settings resets all scummvm specific settings ConfMan.setBool("speech_mute", false); ConfMan.setInt("speech_volume", volume); syncSoundSettings(); } } else if (getDragStatus() == 2 && !_mixer->hasActiveChannelOfType(Audio::Mixer::kMusicSoundType) && _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) > 0) { // Finished dragging _mixer->stopID(SoundIds::kOrgue); do { Common::File *audioFile = new Common::File(); if (!audioFile->open("ORGUE.WAV")) { warning("Failed to open sound file %s", "ORGUE.WAV"); delete audioFile; break; } Audio::SeekableAudioStream *audioDecoder = Audio::makeWAVStream(audioFile, DisposeAfterUse::YES); // We lost ownership of the audioFile just set it to nullptr and don't use it audioFile = nullptr; if (!audioDecoder) { break; } _mixer->playStream(Audio::Mixer::kMusicSoundType, nullptr, audioDecoder, SoundIds::kOrgue); // We lost ownership of the audioDecoder just set it to nullptr and don't use it audioDecoder = nullptr; } while (false); } } else { if (hoveredBox != uint(-1)) { hoveredBox = uint(-1); drawState = 2; } if (volumeForeColor != 243) { volumeForeColor = 243; drawState = 1; } } } if (getNextKey().keycode == Common::KEYCODE_ESCAPE && _isPlaying) { selectedMsg = 26; } if (selectedMsg == 27 || selectedMsg == 28 || selectedMsg == 40 || selectedMsg == 42) { // New game, Load game, Quit, Visit if (!_isPlaying || _isVisiting) { end = true; } else { Common::Rect rct; if (getLanguage() == Common::DE_DEU) { rct = Common::Rect(286, 433, 555, 475); } else if (getLanguage() == Common::ES_ESP || getLanguage() == Common::IT_ITA) { rct = Common::Rect(250, 420, 530, 465); } else { rct = Common::Rect(235, 420, 505, 465); } end = displayYesNoBox(optionsSurface, rct, 57); } drawState = 1; if (!end) { selectedMsg = 0; } } if (selectedMsg == 25) { // Documentation area _docManager.handleDocArea(); drawState = 1; resetScreen = true; forceEvents = true; waitMouseRelease(); selectedMsg = 0; } else if (selectedMsg == 26) { // Continue game end = true; } else if (selectedMsg == 28) { Common::String saveName; bool wasVisiting = _isVisiting; _isVisiting = false; uint saveNumber = displayFilePicker(bgFrame, false, saveName); if (saveNumber == uint(-1)) { _isVisiting = wasVisiting; drawState = 1; selectedMsg = 0; } else { _loadedSave = saveNumber; end = true; } waitMouseRelease(); } else if (selectedMsg == 42) { Common::String saveName; bool wasVisiting = _isVisiting; _isVisiting = true; uint saveNumber = displayFilePicker(bgFrame, false, saveName); if (saveNumber == uint(-1)) { _isVisiting = wasVisiting; drawState = 1; selectedMsg = 0; } else { _loadedSave = saveNumber; end = true; } waitMouseRelease(); } else if (selectedMsg == 29) { Common::String saveName; uint saveNumber = displayFilePicker(bgFrame, true, saveName); if (saveNumber != uint(-1)) { saveGame(_isVisiting, saveNumber, saveName); } drawState = 1; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 30) { ConfMan.setBool("subtitles", false); drawState = 1; menuEntries[selectedBox] = 31; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 31) { ConfMan.setBool("subtitles", true); drawState = 1; menuEntries[selectedBox] = 30; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 32) { ConfMan.setBool("music_mute", true); syncSoundSettings(); drawState = 1; menuEntries[selectedBox] = 33; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 33) { ConfMan.setBool("mute", false); ConfMan.setBool("music_mute", false); syncSoundSettings(); drawState = 1; menuEntries[selectedBox] = 32; selectedMsg = 0; waitMouseRelease(); } #if 0 // Music on disk settings else if (selectedMsg == 35) { drawState = 1; menuEntries[selectedBox] = 34; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 34) { drawState = 1; menuEntries[selectedBox] = 36; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 36) { drawState = 1; menuEntries[selectedBox] = 35; selectedMsg = 0; waitMouseRelease(); } #endif else if (selectedMsg == 39) { // Volume selectedMsg = 0; } else if (selectedMsg == 47) { // Unknown selectedMsg = 0; } else if (selectedMsg == 48) { ConfMan.setInt("omni3d_speed", 1); drawState = 1; menuEntries[selectedBox] = 51; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 51) { ConfMan.setInt("omni3d_speed", 2); drawState = 1; menuEntries[selectedBox] = 52; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 52) { ConfMan.setInt("omni3d_speed", 3); drawState = 1; menuEntries[selectedBox] = 49; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 49) { ConfMan.setInt("omni3d_speed", 4); drawState = 1; menuEntries[selectedBox] = 50; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 50) { ConfMan.setInt("omni3d_speed", 0); drawState = 1; menuEntries[selectedBox] = 48; selectedMsg = 0; waitMouseRelease(); } else if (selectedMsg == 43) { displayCredits(); drawState = 1; resetScreen = true; forceEvents = true; selectedMsg = 0; waitMouseRelease(); } } } showMouse(false); if (selectedMsg == 42) { _abortCommand = kAbortLoadGame; // For return value selectedMsg = 28; } else if (selectedMsg == 28) { _abortCommand = kAbortLoadGame; } else if (selectedMsg == 40) { _abortCommand = kAbortQuit; } else if (selectedMsg == 27) { _abortCommand = kAbortNewGame; _isVisiting = false; } else if (shouldAbort()) { // Fake a quit selectedMsg = 40; // shouldAbort called earlier has already set _abortCommand // If GMM is called on main menu in game, return value is ignored so quit isn't important // If GMM is called on main menu out of game, GMM can only quit game and don't load } ConfMan.flushToDisk(); syncOmni3DSettings(); musicUpdate(); delete imageDecoder; return selectedMsg; } uint CryOmni3DEngine_Versailles::displayYesNoBox(Graphics::ManagedSurface &surface, const Common::Rect &position, uint msg_id) { uint confirmWidth = _fontManager.getStrWidth(_messages[53]); uint cancelWidth = _fontManager.getStrWidth(_messages[54]); uint oldFont = _fontManager.getCurrentFont(); _fontManager.setSurface(&surface); _fontManager.setForeColor(240); _fontManager.setLineHeight(20); surface.frameRect(position, 243); _fontManager.setupBlock(Common::Rect(position.left + 5, position.top + 5, position.right - 5, position.bottom - 5)); _fontManager.setCurrentFont(5); _fontManager.displayBlockText(_messages[msg_id]); _fontManager.setCurrentFont(3); MouseBoxes boxes(2); boxes.setupBox(1, position.left + 5, position.bottom - 15, position.left + confirmWidth, position.bottom, &_messages[53]); boxes.setupBox(0, position.right - cancelWidth - 5, position.bottom - 15, position.right, position.bottom, &_messages[54]); bool end = false; bool redraw = true; uint result = uint(-1); while (!end || redraw) { if (redraw) { for (uint boxId = 0; boxId < 2; boxId++) { if (boxId == result) { _fontManager.setForeColor(240); } else { _fontManager.setForeColor(243); } boxes.display(boxId, _fontManager); } redraw = false; g_system->copyRectToScreen(surface.getPixels(), surface.pitch, 0, 0, surface.w, surface.h); } g_system->updateScreen(); g_system->delayMillis(10); if (pollEvents()) { Common::Point mouse = getMousePos(); uint hit_result = uint(-1); if (boxes.hitTest(1, mouse)) { hit_result = 1; } else if (boxes.hitTest(0, mouse)) { hit_result = 0; } if (!end && hit_result != result) { result = hit_result; redraw = true; } if ((getCurrentMouseButton() == 1) && (result != uint(-1))) { end = true; } Common::KeyCode keyPressed = getNextKey().keycode; if (keyPressed == Common::KEYCODE_ESCAPE) { result = 0; redraw = true; end = true; } else if (keyPressed == Common::KEYCODE_RETURN) { result = 1; redraw = true; end = true; } } } _fontManager.setCurrentFont(oldFont); return result; } uint CryOmni3DEngine_Versailles::displayFilePicker(const Graphics::Surface *bgFrame, bool saveMode, Common::String &saveName) { Graphics::ManagedSurface surface(bgFrame->w, bgFrame->h, bgFrame->format); surface.blitFrom(*bgFrame); drawMenuTitle(&surface, 243); int subtitleId; if (_isVisiting) { subtitleId = saveMode ? 45 : 46; } else { subtitleId = saveMode ? 29 : 28; } _fontManager.displayStr(164, 214, _messages[subtitleId]); // Draw an empty screen before we list saves showMouse(false); g_system->copyRectToScreen(surface.getPixels(), surface.pitch, 0, 0, surface.w, surface.h); g_system->updateScreen(); Common::Array savesList; getSavesList(_isVisiting, savesList); Common::String saveNameBackup; showMouse(true); MouseBoxes boxes(10); // 6 files + Yes/No/Up/Down buttons // Yes/No buttons const Common::String &okMsg = _messages[53]; uint okWidth = _fontManager.getStrWidth(okMsg); boxes.setupBox(6, 246, 430, 246 + okWidth, 450, &okMsg); const Common::String &cancelMsg = _messages[54]; uint cancelWidth = _fontManager.getStrWidth(cancelMsg); boxes.setupBox(7, 146, 430, 146 + cancelWidth, 450, &cancelMsg); // Up/Down buttons boxes.setupBox(8, 428, 320, 448, 340); boxes.setupBox(9, 428, 360, 448, 380); surface.transBlitFrom(_sprites.getSurface(162), Common::Point(428, 320), _sprites.getKeyColor(162)); surface.transBlitFrom(_sprites.getSurface(185), Common::Point(428, 360), _sprites.getKeyColor(185)); setCursor(181); uint fileListOffset = CLIP(ConfMan.getInt(_isVisiting ? "visits_list_off" : "saves_list_off"), 0, 100 - 6); uint boxHovered = uint(-1); uint boxSelected = uint(-1); bool textCursorState = false; uint textCursorNextState = 0; uint textCursorPos = uint(-1); bool autoRepeatInhibit = false; uint autoRepeatDelay = 250; uint autoRepeatEndInhibit = 0; bool finished = false; bool filesListChanged = true; bool redraw = false; while (!finished) { if (filesListChanged || redraw) { if (filesListChanged) { for (uint file = 0, fileY = 280; file < 6; file++, fileY += 20) { boxes.setupBox(file, 146, fileY, 408, fileY + 14, &savesList[file + fileListOffset]); } // Redraw background as file list changed surface.blitFrom(*bgFrame, Common::Rect(116, 280, 408, 400), Common::Point(116, 280)); filesListChanged = false; } // Don't redraw the scroll buttons for (uint box = 0; box < 8; box++) { if (box == boxSelected) { // Selected _fontManager.setForeColor(240); } else if (box == 6 && boxSelected == uint(-1)) { // Ok and no file selected _fontManager.setForeColor(245); } else if (box == boxHovered) { // Hovered _fontManager.setForeColor(241); } else { // Other cases _fontManager.setForeColor(243); } if (box == boxSelected && saveMode) { Common::Rect boxRct = boxes.getBoxRect(box); boxRct.top -= 2; surface.blitFrom(*bgFrame, boxRct, Common::Point(boxRct.left, boxRct.top)); boxRct.top += 2; if (textCursorState) { surface.vLine(textCursorPos, boxRct.top, boxRct.top + 11, 240); } } boxes.display(box, _fontManager); if (box < 6) { // Draw line below surface.hLine(116, 280 + box * 20 + 15, 407, 243); // minus 1 because hLine draws inclusive // Display file number _fontManager.displayInt(126, 280 + box * 20, fileListOffset + box + 1); } } redraw = false; g_system->copyRectToScreen(surface.getPixels(), surface.pitch, 0, 0, surface.w, surface.h); } g_system->updateScreen(); g_system->delayMillis(10); pollEvents(); Common::KeyState key = getNextKey(); uint mousePressed = getCurrentMouseButton(); if (!mousePressed) { bool boxFound = false; // Don't handle scroll arrows hovering for (uint box = 0; box < 8; box++) { if (boxes.hitTest(box, getMousePos())) { boxFound = true; if (boxHovered != box) { boxHovered = box; redraw = true; } } } if (!boxFound && boxHovered != uint(-1)) { boxHovered = uint(-1); redraw = true; } } if (key == Common::KEYCODE_RETURN || (mousePressed == 1 && boxHovered == 6)) { // OK if (boxSelected != uint(-1)) { Common::String &selectedSaveName = savesList[boxSelected + fileListOffset]; if (!selectedSaveName.size()) { selectedSaveName = _messages[56]; // No name } redraw = true; finished = true; } } else if (mousePressed == 1) { if (boxHovered == 7) { // Cancel boxSelected = uint(-1); finished = true; } else if (boxHovered != uint(-1) && boxHovered != boxSelected) { // This can only be a file bool existingSave = (savesList[boxHovered + fileListOffset] != _messages[55]); // Don't allow to save on slot 0 when visiting to avoid problems with original visit save bool validSave = !(_isVisiting && saveMode && boxSelected == 0); if ((saveMode || existingSave) && validSave) { // Restore old name if (saveMode && boxSelected != uint(-1)) { savesList[boxSelected + fileListOffset] = saveNameBackup; filesListChanged = true; } boxSelected = boxHovered; // Backup new one saveNameBackup = savesList[boxSelected + fileListOffset]; // Not an existing save clear free name if (!existingSave) { savesList[boxSelected + fileListOffset] = ""; } redraw = true; } } } if (boxSelected != uint(-1) && saveMode) { if (key.keycode != Common::KEYCODE_INVALID) { // Reference means we edit in place Common::String &selectedSaveName = savesList[boxSelected + fileListOffset]; if (key == Common::KEYCODE_BACKSPACE && selectedSaveName.size() > 0) { selectedSaveName.deleteLastChar(); textCursorNextState = 0; redraw = true; } else if (key.ascii >= 32 && key.ascii < 256 && selectedSaveName.size() < 20) { selectedSaveName += key.ascii; textCursorNextState = 0; redraw = true; } } if (g_system->getMillis() > textCursorNextState) { textCursorNextState = g_system->getMillis() + 200; // Blink at 200ms period uint width = _fontManager.getStrWidth(savesList[boxSelected + fileListOffset]); Common::Rect boxRct = boxes.getBoxRect(boxSelected); textCursorPos = boxRct.left + width; textCursorState = !textCursorState; redraw = true; } } if (!autoRepeatInhibit) { bool autoRepeatTrigger = false; uint oldFileListOffset = fileListOffset; if (mousePressed) { if (boxes.hitTest(8, getMousePos()) && fileListOffset > 0) { fileListOffset--; autoRepeatTrigger = true; } else if (boxes.hitTest(9, getMousePos()) && fileListOffset < 99 - 6) { fileListOffset++; autoRepeatTrigger = true; } } else if (key == Common::KEYCODE_UP) { if (fileListOffset > 0) { fileListOffset--; autoRepeatTrigger = true; } } else if (key == Common::KEYCODE_DOWN) { if (fileListOffset < 99 - 6) { fileListOffset++; autoRepeatTrigger = true; } } else if (key == Common::KEYCODE_PAGEUP) { if (fileListOffset > 6) { fileListOffset -= 6; } else { fileListOffset = 0; } } else if (key == Common::KEYCODE_PAGEDOWN) { if (fileListOffset < 99 - 6 - 6) { fileListOffset += 6; } else { fileListOffset = 99 - 6; } } if (autoRepeatTrigger) { // Restore old name if (saveMode && boxSelected != uint(-1)) { savesList[boxSelected + oldFileListOffset] = saveNameBackup; } boxHovered = uint(-1); boxSelected = uint(-1); autoRepeatInhibit = true; autoRepeatEndInhibit = g_system->getMillis() + autoRepeatDelay; filesListChanged = true; } } if (autoRepeatInhibit && g_system->getMillis() > autoRepeatEndInhibit) { autoRepeatInhibit = false; autoRepeatDelay = 60; // Next rounds will wait 60ms after first one } if (!mousePressed && key == Common::KEYCODE_INVALID) { // Nothing was clicked or pressed: set back autoRepeatDelay to 250ms autoRepeatDelay = 250; } } if (boxSelected != uint(-1)) { saveName = savesList[boxSelected + fileListOffset]; ConfMan.setInt(_isVisiting ? "visits_list_off" : "saves_list_off", fileListOffset); return boxSelected + fileListOffset + 1; } else { return uint(-1); } } const MsgBoxParameters CryOmni3DEngine_Versailles::kWarpMsgBoxParameters = { 9, 241, 22, 2, 1, 36, 18, 20, 10, 5 }; const MsgBoxParameters CryOmni3DEngine_Versailles::kFixedimageMsgBoxParameters = { 3, 241, 22, 2, 1, 40, 20, 20, 10, 3 }; void CryOmni3DEngine_Versailles::displayMessageBox(const MsgBoxParameters ¶ms, const Graphics::Surface *surface, const Common::String &msg, const Common::Point &position, const Common::Functor0 &callback) { Graphics::ManagedSurface dstSurface; dstSurface.create(surface->w, surface->h, surface->format); dstSurface.blitFrom(*surface); _fontManager.setSurface(&dstSurface); _fontManager.setCurrentFont(params.font); _fontManager.setTransparentBackground(true); _fontManager.setForeColor(params.foreColor); _fontManager.setLineHeight(params.lineHeight); _fontManager.setSpaceWidth(params.spaceWidth); _fontManager.setCharSpacing(params.charSpacing); uint width = params.initialWidth; uint height = params.initialHeight; uint lineCount = 0; Common::Point pt = position; Common::Rect rct; bool notEnough = true; bool tooLarge = false; while (notEnough && !tooLarge) { width += params.incrementWidth; height += params.incrementHeight; rct = Common::Rect::center(pt.x, pt.y, width, height); if (rct.left < 10) { rct.left = 10; if (pt.x < 320) { pt.x += 10; } } if (rct.right >= 630) { rct.right = 630; if (pt.x > 320) { pt.x -= 10; } } if (rct.top <= 10) { rct.top = 10; if (pt.y < 240) { pt.y += 10; } } if (rct.bottom >= 470) { rct.bottom = 470; if (pt.y > 235) { // sic. pt.y -= 10; } } if (rct.left == 10 && rct.top == 10 && rct.right == 630 && rct.bottom == 470) { tooLarge = true; } lineCount = _fontManager.getLinesCount(msg, rct.width() - 12); if (lineCount && lineCount * _fontManager.lineHeight() + 18 < (uint)rct.height()) { notEnough = false; } } rct.setHeight(lineCount * _fontManager.lineHeight() + 12); if (rct.bottom > 479) { rct.bottom = 479; } Graphics::Surface subSurface = dstSurface.getSubArea(rct); makeTranslucent(subSurface, surface->getSubArea(rct)); rct.grow(-6); _fontManager.setupBlock(rct); _fontManager.displayBlockText(msg); drawCountdown(&dstSurface); g_system->copyRectToScreen(dstSurface.getPixels(), dstSurface.pitch, 0, 0, dstSurface.w, dstSurface.h); waitMouseRelease(); uint disappearTime = g_system->getMillis() + msg.size() * params.timeoutChar * 10; bool finished = false; while (!finished) { callback(); if (g_system->getMillis() > disappearTime) { finished = true; } if (getCurrentMouseButton() == 1) { finished = true; } } // Restore image g_system->copyRectToScreen(surface->getPixels(), surface->pitch, 0, 0, surface->w, surface->h); } void CryOmni3DEngine_Versailles::displayMessageBoxWarp(const Common::String &message) { Common::Point mousePos = getMousePos(); mousePos += Common::Point(0, 32); if (mousePos.x > 639) { mousePos.x = 639; } if (mousePos.y > 479) { mousePos.y = 479; } displayMessageBox(kWarpMsgBoxParameters, _omni3dMan.getSurface(), message, mousePos, Common::Functor0Mem(this, &CryOmni3DEngine_Versailles::warpMsgBoxCB)); } void CryOmni3DEngine_Versailles::displayCredits() { waitMouseRelease(); Graphics::ManagedSurface creditsSurface; Image::ImageDecoder *imageDecoder = loadHLZ("credits.hlz"); if (!imageDecoder) { return; } const Graphics::Surface *bgFrame = imageDecoder->getSurface(); byte palette[256 * 3]; memset(palette, 0, 256 * 3); // getPalette returns the first color not index 0 memcpy(palette + 3 * imageDecoder->getPaletteStartIndex(), imageDecoder->getPalette(), 3 * imageDecoder->getPaletteColorCount()); copySubPalette(palette, _cursorPalette, 240, 8); creditsSurface.create(bgFrame->w, bgFrame->h, bgFrame->format); _fontManager.setCurrentFont(3); _fontManager.setTransparentBackground(true); _fontManager.setForeColor(243); _fontManager.setLineHeight(14); _fontManager.setSpaceWidth(0); _fontManager.setCharSpacing(1); _fontManager.setSurface(&creditsSurface); Common::File creditsFile; if (!creditsFile.open(_localizedFilenames[LocalizedFilenames::kCredits])) { warning("Failed to open credits file: %s", _localizedFilenames[LocalizedFilenames::kCredits].c_str()); delete imageDecoder; return; } showMouse(false); char line[256]; bool end = false; bool calculatedScreen = false; uint lineHeight = 20; uint currentY = 0; int32 fileOffset = 0; bool skipScreen = false; while (!end && creditsFile.readLine(line, ARRAYSIZE(line))) { // Remove line ending line[strlen(line) - 1] = '\0'; if (!strncmp(line, "###", 3)) { // Prefix for commands if (!strncmp(line + 3, "ECRAN", 5)) { // ECRAN command if (calculatedScreen) { g_system->copyRectToScreen(creditsSurface.getPixels(), creditsSurface.pitch, 0, 0, creditsSurface.w, creditsSurface.h); if (skipScreen) { // Just display palette setPalette(palette, 0, 256); } else { fadeInPalette(palette); } skipScreen = false; // Wait uint endScreenTime = g_system->getMillis() + 6000; while (g_system->getMillis() < endScreenTime && !skipScreen) { g_system->updateScreen(); g_system->delayMillis(10); if (pollEvents()) { if (getCurrentMouseButton() == 1) { skipScreen = true; } Common::KeyCode kc = getNextKey().keycode; while (kc != Common::KEYCODE_INVALID) { if (kc == Common::KEYCODE_SPACE) { skipScreen = true; break; } else if (kc == Common::KEYCODE_ESCAPE) { skipScreen = true; end = true; break; } kc = getNextKey().keycode; } clearKeys(); } if (shouldAbort()) { skipScreen = true; end = true; } } if (!skipScreen) { fadeOutPalette(); fillSurface(0); } currentY = 0; fileOffset = creditsFile.pos(); calculatedScreen = false; } else { // We just finished calculated all lines, roll back and display them creditsFile.seek(fileOffset, SEEK_SET); calculatedScreen = true; if (currentY <= 480 - lineHeight) { // Center in screen currentY = (480 - lineHeight) / 2 - currentY / 2; } else { currentY = 3; } creditsSurface.blitFrom(*bgFrame); } } else if (!strcmp(line + 3, "T0")) { _fontManager.setCurrentFont(1); lineHeight = _fontManager.getFontMaxHeight() + 10; } else if (!strcmp(line + 3, "T1")) { _fontManager.setCurrentFont(2); lineHeight = _fontManager.getFontMaxHeight() + 10; } else if (!strcmp(line + 3, "T2")) { _fontManager.setCurrentFont(4); lineHeight = _fontManager.getFontMaxHeight() + 10; } else if (!strcmp(line + 3, "T3")) { _fontManager.setCurrentFont(2); lineHeight = _fontManager.getFontMaxHeight() + 10; } else if (!strcmp(line + 3, "T4")) { _fontManager.setCurrentFont(5); lineHeight = _fontManager.getFontMaxHeight() + 10; } else if (!strcmp(line + 3, "T5")) { _fontManager.setCurrentFont(6); lineHeight = _fontManager.getFontMaxHeight() + 10; } else { warning("Unknown ### command : %s", line + 3); } } else { // Text if (calculatedScreen) { uint width = _fontManager.getStrWidth(line); // Center around 315 _fontManager.displayStr(315 - width / 2, currentY, line); } currentY += lineHeight; } } showMouse(true); delete imageDecoder; } } // End of namespace Versailles } // End of namespace CryOmni3D