scummvm/engines/twine/text.cpp

820 lines
21 KiB
C++
Raw Normal View History

/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "twine/text.h"
#include "common/algorithm.h"
2020-11-06 20:49:18 +01:00
#include "common/endian.h"
#include "common/memstream.h"
#include "common/scummsys.h"
#include "common/str.h"
#include "common/system.h"
#include "twine/hqr.h"
2020-10-23 13:44:38 +02:00
#include "twine/input.h"
2020-10-24 12:32:00 +02:00
#include "twine/interface.h"
#include "twine/menu.h"
#include "twine/renderer.h"
#include "twine/resources.h"
#include "twine/scene.h"
#include "twine/screens.h"
#include "twine/sound.h"
#include "twine/twine.h"
namespace TwinE {
2020-10-14 14:20:38 +02:00
/** FLA movie extension */
#define VOX_EXT ".vox"
#define INDEXOFFSET 0
#define DIALOGSOFFSET 1
2020-12-10 20:27:44 +01:00
Text::Text(TwinEEngine *engine) : _engine(engine) {
Common::fill(&currMenuTextBuffer[0], &currMenuTextBuffer[256], 0);
}
2020-11-17 19:15:36 +01:00
Text::~Text() {
free(dialTextPtr);
free(dialOrderPtr);
}
void Text::initVoxBank(int32 bankIdx) {
static const char *LanguageSuffixTypes[] = {
"sys",
"cre",
"gam", // global game voices (e.g. inventory descriptions)
"000", // Citadel Island voices
"001", // Principal Island voices
"002", // White Leaf Desert voices
"003", // Proxima Island voices
"004", // Rebellion Island voices
"005", // Hamalayi Mountains - sourthern range voices
"006", // Hamalayi Mountains - northern range voices
"007", // Tippett Island voices
"008", // Brundle Island voices
"009", // Fortress Island voices
"010", // Polar Island voices
"011" //
};
if (bankIdx < 0 || bankIdx >= ARRAYSIZE(LanguageSuffixTypes)) {
error("bankIdx is out of bounds: %i", bankIdx);
}
2020-10-14 14:20:38 +02:00
// get the correct vox hqr file
currentVoxBankFile = Common::String::format("%s%s" VOX_EXT, LanguageTypes[_engine->cfgfile.LanguageId].id, LanguageSuffixTypes[bankIdx]);
// TODO: loop through other languages and take the scummvm settings regarding voices into account...
2020-10-14 14:20:38 +02:00
// TODO check the rest to reverse
}
bool Text::initVoxToPlay(int32 index) { // setVoxFileAtDigit
currDialTextEntry = 0;
2020-10-14 14:20:38 +02:00
voxHiddenIndex = 0;
2020-10-24 12:32:00 +02:00
hasHiddenVox = false;
2020-10-14 14:20:38 +02:00
Common::MemoryReadStream stream((const byte *)dialOrderPtr, dialOrderSize);
2020-10-14 14:20:38 +02:00
// choose right text from order index
for (int32 i = 0; i < numDialTextEntries; i++) {
int32 orderIdx = stream.readSint16LE();
2020-10-14 14:20:38 +02:00
if (orderIdx == index) {
currDialTextEntry = i;
2020-10-14 14:20:38 +02:00
break;
}
}
_engine->_sound->playVoxSample(currDialTextEntry);
2020-10-14 14:20:38 +02:00
return true;
2020-10-14 14:20:38 +02:00
}
bool Text::playVox(int32 index) {
if (!_engine->cfgfile.Voice) {
return false;
}
if (hasHiddenVox && !_engine->_sound->isSamplePlaying(index)) {
_engine->_sound->playVoxSample(index);
return true;
2020-10-14 14:20:38 +02:00
}
return false;
2020-10-14 14:20:38 +02:00
}
bool Text::playVoxSimple(int32 index) {
if (_engine->_sound->isSamplePlaying(index)) {
return true;
2020-10-14 14:20:38 +02:00
}
return playVox(index);
2020-10-14 14:20:38 +02:00
}
bool Text::stopVox(int32 index) {
if (!_engine->_sound->isSamplePlaying(index)) {
return false;
}
2020-10-24 12:32:00 +02:00
hasHiddenVox = false;
_engine->_sound->stopSample(index);
return true;
2020-10-14 14:20:38 +02:00
}
void Text::initTextBank(int32 bankIdx) {
2020-10-14 14:20:38 +02:00
// don't load if we already have the dialogue text bank loaded
if (bankIdx == currentBankIdx) {
2020-10-14 14:20:38 +02:00
return;
}
2020-10-14 14:20:38 +02:00
currentBankIdx = bankIdx;
// get index according with language
2020-10-27 17:33:09 +01:00
const int32 size = _engine->isLBA1() ? 28 : 30;
2020-11-01 22:25:16 +01:00
// the text banks indices are split into index and dialogs - each entry thus consists of two entries in the hqr
// every 28 entries starts a new language
const int32 languageIndex = _engine->cfgfile.LanguageId * size + (int)bankIdx * 2;
dialOrderSize = HQR::getAllocEntry((uint8 **)&dialOrderPtr, Resources::HQR_TEXT_FILE, languageIndex + INDEXOFFSET);
if (dialOrderSize == 0) {
2020-10-22 13:30:06 +02:00
warning("Failed to initialize text bank %i from file %s", languageIndex, Resources::HQR_TEXT_FILE);
return;
}
2020-10-14 14:20:38 +02:00
numDialTextEntries = dialOrderSize / 2;
2020-10-14 14:20:38 +02:00
if (HQR::getAllocEntry((uint8 **)&dialTextPtr, Resources::HQR_TEXT_FILE, languageIndex + DIALOGSOFFSET) == 0) {
2020-10-22 13:30:06 +02:00
warning("Failed to initialize additional text bank %i from file %s", languageIndex + 1, Resources::HQR_TEXT_FILE);
return;
}
initVoxBank(bankIdx);
2020-10-14 14:20:38 +02:00
}
void Text::drawCharacter(int32 x, int32 y, uint8 character) { // drawCharacter
const uint8 sizeX = getCharWidth(character);
uint8 sizeY = getCharHeight(character);
Common::MemoryReadStream stream(_engine->_resources->fontPtr, _engine->_resources->fontBufSize);
stream.seek(character * 4);
stream.seek(stream.readSint16LE());
stream.skip(2);
x += stream.readByte();
y += stream.readByte();
2020-10-14 14:20:38 +02:00
2020-11-22 20:31:24 +01:00
const uint8 usedColor = _dialTextColor;
2020-10-14 14:20:38 +02:00
2020-11-16 18:01:16 +01:00
uint8 *screen2 = (uint8 *)_engine->frontVideoBuffer.getBasePtr(x, y);
2020-10-14 14:20:38 +02:00
int32 tempX = x;
int32 tempY = y;
2020-10-14 14:20:38 +02:00
const int32 toNextLine = SCREEN_WIDTH - sizeX;
2020-10-14 14:20:38 +02:00
do {
uint8 index = stream.readByte();
2020-10-14 14:20:38 +02:00
do {
const uint8 jump = stream.readByte();
screen2 += jump;
2020-10-14 14:20:38 +02:00
tempX += jump;
if (--index == 0) {
screen2 += toNextLine;
2020-10-14 14:20:38 +02:00
tempY++;
tempX = x;
sizeY--;
if (sizeY <= 0) {
return;
}
break;
}
uint8 number = stream.readByte();
for (uint8 i = 0; i < number; i++) {
if (tempX >= SCREEN_TEXTLIMIT_LEFT && tempX < SCREEN_TEXTLIMIT_RIGHT && tempY >= SCREEN_TEXTLIMIT_TOP && tempY < SCREEN_TEXTLIMIT_BOTTOM) {
*((uint8 *)_engine->frontVideoBuffer.getBasePtr(tempX, tempY)) = usedColor;
2020-10-14 14:20:38 +02:00
}
screen2++;
tempX++;
}
if (--index == 0) {
screen2 += toNextLine;
tempY++;
tempX = x;
2020-10-14 14:20:38 +02:00
sizeY--;
if (sizeY <= 0) {
return;
2020-10-14 14:20:38 +02:00
}
break;
2020-10-14 14:20:38 +02:00
}
} while (1);
} while (1);
}
void Text::drawCharacterShadow(int32 x, int32 y, uint8 character, int32 color) { // drawDoubleLetter
2020-11-22 21:05:59 +01:00
if (character == ' ') {
return;
}
// shadow color
setFontColor(0);
drawCharacter(x + 2, y + 4, character);
2020-10-14 14:20:38 +02:00
2020-11-22 21:05:59 +01:00
// text color
setFontColor(color);
drawCharacter(x, y, character);
2020-10-14 14:20:38 +02:00
2020-11-22 21:05:59 +01:00
int32 left = x;
int32 top = y;
// FIXME: get right font size
int32 right = x + 32;
int32 bottom = y + 38;
2020-10-14 14:20:38 +02:00
2020-11-22 21:05:59 +01:00
_engine->copyBlockPhys(left, top, right, bottom);
2020-10-14 14:20:38 +02:00
}
2020-11-22 21:05:59 +01:00
void Text::drawText(int32 x, int32 y, const char *dialogue) {
// if the font is not defined
if (_engine->_resources->fontPtr == nullptr) {
2020-10-14 14:20:38 +02:00
return;
}
2020-10-14 14:20:38 +02:00
do {
2020-10-24 12:32:00 +02:00
const uint8 currChar = (uint8) * (dialogue++); // read the next char from the string
2020-10-14 14:20:38 +02:00
if (currChar == '\0') {
2020-10-14 14:20:38 +02:00
break;
}
2020-10-14 14:20:38 +02:00
if (currChar == ' ') {
2020-11-22 20:31:24 +01:00
x += _dialCharSpace;
} else {
2020-11-26 23:10:06 +01:00
int32 dialTextSize = getCharWidth(currChar);
drawCharacter(x, y, currChar); // draw the character on screen
2020-10-14 14:20:38 +02:00
// add the length of the space between 2 characters
2020-11-22 20:31:24 +01:00
x += _dialSpaceBetween;
2020-10-14 14:20:38 +02:00
// add the length of the current character
2020-11-26 23:10:06 +01:00
x += dialTextSize;
2020-10-14 14:20:38 +02:00
}
} while (1);
}
int32 Text::getTextSize(const char *dialogue) { // SizeFont
2020-11-26 23:10:06 +01:00
int32 dialTextSize = 0;
2020-10-14 14:20:38 +02:00
do {
2020-11-22 21:05:59 +01:00
const uint8 currChar = (uint8) * (dialogue++);
if (currChar == 0) {
2020-10-14 14:20:38 +02:00
break;
2020-11-22 21:05:59 +01:00
}
2020-10-14 14:20:38 +02:00
if (currChar == ' ') {
2020-11-26 23:10:06 +01:00
dialTextSize += _dialCharSpace;
2020-10-14 14:20:38 +02:00
} else {
2020-11-26 23:10:06 +01:00
dialTextSize += _dialSpaceBetween;
dialTextSize += getCharWidth(currChar);
2020-10-14 14:20:38 +02:00
}
} while (1);
2020-11-26 23:10:06 +01:00
return dialTextSize;
2020-10-14 14:20:38 +02:00
}
void Text::initDialogueBox() { // InitDialWindow
2020-11-25 21:36:18 +01:00
_engine->_interface->blitBox(_dialTextBox, _engine->workVideoBuffer, _engine->frontVideoBuffer);
2020-10-14 14:20:38 +02:00
if (drawTextBoxBackground) {
2020-11-25 18:20:38 +01:00
_engine->_menu->drawBox(_dialTextBox);
Common::Rect rect(_dialTextBox);
rect.grow(-1);
_engine->_interface->drawTransparentBox(rect, 3);
2020-10-14 14:20:38 +02:00
}
2020-11-25 18:20:38 +01:00
_engine->copyBlockPhys(_dialTextBox);
_fadeInCharactersPos = 0;
2020-11-25 21:36:18 +01:00
_engine->_interface->blitBox(_dialTextBox, _engine->frontVideoBuffer, _engine->workVideoBuffer);
2020-10-14 14:20:38 +02:00
}
void Text::initInventoryDialogueBox() { // SecondInitDialWindow
2020-11-25 21:36:18 +01:00
_engine->_interface->blitBox(_dialTextBox, _engine->workVideoBuffer, _engine->frontVideoBuffer);
2020-11-25 18:20:38 +01:00
_engine->copyBlockPhys(_dialTextBox);
_fadeInCharactersPos = 0;
2020-10-14 14:20:38 +02:00
}
void Text::initInventoryText(int index) {
// 100 if the offset for the inventory item descriptions
//
initText(100 + index);
}
void Text::initItemFoundText(int index) {
initText(100 + index);
}
2020-10-14 14:20:38 +02:00
// TODO: refactor this code
void Text::initText(int32 index) {
2020-10-14 14:20:38 +02:00
if (!getText(index)) {
2020-11-22 18:54:05 +01:00
_hasValidTextHandle = false;
2020-10-14 14:20:38 +02:00
return;
}
_progressiveTextBufferPtr = _progressiveTextBuffer;
2020-10-14 14:20:38 +02:00
2020-11-22 18:54:05 +01:00
_hasValidTextHandle = true;
2020-10-14 14:20:38 +02:00
2020-11-22 20:31:24 +01:00
_dialTextBoxCurrentLine = 0;
_progressiveTextBuffer[0] = '\0';
_fadeInCharactersPos = 0;
_dialTextXPos = _dialTextBox.left + 8;
2020-11-22 20:31:24 +01:00
_progressiveTextEnd = false;
_progressiveTextNextPage = false;
_dialTextYPos = _dialTextBox.top + 8;
2020-12-15 18:23:37 +01:00
_progressiveTextNextWord = _currDialTextPtr;
2020-10-14 14:20:38 +02:00
// lba font is get while engine start
setFontParameters(2, 7);
}
void Text::initProgressiveTextBuffer() {
Common::fill(&_progressiveTextBuffer[0], &_progressiveTextBuffer[256], ' ');
// the end of the buffer defines how fast the next page is shown - as the
// whitespaces are handled in the fade in process, too. But we need at least 32 chars,
// to completly fade in the last characters of a full page (see TEXT_MAX_FADE_IN_CHR)
_progressiveTextBuffer[255] = '\0';
_progressiveTextBufferPtr = _progressiveTextBuffer;
2020-11-22 20:31:24 +01:00
_dialTextBoxCurrentLine = 0;
2020-10-14 14:20:38 +02:00
}
void Text::fillFadeInBuffer(int16 x, int16 y, int16 chr) {
if (_fadeInCharactersPos < TEXT_MAX_FADE_IN_CHR) {
_fadeInCharacters[_fadeInCharactersPos].chr = chr;
_fadeInCharacters[_fadeInCharactersPos].x = x;
_fadeInCharacters[_fadeInCharactersPos].y = y;
_fadeInCharactersPos++;
return;
}
2020-10-14 14:20:38 +02:00
int32 counter2 = 0;
while (counter2 < TEXT_MAX_FADE_IN_CHR - 1) {
const int32 var1 = (counter2 + 1);
const int32 var2 = counter2;
_fadeInCharacters[var2] = _fadeInCharacters[var1];
counter2++;
2020-10-14 14:20:38 +02:00
}
_fadeInCharacters[TEXT_MAX_FADE_IN_CHR - 1].chr = chr;
_fadeInCharacters[TEXT_MAX_FADE_IN_CHR - 1].x = x;
_fadeInCharacters[TEXT_MAX_FADE_IN_CHR - 1].y = y;
2020-10-14 14:20:38 +02:00
}
Text::WordSize Text::getWordSize(const char *completeText, char *wordBuf, int32 wordBufSize) {
2020-10-14 14:20:38 +02:00
int32 temp = 0;
const char *arg2Save = wordBuf;
2020-10-14 14:20:38 +02:00
while (*completeText != '\0' && *completeText != '\1' && *completeText != ' ') {
2020-10-14 14:20:38 +02:00
temp++;
*wordBuf++ = *completeText++;
if (temp >= wordBufSize - 1) {
break;
}
2020-10-14 14:20:38 +02:00
}
WordSize size;
size.inChar = temp;
*wordBuf = '\0';
size.inPixel = getTextSize(arg2Save);
return size;
2020-10-14 14:20:38 +02:00
}
void Text::processTextLine() {
2020-12-15 18:23:37 +01:00
const char *buffer = _progressiveTextNextWord;
2020-11-22 20:31:24 +01:00
_dialCharSpace = 7;
2020-12-15 19:25:04 +01:00
bool moreWordsFollowing = true;
2020-10-14 14:20:38 +02:00
int32 lineBreakX = 0;
2020-12-15 19:25:04 +01:00
int32 spaceCharCount = 0;
2020-11-22 22:00:19 +01:00
_progressiveTextBuffer[0] = '\0';
2020-10-14 14:20:38 +02:00
for (;;) {
2020-10-20 23:52:15 +02:00
if (*buffer == ' ') {
2020-10-14 14:20:38 +02:00
buffer++;
continue;
}
2020-11-20 19:03:53 +01:00
if (*buffer == '\0') {
break;
}
if (*buffer == '\1') {
2020-12-15 19:25:04 +01:00
moreWordsFollowing = false;
buffer++;
break;
}
2020-10-14 14:20:38 +02:00
2020-12-15 18:23:37 +01:00
_progressiveTextNextWord = buffer;
char wordBuf[256] = "";
WordSize wordSize = getWordSize(buffer, wordBuf, sizeof(wordBuf));
2020-12-15 18:18:28 +01:00
if (lineBreakX + _dialCharSpace + wordSize.inPixel >= _dialTextBoxMaxX) {
break;
}
if (*wordBuf == '@') {
2020-12-15 19:25:04 +01:00
moreWordsFollowing = false;
buffer++;
if (lineBreakX == 0) {
lineBreakX = 7;
2020-11-22 22:00:19 +01:00
*(_progressiveTextBuffer + 0) = ' ';
*(_progressiveTextBuffer + 1) = ' ';
}
if (wordBuf[1] == 'P') {
_dialTextBoxCurrentLine = _dialTextBoxLines;
buffer++;
2020-10-14 14:20:38 +02:00
}
break;
}
buffer += wordSize.inChar;
2020-12-15 18:23:37 +01:00
_progressiveTextNextWord = buffer;
2020-11-22 22:04:07 +01:00
strncat(_progressiveTextBuffer, wordBuf, sizeof(_progressiveTextBuffer) - strlen(_progressiveTextBuffer) - 1);
strncat(_progressiveTextBuffer, " ", sizeof(_progressiveTextBuffer) - strlen(_progressiveTextBuffer) - 1); // not 100% accurate
2020-12-15 19:25:04 +01:00
spaceCharCount++;
lineBreakX += wordSize.inPixel + _dialCharSpace;
2020-12-15 18:23:37 +01:00
if (*_progressiveTextNextWord != '\0') {
_progressiveTextNextWord++;
continue;
2020-10-14 14:20:38 +02:00
}
break;
}
2020-12-15 19:25:04 +01:00
if (spaceCharCount > 0) {
spaceCharCount--;
}
2020-10-14 14:20:38 +02:00
2020-12-15 19:25:04 +01:00
if (*_progressiveTextNextWord != '\0' && moreWordsFollowing) {
if (spaceCharCount <= 0) {
spaceCharCount = 1;
}
2020-12-15 19:25:04 +01:00
// split the remaining space between the words
_dialCharSpace += (_dialTextBoxMaxX - lineBreakX) / spaceCharCount;
2020-10-14 14:20:38 +02:00
}
2020-12-15 18:23:37 +01:00
_progressiveTextNextWord = buffer;
2020-10-14 14:20:38 +02:00
_progressiveTextBufferPtr = _progressiveTextBuffer;
2020-10-14 14:20:38 +02:00
}
void Text::renderContinueReadingTriangle() {
2020-11-25 18:20:38 +01:00
const int32 right = _dialTextBox.right - 3;
const int32 left = _dialTextBox.right - 24;
const int32 top = _dialTextBox.bottom - 24;
const int32 bottom = _dialTextBox.bottom - 3;
2020-11-08 13:31:57 +01:00
2020-11-29 14:26:56 +01:00
Vertex vertices[3];
vertices[0].colorIndex = _dialTextStopColor;
vertices[0].x = right;
vertices[0].y = top;
vertices[1].colorIndex = _dialTextStopColor;
vertices[1].x = left;
vertices[1].y = bottom;
vertices[2].colorIndex = _dialTextStartColor;
vertices[2].x = _engine->_renderer->vertexCoordinates[1];
vertices[2].y = _engine->_renderer->vertexCoordinates[5];
2020-11-29 18:06:29 +01:00
CmdRenderPolygon polygon;
2020-11-29 14:26:56 +01:00
polygon.numVertices = 3;
polygon.colorIndex = _dialTextStopColor;
polygon.renderType = POLYGONTYPE_FLAT;
_engine->_renderer->renderPolygons(polygon, vertices);
2020-10-14 14:20:38 +02:00
_engine->copyBlockPhys(Common::Rect(left, top, right, bottom));
2020-10-14 14:20:38 +02:00
}
void Text::fadeInCharacters(int32 counter, int32 fontColor) {
2020-10-14 14:20:38 +02:00
while (--counter >= 0) {
const BlendInCharacter *ptr = &_fadeInCharacters[counter];
2020-10-20 23:52:15 +02:00
setFontColor(fontColor);
drawCharacterShadow(ptr->x, ptr->y, ptr->chr, fontColor);
2020-11-22 20:31:24 +01:00
fontColor -= _dialTextStepSize;
if (fontColor > _dialTextStopColor) {
fontColor = _dialTextStopColor;
2020-10-20 23:52:15 +02:00
}
2020-11-20 19:03:53 +01:00
}
2020-10-14 14:20:38 +02:00
}
int32 Text::getCharWidth(uint8 chr) const {
Common::MemoryReadStream stream(_engine->_resources->fontPtr, _engine->_resources->fontBufSize);
stream.seek(chr * 4);
stream.seek(stream.readSint16LE());
return stream.readByte();
2020-10-14 14:20:38 +02:00
}
int32 Text::getCharHeight(uint8 chr) const {
Common::MemoryReadStream stream(_engine->_resources->fontPtr, _engine->_resources->fontBufSize);
stream.seek(chr * 4);
stream.seek(stream.readSint16LE() + 1);
return stream.readByte();
}
2020-10-14 14:20:38 +02:00
// TODO: refactor this code
2020-12-15 19:26:22 +01:00
ProgressiveTextState Text::updateProgressiveText() {
2020-11-22 18:54:05 +01:00
if (!_hasValidTextHandle) {
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::End;
2020-10-14 14:20:38 +02:00
}
if (*_progressiveTextBufferPtr == '\0') {
2020-11-22 20:31:24 +01:00
if (_progressiveTextEnd) {
if (renderTextTriangle) {
renderContinueReadingTriangle();
2020-10-14 14:20:38 +02:00
}
2020-11-22 18:54:05 +01:00
_hasValidTextHandle = false;
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::End;
2020-10-14 14:20:38 +02:00
}
2020-11-22 20:31:24 +01:00
if (_progressiveTextNextPage) {
2020-11-25 21:36:18 +01:00
_engine->_interface->blitBox(_dialTextBox, _engine->workVideoBuffer, _engine->frontVideoBuffer);
2020-11-25 18:20:38 +01:00
_engine->copyBlockPhys(_dialTextBox);
_fadeInCharactersPos = 0;
2020-11-22 20:31:24 +01:00
_progressiveTextNextPage = false;
_dialTextXPos = _dialTextBox.left + 8;
_dialTextYPos = _dialTextBox.top + 8;
2020-10-14 14:20:38 +02:00
}
2020-12-15 18:23:37 +01:00
if (*_progressiveTextNextWord == '\0') {
2020-10-14 14:20:38 +02:00
initProgressiveTextBuffer();
2020-11-22 20:31:24 +01:00
_progressiveTextEnd = true;
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::UNK1;
2020-10-14 14:20:38 +02:00
}
processTextLine();
}
2020-12-17 08:55:41 +01:00
const char currentChar = *_progressiveTextBufferPtr;
2020-10-14 14:20:38 +02:00
// RECHECK this later
2020-12-17 08:55:41 +01:00
if (currentChar == '\0') {
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::UNK1;
2020-10-14 14:20:38 +02:00
}
2020-12-17 08:55:41 +01:00
fillFadeInBuffer(_dialTextXPos, _dialTextYPos, currentChar);
fadeInCharacters(_fadeInCharactersPos, _dialTextStartColor);
2020-12-17 08:55:41 +01:00
int8 charWidth = getCharWidth(currentChar);
2020-10-14 14:20:38 +02:00
if (currentChar == ' ') {
_dialTextXPos += _dialCharSpace + 1;
2020-10-14 14:20:38 +02:00
} else {
_dialTextXPos += charWidth + 2;
2020-10-14 14:20:38 +02:00
}
// next character
_progressiveTextBufferPtr++;
2020-10-14 14:20:38 +02:00
if (*_progressiveTextBufferPtr != '\0') {
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::UNK1;
}
2020-10-14 14:20:38 +02:00
2020-11-22 20:31:24 +01:00
const int32 lineHeight = 38;
_dialTextYPos += lineHeight;
_dialTextXPos = _dialTextBox.left + 8;
2020-10-14 14:20:38 +02:00
2020-11-22 20:31:24 +01:00
if (_progressiveTextNextPage && !_progressiveTextEnd) {
renderContinueReadingTriangle();
2020-12-15 21:17:01 +01:00
return ProgressiveTextState::NextPage;
2020-10-14 14:20:38 +02:00
}
2020-11-22 20:31:24 +01:00
_dialTextBoxCurrentLine++;
if (_dialTextBoxCurrentLine < _dialTextBoxLines) {
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::UNK1;
2020-10-14 14:20:38 +02:00
}
initProgressiveTextBuffer();
2020-11-22 20:31:24 +01:00
_progressiveTextNextPage = true;
2020-10-14 14:20:38 +02:00
2020-12-15 18:23:37 +01:00
if (*_progressiveTextNextWord == '\0') {
2020-11-22 20:31:24 +01:00
_progressiveTextEnd = true;
2020-10-14 14:20:38 +02:00
}
2020-12-15 19:26:22 +01:00
return ProgressiveTextState::UNK1;
2020-10-14 14:20:38 +02:00
}
// TODO: refactor this code
bool Text::drawTextFullscreen(int32 index) {
ScopedKeyMap scoped(_engine, cutsceneKeyMapId);
_engine->_interface->saveClip();
_engine->_interface->resetClip();
_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
2020-10-14 14:20:38 +02:00
// get right VOX entry index
initVoxToPlay(index);
2020-10-14 14:20:38 +02:00
bool aborted = false;
2020-10-14 14:20:38 +02:00
// if we don't display text, than still plays vox file
if (_engine->cfgfile.FlagDisplayText) {
2020-10-14 14:20:38 +02:00
initText(index);
initDialogueBox();
2020-12-15 19:26:22 +01:00
ProgressiveTextState printedText;
for (;;) {
_engine->readKeys();
2020-11-22 19:04:32 +01:00
printedText = updateProgressiveText();
2020-10-14 14:20:38 +02:00
playVox(currDialTextEntry);
2020-12-15 19:26:22 +01:00
if (printedText == ProgressiveTextState::End && !_engine->_sound->isSamplePlaying(currDialTextEntry)) {
2020-10-14 14:20:38 +02:00
break;
}
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
aborted = true;
break;
}
_engine->_system->delayMillis(1);
}
2020-10-24 12:32:00 +02:00
hasHiddenVox = false;
2020-10-14 14:20:38 +02:00
2020-11-22 18:54:05 +01:00
_hasValidTextHandle = false;
2020-10-14 14:20:38 +02:00
2020-12-15 19:26:22 +01:00
if (printedText == ProgressiveTextState::End) {
stopVox(currDialTextEntry);
// wait displaying text
for (;;) {
_engine->readKeys();
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
aborted = true;
break;
}
_engine->_system->delayMillis(1);
}
}
2020-10-14 14:20:38 +02:00
} else { // RECHECK THIS
2020-10-24 12:32:00 +02:00
while (playVox(currDialTextEntry)) {
_engine->readKeys();
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
aborted = true;
break;
}
2020-10-24 12:32:00 +02:00
_engine->_system->delayMillis(1);
}
2020-10-24 12:32:00 +02:00
hasHiddenVox = false;
2020-10-14 14:20:38 +02:00
voxHiddenIndex = 0;
}
stopVox(currDialTextEntry);
2020-10-14 14:20:38 +02:00
_engine->_interface->loadClip();
return aborted;
2020-10-14 14:20:38 +02:00
}
void Text::setFontParameters(int32 spaceBetween, int32 charSpace) {
2020-11-22 20:31:24 +01:00
_dialSpaceBetween = spaceBetween;
_dialCharSpace = charSpace;
2020-10-14 14:20:38 +02:00
}
void Text::setFontCrossColor(int32 color) {
2020-11-22 20:31:24 +01:00
_dialTextStepSize = -1;
_dialTextBufferSize = 14;
_dialTextStartColor = color << 4;
2020-11-22 22:04:07 +01:00
_dialTextStopColor = _dialTextStartColor + 12;
2020-10-14 14:20:38 +02:00
}
void Text::setFontColor(int32 color) {
2020-11-22 20:31:24 +01:00
_dialTextColor = color;
2020-10-14 14:20:38 +02:00
}
void Text::setTextCrossColor(int32 stopColor, int32 startColor, int32 stepSize) {
2020-11-22 20:31:24 +01:00
_dialTextStartColor = startColor;
_dialTextStopColor = stopColor;
_dialTextStepSize = stepSize;
_dialTextBufferSize = ((startColor - stopColor) + 1) / stepSize;
2020-10-14 14:20:38 +02:00
}
bool Text::getText(int32 index) {
const int16 *localTextBuf = (const int16 *)dialTextPtr;
const int16 *localOrderBuf = (const int16 *)dialOrderPtr;
2020-10-14 14:20:38 +02:00
2020-11-22 20:42:20 +01:00
const int32 numEntries = numDialTextEntries;
int32 currIdx = 0;
2020-10-14 14:20:38 +02:00
// choose right text from order index
do {
const int16 orderIdx = READ_LE_UINT16(localOrderBuf);
2020-10-20 23:52:15 +02:00
if (orderIdx == index) {
2020-10-14 14:20:38 +02:00
break;
2020-10-20 23:52:15 +02:00
}
2020-10-14 14:20:38 +02:00
currIdx++;
localOrderBuf++;
2020-11-22 20:42:20 +01:00
} while (currIdx < numEntries);
2020-10-14 14:20:38 +02:00
if (currIdx >= numEntries) {
return false;
}
2020-10-14 14:20:38 +02:00
2020-11-22 20:59:20 +01:00
const int32 ptrCurrentEntry = READ_LE_INT16(&localTextBuf[currIdx]);
const int32 ptrNextEntry = READ_LE_INT16(&localTextBuf[currIdx + 1]);
2020-10-14 14:20:38 +02:00
2020-11-22 20:42:20 +01:00
_currDialTextPtr = dialTextPtr + ptrCurrentEntry;
2020-11-22 20:31:24 +01:00
_currDialTextSize = ptrNextEntry - ptrCurrentEntry;
2020-10-14 14:20:38 +02:00
// RECHECK: this was added for vox playback
currDialTextEntry = currIdx;
return true;
2020-10-14 14:20:38 +02:00
}
void Text::copyText(const char *src, char *dst, int32 size) {
for (int32 i = 0; i < size; i++) {
2020-10-14 14:20:38 +02:00
*(dst++) = *(src++);
2020-10-20 23:52:15 +02:00
}
2020-10-14 14:20:38 +02:00
}
bool Text::getMenuText(int32 index, char *text, uint32 textSize) {
2020-12-10 20:27:44 +01:00
if (index == currMenuTextIndex) {
if (currMenuTextBank == _engine->_scene->sceneTextBank) {
Common::strlcpy(text, currMenuTextBuffer, textSize);
return true;
2020-10-14 14:20:38 +02:00
}
}
if (!getText(index)) {
// if doesn't have text
text[0] = '\0';
return false;
2020-10-14 14:20:38 +02:00
}
2020-11-22 20:42:20 +01:00
if (_currDialTextSize - 1 > 0xFF) {
2020-11-22 20:31:24 +01:00
_currDialTextSize = 0xFF;
}
2020-10-14 14:20:38 +02:00
2020-11-22 20:31:24 +01:00
copyText(_currDialTextPtr, text, _currDialTextSize);
_currDialTextSize++;
2020-12-10 20:27:44 +01:00
copyText(text, currMenuTextBuffer, _currDialTextSize);
2020-10-14 14:20:38 +02:00
2020-12-10 20:27:44 +01:00
currMenuTextIndex = index;
currMenuTextBank = _engine->_scene->sceneTextBank;
return true;
2020-10-14 14:20:38 +02:00
}
2020-11-22 22:04:07 +01:00
void Text::textClipFull() {
const int padding = 9;
2020-11-25 18:20:38 +01:00
_dialTextBox.left = padding - 1;
_dialTextBox.top = padding - 1;
_dialTextBox.right = SCREEN_WIDTH - padding;
_dialTextBox.bottom = SCREEN_HEIGHT - padding;
2020-10-14 14:20:38 +02:00
2020-11-22 20:31:24 +01:00
_dialTextBoxLines = 11;
2020-12-15 18:18:28 +01:00
_dialTextBoxMaxX = SCREEN_WIDTH - 33;
2020-10-14 14:20:38 +02:00
}
2020-11-22 22:04:07 +01:00
void Text::textClipSmall() {
const int padding = 17;
2020-11-25 18:20:38 +01:00
_dialTextBox.left = padding - 1;
2020-12-15 18:18:28 +01:00
_dialTextBox.top = SCREEN_HEIGHT - 146;
2020-11-25 18:20:38 +01:00
_dialTextBox.right = SCREEN_WIDTH - padding;
_dialTextBox.bottom = SCREEN_HEIGHT - padding;
2020-11-22 22:04:07 +01:00
2020-11-22 20:31:24 +01:00
_dialTextBoxLines = 3;
2020-12-15 18:18:28 +01:00
_dialTextBoxMaxX = SCREEN_WIDTH - 49;
2020-10-14 14:20:38 +02:00
}
2020-10-24 12:32:00 +02:00
void Text::drawAskQuestion(int32 index) {
2020-10-14 14:20:38 +02:00
// get right VOX entry index
initVoxToPlay(index);
2020-10-14 14:20:38 +02:00
initText(index);
initDialogueBox();
2020-12-15 19:26:22 +01:00
ProgressiveTextState textStatus = ProgressiveTextState::UNK1;
2020-10-14 14:20:38 +02:00
do {
_engine->readKeys();
2020-11-22 19:04:32 +01:00
textStatus = updateProgressiveText();
2020-10-14 14:20:38 +02:00
2020-12-15 21:17:01 +01:00
if (textStatus == ProgressiveTextState::NextPage) {
2020-10-14 14:20:38 +02:00
do {
_engine->readKeys();
if (_engine->shouldQuit()) {
break;
}
2020-10-24 12:32:00 +02:00
if (!playVoxSimple(currDialTextEntry)) {
break;
}
_engine->_system->delayMillis(1);
2020-10-24 12:32:00 +02:00
} while (!_engine->_input->toggleAbortAction());
2020-10-14 14:20:38 +02:00
}
_engine->_system->delayMillis(1);
2020-12-15 19:26:22 +01:00
} while (textStatus != ProgressiveTextState::End);
2020-10-14 14:20:38 +02:00
while (playVoxSimple(currDialTextEntry)) {
2020-10-24 12:32:00 +02:00
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
stopVox(currDialTextEntry);
break;
}
2020-10-24 12:32:00 +02:00
_engine->_system->delayMillis(1);
}
2020-10-14 14:20:38 +02:00
2020-10-24 12:32:00 +02:00
hasHiddenVox = false;
voxHiddenIndex = 0;
2020-11-22 18:54:05 +01:00
_hasValidTextHandle = false;
2020-10-14 14:20:38 +02:00
}
} // namespace TwinE