mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-02 23:01:42 +00:00
764 lines
20 KiB
C++
764 lines
20 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 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"
|
|
#include "common/endian.h"
|
|
#include "common/memstream.h"
|
|
#include "common/scummsys.h"
|
|
#include "common/str.h"
|
|
#include "common/system.h"
|
|
#include "common/util.h"
|
|
#include "twine/audio/sound.h"
|
|
#include "twine/input.h"
|
|
#include "twine/menu/interface.h"
|
|
#include "twine/menu/menu.h"
|
|
#include "twine/renderer/renderer.h"
|
|
#include "twine/renderer/screens.h"
|
|
#include "twine/resources/hqr.h"
|
|
#include "twine/resources/resources.h"
|
|
#include "twine/scene/scene.h"
|
|
#include "twine/twine.h"
|
|
|
|
namespace TwinE {
|
|
|
|
/** FLA movie extension */
|
|
#define VOX_EXT ".vox"
|
|
|
|
#define INDEXOFFSET 0
|
|
#define DIALOGSOFFSET 1
|
|
|
|
Text::Text(TwinEEngine *engine) : _engine(engine) {
|
|
Common::fill(&currMenuTextBuffer[0], &currMenuTextBuffer[256], 0);
|
|
}
|
|
|
|
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);
|
|
}
|
|
// 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...
|
|
|
|
// TODO check the rest to reverse
|
|
}
|
|
|
|
bool Text::initVoxToPlay(int32 index) { // setVoxFileAtDigit
|
|
currDialTextEntry = 0;
|
|
voxHiddenIndex = 0;
|
|
hasHiddenVox = false;
|
|
|
|
Common::MemoryReadStream stream((const byte *)dialOrderPtr, dialOrderSize);
|
|
// choose right text from order index
|
|
for (int32 i = 0; i < numDialTextEntries; i++) {
|
|
const int32 orderIdx = stream.readSint16LE();
|
|
if (orderIdx == index) {
|
|
currDialTextEntry = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_engine->_sound->playVoxSample(currDialTextEntry);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Text::playVox(int32 index) {
|
|
if (!_engine->cfgfile.Voice) {
|
|
return false;
|
|
}
|
|
if (hasHiddenVox && !_engine->_sound->isSamplePlaying(index)) {
|
|
_engine->_sound->playVoxSample(index);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Text::playVoxSimple(int32 index) {
|
|
if (_engine->_sound->isSamplePlaying(index)) {
|
|
return true;
|
|
}
|
|
return playVox(index);
|
|
}
|
|
|
|
bool Text::stopVox(int32 index) {
|
|
if (!_engine->_sound->isSamplePlaying(index)) {
|
|
return false;
|
|
}
|
|
hasHiddenVox = false;
|
|
_engine->_sound->stopSample(index);
|
|
return true;
|
|
}
|
|
|
|
void Text::initTextBank(int32 bankIdx) {
|
|
// don't load if we already have the dialogue text bank loaded
|
|
if (bankIdx == currentBankIdx) {
|
|
return;
|
|
}
|
|
|
|
currentBankIdx = bankIdx;
|
|
|
|
// get index according with language
|
|
const int32 size = _engine->isLBA1() ? 28 : 30;
|
|
// 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) {
|
|
warning("Failed to initialize text bank %i from file %s", languageIndex, Resources::HQR_TEXT_FILE);
|
|
return;
|
|
}
|
|
|
|
numDialTextEntries = dialOrderSize / 2;
|
|
|
|
if (HQR::getAllocEntry((uint8 **)&dialTextPtr, Resources::HQR_TEXT_FILE, languageIndex + DIALOGSOFFSET) == 0) {
|
|
warning("Failed to initialize additional text bank %i from file %s", languageIndex + 1, Resources::HQR_TEXT_FILE);
|
|
return;
|
|
}
|
|
initVoxBank(bankIdx);
|
|
}
|
|
|
|
void Text::initSceneTextBank() {
|
|
initTextBank(_engine->_scene->sceneTextBank + TextBankId::Citadel_Island);
|
|
}
|
|
|
|
void Text::drawCharacter(int32 x, int32 y, uint8 character) { // drawCharacter
|
|
Common::MemoryReadStream stream(_engine->_resources->fontPtr, _engine->_resources->fontBufSize);
|
|
stream.seek(character * 4);
|
|
stream.seek(stream.readSint16LE());
|
|
/*uint8 charWidth =*/ stream.readByte();
|
|
const uint8 sizeY = stream.readByte();
|
|
x += stream.readByte();
|
|
y += stream.readByte();
|
|
|
|
const uint8 usedColor = _dialTextColor;
|
|
|
|
int32 tempX = x;
|
|
int32 tempY = y;
|
|
|
|
for (uint8 fontY = 0; fontY < sizeY; ++fontY) {
|
|
uint8 index = stream.readByte();
|
|
do {
|
|
const uint8 jump = stream.readByte();
|
|
if (--index == 0) {
|
|
tempY++;
|
|
tempX = x;
|
|
break;
|
|
}
|
|
const uint8 number = stream.readByte();
|
|
tempX += jump;
|
|
uint8* basePtr = (uint8 *)_engine->frontVideoBuffer.getBasePtr(tempX, tempY);
|
|
for (uint8 i = 0; i < number; i++) {
|
|
if (tempX >= SCREEN_TEXTLIMIT_LEFT && tempX < SCREEN_TEXTLIMIT_RIGHT && tempY >= SCREEN_TEXTLIMIT_TOP && tempY < SCREEN_TEXTLIMIT_BOTTOM) {
|
|
*basePtr = usedColor;
|
|
}
|
|
|
|
++basePtr;
|
|
tempX++;
|
|
}
|
|
|
|
if (--index == 0) {
|
|
tempY++;
|
|
tempX = x;
|
|
break;
|
|
}
|
|
} while (1);
|
|
}
|
|
}
|
|
|
|
void Text::drawCharacterShadow(int32 x, int32 y, uint8 character, int32 color, Common::Rect &dirtyRect) { // drawDoubleLetter
|
|
if (character == ' ') {
|
|
return;
|
|
}
|
|
// shadow color
|
|
setFontColor(0);
|
|
drawCharacter(x + 2, y + 4, character);
|
|
|
|
// text color
|
|
setFontColor(color);
|
|
drawCharacter(x, y, character);
|
|
|
|
// TODO: get font size
|
|
const Common::Rect rect(x, y, x + 32, y + 38);
|
|
if (dirtyRect.isEmpty()) {
|
|
dirtyRect = rect;
|
|
} else {
|
|
dirtyRect.extend(rect);
|
|
}
|
|
}
|
|
|
|
void Text::drawText(int32 x, int32 y, const char *dialogue) {
|
|
// if the font is not defined
|
|
if (_engine->_resources->fontPtr == nullptr) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
const uint8 currChar = (uint8)*dialogue++; // read the next char from the string
|
|
if (currChar == '\0') {
|
|
break;
|
|
}
|
|
|
|
if (currChar == ' ') {
|
|
x += _dialCharSpace;
|
|
} else {
|
|
const int32 dialTextSize = getCharWidth(currChar);
|
|
drawCharacter(x, y, currChar); // draw the character on screen
|
|
// add the length of the space between 2 characters
|
|
x += _dialSpaceBetween;
|
|
// add the length of the current character
|
|
x += dialTextSize;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
int32 Text::getTextSize(const char *dialogue) { // SizeFont
|
|
int32 dialTextSize = 0;
|
|
|
|
do {
|
|
const uint8 currChar = (uint8) * (dialogue++);
|
|
if (currChar == '\0') {
|
|
break;
|
|
}
|
|
|
|
if (currChar == ' ') {
|
|
dialTextSize += _dialCharSpace;
|
|
} else {
|
|
dialTextSize += _dialSpaceBetween;
|
|
dialTextSize += getCharWidth(currChar);
|
|
}
|
|
} while (1);
|
|
|
|
return dialTextSize;
|
|
}
|
|
|
|
void Text::initDialogueBox() { // InitDialWindow
|
|
_engine->_interface->blitBox(_dialTextBox, _engine->workVideoBuffer, _engine->frontVideoBuffer);
|
|
|
|
if (drawTextBoxBackground) {
|
|
_engine->_menu->drawBox(_dialTextBox);
|
|
Common::Rect rect(_dialTextBox);
|
|
rect.grow(-1);
|
|
_engine->_interface->drawTransparentBox(rect, 3);
|
|
}
|
|
|
|
_engine->copyBlockPhys(_dialTextBox);
|
|
_fadeInCharactersPos = 0;
|
|
_engine->_interface->blitBox(_dialTextBox, _engine->frontVideoBuffer, _engine->workVideoBuffer);
|
|
}
|
|
|
|
void Text::initInventoryDialogueBox() { // SecondInitDialWindow
|
|
_engine->_interface->blitBox(_dialTextBox, _engine->workVideoBuffer, _engine->frontVideoBuffer);
|
|
_engine->copyBlockPhys(_dialTextBox);
|
|
_fadeInCharactersPos = 0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// TODO: refactor this code
|
|
void Text::initText(int32 index) {
|
|
if (!getText(index)) {
|
|
_hasValidTextHandle = false;
|
|
return;
|
|
}
|
|
|
|
_progressiveTextBufferPtr = _progressiveTextBuffer;
|
|
|
|
_hasValidTextHandle = true;
|
|
|
|
_dialTextBoxCurrentLine = 0;
|
|
_progressiveTextBuffer[0] = '\0';
|
|
_fadeInCharactersPos = 0;
|
|
_dialTextXPos = _dialTextBox.left + 8;
|
|
_dialTextYPos = _dialTextBox.top + 8;
|
|
_currentTextPosition = _currDialTextPtr;
|
|
|
|
// lba font is get while engine start
|
|
setFontParameters(2, 7);
|
|
|
|
// fetch the first line
|
|
processTextLine();
|
|
}
|
|
|
|
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;
|
|
_dialTextBoxCurrentLine = 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
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++;
|
|
}
|
|
_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;
|
|
}
|
|
|
|
Text::WordSize Text::getWordSize(const char *completeText, char *wordBuf, int32 wordBufSize) {
|
|
int32 temp = 0;
|
|
const char *arg2Save = wordBuf;
|
|
|
|
while (*completeText != '\0' && *completeText != '\1' && *completeText != ' ') {
|
|
temp++;
|
|
*wordBuf++ = *completeText++;
|
|
if (temp >= wordBufSize - 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
WordSize size;
|
|
size.inChar = temp;
|
|
*wordBuf = '\0';
|
|
size.inPixel = getTextSize(arg2Save);
|
|
return size;
|
|
}
|
|
|
|
void Text::processTextLine() {
|
|
const char *buffer = _currentTextPosition;
|
|
_dialCharSpace = 7;
|
|
bool moreWordsFollowing = true;
|
|
|
|
int32 lineBreakX = 0;
|
|
int32 spaceCharCount = 0;
|
|
_progressiveTextBuffer[0] = '\0';
|
|
|
|
for (;;) {
|
|
if (*buffer == ' ') {
|
|
buffer++;
|
|
continue;
|
|
}
|
|
if (*buffer == '\0') {
|
|
break;
|
|
}
|
|
if (*buffer == '\1') {
|
|
moreWordsFollowing = false;
|
|
buffer++;
|
|
break;
|
|
}
|
|
|
|
_currentTextPosition = buffer;
|
|
char wordBuf[256] = "";
|
|
WordSize wordSize = getWordSize(buffer, wordBuf, sizeof(wordBuf));
|
|
if (lineBreakX + _dialCharSpace + wordSize.inPixel >= _dialTextBoxMaxX) {
|
|
break;
|
|
}
|
|
|
|
// @ is a line break
|
|
if (*wordBuf == '@') {
|
|
moreWordsFollowing = false;
|
|
buffer++;
|
|
if (lineBreakX == 0) {
|
|
lineBreakX = 7;
|
|
*(_progressiveTextBuffer + 0) = ' ';
|
|
*(_progressiveTextBuffer + 1) = '\0';
|
|
}
|
|
// new page?
|
|
if (wordBuf[1] == 'P') {
|
|
_dialTextBoxCurrentLine = _dialTextBoxLines;
|
|
buffer++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
buffer += wordSize.inChar;
|
|
_currentTextPosition = buffer;
|
|
strncat(_progressiveTextBuffer, wordBuf, sizeof(_progressiveTextBuffer) - strlen(_progressiveTextBuffer) - 1);
|
|
strncat(_progressiveTextBuffer, " ", sizeof(_progressiveTextBuffer) - strlen(_progressiveTextBuffer) - 1);
|
|
spaceCharCount++;
|
|
|
|
lineBreakX += wordSize.inPixel + _dialCharSpace;
|
|
if (*_currentTextPosition != '\0') {
|
|
_currentTextPosition++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (spaceCharCount > 0) {
|
|
spaceCharCount--;
|
|
}
|
|
|
|
if (*_currentTextPosition != '\0' && moreWordsFollowing) {
|
|
if (spaceCharCount <= 0) {
|
|
spaceCharCount = 1;
|
|
}
|
|
// split the remaining space between the words
|
|
_dialCharSpace += (_dialTextBoxMaxX - lineBreakX) / spaceCharCount;
|
|
}
|
|
|
|
_currentTextPosition = buffer;
|
|
|
|
_progressiveTextBufferPtr = _progressiveTextBuffer;
|
|
}
|
|
|
|
void Text::renderContinueReadingTriangle() {
|
|
const int32 border = 3;
|
|
const int32 size = 21;
|
|
const int16 color = 136;
|
|
const int32 right = _dialTextBox.right - border;
|
|
const int32 left = _dialTextBox.right - (size + border);
|
|
const int32 top = _dialTextBox.bottom - (size + border);
|
|
const int32 bottom = _dialTextBox.bottom - border;
|
|
|
|
Vertex vertices[3];
|
|
|
|
vertices[0].colorIndex = color;
|
|
vertices[0].x = right;
|
|
vertices[0].y = top;
|
|
|
|
vertices[1].colorIndex = color;
|
|
vertices[1].x = left;
|
|
vertices[1].y = bottom;
|
|
|
|
vertices[2].colorIndex = color;
|
|
vertices[2].x = right;
|
|
vertices[2].y = bottom;
|
|
|
|
CmdRenderPolygon polygon;
|
|
polygon.numVertices = ARRAYSIZE(vertices);
|
|
polygon.colorIndex = _dialTextStopColor;
|
|
polygon.renderType = POLYGONTYPE_FLAT;
|
|
_engine->_renderer->renderPolygons(polygon, vertices);
|
|
|
|
_engine->copyBlockPhys(Common::Rect(left, top, right, bottom));
|
|
}
|
|
|
|
void Text::fadeInCharacters(int32 counter, int32 fontColor) {
|
|
Common::Rect dirtyRect;
|
|
while (--counter >= 0) {
|
|
const BlendInCharacter *ptr = &_fadeInCharacters[counter];
|
|
setFontColor(fontColor);
|
|
drawCharacterShadow(ptr->x, ptr->y, ptr->chr, fontColor, dirtyRect);
|
|
fontColor -= _dialTextStepSize;
|
|
if (fontColor > _dialTextStopColor) {
|
|
fontColor = _dialTextStopColor;
|
|
}
|
|
}
|
|
if (dirtyRect.isEmpty()) {
|
|
return;
|
|
}
|
|
_engine->copyBlockPhys(dirtyRect);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
void Text::fadeInRemainingChars() {
|
|
if (_fadeInCharactersPos <= 0) {
|
|
return;
|
|
}
|
|
fadeInCharacters(_fadeInCharactersPos, _dialTextStopColor);
|
|
--_fadeInCharactersPos;
|
|
}
|
|
|
|
ProgressiveTextState Text::updateProgressiveText() {
|
|
if (!_hasValidTextHandle) {
|
|
return ProgressiveTextState::End;
|
|
}
|
|
|
|
if (*_progressiveTextBufferPtr == '\0') {
|
|
initProgressiveTextBuffer();
|
|
processTextLine();
|
|
initDialogueBox();
|
|
_dialTextXPos = _dialTextBox.left + 8;
|
|
_dialTextYPos = _dialTextBox.top + 8;
|
|
}
|
|
const char currentChar = *_progressiveTextBufferPtr;
|
|
assert(currentChar != '\0');
|
|
fillFadeInBuffer(_dialTextXPos, _dialTextYPos, currentChar);
|
|
fadeInCharacters(_fadeInCharactersPos, _dialTextStartColor);
|
|
const int8 charWidth = getCharWidth(currentChar);
|
|
|
|
if (currentChar == ' ') {
|
|
_dialTextXPos += _dialCharSpace + 1;
|
|
} else {
|
|
_dialTextXPos += charWidth + 2;
|
|
}
|
|
|
|
// next character
|
|
_progressiveTextBufferPtr++;
|
|
|
|
// reaching 0-byte means a new line - as we are fading in per line
|
|
if (*_progressiveTextBufferPtr != '\0') {
|
|
return ProgressiveTextState::ContinueRunning;
|
|
}
|
|
|
|
if (*_currentTextPosition == '\0') {
|
|
_hasValidTextHandle = false;
|
|
renderContinueReadingTriangle();
|
|
return ProgressiveTextState::End;
|
|
}
|
|
|
|
// reached a new line that is about get faded in
|
|
_dialTextBoxCurrentLine++;
|
|
|
|
const int32 lineHeight = 38;
|
|
_dialTextYPos += lineHeight;
|
|
_dialTextXPos = _dialTextBox.left + 8;
|
|
|
|
if (_dialTextBoxCurrentLine >= _dialTextBoxLines) {
|
|
renderContinueReadingTriangle();
|
|
return ProgressiveTextState::NextPage;
|
|
}
|
|
|
|
processTextLine();
|
|
|
|
return ProgressiveTextState::ContinueRunning;
|
|
}
|
|
|
|
bool Text::displayText(int32 index, bool showText, bool playVox) {
|
|
if (playVox) {
|
|
// get right VOX entry index
|
|
initVoxToPlay(index);
|
|
}
|
|
|
|
bool aborted = false;
|
|
|
|
// if we don't display text, than still plays vox file
|
|
if (showText) {
|
|
initText(index);
|
|
initDialogueBox();
|
|
|
|
ScopedKeyMap uiKeyMap(_engine, uiKeyMapId);
|
|
ProgressiveTextState textState = ProgressiveTextState::ContinueRunning;
|
|
for (;;) {
|
|
ScopedFPS scopedFps(66);
|
|
_engine->readKeys();
|
|
if (textState == ProgressiveTextState::ContinueRunning) {
|
|
textState = updateProgressiveText();
|
|
} else {
|
|
fadeInRemainingChars();
|
|
}
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UINextPage)) {
|
|
if (textState == ProgressiveTextState::End) {
|
|
stopVox(currDialTextEntry);
|
|
break;
|
|
}
|
|
if (textState == ProgressiveTextState::NextPage) {
|
|
textState = ProgressiveTextState::ContinueRunning;
|
|
}
|
|
}
|
|
if (_engine->_input->toggleAbortAction() || _engine->shouldQuit()) {
|
|
stopVox(currDialTextEntry);
|
|
aborted = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (_engine->_sound->isSamplePlaying(currDialTextEntry)) {
|
|
ScopedFPS scopedFps;
|
|
_engine->readKeys();
|
|
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
|
|
stopVox(currDialTextEntry);
|
|
aborted = true;
|
|
break;
|
|
}
|
|
}
|
|
voxHiddenIndex = 0;
|
|
hasHiddenVox = false;
|
|
_hasValidTextHandle = false;
|
|
|
|
return aborted;
|
|
}
|
|
|
|
bool Text::drawTextFullscreen(int32 index) {
|
|
_engine->_interface->saveClip();
|
|
_engine->_interface->resetClip();
|
|
_engine->_screens->copyScreen(_engine->frontVideoBuffer, _engine->workVideoBuffer);
|
|
const bool aborted = displayText(index, _engine->cfgfile.FlagDisplayText, true);
|
|
_engine->_interface->loadClip();
|
|
return aborted;
|
|
}
|
|
|
|
void Text::setFontParameters(int32 spaceBetween, int32 charSpace) {
|
|
_dialSpaceBetween = spaceBetween;
|
|
_dialCharSpace = charSpace;
|
|
}
|
|
|
|
void Text::setFontCrossColor(int32 color) {
|
|
_dialTextStepSize = -1;
|
|
_dialTextBufferSize = 14;
|
|
_dialTextStartColor = color << 4;
|
|
_dialTextStopColor = _dialTextStartColor + 12;
|
|
}
|
|
|
|
void Text::setFontColor(int32 color) {
|
|
_dialTextColor = color;
|
|
}
|
|
|
|
void Text::setTextCrossColor(int32 stopColor, int32 startColor, int32 stepSize) {
|
|
_dialTextStartColor = startColor;
|
|
_dialTextStopColor = stopColor;
|
|
_dialTextStepSize = stepSize;
|
|
_dialTextBufferSize = ((startColor - stopColor) + 1) / stepSize;
|
|
}
|
|
|
|
bool Text::getText(int32 index) {
|
|
const int16 *localTextBuf = (const int16 *)dialTextPtr;
|
|
const int16 *localOrderBuf = (const int16 *)dialOrderPtr;
|
|
|
|
const int32 numEntries = numDialTextEntries;
|
|
int32 currIdx = 0;
|
|
// choose right text from order index
|
|
do {
|
|
const int16 orderIdx = READ_LE_UINT16(localOrderBuf);
|
|
if (orderIdx == index) {
|
|
break;
|
|
}
|
|
currIdx++;
|
|
localOrderBuf++;
|
|
} while (currIdx < numEntries);
|
|
|
|
if (currIdx >= numEntries) {
|
|
return false;
|
|
}
|
|
|
|
const int32 ptrCurrentEntry = READ_LE_INT16(&localTextBuf[currIdx]);
|
|
const int32 ptrNextEntry = READ_LE_INT16(&localTextBuf[currIdx + 1]);
|
|
|
|
_currDialTextPtr = dialTextPtr + ptrCurrentEntry;
|
|
_currDialTextSize = ptrNextEntry - ptrCurrentEntry;
|
|
|
|
// RECHECK: this was added for vox playback
|
|
currDialTextEntry = currIdx;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Text::copyText(const char *src, char *dst, int32 size) {
|
|
for (int32 i = 0; i < size; i++) {
|
|
*(dst++) = *(src++);
|
|
}
|
|
}
|
|
|
|
bool Text::getMenuText(int32 index, char *text, uint32 textSize) {
|
|
if (index == currMenuTextIndex) {
|
|
if (currMenuTextBank == _engine->_scene->sceneTextBank) {
|
|
Common::strlcpy(text, currMenuTextBuffer, textSize);
|
|
return true;
|
|
}
|
|
}
|
|
if (!getText(index)) {
|
|
// if doesn't have text
|
|
text[0] = '\0';
|
|
return false;
|
|
}
|
|
|
|
if (_currDialTextSize - 1 > 0xFF) {
|
|
_currDialTextSize = 0xFF;
|
|
}
|
|
|
|
copyText(_currDialTextPtr, text, _currDialTextSize);
|
|
_currDialTextSize++;
|
|
copyText(text, currMenuTextBuffer, _currDialTextSize);
|
|
|
|
currMenuTextIndex = index;
|
|
currMenuTextBank = _engine->_scene->sceneTextBank;
|
|
return true;
|
|
}
|
|
|
|
void Text::textClipFull() {
|
|
const int padding = 9;
|
|
_dialTextBox.left = padding - 1;
|
|
_dialTextBox.top = padding - 1;
|
|
_dialTextBox.right = SCREEN_WIDTH - padding;
|
|
_dialTextBox.bottom = SCREEN_HEIGHT - padding;
|
|
|
|
_dialTextBoxLines = 11;
|
|
_dialTextBoxMaxX = SCREEN_WIDTH - 33;
|
|
}
|
|
|
|
void Text::textClipSmall() {
|
|
const int padding = 17;
|
|
_dialTextBox.left = padding - 1;
|
|
_dialTextBox.top = SCREEN_HEIGHT - 146;
|
|
_dialTextBox.right = SCREEN_WIDTH - padding;
|
|
_dialTextBox.bottom = SCREEN_HEIGHT - padding;
|
|
|
|
_dialTextBoxLines = 3;
|
|
_dialTextBoxMaxX = SCREEN_WIDTH - 49;
|
|
}
|
|
|
|
void Text::drawAskQuestion(int32 index) {
|
|
displayText(index, true, true);
|
|
}
|
|
|
|
} // namespace TwinE
|