/* 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