252 lines
6.6 KiB
C++
252 lines
6.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 "agds/dialog.h"
|
|
#include "agds/agds.h"
|
|
#include "agds/object.h"
|
|
#include "agds/systemVariable.h"
|
|
#include "common/debug.h"
|
|
|
|
namespace AGDS {
|
|
|
|
void Dialog::parseDialogDefs(const Common::String &defs) {
|
|
_dialogDefs.clear();
|
|
Common::String name, value;
|
|
bool readName = true;
|
|
for (uint32 p = 0, size = defs.size(); p < size; ++p) {
|
|
char ch = defs[p];
|
|
if (ch == ' ') {
|
|
continue;
|
|
} else if (ch == '\n' || ch == '\r' || p + 1 == size) {
|
|
if (p + 1 == size)
|
|
value += ch;
|
|
// debug("dialog definition: '%s' = '%s'", name.c_str(), value.c_str());
|
|
if (!name.empty() && !value.empty()) {
|
|
_dialogDefs[name] = atoi(value.c_str());
|
|
}
|
|
readName = true;
|
|
name.clear();
|
|
value.clear();
|
|
continue;
|
|
} else if (ch == '=') {
|
|
if (readName) {
|
|
readName = false;
|
|
} else {
|
|
warning("equal sign in value, skipping");
|
|
}
|
|
} else {
|
|
if (readName)
|
|
name += ch;
|
|
else
|
|
value += ch;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Dialog::load(const Common::String &processName, const Common::String &dialogScript, const Common::String &defs) {
|
|
_currentDef.clear();
|
|
_sounds.clear();
|
|
parseDialogDefs(defs);
|
|
_dialogProcessName = processName;
|
|
_dialogScript = dialogScript;
|
|
_dialogScriptPos = 0;
|
|
_engine->getSystemVariable("dialog_var")->setInteger(-1);
|
|
}
|
|
|
|
int Dialog::textDelay(const Common::String &str) {
|
|
int speed = _engine->getSystemVariable("text_speed")->getInteger();
|
|
if (speed < 0)
|
|
speed = 0;
|
|
else if (speed > 100)
|
|
speed = 100;
|
|
speed /= 10;
|
|
|
|
int delay = str.size();
|
|
if (delay < 20)
|
|
delay = 20;
|
|
|
|
// this looks like an error in original code
|
|
// original first value in this table is 0x0FFFFFEB4 (-332)
|
|
// 0x0FFFFFEB4 * 20 and then 16-to-32 conversion gives 0xffffe610
|
|
// 0xe610 / 20 = 2944
|
|
static const int delays[] = {
|
|
2944, 631, 398, 251, 158,
|
|
100, 63, 40, 25, 16, 10};
|
|
return delays[speed] * delay / 41 + 1;
|
|
}
|
|
|
|
bool Dialog::tick() {
|
|
if (_dialogProcessName.empty())
|
|
return false;
|
|
|
|
auto dialog_var = _engine->getSystemVariable("dialog_var");
|
|
int dialog_var_value = dialog_var->getInteger();
|
|
if (dialog_var_value != 0) {
|
|
return false;
|
|
}
|
|
|
|
uint n = _dialogScript.size();
|
|
if (_dialogScriptPos >= n) {
|
|
|
|
if (!_dialogProcessName.empty()) {
|
|
debug("end of dialog, running %s", _dialogProcessName.c_str());
|
|
dialog_var->setInteger(-2);
|
|
_engine->reactivate(_dialogProcessName, "end of dialog");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Common::String &line = _dialogLine;
|
|
line.clear();
|
|
|
|
bool command = _dialogScript[_dialogScriptPos] == '@';
|
|
while (_dialogScriptPos < n) {
|
|
if (!command && _dialogScript[_dialogScriptPos] == '@')
|
|
break;
|
|
|
|
while (_dialogScriptPos < n && _dialogScript[_dialogScriptPos] != '\n' && _dialogScript[_dialogScriptPos] != '\r') {
|
|
line += _dialogScript[_dialogScriptPos++];
|
|
}
|
|
if (!command)
|
|
line += '\n';
|
|
|
|
while (_dialogScriptPos < n && (_dialogScript[_dialogScriptPos] == '\n' || _dialogScript[_dialogScriptPos] == '\r'))
|
|
++_dialogScriptPos;
|
|
|
|
if (command)
|
|
break;
|
|
}
|
|
|
|
if (line.empty())
|
|
return true;
|
|
|
|
debug("dialog line: %s", line.c_str());
|
|
if (line[0] == '@') {
|
|
processDirective(line);
|
|
} else {
|
|
debug("text: %s", line.c_str() + 1);
|
|
dialog_var->setInteger(-3);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Dialog::processSoundDirective(const Common::String &line) {
|
|
debug("sound: %s", line.c_str());
|
|
auto arg1 = line.find('(');
|
|
if (arg1 == line.npos) {
|
|
warning("invalid sound directive");
|
|
return;
|
|
}
|
|
++arg1;
|
|
|
|
auto comma1 = line.find(',', arg1);
|
|
if (comma1 == line.npos) {
|
|
warning("invalid sound directive, missing arg2");
|
|
return;
|
|
}
|
|
++comma1;
|
|
auto comma2 = line.find(',', comma1);
|
|
if (comma2 == line.npos) {
|
|
warning("invalid sound directive, missing arg3");
|
|
return;
|
|
}
|
|
auto end = line.find(')', comma2 + 1);
|
|
if (end == line.npos) {
|
|
warning("invalid sound directive");
|
|
return;
|
|
}
|
|
--end;
|
|
Common::String name = line.substr(arg1, comma1 - arg1 - 1);
|
|
while (line[comma1] == ' ')
|
|
++comma1;
|
|
Common::String sample = line.substr(comma1, comma2 - comma1);
|
|
while (line[comma2] == ' ')
|
|
++comma2;
|
|
Common::String step = line.substr(comma2 + 1, end - comma2);
|
|
debug("sound args = %s,%s,%s", name.c_str(), sample.c_str(), step.c_str());
|
|
_sounds.push_back(Sound(name, sample, atoi(step.c_str())));
|
|
}
|
|
|
|
void Dialog::processDirective(Common::String line) {
|
|
if (line.size() < 2 || line[1] == '@') {
|
|
debug("comment, bailing out");
|
|
return;
|
|
}
|
|
|
|
line.erase(0, 1);
|
|
|
|
if (line.hasPrefix("sound")) {
|
|
processSoundDirective(line);
|
|
} else {
|
|
DialogDefsType::const_iterator it = _dialogDefs.find(line);
|
|
if (it != _dialogDefs.end()) {
|
|
int value = it->_value;
|
|
_currentDef = line;
|
|
if (!_currentDef.hasPrefix("vybervarianty") && !_currentDef.hasPrefix("varianta")) {
|
|
_currentSoundIndex = -1;
|
|
|
|
for (uint s = 0; s < _sounds.size(); ++s) {
|
|
auto &sound = _sounds[s];
|
|
if (_currentDef.hasPrefixIgnoreCase(sound.Name)) {
|
|
_currentSoundIndex = s;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
debug("dialog value %s = %d (0x%04x), sample index: %d", line.c_str(), value, value, _currentSoundIndex);
|
|
_engine->getSystemVariable("dialog_var")->setInteger(value);
|
|
} else
|
|
warning("invalid dialog directive: %s", line.c_str());
|
|
}
|
|
}
|
|
|
|
Common::String Dialog::getNextDialogSound() {
|
|
if (_currentDef.empty())
|
|
return Common::String();
|
|
|
|
debug("getNextDialogSound %s %d", _currentDef.c_str(), _currentSoundIndex);
|
|
if (_currentSoundIndex < 0)
|
|
return Common::String();
|
|
|
|
auto &sound = _sounds[_currentSoundIndex];
|
|
auto &sample = sound.Sample;
|
|
auto currentSample = sample;
|
|
|
|
int carry = sound.Step;
|
|
for (auto pos = sample.size() - 1; pos > 0 && sample[pos] >= '0' && sample[pos] <= '9'; --pos) {
|
|
int d = sample[pos] - '0';
|
|
d += carry;
|
|
carry = d / 10;
|
|
sample.setChar('0' + (d % 10), pos);
|
|
if (carry == 0)
|
|
break;
|
|
}
|
|
if (carry != 0)
|
|
warning("sample index overflow, %s", sample.c_str());
|
|
|
|
debug("returning sample name %s", currentSample.c_str());
|
|
return currentSample + ".ogg";
|
|
}
|
|
|
|
} // namespace AGDS
|