/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "engines/stark/services/dialogplayer.h" #include "engines/stark/services/diary.h" #include "engines/stark/services/global.h" #include "engines/stark/services/services.h" #include "engines/stark/services/userinterface.h" #include "engines/stark/resources/script.h" #include "engines/stark/resources/speech.h" namespace Stark { DialogPlayer::DialogPlayer() : _currentDialog(nullptr), _currentReply(nullptr), _interruptedDialog(nullptr), _interruptedReply(nullptr), _speechReady(false), _singleSpeech(nullptr), _optionsAvailable(false) { } DialogPlayer::~DialogPlayer() {} void DialogPlayer::run(Resources::Dialog *dialog) { reset(); StarkUserInterface->setInteractive(false); if (!_currentDialog) { Common::String dialogTitle = dialog->getDiaryTitle(); int32 characterId = dialog->getCharacter(); Common::String characterName = StarkGlobal->getCharacterName(characterId); StarkDiary->openDialog(dialogTitle, characterName, characterId); } _currentDialog = dialog; buildOptions(); } void DialogPlayer::playSingle(Resources::Speech *speech) { reset(); _singleSpeech = speech; _speechReady = true; } bool DialogPlayer::isRunning() const { return _currentDialog != nullptr || _interruptedDialog != nullptr; } bool DialogPlayer::isSpeechReady() const { return _speechReady; } bool DialogPlayer::isSpeechReady(Resources::Speech *speech) const { return _speechReady && _singleSpeech == speech; } Resources::Speech *DialogPlayer::acquireReadySpeech() { assert(_speechReady); _speechReady = false; if (_singleSpeech) { return _singleSpeech; } else { return _currentReply->getCurrentSpeech(); } } bool DialogPlayer::areOptionsAvailable() const { return _optionsAvailable; } Common::Array DialogPlayer::listOptions() const { return _options; } void DialogPlayer::removeLastOnlyOption() { int32 lastOnlyOptionIndex = -1; for (uint i = 0; i < _options.size(); i++) { Resources::Dialog::Topic *topic = _options[i]._topic; Resources::Dialog::Reply *reply = topic->getReply(_options[i]._replyIndex); if (reply->isLastOnly()) { lastOnlyOptionIndex = i; break; } } if (lastOnlyOptionIndex >= 0) { _options.remove_at(lastOnlyOptionIndex); } } void DialogPlayer::buildOptions() { Resources::Dialog::TopicArray availableTopics = _currentDialog->listAvailableTopics(); for (uint i = 0; i < availableTopics.size(); i++) { Option option; option._type = kOptionTypeAsk; option._topic = availableTopics[i]; option._caption = availableTopics[i]->getCaption(); option._replyIndex = availableTopics[i]->getNextReplyIndex(); Resources::Dialog::Reply *reply = availableTopics[i]->getReply(option._replyIndex); if (reply->checkCondition()) { _options.push_back(option); } } if (_options.size() > 1) { removeLastOnlyOption(); } if (_options.size() == 1) { // Only one option, just run it selectOption(0); } else { _optionsAvailable = true; } } void DialogPlayer::selectOption(uint32 index) { _optionsAvailable = false; Option &option = _options[index]; //TODO: Complete switch (option._type) { case kOptionTypeAsk: { Resources::Dialog::Topic *topic = option._topic; // Set the current reply _currentReply = topic->startReply(option._replyIndex); Resources::Speech *speech = _currentReply->getCurrentSpeech(); if (speech) { StarkDiary->logSpeech(speech->getPhrase(), speech->getCharacterId()); _speechReady = true; } else { onReplyEnd(); } break; } default: error("Unhandled option type %d", option._type); } } void DialogPlayer::onReplyEnd() { Resources::Script *nextScript = _currentDialog->getNextScript(_currentReply); Resources::Dialog *nextDialog = _currentDialog->getNextDialog(_currentReply); if (nextScript) { // Save the dialog player's state before running the script, // so that we can restore it when the script ends. // The script might run another dialog. saveToInterruptionSlot(); nextScript->addReturnObject(_currentDialog); nextScript->execute(Resources::Script::kCallModeDialogCreateSelections); } else if (nextDialog) { run(nextDialog); } else { // Quit the dialog reset(); StarkUserInterface->setInteractive(true); } } void DialogPlayer::reset() { if (_currentDialog) { StarkDiary->closeDialog(); } _currentDialog = nullptr; _currentReply = nullptr; _singleSpeech = nullptr; _speechReady = false; _optionsAvailable = false; _options.clear(); } void DialogPlayer::update() { if (_singleSpeech || !_currentDialog || !_currentReply) { return; // Nothing to do } //TODO: Complete Resources::Speech *speech = _currentReply->getCurrentSpeech(); if (speech && _speechReady) { // A new line is already ready, no need to prepare another one return; } if (!speech || !speech->isPlaying()) { // A line has ended, play the next one _currentReply->goToNextLine(); speech = _currentReply->getCurrentSpeech(); if (speech) { StarkDiary->logSpeech(speech->getPhrase(), speech->getCharacterId()); _speechReady = true; } else { onReplyEnd(); } } } void DialogPlayer::resume(Resources::Dialog *dialog) { assert(_interruptedDialog == dialog); // Restore our state from before running the script restoreFromInterruptionSlot(); Resources::Dialog *nextDialog = _currentDialog->getNextDialog(_currentReply); if (nextDialog) { run(nextDialog); } else { // Quit the dialog reset(); StarkUserInterface->setInteractive(true); } } void DialogPlayer::saveToInterruptionSlot() { _interruptedDialog = _currentDialog; _interruptedReply = _currentReply; } void DialogPlayer::restoreFromInterruptionSlot() { _currentDialog = _interruptedDialog; _currentReply = _interruptedReply; _interruptedDialog = nullptr; _interruptedReply = nullptr; } } // End of namespace Stark