scummvm/engines/mutationofjb/commands/saycommand.cpp
2021-12-26 18:48:43 +01:00

169 lines
4.6 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "mutationofjb/commands/saycommand.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "common/str.h"
#include "common/debug.h"
#include "common/debug-channels.h"
/** @file
* <firstLine> { <CRLF> <additionalLine> }
*
* firstLine ::= ("SM" | "SLM" | "NM" | "NLM") " " <lineToSay> [ "<" <voiceFile> | "<!" ]
* additionalLine ::= <skipped> " " <lineToSay> ( "<" <voiceFile> | "<!" )
*
* Say command comes in four variants: SM, SLM, NM and NLM.
* Note: In script files, they are usually written as *SM.
* The asterisk is ignored by the readLine function.
*
* Each of them plays a voice file (if present) and/or shows a message
* (if voice file not present or subtitles are enabled).
*
* The difference between versions starting with "S" and "N" is that
* the "N" version does not show talking animation.
*
* The "L" versions are "blocking", i.e. they wait for the previous say command to finish.
*
* If the line ends with "<!", it means the message continues to the next line.
* Next line usually has "SM" (or other variant) repeated, but it does not have to.
* Then we have the rest of the string to say (which is concatenated with the previous line)
* and possibly the voice file or "<!" again.
*/
namespace MutationOfJB {
bool SayCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) {
bool waitForPrevious = false;
bool talkingAnimation = false;
if (line.hasPrefix("SM")) {
waitForPrevious = false;
talkingAnimation = true;
} else if (line.hasPrefix("SLM")) {
waitForPrevious = true;
talkingAnimation = true;
} else if (line.hasPrefix("NM")) {
waitForPrevious = false;
talkingAnimation = false;
} else if (line.hasPrefix("NLM")) {
waitForPrevious = true;
talkingAnimation = false;
} else {
return false;
}
Common::String currentLine = line;
Common::String lineToSay;
Common::String voiceFile;
bool cont = false;
bool firstPass = true;
do {
cont = false;
uint startPos;
for (startPos = 0; startPos < currentLine.size(); ++startPos) {
if (currentLine[startPos] == ' ') {
break;
}
}
if (startPos == currentLine.size()) {
if (!firstPass) {
warning("Unable to parse line '%s'", currentLine.c_str());
break;
}
}
if (startPos != currentLine.size()) {
startPos++;
}
uint endPos;
for (endPos = startPos; endPos < currentLine.size(); ++endPos) {
if (currentLine[endPos] == '<') {
break;
}
}
Common::String talkStr(currentLine.c_str() + startPos, endPos - startPos);
if (endPos != currentLine.size()) {
const char *end = currentLine.c_str() + endPos + 1;
if (end[0] == '!') {
cont = true;
} else {
voiceFile = end;
}
}
if (talkStr.lastChar() == '~') {
debug("Found say command ending with '~'. Please take a look.");
}
if (lineToSay.empty()) {
lineToSay = talkStr;
} else {
lineToSay += " " + talkStr;
}
if (cont) {
if (!parseCtx.readLine(currentLine)) {
cont = false;
}
}
firstPass = false;
} while (cont);
command = new SayCommand(lineToSay, voiceFile, waitForPrevious, talkingAnimation);
return true;
}
Command::ExecuteResult SayCommand::execute(ScriptExecutionContext &scriptExecCtx) {
Game &game = scriptExecCtx.getGame();
if (_waitForPrevious) {
if (game.getActiveSayTask()) {
return InProgress;
}
}
TaskPtr task(new SayTask(_lineToSay, game.getGameData()._color));
game.getTaskManager().startTask(task);
return Finished;
}
Common::String SayCommand::debugString() const {
return Common::String::format("SHOWMSG%s%s '%s' '%s'", _waitForPrevious ? "+WAIT" : "", _talkingAnimation ? "+TALKANIM" : "", _lineToSay.c_str(), _voiceFile.c_str());
}
}