diff options
author | Le Philousophe | 2019-03-05 19:28:12 +0100 |
---|---|---|
committer | Eugene Sandulenko | 2019-06-01 22:43:48 +0200 |
commit | 531aa8392e40458d52f22edef71abf3506ca63da (patch) | |
tree | 5446a4eac74b59f3df4301cf4f0ed18f6ee5b586 /engines/cryomni3d/versailles/dialogs_manager.cpp | |
parent | 97397bdaffed7f7bdf97ccde20bbcd41df4e63fb (diff) | |
download | scummvm-rg350-531aa8392e40458d52f22edef71abf3506ca63da.tar.gz scummvm-rg350-531aa8392e40458d52f22edef71abf3506ca63da.tar.bz2 scummvm-rg350-531aa8392e40458d52f22edef71abf3506ca63da.zip |
CRYOMNI3D: Add engine for Versailles 1685
Diffstat (limited to 'engines/cryomni3d/versailles/dialogs_manager.cpp')
-rw-r--r-- | engines/cryomni3d/versailles/dialogs_manager.cpp | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/engines/cryomni3d/versailles/dialogs_manager.cpp b/engines/cryomni3d/versailles/dialogs_manager.cpp new file mode 100644 index 0000000000..4180a65d98 --- /dev/null +++ b/engines/cryomni3d/versailles/dialogs_manager.cpp @@ -0,0 +1,379 @@ +/* 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/decoders/wave.h" +#include "common/file.h" +#include "common/system.h" + +#include "cryomni3d/video/hnm_decoder.h" + +#include "cryomni3d/versailles/dialogs_manager.h" +#include "cryomni3d/versailles/engine.h" + +namespace CryOmni3D { +namespace Versailles { + +bool Versailles_DialogsManager::play(const Common::String &sequence) { + // Prepare with specific Versailles stuff + if (!_engine->preprocessDialog(sequence)) { + return false; + } + + _engine->musicSetQuiet(true); + + _engine->setCursor(181); + // No need to adjust hide cursor counter, there isn't any in ScummVM + bool cursorWasVisible = g_system->showMouse(true); + + bool slowStop = false; + bool didSth = DialogsManager::play(sequence, slowStop); + + g_system->showMouse(cursorWasVisible); + + if (didSth && slowStop) { + if (_engine->showSubtitles()) { + bool skip = false; + unsigned int end = g_system->getMillis() + 2000; + while (!g_engine->shouldQuit() && g_system->getMillis() < end && !skip) { + g_system->updateScreen(); + if (_engine->pollEvents() && + (_engine->checkKeysPressed(1, Common::KEYCODE_SPACE) || + _engine->getCurrentMouseButton() == 1)) { + skip = true; + } + } + } + } + _engine->postprocessDialog(sequence); + + _engine->musicSetQuiet(false); + + _lastImage.free(); + + _engine->waitMouseRelease(); + return didSth; +} + +void Versailles_DialogsManager::executeShow(const Common::String &show) { + Common::HashMap<Common::String, ShowCallback>::iterator showIt = _shows.find(show); + + if (showIt == _shows.end()) { + error("Missing show %s", show.c_str()); + } + + _lastImage.free(); + + ShowCallback cb = showIt->_value; + (_engine->*cb)(); +} + +void Versailles_DialogsManager::playDialog(const Common::String &video, const Common::String &sound, + const Common::String &text, const SubtitlesSettings &settings) { + Common::String videoFName(video); + Common::String soundFName(sound); + + videoFName += ".hnm"; + // Don't look for HNS file here + + while (soundFName.size() < 8) { + soundFName += '_'; + } + soundFName += ".wav"; + + Video::HNMDecoder *videoDecoder = new Video::HNMDecoder(true); + + if (!videoDecoder->loadFile(videoFName)) { + warning("Failed to open movie file %s/%s", video.c_str(), videoFName.c_str()); + delete videoDecoder; + return; + } + + Common::File *audioFile = new Common::File(); + if (!audioFile->open(soundFName)) { + warning("Failed to open sound file %s/%s", sound.c_str(), soundFName.c_str()); + delete videoDecoder; + delete audioFile; + return; + } + + 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) { + delete videoDecoder; + return; + } + + g_system->showMouse(false); + + uint16 width = videoDecoder->getWidth(); + uint16 height = videoDecoder->getHeight(); + + // Preload first frame to draw subtitles from it + const Graphics::Surface *firstFrame = videoDecoder->decodeNextFrame(); + assert(firstFrame != nullptr); + + if (videoDecoder->hasDirtyPalette()) { + const byte *palette = videoDecoder->getPalette(); + _engine->setupPalette(palette, 0, 256); + } + + FontManager &fontManager = _engine->_fontManager; + _lastImage.create(firstFrame->w, firstFrame->h, firstFrame->format); + _lastImage.blitFrom(*firstFrame); + + fontManager.setCurrentFont(7); + fontManager.setTransparentBackground(true); + fontManager.setForeColor(241); + fontManager.setLineHeight(22); + fontManager.setSpaceWidth(2); + fontManager.setCharSpacing(1); + + if (_engine->showSubtitles()) { + Common::Rect block = settings.textRect; + + unsigned int lines = fontManager.getLinesCount(text, block.width() - 8); + if (lines == 0) { + lines = 5; + } + unsigned int blockHeight = fontManager.lineHeight() * lines + 6; + block.setHeight(blockHeight); + + if (block.bottom >= 480) { + block.bottom = 470; + warning("Dialog text is really too long"); + } + + // Make only the block area translucent inplace + Graphics::Surface blockSurface = _lastImage.getSubArea(block); + _engine->makeTranslucent(blockSurface, blockSurface); + + fontManager.setSurface(&_lastImage); + block.grow(-4); + fontManager.setupBlock(block); + fontManager.displayBlockText(text); + } + + g_system->copyRectToScreen(_lastImage.getPixels(), _lastImage.pitch, 0, 0, width, height); + g_system->updateScreen(); + + const Common::Rect &drawRect = settings.drawRect; + + if (audioDecoder->getLength() == 0) { + // Empty wave file + delete audioDecoder; + + unsigned int duration = 100 * text.size(); + if (duration < 1000) { + duration = 1000; + } + + bool skipWait = false; + unsigned int end = g_system->getMillis() + duration; + while (!g_engine->shouldQuit() && g_system->getMillis() < end && !skipWait) { + if (_engine->pollEvents() && _engine->checkKeysPressed(1, Common::KEYCODE_SPACE)) { + skipWait = true; + } + } + } else { + // Let start the show! + videoDecoder->start(); + + Audio::SoundHandle audioHandle; + _engine->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &audioHandle, audioDecoder); + // We lost ownership of the audioDecoder just set it to nullptr and don't use it + audioDecoder = nullptr; + + bool skipVideo = false; + while (!g_engine->shouldQuit() && _engine->_mixer->isSoundHandleActive(audioHandle) && !skipVideo) { + if (_engine->pollEvents() && _engine->checkKeysPressed(1, Common::KEYCODE_SPACE)) { + skipVideo = true; + } + + if (videoDecoder->needsUpdate()) { + const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); + + if (frame) { + if (videoDecoder->hasDirtyPalette()) { + const byte *palette = videoDecoder->getPalette(); + _engine->setupPalette(palette, 0, 256); + } + + // Only refresh the moving part of the animation + const Graphics::Surface subFrame = frame->getSubArea(drawRect); + g_system->copyRectToScreen(subFrame.getPixels(), subFrame.pitch, drawRect.left, drawRect.top, + subFrame.w, subFrame.h); + } + } + g_system->updateScreen(); + } + _engine->_mixer->stopHandle(audioHandle); + } + + // It's intentional that _lastImage is set with the first video image + + delete videoDecoder; + g_system->showMouse(true); +} + +void Versailles_DialogsManager::displayMessage(const Common::String &text) { + _engine->displayMessageBoxWarp(text); +} + +unsigned int Versailles_DialogsManager::askPlayerQuestions(const Common::String &video, + const Common::StringArray &questions) { + if (_lastImage.empty()) { + loadFrame(video); + } + + if (questions.size() == 0 || questions.size() > 5) { + return -1; + } + + FontManager &fontManager = _engine->_fontManager; + fontManager.setCurrentFont(7); + fontManager.setTransparentBackground(true); + fontManager.setLineHeight(18); + fontManager.setSpaceWidth(2); + fontManager.setSurface(&_lastImage); + + int16 tops[5]; + int16 bottoms[5]; + int16 currentHeight = 0; + unsigned int questionId = 0; + for (Common::StringArray::const_iterator it = questions.begin(); it != questions.end(); + it++, questionId++) { + tops[questionId] = currentHeight; + unsigned int lines = fontManager.getLinesCount(*it, 598); + if (lines == 0) { + lines = 1; + } + currentHeight += 18 * lines; + bottoms[questionId] = currentHeight; + } + + int offsetY = 480 - (bottoms[questions.size() - 1] - tops[0]); + if (offsetY > 402) { + offsetY = 402; + } else if (offsetY < 2) { + offsetY = 2; + } + + for (questionId = 0; questionId < questions.size(); questionId++) { + tops[questionId] += offsetY; + bottoms[questionId] += offsetY; + } + + _engine->setCursor(181); + Graphics::Surface alphaSurface = _lastImage.getSubArea(Common::Rect(0, offsetY - 2, 640, 480)); + _engine->makeTranslucent(alphaSurface, alphaSurface); + + bool finished = false; + bool update = true; + unsigned int selectedQuestion = -1; + while (!finished) { + if (update) { + update = false; + questionId = 0; + for (Common::StringArray::const_iterator it = questions.begin(); it != questions.end(); + it++, questionId++) { + fontManager.setForeColor(selectedQuestion == questionId ? 241 : 245); + fontManager.setupBlock(Common::Rect(10, tops[questionId], 608, bottoms[questionId])); + fontManager.displayBlockText(*it); + } + g_system->copyRectToScreen(_lastImage.getPixels(), _lastImage.pitch, 0, 0, _lastImage.w, + _lastImage.h); + } + g_system->updateScreen(); + + if (_engine->pollEvents()) { + _engine->clearKeys(); + if (g_engine->shouldQuit()) { + finished = true; + selectedQuestion = -1; + break; + } + Common::Point mousePos = _engine->getMousePos(); + if (_engine->getDragStatus() == kDragStatus_Finished && selectedQuestion != -1u) { + finished = true; + } else if (mousePos.x >= 608 || mousePos.y < offsetY) { + if (selectedQuestion != -1u) { + selectedQuestion = -1; + update = true; + } + } else { + for (questionId = 0; questionId < questions.size(); questionId++) { + if (mousePos.y > tops[questionId] && mousePos.y < bottoms[questionId]) { + break; + } + } + if (questionId < questions.size()) { + if (selectedQuestion != questionId) { + selectedQuestion = questionId; + update = true; + } + } else { + selectedQuestion = -1; + update = true; + } + } + } + } + + return selectedQuestion; +} + +void Versailles_DialogsManager::loadFrame(const Common::String &video) { + Common::String videoFName(video); + int lastDotPos = videoFName.size() - 1; + for (; lastDotPos >= 0; --lastDotPos) { + if (videoFName[lastDotPos] == '.') { + break; + } + } + if (lastDotPos > -1) { + videoFName.erase(lastDotPos); + videoFName += ".hnm"; + } + + Video::HNMDecoder *videoDecoder = new Video::HNMDecoder(); + + if (!videoDecoder->loadFile(videoFName)) { + warning("Failed to open movie file %s/%s", video.c_str(), videoFName.c_str()); + delete videoDecoder; + return; + } + + if (videoDecoder->hasDirtyPalette()) { + const byte *palette = videoDecoder->getPalette(); + _engine->setupPalette(palette, 0, 256); + } + + // Preload first frame to draw subtitles from it + const Graphics::Surface *firstFrame = videoDecoder->decodeNextFrame(); + _lastImage.create(firstFrame->w, firstFrame->h, firstFrame->format); + _lastImage.blitFrom(*firstFrame); +} + +} // End of namespace Versailles +} // End of namespace CryOmni3D |