scummvm/engines/trecision/text.cpp
2022-01-25 10:36:08 +02:00

481 lines
12 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 "common/config-manager.h"
#include "common/str.h"
#include "trecision/actor.h"
#include "trecision/animmanager.h"
#include "trecision/graphics.h"
#include "trecision/sound.h"
#include "trecision/trecision.h"
#include "trecision/text.h"
#include "trecision/scheduler.h"
#include "trecision/video.h"
namespace Trecision {
TextManager::TextManager(TrecisionEngine *vm) : _vm(vm) {
_someoneSpeakTime = 0;
_subStringAgain = false;
_talkTime = 0;
for (int i = 0; i < MAXSUBSTRING; ++i) {
for (int j = 0; j < MAXLENSUBSTRING; ++j) {
_subString[i][j] = 0;
}
}
_subStringUsed = 0;
_subStringStart = 0;
_curSentenceId = 0;
_curSubString = 0;
_talkingPersonId = 0;
}
TextManager::~TextManager() {
}
Common::Point TextManager::positionString(uint16 x, uint16 y, const char *string, bool characterFl) {
uint16 lenText = _vm->textLength(string);
Common::Point pos;
if (lenText > 960)
lenText = (lenText * 2 / 5);
else if (lenText > 320)
lenText = (lenText * 3 / 5);
if (x > (lenText >> 1))
x -= (lenText >> 1);
else
x = 0;
pos.x = CLIP<uint16>(x, 5, MAXX - lenText - 5);
pos.y = characterFl ? 0 : VIDEOTOP;
pos.y += y - 1; //15
if (pos.y <= VIDEOTOP)
pos.y = VIDEOTOP + 1;
return pos;
}
void TextManager::formattingSuperString() {
_subStringUsed = 0;
_subStringAgain = true;
while (_subStringAgain) {
formattingOneString();
++_subStringUsed;
}
}
void TextManager::formattingOneString() {
uint16 i;
memset(_subString[_subStringUsed], '\0', MAXLENSUBSTRING);
const uint16 available = (_superString.size() - _subStringStart);
for (i = 0; i < available; ++i) {
switch (_superString[i + _subStringStart]) {
case '\0':
_subStringAgain = false;
return;
case '@':
_subStringAgain = true;
_subStringStart += (i + 1);
return;
default:
_subString[_subStringUsed][i] = _superString[i + _subStringStart];
break;
}
}
_subString[_subStringUsed][i] = '\0';
_subStringAgain = false;
}
void TextManager::characterTalk(Common::String s) {
_vm->_flagSomeoneSpeaks = true;
_vm->_flagCharacterSpeak = true;
_vm->_flagSkipTalk = false;
_superString = s;
_subStringStart = 0;
_curSubString = 0;
formattingSuperString();
characterContinueTalk();
_vm->_scheduler->initCharacterQueue();
_vm->_actor->actorStop();
}
void TextManager::characterContinueTalk() {
Common::Point pos;
_vm->_flagSkipTalk = false;
_vm->_characterSpeakTime = _vm->_curTime;
_subStringAgain = (_curSubString < (_subStringUsed - 1));
if (_vm->_flagShowCharacter || _vm->_animMgr->isActionActive())
pos = positionString(_vm->_actor->_area[0], _vm->_actor->_area[2], _subString[_curSubString], true);
else
pos = positionString(MAXX / 2, 30, _subString[_curSubString], false);
clearLastText();
if (ConfMan.getBool("subtitles"))
addText(pos, _subString[_curSubString], COLOR_OBJECT);
if (!_vm->_flagDialogActive) {
if (_curSubString)
_lastFilename = Common::String::format("s%04d%c.wav", _curSentenceId, _curSubString + 'a');
else
_lastFilename = Common::String::format("s%04d.wav", _curSentenceId);
}
_talkTime = _vm->_soundMgr->talkStart(_lastFilename);
if (!_talkTime)
_talkTime = Common::String(_subString[_curSubString]).size() * 5 / 2 + 50;
++_curSubString;
_vm->_scheduler->doEvent(MC_STRING, ME_CHARACTERSPEAKING, MP_DEFAULT, 0, 0, 0, 0);
}
void TextManager::characterMute() {
_vm->_flagSomeoneSpeaks = false;
_vm->_flagCharacterSpeak = false;
_vm->_flagSkipTalk = false;
_vm->_characterSpeakTime = 0;
clearLastText();
_vm->_lastObj = 0;
_vm->_lastInv = 0;
redrawString();
_vm->_soundMgr->stopSoundType(kSoundTypeSpeech);
if ((_vm->_curRoom == kRoom12CU) || (_vm->_curRoom == kRoom13CU))
_vm->changeRoom(_vm->_oldRoom);
}
void TextManager::someoneContinueTalk() {
_someoneSpeakTime = _vm->_curTime;
_vm->_flagSkipTalk = false;
_subStringAgain = (_curSubString < (_subStringUsed - 1));
Common::Point pos;
if (_talkingPersonId)
pos = positionString(_vm->_obj[_talkingPersonId]._area.left, _vm->_obj[_talkingPersonId]._area.top, _subString[_curSubString], false);
else
pos = positionString(_vm->_actor->_area[0], _vm->_actor->_area[2], _subString[_curSubString], true);
clearLastText();
if (ConfMan.getBool("subtitles"))
addText(pos, _subString[_curSubString], HYELLOW);
if (_curSubString)
_lastFilename = Common::String::format("s%04d%c.wav", _curSentenceId, _curSubString + 'a');
else
_lastFilename = Common::String::format("s%04d.wav", _curSentenceId);
_talkTime = _vm->_soundMgr->talkStart(_lastFilename);
if (!_talkTime)
_talkTime = Common::String(_subString[_curSubString]).size() * 5 / 2 + 50;
++_curSubString;
_vm->_scheduler->doEvent(MC_STRING, ME_SOMEONESPEAKING, MP_DEFAULT, 0, 0, 0, 0);
}
void TextManager::someoneMute() {
_vm->_flagCharacterSpeak = false;
_vm->_flagSkipTalk = false;
_vm->_flagSomeoneSpeaks = false;
_someoneSpeakTime = 0;
clearLastText();
_vm->_lastObj = 0;
_vm->_lastInv = 0;
redrawString();
_vm->_soundMgr->stopSoundType(kSoundTypeSpeech);
}
// ******************************************************* //
void TextManager::doString() {
switch (_vm->_curMessage->_event) {
case ME_CHARACTERSPEAKING:
if (_vm->_flagCharacterSpeak) {
if (_vm->_flagSkipTalk || (_vm->_curTime > _talkTime + _vm->_characterSpeakTime)) {
if (_subStringAgain)
characterContinueTalk();
else
characterMute();
} else
_vm->reEvent();
}
break;
case ME_SOMEONESPEAKING:
if (_vm->_flagSomeoneSpeaks) {
if (_vm->_flagSkipTalk || (_vm->_curTime >= (_talkTime + _someoneSpeakTime))) {
if (_subStringAgain)
someoneContinueTalk();
else {
someoneMute();
}
} else
_vm->reEvent();
}
break;
default:
break;
}
}
void TextManager::showObjName(uint16 obj, bool show) {
static const char *dunno = "?";
Common::String desc;
if (_vm->_flagSomeoneSpeaks)
return;
if (_vm->_lastInv) {
clearLastText();
_vm->_lastInv = 0;
}
if (_vm->_flagUseWithStarted) {
if (!show) {
clearLastText();
_vm->_lastObj = obj;
return;
}
if ((_vm->_obj[_vm->_curObj].isFlagRoomOut() || _vm->_obj[_vm->_curObj].isFlagRoomIn()) && !_vm->_obj[_vm->_curObj].isFlagExamine())
return;
desc = _vm->_sysText[kMessageUse];
if (_vm->_useWithInv[USED])
desc += _vm->_objName[_vm->_inventoryObj[_vm->_useWith[USED]]._name];
else if (_vm->_obj[_vm->_useWith[USED]].isModeHidden())
desc += dunno;
else
desc += _vm->_objName[_vm->_obj[_vm->_useWith[USED]]._name];
desc += _vm->_sysText[kMessageWith];
if (obj && (_vm->_useWithInv[USED] || (obj != _vm->_useWith[USED]))) {
if (_vm->_obj[obj].isModeHidden())
desc += dunno;
else
desc += _vm->_objName[_vm->_obj[obj]._name];
}
_vm->_lastObj = (obj | 0x8000);
const uint16 lenText = _vm->textLength(desc);
const Common::Point pos(CLIP(320 - (lenText / 2), 2, MAXX - 2 - lenText), MAXY - CARHEI);
if (_vm->_lastObj)
clearLastText();
addText(pos, desc.c_str(), COLOR_INVENTORY);
} else {
if (!obj || !show) {
clearLastText();
_vm->_lastObj = obj;
return;
}
if (obj == _vm->_lastObj)
return;
if (!_vm->_obj[obj].isFlagExamine()) {
if (_vm->_obj[obj].isFlagDone() || _vm->_room[_vm->_obj[obj]._goRoom].isDone()) {
desc = _vm->_sysText[kMessageGoto];
if (_vm->_obj[obj].isModeHidden())
desc += dunno;
else
desc += _vm->_objName[_vm->_obj[obj]._name];
} else
desc = _vm->_sysText[kMessageGoto2];
} else if (_vm->_obj[obj].isModeHidden())
desc = dunno;
else
desc = _vm->_objName[_vm->_obj[obj]._name];
const uint16 x = (_vm->_obj[obj]._area.left + _vm->_obj[obj]._area.right) / 2;
const uint16 y = (obj == oWHEELS2C) ? 187 : _vm->_obj[obj]._area.top;
Common::Point pos = positionString(x, y, desc.c_str(), false);
if (_vm->_lastObj)
clearLastText();
_vm->_lastObj = obj;
addText(pos, desc.c_str(), COLOR_OBJECT);
}
}
void TextManager::someoneSay(uint16 sentence, uint16 person) {
_talkingPersonId = person;
_vm->_flagSomeoneSpeaks = true;
_vm->_flagSkipTalk = false;
_curSentenceId = sentence;
_superString = _vm->_sentence[sentence];
_subStringStart = 0;
_curSubString = 0;
formattingSuperString();
someoneContinueTalk();
}
void TextManager::characterSay(uint16 i) {
_curSentenceId = i;
// if he took some action
if (_vm->_sentence[i][0] == '*' && !_vm->_animMgr->isActionActive())
_vm->startCharacterAction(hBOH, 0, 0, 0);
else
characterTalk(_vm->_sentence[i]);
}
void TextManager::characterSayInAction(uint16 ss) {
const char *s = _vm->_sentence[ss];
if (s[0] == '*')
return;
_curSentenceId = ss;
_vm->_flagSomeoneSpeaks = true;
_vm->_flagCharacterSpeak = true;
_vm->_flagSkipTalk = false;
_superString = s;
_subStringStart = 0;
_curSubString = 0;
formattingSuperString();
characterContinueTalk();
}
void TextManager::addText(Common::Point pos, const char *text, uint16 textCol) {
StackText t;
t._x = pos.x;
t._y = pos.y;
t._textColor = textCol;
t._clear = false;
t._text = text;
_textStack.push_back(t);
}
void TextManager::clearLastText() {
if (!_textStack.empty()) {
if (!_textStack.back()._clear)
// The last entry is a string to be shown, remove it
_textStack.pop_back();
} else {
StackText t;
t._clear = true;
_textStack.push_back(t);
}
}
void TextManager::drawText(StackText *text) {
_curString._rect.left = text->_x;
_curString._rect.top = text->_y;
_curString._rect.setWidth(_vm->textLength(text->_text));
int16 w = _curString._rect.width();
if (text->_y == MAXY - CARHEI && w > 600)
w = w * 3 / 5;
else if (text->_y != MAXY - CARHEI && w > 960)
w = w * 2 / 5;
else if (text->_y != MAXY - CARHEI && w > 320)
w = w * 3 / 5;
_curString._rect.setWidth(w);
_curString._text = text->_text;
uint16 height = _curString.calcHeight(_vm);
_curString._subtitleRect = Common::Rect(_curString._rect.width(), height);
_curString._rect.setHeight(height);
_curString._textColor = text->_textColor;
if (_curString._rect.top <= height)
_curString._rect.top += height;
else
_curString._rect.top -= height;
if (_curString._rect.top <= VIDEOTOP)
_curString._rect.top = VIDEOTOP + 1;
_vm->_textStatus |= TEXT_DRAW;
}
void TextManager::clearText() {
if (_oldString._text.empty() && !_curString._text.empty()) {
_oldString.set(&_curString);
_curString._text.clear();
_vm->_textStatus |= TEXT_DEL;
}
}
void TextManager::drawTexts() {
for (Common::List<StackText>::iterator it = _textStack.begin(); it != _textStack.end(); ++it) {
if (it->_clear)
clearText();
else
drawText(&*it);
}
}
void TextManager::redrawString() {
if (!_vm->_flagDialogActive && !_vm->_flagDialogMenuActive && !_vm->_flagSomeoneSpeaks && !_vm->_flagScriptActive && _vm->_graphicsMgr->isCursorVisible()) {
if (_vm->isInventoryArea(_vm->_mousePos))
_vm->showIconName();
else {
_vm->checkMask(_vm->_mousePos);
showObjName(_vm->_curObj, true);
}
}
}
void TextManager::clearTextStack() {
_textStack.clear();
}
void TextManager::drawCurString() {
_curString.draw(_vm);
_vm->_graphicsMgr->addDirtyRect(_curString._rect, false);
}
Common::Rect TextManager::getOldTextRect() const {
return _oldString._rect;
}
void TextManager::clearOldText() {
_oldString._text.clear();
}
} // End of namespace Trecision