Initial commit
This commit is contained in:
394
engines/kyra/text/text.cpp
Normal file
394
engines/kyra/text/text.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
/* 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 "kyra/text/text.h"
|
||||
#include "kyra/kyra_v1.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
TextDisplayer::TextDisplayer(KyraEngine_v1 *vm, Screen *screen) {
|
||||
_screen = screen;
|
||||
_vm = vm;
|
||||
|
||||
_talkCoords.y = 0x88;
|
||||
_talkCoords.x = 0;
|
||||
_talkCoords.w = 0;
|
||||
_talkMessageY = 0xC;
|
||||
_talkMessageH = 0;
|
||||
_talkMessagePrinted = false;
|
||||
_langExtraSpacing = (vm->gameFlags().lang == Common::KO_KOR) ? 2 : 0;
|
||||
_lineBreakChar = (_vm->gameFlags().platform == Common::kPlatformMacintosh) ? '\n' : '\r';
|
||||
memset(_talkSubstrings, 0, sizeof(_talkSubstrings));
|
||||
memset(_talkBuffer, 0, sizeof(_talkBuffer));
|
||||
}
|
||||
|
||||
void TextDisplayer::setTalkCoords(uint16 y) {
|
||||
_talkCoords.y = y;
|
||||
}
|
||||
|
||||
int TextDisplayer::getCenterStringX(const Common::String &str, int x1, int x2) {
|
||||
_screen->_charSpacing = -2;
|
||||
int strWidth = _screen->getTextWidth(str.c_str());
|
||||
_screen->_charSpacing = 0;
|
||||
int w = x2 - x1 + 1;
|
||||
return x1 + (w - strWidth) / 2;
|
||||
}
|
||||
|
||||
int TextDisplayer::getCharLength(const char *str, int len) {
|
||||
int charsCount = 0;
|
||||
if (*str) {
|
||||
_screen->_charSpacing = -2;
|
||||
int i = 0;
|
||||
while (i <= len && *str) {
|
||||
uint c = *str++;
|
||||
c &= 0xFF;
|
||||
if (c > 0x7F && (_vm->gameFlags().lang == Common::JA_JPN || _vm->gameFlags().lang == Common::KO_KOR)) {
|
||||
c = READ_LE_UINT16(str - 1);
|
||||
++charsCount;
|
||||
++str;
|
||||
}
|
||||
i += _screen->getCharWidth(c);
|
||||
++charsCount;
|
||||
}
|
||||
_screen->_charSpacing = 0;
|
||||
}
|
||||
return charsCount;
|
||||
}
|
||||
|
||||
int TextDisplayer::dropCRIntoString(char *str, int offs) {
|
||||
int pos = 0;
|
||||
str += offs;
|
||||
while (*str) {
|
||||
if (*str == ' ') {
|
||||
*str = _lineBreakChar;
|
||||
return pos;
|
||||
}
|
||||
++str;
|
||||
++pos;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *TextDisplayer::preprocessString(const char *str) {
|
||||
if (str != _talkBuffer) {
|
||||
assert(strlen(str) < sizeof(_talkBuffer) - 1);
|
||||
Common::strlcpy(_talkBuffer, str, sizeof(_talkBuffer));
|
||||
}
|
||||
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN)
|
||||
return _talkBuffer;
|
||||
|
||||
char *p = _talkBuffer;
|
||||
while (*p) {
|
||||
if (*p == _lineBreakChar) {
|
||||
return _talkBuffer;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
p = _talkBuffer;
|
||||
|
||||
static const uint16 limDef[2] = { 176, 352 };
|
||||
static const uint16 limKor[2] = { 240, 480 };
|
||||
const uint16 *lim = (_vm->gameFlags().lang == Common::KO_KOR) ? limKor : limDef;
|
||||
|
||||
Screen::FontId curFont = _screen->setFont(_vm->gameFlags().lang == Common::Language::ZH_TWN && _vm->gameFlags().gameID == GI_LOL ? Screen::FID_CHINESE_FNT :
|
||||
_vm->gameFlags().lang == Common::KO_KOR ? Screen::FID_KOREAN_FNT : Screen::FID_8_FNT);
|
||||
_screen->_charSpacing = -2;
|
||||
int textWidth = _screen->getTextWidth(p);
|
||||
_screen->_charSpacing = 0;
|
||||
if (textWidth > lim[0]) {
|
||||
if (textWidth > lim[1]) {
|
||||
int count = getCharLength(p, textWidth / 3);
|
||||
int offs = dropCRIntoString(p, count);
|
||||
p += count + offs;
|
||||
_screen->_charSpacing = -2;
|
||||
textWidth = _screen->getTextWidth(p);
|
||||
_screen->_charSpacing = 0;
|
||||
count = getCharLength(p, textWidth / 2);
|
||||
dropCRIntoString(p, count);
|
||||
} else {
|
||||
int count = getCharLength(p, textWidth / 2);
|
||||
dropCRIntoString(p, count);
|
||||
}
|
||||
}
|
||||
_screen->setFont(curFont);
|
||||
|
||||
return _talkBuffer;
|
||||
}
|
||||
|
||||
int TextDisplayer::buildMessageSubstrings(const char *str) {
|
||||
int currentLine = 0;
|
||||
int pos = 0;
|
||||
while (*str) {
|
||||
if (*str == _lineBreakChar) {
|
||||
assert(currentLine < TALK_SUBSTRING_NUM);
|
||||
_talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
|
||||
++currentLine;
|
||||
pos = 0;
|
||||
} else {
|
||||
_talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = *str;
|
||||
++pos;
|
||||
if (_vm->game() == GI_KYRA2 && _vm->gameFlags().lang == Common::ZH_TWN && pos == 32) {
|
||||
_talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
|
||||
++currentLine;
|
||||
pos = 0;
|
||||
} else if (pos >= TALK_SUBSTRING_LEN - 2) {
|
||||
pos = TALK_SUBSTRING_LEN - 2;
|
||||
}
|
||||
}
|
||||
++str;
|
||||
}
|
||||
_talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
|
||||
return currentLine + 1;
|
||||
}
|
||||
|
||||
int TextDisplayer::getWidestLineWidth(int linesCount) {
|
||||
int maxWidth = 0;
|
||||
_screen->_charSpacing = -2;
|
||||
for (int l = 0; l < linesCount; ++l) {
|
||||
int w = _screen->getTextWidth(&_talkSubstrings[l * TALK_SUBSTRING_LEN]);
|
||||
if (maxWidth < w) {
|
||||
maxWidth = w;
|
||||
}
|
||||
}
|
||||
_screen->_charSpacing = 0;
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
void TextDisplayer::calcWidestLineBounds(int &x1, int &x2, int w, int cx) {
|
||||
int margin = (_vm->game() == GI_KYRA2 && _vm->gameFlags().lang == Common::ZH_TWN) ? 8 : 12;
|
||||
x1 = cx - w / 2;
|
||||
if (x1 + w >= Screen::SCREEN_W - margin) {
|
||||
x1 = Screen::SCREEN_W - margin - w - 1;
|
||||
} else if (x1 < margin) {
|
||||
x1 = margin;
|
||||
}
|
||||
x2 = x1 + w + 1;
|
||||
}
|
||||
|
||||
void TextDisplayer::restoreTalkTextMessageBkgd(int srcPage, int dstPage) {
|
||||
if (_talkMessagePrinted) {
|
||||
_talkMessagePrinted = false;
|
||||
_screen->copyRegion(_talkCoords.x, _talkCoords.y, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, srcPage, dstPage, Screen::CR_NO_P_CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDisplayer::printTalkTextMessage(const char *text, int x, int y, uint8 color, int srcPage, int dstPage) {
|
||||
char *str = preprocessString(text);
|
||||
int lineCount = buildMessageSubstrings(str);
|
||||
// For Chinese we call this before recalculating the line count
|
||||
int w = getWidestLineWidth(lineCount);
|
||||
int marginTop = 0;
|
||||
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN) {
|
||||
lineCount = (strlen(str) + 31) >> 5;
|
||||
marginTop = 10;
|
||||
w = MIN<int>(w, 302);
|
||||
}
|
||||
|
||||
int top = y - (lineCount * _screen->getFontHeight() + (lineCount - 1) * _screen->_lineSpacing) - _langExtraSpacing;
|
||||
if (top < marginTop)
|
||||
top = marginTop;
|
||||
|
||||
_talkMessageY = top;
|
||||
_talkMessageH = (lineCount * _screen->getFontHeight() + (lineCount - 1) * _screen->_lineSpacing) + _langExtraSpacing;
|
||||
|
||||
int x1 = 12;
|
||||
int x2 = Screen::SCREEN_W - 12;
|
||||
if (_vm->gameFlags().lang != Common::ZH_TWN || lineCount == 1)
|
||||
calcWidestLineBounds(x1, x2, w, x);
|
||||
|
||||
_talkCoords.x = x1;
|
||||
_talkCoords.w = w + 2;
|
||||
_screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkCoords.y, _talkCoords.w, _talkMessageH, srcPage, dstPage, Screen::CR_NO_P_CHECK);
|
||||
int curPage = _screen->_curPage;
|
||||
_screen->_curPage = srcPage;
|
||||
|
||||
if (_vm->gameFlags().platform == Common::kPlatformAmiga)
|
||||
setTextColor(color);
|
||||
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN && lineCount > 1) {
|
||||
// The Chinese version leaves the wrapping to the default font handling
|
||||
printText(_talkSubstrings, 12, top, color, 0xC, 0xC);
|
||||
} else {
|
||||
for (int i = 0; i < lineCount; ++i) {
|
||||
top = i * (_screen->getFontHeight() + _screen->_lineSpacing) + _talkMessageY;
|
||||
char *msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
|
||||
int left = getCenterStringX(msg, x1, x2);
|
||||
printText(msg, left, top, color, 0xC, _vm->gameFlags().lang == Common::ZH_TWN ? 0xC : 0);
|
||||
}
|
||||
}
|
||||
_screen->_curPage = curPage;
|
||||
_talkMessagePrinted = true;
|
||||
}
|
||||
|
||||
void TextDisplayer::printText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
|
||||
Common::String revBuffer;
|
||||
const char *tmp = str.c_str();
|
||||
if (_vm->gameFlags().lang == Common::HE_ISR) {
|
||||
for (int i = str.size() - 1; i >= 0; --i)
|
||||
revBuffer += str[i];
|
||||
tmp = revBuffer.c_str();
|
||||
}
|
||||
uint8 colorMap[] = { 0, 15, 12, 12 };
|
||||
colorMap[3] = c1;
|
||||
_screen->setTextColor(colorMap, 0, 3);
|
||||
_screen->_charSpacing = -2;
|
||||
int ls = _screen->_lineSpacing;
|
||||
_screen->_lineSpacing = _langExtraSpacing >> 1;
|
||||
_screen->printText(tmp, x, y, c0, c2);
|
||||
_screen->_charSpacing = 0;
|
||||
_screen->_lineSpacing = ls;
|
||||
}
|
||||
|
||||
void TextDisplayer::printCharacterText(const char *text, int8 charNum, int charX) {
|
||||
int top, left, w, x;
|
||||
char *msg;
|
||||
|
||||
text = preprocessString(text);
|
||||
int lineCount = buildMessageSubstrings(text);
|
||||
// For Chinese we call this before recalculating the line count
|
||||
w = getWidestLineWidth(lineCount);
|
||||
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN) {
|
||||
lineCount = (strlen(text) + 31) >> 5;
|
||||
w = MIN<int>(w, 302);
|
||||
}
|
||||
|
||||
int x1 = 12;
|
||||
int x2 = Screen::SCREEN_W - 12;
|
||||
if (_vm->gameFlags().lang != Common::ZH_TWN || lineCount == 1) {
|
||||
x = charX;
|
||||
calcWidestLineBounds(x1, x2, w, x);
|
||||
}
|
||||
|
||||
uint8 color = 0;
|
||||
if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
|
||||
const uint8 colorTable[] = { 0x1F, 0x1B, 0xC9, 0x80, 0x1E, 0x81, 0x11, 0xD8, 0x55, 0x3A, 0x3A };
|
||||
color = colorTable[charNum];
|
||||
|
||||
setTextColor(color);
|
||||
} else {
|
||||
const uint8 colorTable[] = { 0x0F, 0x09, 0xC9, 0x80, 0x05, 0x81, 0x0E, 0xD8, 0x55, 0x3A, 0x3A };
|
||||
color = colorTable[charNum];
|
||||
}
|
||||
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN && lineCount > 1) {
|
||||
// The Chinese version leaves the wrapping to the default font handling
|
||||
printText(_talkSubstrings, 12, _talkMessageY, color, 0xC, 0xC);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < lineCount; ++i) {
|
||||
top = i * (_screen->getFontHeight() + _screen->_lineSpacing) + _talkMessageY;
|
||||
msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
|
||||
left = getCenterStringX(msg, x1, x2);
|
||||
printText(msg, left, top, color, 0xC, _vm->gameFlags().lang == Common::ZH_TWN ? 0xC : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDisplayer::setTextColor(uint8 color) {
|
||||
byte r, g, b;
|
||||
|
||||
switch (color) {
|
||||
case 4:
|
||||
// 0x09E
|
||||
r = 0;
|
||||
g = 37;
|
||||
b = 58;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// 0xFF5
|
||||
r = 63;
|
||||
g = 63;
|
||||
b = 21;
|
||||
break;
|
||||
|
||||
case 27:
|
||||
// 0x5FF
|
||||
r = 21;
|
||||
g = 63;
|
||||
b = 63;
|
||||
break;
|
||||
|
||||
case 34:
|
||||
// 0x8E5
|
||||
r = 33;
|
||||
g = 58;
|
||||
b = 21;
|
||||
break;
|
||||
|
||||
case 58:
|
||||
// 0x9FB
|
||||
r = 37;
|
||||
g = 63;
|
||||
b = 46;
|
||||
break;
|
||||
|
||||
case 85:
|
||||
// 0x7CF
|
||||
r = 29;
|
||||
g = 50;
|
||||
b = 63;
|
||||
break;
|
||||
|
||||
case 114:
|
||||
case 117:
|
||||
// 0xFAF
|
||||
r = 63;
|
||||
g = 42;
|
||||
b = 63;
|
||||
break;
|
||||
|
||||
case 128:
|
||||
case 129:
|
||||
// 0xFCC
|
||||
r = 63;
|
||||
g = 50;
|
||||
b = 50;
|
||||
break;
|
||||
|
||||
case 201:
|
||||
// 0xFD8
|
||||
r = 63;
|
||||
g = 54;
|
||||
b = 33;
|
||||
break;
|
||||
|
||||
case 216:
|
||||
// 0xFC6
|
||||
r = 63;
|
||||
g = 50;
|
||||
b = 25;
|
||||
break;
|
||||
|
||||
default:
|
||||
// 0xEEE
|
||||
r = 58;
|
||||
g = 58;
|
||||
b = 58;
|
||||
}
|
||||
|
||||
_screen->setPaletteIndex(0x10, r, g, b);
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
83
engines/kyra/text/text.h
Normal file
83
engines/kyra/text/text.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KYRA_TEXT_H
|
||||
#define KYRA_TEXT_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "kyra/graphics/screen.h"
|
||||
|
||||
namespace Kyra {
|
||||
class KyraEngine_v1;
|
||||
|
||||
class TextDisplayer {
|
||||
public:
|
||||
TextDisplayer(KyraEngine_v1 *vm, Screen *screen);
|
||||
virtual ~TextDisplayer() {}
|
||||
|
||||
int maxSubstringLen() const { return TALK_SUBSTRING_LEN; }
|
||||
|
||||
void setTalkCoords(uint16 y);
|
||||
int getCenterStringX(const Common::String &str, int x1, int x2);
|
||||
int getCharLength(const char *str, int len);
|
||||
int dropCRIntoString(char *str, int offs);
|
||||
virtual char *preprocessString(const char *str);
|
||||
int buildMessageSubstrings(const char *str);
|
||||
int getWidestLineWidth(int linesCount);
|
||||
virtual void calcWidestLineBounds(int &x1, int &x2, int w, int cx);
|
||||
virtual void restoreTalkTextMessageBkgd(int srcPage, int dstPage);
|
||||
void printTalkTextMessage(const char *text, int x, int y, uint8 color, int srcPage, int dstPage);
|
||||
virtual void printText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
|
||||
void printCharacterText(const char *text, int8 charNum, int charX);
|
||||
|
||||
uint16 _talkMessageY;
|
||||
uint16 _talkMessageH;
|
||||
int _langExtraSpacing;
|
||||
bool printed() const { return _talkMessagePrinted; }
|
||||
|
||||
protected:
|
||||
Screen *_screen;
|
||||
KyraEngine_v1 *_vm;
|
||||
|
||||
struct TalkCoords {
|
||||
uint16 y, x, w;
|
||||
};
|
||||
|
||||
// TODO: AMIGA and LoK specific, move to a better location
|
||||
void setTextColor(uint8 color);
|
||||
|
||||
enum {
|
||||
TALK_SUBSTRING_LEN = 80,
|
||||
TALK_SUBSTRING_NUM = 6
|
||||
};
|
||||
|
||||
char _talkBuffer[1040];
|
||||
char _talkSubstrings[TALK_SUBSTRING_LEN * TALK_SUBSTRING_NUM];
|
||||
TalkCoords _talkCoords;
|
||||
bool _talkMessagePrinted;
|
||||
|
||||
char _lineBreakChar;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
251
engines/kyra/text/text_eob_segacd.cpp
Normal file
251
engines/kyra/text/text_eob_segacd.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#include "kyra/engine/eob.h"
|
||||
#include "kyra/graphics/screen_eob.h"
|
||||
#include "kyra/graphics/screen_eob_segacd.h"
|
||||
#include "kyra/text/text_eob_segacd.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _engine(engine), _screen(scr), _renderer(scr->sega_getRenderer()),
|
||||
_curDim(0), _textColor(0xFF), _curPosY(0), _curPosX(0) {
|
||||
assert(_renderer);
|
||||
_msgRenderBufferSize = 320 * 48;
|
||||
_msgRenderBuffer = new uint8[_msgRenderBufferSize]();
|
||||
}
|
||||
|
||||
TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
|
||||
delete[] _msgRenderBuffer;
|
||||
}
|
||||
|
||||
void TextDisplayer_SegaCD::printDialogueText(int id, const char *string1, const char *string2) {
|
||||
if (string1 && string2) {
|
||||
_engine->runDialogue(id, 2, 2, string1, string2);
|
||||
} else {
|
||||
_screen->hideMouse();
|
||||
_engine->seq_segaPlaySequence(id);
|
||||
_screen->showMouse();
|
||||
}
|
||||
}
|
||||
|
||||
void TextDisplayer_SegaCD::printDialogueText(const char *str, bool wait) {
|
||||
int cs = _screen->setFontStyles(Screen::FID_8_FNT, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleNone : Font::kStyleFullWidth);
|
||||
clearDim(_curDim);
|
||||
|
||||
if (wait) {
|
||||
printShadedText(str, 32, 12);
|
||||
_engine->resetSkipFlag();
|
||||
_renderer->render(0);
|
||||
_screen->updateScreen();
|
||||
_engine->delay(500);
|
||||
} else {
|
||||
printShadedText(str, 0, 0);
|
||||
_renderer->render(0);
|
||||
_screen->updateScreen();
|
||||
}
|
||||
|
||||
_screen->setFontStyles(Screen::FID_8_FNT, cs);
|
||||
}
|
||||
|
||||
void TextDisplayer_SegaCD::printShadedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, int marginRight, bool screenUpdate) {
|
||||
const ScreenDim *s = &_dimTable[_curDim];
|
||||
if (x == -1)
|
||||
x = s->sx;
|
||||
if (y == -1)
|
||||
y = s->sy;
|
||||
if (textColor == -1)
|
||||
textColor = s->col1;
|
||||
if (shadowColor == -1)
|
||||
shadowColor = 0;
|
||||
if (pitchW == -1)
|
||||
pitchW = s->w;
|
||||
if (pitchH == -1)
|
||||
pitchH = s->h;
|
||||
|
||||
_screen->setTextMarginRight(pitchW - marginRight);
|
||||
_screen->printShadedText(str, x, y, textColor, 0, shadowColor, pitchW >> 3);
|
||||
|
||||
if (!screenUpdate)
|
||||
return;
|
||||
|
||||
if (s->column) {
|
||||
for (int i = 0; i < (pitchH >> 3); ++i)
|
||||
_screen->sega_loadTextBufferToVRAM(i * (pitchW << 2), ((s->line & 0x7FF) + i * s->column) << 5, pitchW << 2);
|
||||
} else {
|
||||
_screen->sega_loadTextBufferToVRAM(0, (s->line & 0x7FF) << 5, (pitchW * pitchH) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
int TextDisplayer_SegaCD::clearDim(int dim) {
|
||||
int res = _curDim;
|
||||
_curDim = dim;
|
||||
_curPosY = _curPosX = 0;
|
||||
const ScreenDim *s = &_dimTable[dim];
|
||||
_renderer->memsetVRAM((s->line & 0x7FF) << 5, s->col2, (s->w * s->h) >> 1);
|
||||
_screen->sega_clearTextBuffer(s->col2);
|
||||
memset(_msgRenderBuffer, s->col2, _msgRenderBufferSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
void TextDisplayer_SegaCD::displayText(char *str, ...) {
|
||||
_screen->sega_setTextBuffer(_msgRenderBuffer, _msgRenderBufferSize);
|
||||
int cs = _screen->setFontStyles(Screen::FID_8_FNT, Font::kStyleFullWidth);
|
||||
_screen->setFontStyles(Screen::FID_8_FNT, cs | Font::kStyleFullWidth);
|
||||
char tmp[3] = " ";
|
||||
int posX = _curPosX;
|
||||
bool updated = false;
|
||||
|
||||
va_list args;
|
||||
va_start(args, str);
|
||||
int tc = va_arg(args, int);
|
||||
va_end(args);
|
||||
|
||||
if (tc != -1)
|
||||
SWAP(_textColor, tc);
|
||||
|
||||
for (const char *pos = str; *pos; ) {
|
||||
uint8 cmd = fetchCharacter(tmp, pos);
|
||||
updated = false;
|
||||
|
||||
if (_dimTable[_curDim].h < _curPosY + _screen->getFontHeight()) {
|
||||
_curPosY -= _screen->getFontHeight();
|
||||
linefeed();
|
||||
}
|
||||
|
||||
if (cmd == 6) {
|
||||
_textColor = (uint8)*pos++;
|
||||
} else if (cmd == 2) {
|
||||
pos++;
|
||||
} else if (cmd == 13) {
|
||||
_curPosX = 0;
|
||||
_curPosY += _screen->getFontHeight();
|
||||
} else if (cmd == 9) {
|
||||
_curPosX = posX;
|
||||
_curPosY += _screen->getFontHeight();
|
||||
} else {
|
||||
if (((tmp[0] == ' ' || (tmp[0] == '\x81' && tmp[1] == '\x40')) && (_curPosX + _screen->getTextWidth(tmp) + _screen->getTextWidth((const char*)(pos), true) >= _dimTable[_curDim].w)) || (_curPosX + _screen->getTextWidth(tmp) >= _dimTable[_curDim].w)) {
|
||||
// Skip space at the beginning of the new line
|
||||
if (tmp[0] == ' ' || (tmp[0] == '\x81' && tmp[1] == '\x40'))
|
||||
fetchCharacter(tmp, pos);
|
||||
|
||||
_curPosX = 0;
|
||||
_curPosY += _screen->getFontHeight();
|
||||
if (_dimTable[_curDim].h < _curPosY + _screen->getFontHeight()) {
|
||||
_curPosY -= _screen->getFontHeight();
|
||||
linefeed();
|
||||
}
|
||||
}
|
||||
|
||||
printShadedText(tmp, _curPosX, _curPosY, _textColor);
|
||||
_curPosX += _screen->getTextWidth(tmp);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
printShadedText("", _curPosX, _curPosY, _textColor);
|
||||
|
||||
if (tc != -1)
|
||||
SWAP(_textColor, tc);
|
||||
|
||||
_renderer->render(Screen_EoB::kSegaRenderPage);
|
||||
_screen->setFontStyles(Screen::FID_8_FNT, cs);
|
||||
_screen->copyRegion(8, 176, 8, 176, 280, 24, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
|
||||
_screen->sega_setTextBuffer(0, 0);
|
||||
}
|
||||
|
||||
uint8 TextDisplayer_SegaCD::fetchCharacter(char *dest, const char *&src) {
|
||||
uint8 c = (uint8)*src++;
|
||||
|
||||
if (c <= (uint8)'\r') {
|
||||
dest[0] = '\0';
|
||||
return c;
|
||||
}
|
||||
|
||||
dest[0] = (char)c;
|
||||
dest[1] = (c <= 0x7F || (c >= 0xA1 && c <= 0xDF)) ? '\0' : *src++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TextDisplayer_SegaCD::linefeed() {
|
||||
copyTextBufferLine(_screen->getFontHeight(), 0, (_dimTable[_curDim].h & ~7) - _screen->getFontHeight(), _dimTable[_curDim].w >> 3);
|
||||
clearTextBufferLine(_screen->getFontHeight(), _screen->getFontHeight(), _dimTable[_curDim].w >> 3, _dimTable[_curDim].col2);
|
||||
}
|
||||
|
||||
void TextDisplayer_SegaCD::clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col) {
|
||||
uint32 *dst = (uint32*)(_msgRenderBuffer + (((y >> 3) * pitch) << 5) + ((y & 7) << 2));
|
||||
int ln = y;
|
||||
uint32 c = col | (col << 8) | (col << 16) | (col << 24);
|
||||
while (lineHeight--) {
|
||||
uint32 *dst2 = dst;
|
||||
for (uint16 w = pitch; w; --w) {
|
||||
*dst = c;
|
||||
dst += 8;
|
||||
}
|
||||
dst = dst2 + 1;
|
||||
if (((++ln) & 7) == 0)
|
||||
dst += ((pitch - 1) << 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TextDisplayer_SegaCD::copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch) {
|
||||
uint32 *src = (uint32*)(_msgRenderBuffer + (((srcY >> 3) * pitch) << 5) + ((srcY & 7) << 2));
|
||||
uint32 *dst = (uint32*)(_msgRenderBuffer + (((dstY >> 3) * pitch) << 5) + ((dstY & 7) << 2));
|
||||
int src_ln = srcY;
|
||||
int dst_ln = dstY;
|
||||
|
||||
while (lineHeight--) {
|
||||
uint32 *src2 = src;
|
||||
uint32 *dst2 = dst;
|
||||
|
||||
for (uint16 w = pitch; w; --w) {
|
||||
*dst = *src;
|
||||
src += 8;
|
||||
dst += 8;
|
||||
}
|
||||
|
||||
src = src2 + 1;
|
||||
dst = dst2 + 1;
|
||||
|
||||
if (((++dst_ln) & 7) == 0)
|
||||
dst += ((pitch - 1) << 3);
|
||||
if (((++src_ln) & 7) == 0)
|
||||
src += ((pitch - 1) << 3);
|
||||
}
|
||||
}
|
||||
|
||||
const ScreenDim TextDisplayer_SegaCD::_dimTable[6] = {
|
||||
{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
|
||||
{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x99, 0x0153, 0x0028 },
|
||||
{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 },
|
||||
{ 0x0001, 0x0017, 0x00D0, 0x0030, 0xff, 0x00, 0x0461, 0x0000 },
|
||||
{ 0x0000, 0x0000, 0x00F0, 0x0100, 0xff, 0x00, 0x600A, 0x0000 },
|
||||
{ 0x0000, 0x0000, 0x0140, 0x00B0, 0xff, 0x11, 0x4001, 0x0000 }
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif // ENABLE_EOB
|
||||
70
engines/kyra/text/text_eob_segacd.h
Normal file
70
engines/kyra/text/text_eob_segacd.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_EOB
|
||||
|
||||
#ifndef KYRA_TEXT_EOB_SEGACD_H
|
||||
#define KYRA_TEXT_EOB_SEGACD_H
|
||||
|
||||
#include "kyra/text/text_rpg.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class EoBEngine;
|
||||
class Screen_EoB;
|
||||
|
||||
class TextDisplayer_SegaCD : public TextDisplayer_rpg {
|
||||
public:
|
||||
TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr);
|
||||
virtual ~TextDisplayer_SegaCD();
|
||||
|
||||
void printDialogueText(int id, const char *string1, const char *string2) override;
|
||||
void printDialogueText(const char *str, bool wait = false) override;
|
||||
void printShadedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) override;
|
||||
int clearDim(int dim) override;
|
||||
|
||||
private:
|
||||
void displayText(char *str, ...) override;
|
||||
uint8 fetchCharacter(char *dest, const char *&src);
|
||||
void linefeed();
|
||||
|
||||
void clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col);
|
||||
void copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch);
|
||||
|
||||
Screen_EoB *_screen;
|
||||
SegaRenderer *_renderer;
|
||||
EoBEngine *_engine;
|
||||
uint8 *_msgRenderBuffer;
|
||||
uint32 _msgRenderBufferSize;
|
||||
|
||||
int _curDim;
|
||||
int _curPosY;
|
||||
int _curPosX;
|
||||
int _textColor;
|
||||
|
||||
static const ScreenDim _dimTable[6];
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ENABLE_EOB
|
||||
667
engines/kyra/text/text_hof.cpp
Normal file
667
engines/kyra/text/text_hof.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
/* 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 "kyra/text/text_hof.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
TextDisplayer_HoF::TextDisplayer_HoF(KyraEngine_HoF *vm, Screen_v2 *screen)
|
||||
: TextDisplayer(vm, screen), _vm(vm) {
|
||||
}
|
||||
|
||||
void TextDisplayer_HoF::backupTalkTextMessageBkgd(int srcPage, int dstPage) {
|
||||
_screen->copyRegion(_talkCoords.x, _talkMessageY, 0, 144, _talkCoords.w, _talkMessageH, srcPage, dstPage);
|
||||
}
|
||||
|
||||
void TextDisplayer_HoF::restoreTalkTextMessageBkgd(int srcPage, int dstPage) {
|
||||
_screen->copyRegion(0, 144, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, srcPage, dstPage);
|
||||
}
|
||||
|
||||
void TextDisplayer_HoF::restoreScreen() {
|
||||
_vm->restorePage3();
|
||||
_vm->drawAnimObjects();
|
||||
_screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, 2, 0, Screen::CR_NO_P_CHECK);
|
||||
_vm->flagAnimObjsForRefresh();
|
||||
_vm->refreshAnimObjects(0);
|
||||
}
|
||||
|
||||
void TextDisplayer_HoF::printCustomCharacterText(const char *text, int x, int y, uint8 c1, int srcPage, int dstPage) {
|
||||
text = preprocessString(text);
|
||||
int lineCount = buildMessageSubstrings(text);
|
||||
int w = getWidestLineWidth(lineCount);
|
||||
int h = lineCount * _vm->_lineHeight;
|
||||
y = MAX(0, y - (lineCount * _vm->_lineHeight));
|
||||
int x1 = 0, x2 = 0;
|
||||
calcWidestLineBounds(x1, x2, w, x);
|
||||
|
||||
_talkCoords.x = x1;
|
||||
_talkCoords.w = w+2;
|
||||
_talkCoords.y = y;
|
||||
_talkMessageY = y;
|
||||
_talkMessageH = h;
|
||||
|
||||
backupTalkTextMessageBkgd(srcPage, dstPage);
|
||||
int curPageBackUp = _screen->_curPage;
|
||||
_screen->_curPage = srcPage;
|
||||
|
||||
if (_vm->textEnabled()) {
|
||||
for (int i = 0; i < lineCount; ++i) {
|
||||
const char *msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
|
||||
if (i == 0 || _vm->gameFlags().lang != Common::ZH_TWN)
|
||||
x = getCenterStringX(msg, x1, x2);
|
||||
printText(msg, x, i * _vm->_lineHeight + _talkMessageY, c1, 0xCF, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_screen->_curPage = curPageBackUp;
|
||||
}
|
||||
|
||||
char *TextDisplayer_HoF::preprocessString(const char *str) {
|
||||
if (str != _talkBuffer) {
|
||||
assert(strlen(str) < sizeof(_talkBuffer) - 1);
|
||||
Common::strlcpy(_talkBuffer, str, sizeof(_talkBuffer));
|
||||
}
|
||||
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN)
|
||||
return _talkBuffer;
|
||||
|
||||
char *p = _talkBuffer;
|
||||
while (*p) {
|
||||
if (*p == '\r')
|
||||
return _talkBuffer;
|
||||
++p;
|
||||
}
|
||||
|
||||
p = _talkBuffer;
|
||||
Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
|
||||
_screen->_charSpacing = -2;
|
||||
int textWidth = _screen->getTextWidth(p);
|
||||
_screen->_charSpacing = 0;
|
||||
|
||||
int maxTextWidth = (_vm->language() == 0) ? 176 : 240;
|
||||
|
||||
if (textWidth > maxTextWidth) {
|
||||
if (textWidth > (maxTextWidth*2)) {
|
||||
int count = getCharLength(p, textWidth / 3);
|
||||
int offs = dropCRIntoString(p, count);
|
||||
p += count + offs;
|
||||
_screen->_charSpacing = -2;
|
||||
textWidth = _screen->getTextWidth(p);
|
||||
_screen->_charSpacing = 0;
|
||||
count = getCharLength(p, textWidth / 2);
|
||||
dropCRIntoString(p, count);
|
||||
} else {
|
||||
int count = getCharLength(p, textWidth / 2);
|
||||
dropCRIntoString(p, count);
|
||||
}
|
||||
}
|
||||
_screen->setFont(curFont);
|
||||
return _talkBuffer;
|
||||
}
|
||||
|
||||
void TextDisplayer_HoF::calcWidestLineBounds(int &x1, int &x2, int w, int x) {
|
||||
x1 = x;
|
||||
x1 -= (w >> 1);
|
||||
x2 = x1 + w + 1;
|
||||
|
||||
if (x1 + w >= 311)
|
||||
x1 = 311 - w - 1;
|
||||
|
||||
if (x1 < 8)
|
||||
x1 = 8;
|
||||
|
||||
x2 = x1 + w + 1;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
int KyraEngine_HoF::chatGetType(const char *str) {
|
||||
str += strlen(str);
|
||||
--str;
|
||||
switch (*str) {
|
||||
case '!':
|
||||
return 2;
|
||||
|
||||
case ')':
|
||||
return -1;
|
||||
|
||||
case '?':
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int KyraEngine_HoF::chatCalcDuration(const Common::String &str) {
|
||||
static const uint8 durationMultiplicator[] = { 16, 14, 12, 10, 8, 8, 7, 6, 5, 4 };
|
||||
|
||||
int duration = str.size();
|
||||
duration *= _flags.isTalkie ? 8 : durationMultiplicator[(_configTextspeed / 10)];
|
||||
return MAX<int>(duration, 120);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::objectChat(const Common::String &str, int object, int vocHigh, int vocLow) {
|
||||
setNextIdleAnimTimer();
|
||||
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
|
||||
objectChatInit(str, object, vocHigh, vocLow);
|
||||
_chatText = str;
|
||||
_chatObject = object;
|
||||
int chatType = chatGetType(str.c_str());
|
||||
if (chatType == -1) {
|
||||
_chatIsNote = true;
|
||||
chatType = 0;
|
||||
}
|
||||
|
||||
if (_mainCharacter.facing > 7)
|
||||
_mainCharacter.facing = 5;
|
||||
|
||||
static const uint8 talkScriptTable[] = {
|
||||
6, 7, 8,
|
||||
3, 4, 5,
|
||||
3, 4, 5,
|
||||
0, 1, 2,
|
||||
0, 1, 2,
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
3, 4, 5
|
||||
};
|
||||
|
||||
assert(_mainCharacter.facing * 3 + chatType < ARRAYSIZE(talkScriptTable));
|
||||
int script = talkScriptTable[_mainCharacter.facing * 3 + chatType];
|
||||
|
||||
static const char *const chatScriptFilenames[] = {
|
||||
"_Z1FSTMT.EMC",
|
||||
"_Z1FQUES.EMC",
|
||||
"_Z1FEXCL.EMC",
|
||||
"_Z1SSTMT.EMC",
|
||||
"_Z1SQUES.EMC",
|
||||
"_Z1SEXCL.EMC",
|
||||
"_Z1BSTMT.EMC",
|
||||
"_Z1BQUES.EMC",
|
||||
"_Z1BEXCL.EMC"
|
||||
};
|
||||
|
||||
objectChatProcess(chatScriptFilenames[script]);
|
||||
_chatIsNote = false;
|
||||
|
||||
_text->restoreScreen();
|
||||
|
||||
_mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
|
||||
updateCharacterAnim(0);
|
||||
|
||||
_chatText = "";
|
||||
_chatObject = -1;
|
||||
|
||||
setNextIdleAnimTimer();
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::objectChatInit(const Common::String &str0, int object, int vocHigh, int vocLow) {
|
||||
Common::String str = _text->preprocessString(str0.c_str());
|
||||
int lineNum = _text->buildMessageSubstrings(str.c_str());
|
||||
|
||||
int yPos = 0, xPos = 0;
|
||||
|
||||
if (!object) {
|
||||
int scale = getScale(_mainCharacter.x1, _mainCharacter.y1);
|
||||
yPos = _mainCharacter.y1 - ((_mainCharacter.height * scale) >> 8) - 8;
|
||||
xPos = _mainCharacter.x1;
|
||||
} else {
|
||||
yPos = _talkObjectList[object].y;
|
||||
xPos = _talkObjectList[object].x;
|
||||
}
|
||||
|
||||
yPos -= lineNum * _lineHeight;
|
||||
yPos = MAX(yPos, 0);
|
||||
_text->_talkMessageY = yPos;
|
||||
_text->_talkMessageH = lineNum * _lineHeight;
|
||||
|
||||
int width = _text->getWidestLineWidth(lineNum);
|
||||
_text->calcWidestLineBounds(xPos, yPos, width, xPos);
|
||||
_text->_talkCoords.x = xPos;
|
||||
_text->_talkCoords.w = width + 2;
|
||||
|
||||
restorePage3();
|
||||
_text->backupTalkTextMessageBkgd(2, 2);
|
||||
|
||||
_chatTextEnabled = textEnabled();
|
||||
if (_chatTextEnabled) {
|
||||
objectChatPrintText(str, object);
|
||||
_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
|
||||
} else {
|
||||
_chatEndTime = _system->getMillis();
|
||||
}
|
||||
|
||||
if (speechEnabled()) {
|
||||
_chatVocHigh = vocHigh;
|
||||
_chatVocLow = vocLow;
|
||||
} else {
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::objectChatPrintText(const Common::String &str0, int object) {
|
||||
int c1 = _talkObjectList[object].color;
|
||||
Common::String str = _text->preprocessString(str0.c_str());
|
||||
int lineNum = _text->buildMessageSubstrings(str.c_str());
|
||||
int maxWidth = _text->getWidestLineWidth(lineNum);
|
||||
int x = (object == 0) ? _mainCharacter.x1 : _talkObjectList[object].x;
|
||||
int cX1 = 0, cX2 = 0;
|
||||
_text->calcWidestLineBounds(cX1, cX2, maxWidth, x);
|
||||
|
||||
for (int i = 0; i < lineNum; ++i) {
|
||||
str = Common::String(&_text->_talkSubstrings[i*_text->maxSubstringLen()]);
|
||||
|
||||
int y = _text->_talkMessageY + i * _lineHeight;
|
||||
if (i == 0 || _flags.lang != Common::ZH_TWN)
|
||||
x = _text->getCenterStringX(str, cX1, cX2);
|
||||
|
||||
_text->printText(str, x, y, c1, 0xCF, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::objectChatProcess(const char *script) {
|
||||
memset(&_chatScriptData, 0, sizeof(_chatScriptData));
|
||||
memset(&_chatScriptState, 0, sizeof(_chatScriptState));
|
||||
|
||||
_emc->load(script, &_chatScriptData, &_opcodesAnimation);
|
||||
_emc->init(&_chatScriptState, &_chatScriptData);
|
||||
_emc->start(&_chatScriptState, 0);
|
||||
while (_emc->isValid(&_chatScriptState))
|
||||
_emc->run(&_chatScriptState);
|
||||
|
||||
_animShapeFilename[2] = _characterShapeFile + '0';
|
||||
uint8 *shapeBuffer = _res->fileData(_animShapeFilename, nullptr);
|
||||
if (shapeBuffer) {
|
||||
int shapeCount = initAnimationShapes(shapeBuffer);
|
||||
|
||||
if (_chatVocHigh >= 0) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
objectChatWaitToFinish();
|
||||
|
||||
uninitAnimationShapes(shapeCount, shapeBuffer);
|
||||
} else {
|
||||
warning("couldn't load file '%s'", _animShapeFilename);
|
||||
}
|
||||
|
||||
_emc->unload(&_chatScriptData);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::objectChatWaitToFinish() {
|
||||
int charAnimFrame = _mainCharacter.animFrame;
|
||||
setCharacterAnimDim(_animShapeWidth, _animShapeHeight);
|
||||
|
||||
_emc->init(&_chatScriptState, &_chatScriptData);
|
||||
_emc->start(&_chatScriptState, 1);
|
||||
|
||||
bool running = true;
|
||||
const uint32 endTime = _chatEndTime;
|
||||
resetSkipFlag();
|
||||
|
||||
while (running && !shouldQuit()) {
|
||||
if (!_emc->isValid(&_chatScriptState))
|
||||
_emc->start(&_chatScriptState, 1);
|
||||
|
||||
_animNeedUpdate = false;
|
||||
while (!_animNeedUpdate && _emc->isValid(&_chatScriptState))
|
||||
_emc->run(&_chatScriptState);
|
||||
|
||||
int curFrame = _animNewFrame;
|
||||
uint32 delayTime = _animDelayTime;
|
||||
|
||||
if (!_chatIsNote)
|
||||
_mainCharacter.animFrame = 33 + curFrame;
|
||||
|
||||
updateCharacterAnim(0);
|
||||
|
||||
uint32 nextFrame = _system->getMillis() + delayTime * _tickLength;
|
||||
|
||||
while (_system->getMillis() < nextFrame && !shouldQuit()) {
|
||||
updateWithText();
|
||||
|
||||
const uint32 curTime = _system->getMillis();
|
||||
if ((textEnabled() && curTime > endTime) || (speechEnabled() && !textEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
|
||||
resetSkipFlag();
|
||||
nextFrame = curTime;
|
||||
running = false;
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
_mainCharacter.animFrame = charAnimFrame;
|
||||
updateCharacterAnim(0);
|
||||
resetCharacterAnimDim();
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::startDialogue(int dlgIndex) {
|
||||
updateDlgBuffer();
|
||||
int csEntry, vocH, unused1, unused2;
|
||||
loadDlgHeader(csEntry, vocH, unused1, unused2);
|
||||
int s = _conversationState[dlgIndex][csEntry];
|
||||
uint8 bufferIndex = 8;
|
||||
|
||||
if (s == -1) {
|
||||
bufferIndex += (dlgIndex * 6);
|
||||
_conversationState[dlgIndex][csEntry] = 0;
|
||||
} else if (!s || s == 2) {
|
||||
bufferIndex += (dlgIndex * 6 + 2);
|
||||
_conversationState[dlgIndex][csEntry] = 1;
|
||||
} else {
|
||||
bufferIndex += (dlgIndex * 6 + 4);
|
||||
_conversationState[dlgIndex][csEntry] = 2;
|
||||
}
|
||||
|
||||
int offs = READ_LE_UINT16(_dlgBuffer + bufferIndex);
|
||||
processDialogue(offs, vocH, csEntry);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::zanthSceneStartupChat() {
|
||||
int lowest = _flags.isTalkie ? 6 : 5;
|
||||
int tableIndex = _mainCharacter.sceneId - READ_LE_UINT16(&_ingameTalkObjIndex[lowest + _newChapterFile]);
|
||||
if (queryGameFlag(0x159) || _newSceneDlgState[tableIndex])
|
||||
return;
|
||||
|
||||
int csEntry, vocH, scIndex1, scIndex2;
|
||||
updateDlgBuffer();
|
||||
loadDlgHeader(csEntry, vocH, scIndex1, scIndex2);
|
||||
|
||||
uint8 bufferIndex = 8 + scIndex1 * 6 + scIndex2 * 4 + tableIndex * 2;
|
||||
int offs = READ_LE_UINT16(_dlgBuffer + bufferIndex);
|
||||
processDialogue(offs, vocH, csEntry);
|
||||
|
||||
_newSceneDlgState[tableIndex] = 1;
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::randomSceneChat() {
|
||||
int lowest = _flags.isTalkie ? 6 : 5;
|
||||
int tableIndex = (_mainCharacter.sceneId - READ_LE_UINT16(&_ingameTalkObjIndex[lowest + _newChapterFile])) << 2;
|
||||
if (queryGameFlag(0x164))
|
||||
return;
|
||||
|
||||
int csEntry, vocH, scIndex1, unused;
|
||||
updateDlgBuffer();
|
||||
loadDlgHeader(csEntry, vocH, scIndex1, unused);
|
||||
|
||||
if (_chatAltFlag) {
|
||||
_chatAltFlag = 0;
|
||||
tableIndex += 2;
|
||||
} else {
|
||||
_chatAltFlag = 1;
|
||||
}
|
||||
|
||||
uint8 bufferIndex = 8 + scIndex1 * 6 + tableIndex;
|
||||
int offs = READ_LE_UINT16(_dlgBuffer + bufferIndex);
|
||||
processDialogue(offs, vocH, csEntry);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::updateDlgBuffer() {
|
||||
static const char suffixTalkie[] = "EFG";
|
||||
static const char suffixTowns[] = "G J";
|
||||
|
||||
if (_currentChapter == _npcTalkChpIndex && _mainCharacter.dlgIndex == _npcTalkDlgIndex)
|
||||
return;
|
||||
|
||||
_npcTalkChpIndex = _currentChapter;
|
||||
_npcTalkDlgIndex = _mainCharacter.dlgIndex;
|
||||
|
||||
Common::String filename = Common::String::format("CH%.02d-S%.02d.DL", _currentChapter, _npcTalkDlgIndex);
|
||||
|
||||
const char *suffix = _flags.isTalkie ? suffixTalkie : suffixTowns;
|
||||
if (_flags.platform != Common::kPlatformDOS || _flags.isTalkie)
|
||||
filename += suffix[_lang];
|
||||
else
|
||||
filename += 'G';
|
||||
|
||||
delete[] _dlgBuffer;
|
||||
_dlgBuffer = _res->fileData(filename.c_str(), nullptr);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::loadDlgHeader(int &csEntry, int &vocH, int &scIndex1, int &scIndex2) {
|
||||
csEntry = READ_LE_UINT16(_dlgBuffer);
|
||||
vocH = READ_LE_UINT16(_dlgBuffer + 2);
|
||||
scIndex1 = READ_LE_UINT16(_dlgBuffer + 4);
|
||||
scIndex2 = READ_LE_UINT16(_dlgBuffer + 6);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::processDialogue(int dlgOffset, int vocH, int csEntry) {
|
||||
int activeTimSequence = -1;
|
||||
int nextTimSequence = -1;
|
||||
int cmd = 0;
|
||||
int vocHi = -1;
|
||||
int vocLo = -1;
|
||||
bool loop = true;
|
||||
int offs = dlgOffset;
|
||||
|
||||
_screen->hideMouse();
|
||||
|
||||
while (loop) {
|
||||
cmd = READ_LE_UINT16(_dlgBuffer + offs);
|
||||
offs += 2;
|
||||
|
||||
nextTimSequence = READ_LE_UINT16(&_ingameTalkObjIndex[cmd]);
|
||||
|
||||
if (nextTimSequence == 10) {
|
||||
if (queryGameFlag(0x3E))
|
||||
nextTimSequence = 14;
|
||||
if (queryGameFlag(0x3F))
|
||||
nextTimSequence = 15;
|
||||
if (queryGameFlag(0x40))
|
||||
nextTimSequence = 16;
|
||||
}
|
||||
|
||||
if (nextTimSequence == 27 && _mainCharacter.sceneId == 34)
|
||||
nextTimSequence = 41;
|
||||
|
||||
if (queryGameFlag(0x72)) {
|
||||
if (nextTimSequence == 18)
|
||||
nextTimSequence = 43;
|
||||
else if (nextTimSequence == 19)
|
||||
nextTimSequence = 44;
|
||||
}
|
||||
|
||||
if (_mainCharacter.x1 > 160) {
|
||||
if (nextTimSequence == 4)
|
||||
nextTimSequence = 46;
|
||||
else if (nextTimSequence == 5)
|
||||
nextTimSequence = 47;
|
||||
}
|
||||
|
||||
if (cmd == 10) {
|
||||
loop = false;
|
||||
|
||||
} else if (cmd == 4) {
|
||||
csEntry = READ_LE_UINT16(_dlgBuffer + offs);
|
||||
setDlgIndex(csEntry);
|
||||
offs += 2;
|
||||
|
||||
} else {
|
||||
Common::String str;
|
||||
if (!_flags.isTalkie || cmd == 11) {
|
||||
int len = READ_LE_UINT16(_dlgBuffer + offs);
|
||||
offs += 2;
|
||||
if (_flags.isTalkie) {
|
||||
vocLo = READ_LE_UINT16(_dlgBuffer + offs);
|
||||
offs += 2;
|
||||
}
|
||||
str = Common::String((const char *) _dlgBuffer + offs, len);
|
||||
offs += len;
|
||||
if (_flags.isTalkie)
|
||||
continue;
|
||||
|
||||
} else if (_flags.isTalkie) {
|
||||
int len = READ_LE_UINT16(_dlgBuffer + offs);
|
||||
offs += 2;
|
||||
static const int irnv[] = { 91, 105, 110, 114, 118 };
|
||||
vocHi = irnv[vocH - 1] + csEntry;
|
||||
vocLo = READ_LE_UINT16(_dlgBuffer + offs);
|
||||
offs += 2;
|
||||
str = Common::String((const char *) _dlgBuffer + offs, len);
|
||||
offs += len;
|
||||
}
|
||||
|
||||
if (!str.empty()) {
|
||||
if ((!_flags.isTalkie && cmd == 11) || (_flags.isTalkie && cmd == 12)) {
|
||||
if (activeTimSequence > -1) {
|
||||
deinitTalkObject(activeTimSequence);
|
||||
activeTimSequence = -1;
|
||||
}
|
||||
objectChat(str, 0, vocHi, vocLo);
|
||||
} else {
|
||||
if (activeTimSequence != nextTimSequence) {
|
||||
if (activeTimSequence > -1)
|
||||
deinitTalkObject(activeTimSequence);
|
||||
initTalkObject(nextTimSequence);
|
||||
activeTimSequence = nextTimSequence;
|
||||
}
|
||||
npcChatSequence(str, nextTimSequence, vocHi, vocLo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (activeTimSequence > -1)
|
||||
deinitTalkObject(activeTimSequence);
|
||||
|
||||
_screen->showMouse();
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::initTalkObject(int index) {
|
||||
TalkObject &object = _talkObjectList[index];
|
||||
|
||||
Common::String STAFilename = Common::String(object.filename) + "_STA.TIM";
|
||||
_TLKFilename = Common::String(object.filename) + "_TLK.TIM";
|
||||
Common::String ENDFilename = Common::String(object.filename) + "_END.TIM";
|
||||
|
||||
_currentTalkSections.STATim = _tim->load(STAFilename.c_str(), &_timOpcodes);
|
||||
_currentTalkSections.TLKTim = _tim->load(_TLKFilename.c_str(), &_timOpcodes);
|
||||
_currentTalkSections.ENDTim = _tim->load(ENDFilename.c_str(), &_timOpcodes);
|
||||
|
||||
if (object.scriptId != -1) {
|
||||
_specialSceneScriptStateBackup[object.scriptId] = _specialSceneScriptState[object.scriptId];
|
||||
_specialSceneScriptState[object.scriptId] = 1;
|
||||
}
|
||||
|
||||
if (_currentTalkSections.STATim) {
|
||||
_tim->resetFinishedFlag();
|
||||
while (!shouldQuit() && !_tim->finished()) {
|
||||
_tim->exec(_currentTalkSections.STATim, false);
|
||||
if (!_chatText.empty())
|
||||
updateWithText();
|
||||
else
|
||||
update();
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::deinitTalkObject(int index) {
|
||||
TalkObject &object = _talkObjectList[index];
|
||||
|
||||
if (_currentTalkSections.ENDTim) {
|
||||
_tim->resetFinishedFlag();
|
||||
while (!shouldQuit() && !_tim->finished()) {
|
||||
_tim->exec(_currentTalkSections.ENDTim, false);
|
||||
if (!_chatText.empty())
|
||||
updateWithText();
|
||||
else
|
||||
update();
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
if (object.scriptId != -1)
|
||||
_specialSceneScriptState[object.scriptId] = _specialSceneScriptStateBackup[object.scriptId];
|
||||
|
||||
_tim->unload(_currentTalkSections.STATim);
|
||||
_tim->unload(_currentTalkSections.TLKTim);
|
||||
_tim->unload(_currentTalkSections.ENDTim);
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::npcChatSequence(const Common::String &str, int objectId, int vocHigh, int vocLow) {
|
||||
_chatText = str;
|
||||
_chatObject = objectId;
|
||||
objectChatInit(str, objectId, vocHigh, vocLow);
|
||||
|
||||
if (!_currentTalkSections.TLKTim)
|
||||
_currentTalkSections.TLKTim = _tim->load(_TLKFilename.c_str(), &_timOpcodes);
|
||||
|
||||
setNextIdleAnimTimer();
|
||||
|
||||
uint32 ct = chatCalcDuration(str);
|
||||
uint32 time = _system->getMillis();
|
||||
_chatEndTime = time + (3 + ct) * _tickLength;
|
||||
uint32 chatAnimEndTime = time + (3 + (ct >> 1)) * _tickLength;
|
||||
|
||||
if (_chatVocHigh >= 0) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
while (((textEnabled() && _chatEndTime > _system->getMillis()) || (speechEnabled() && snd_voiceIsPlaying())) && !(shouldQuit() || skipFlag())) {
|
||||
if ((!speechEnabled() && chatAnimEndTime > _system->getMillis()) || (speechEnabled() && snd_voiceIsPlaying())) {
|
||||
_tim->resetFinishedFlag();
|
||||
while (!_tim->finished() && !skipFlag() && !shouldQuit()) {
|
||||
if (_currentTalkSections.TLKTim)
|
||||
_tim->exec(_currentTalkSections.TLKTim, false);
|
||||
else
|
||||
_tim->resetFinishedFlag();
|
||||
|
||||
updateWithText();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if (_currentTalkSections.TLKTim)
|
||||
_tim->stopCurFunc();
|
||||
}
|
||||
updateWithText();
|
||||
}
|
||||
|
||||
resetSkipFlag();
|
||||
|
||||
_tim->unload(_currentTalkSections.TLKTim);
|
||||
|
||||
_text->restoreScreen();
|
||||
_chatText = "";
|
||||
_chatObject = -1;
|
||||
setNextIdleAnimTimer();
|
||||
}
|
||||
|
||||
void KyraEngine_HoF::setDlgIndex(int dlgIndex) {
|
||||
if (dlgIndex == _mainCharacter.dlgIndex)
|
||||
return;
|
||||
memset(_newSceneDlgState, 0, 32);
|
||||
for (int i = 0; i < 19; i++)
|
||||
memset(_conversationState[i], -1, 14);
|
||||
_chatAltFlag = false;
|
||||
_mainCharacter.dlgIndex = dlgIndex;
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
51
engines/kyra/text/text_hof.h
Normal file
51
engines/kyra/text/text_hof.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KYRA_TEXT_HOF_H
|
||||
#define KYRA_TEXT_HOF_H
|
||||
|
||||
#include "kyra/text/text.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class Screen_v2;
|
||||
class KyraEngine_HoF;
|
||||
|
||||
class TextDisplayer_HoF : public TextDisplayer {
|
||||
friend class KyraEngine_HoF;
|
||||
public:
|
||||
TextDisplayer_HoF(KyraEngine_HoF *vm, Screen_v2 *screen);
|
||||
|
||||
void backupTalkTextMessageBkgd(int srcPage, int dstPage);
|
||||
void restoreTalkTextMessageBkgd(int srcPage, int dstPage) override;
|
||||
void restoreScreen();
|
||||
|
||||
void printCustomCharacterText(const char *src, int x, int y, uint8 c1, int srcPage, int dstPage);
|
||||
|
||||
char *preprocessString(const char *str) override;
|
||||
void calcWidestLineBounds(int &x1, int &x2, int w, int x) override;
|
||||
private:
|
||||
KyraEngine_HoF *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
428
engines/kyra/text/text_lok.cpp
Normal file
428
engines/kyra/text/text_lok.cpp
Normal file
@@ -0,0 +1,428 @@
|
||||
/* 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 "kyra/text/text.h"
|
||||
#include "kyra/engine/kyra_lok.h"
|
||||
#include "kyra/graphics/animator_lok.h"
|
||||
#include "kyra/engine/sprites.h"
|
||||
#include "kyra/engine/timer.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
void KyraEngine_LoK::waitForChatToFinish(int vocFile, int chatDuration, const char *chatStr, uint8 charNum, const bool printText) {
|
||||
bool hasUpdatedNPCs = false;
|
||||
bool runLoop = true;
|
||||
uint8 currPage;
|
||||
|
||||
uint32 timeToEnd = strlen(chatStr) * 8 * _tickLength + _system->getMillis();
|
||||
bool textOnly = textEnabled() && vocFile == - 1;
|
||||
|
||||
if (textOnly && chatDuration != -1) {
|
||||
switch (_configTextspeed) {
|
||||
case 0:
|
||||
chatDuration *= 2;
|
||||
break;
|
||||
case 2:
|
||||
chatDuration /= 4;
|
||||
break;
|
||||
case 3:
|
||||
chatDuration = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (chatDuration != -1)
|
||||
chatDuration *= _tickLength;
|
||||
|
||||
if (vocFile != -1)
|
||||
snd_playVoiceFile(vocFile);
|
||||
|
||||
_timer->disable(14);
|
||||
_timer->disable(18);
|
||||
_timer->disable(19);
|
||||
|
||||
uint32 timeAtStart = _system->getMillis();
|
||||
uint32 loopStart;
|
||||
while (runLoop) {
|
||||
loopStart = _system->getMillis();
|
||||
if (_currentCharacter->sceneId == 210)
|
||||
if (seq_playEnd())
|
||||
break;
|
||||
|
||||
if (_system->getMillis() > timeToEnd && !hasUpdatedNPCs) {
|
||||
hasUpdatedNPCs = true;
|
||||
_timer->disable(15);
|
||||
_currHeadShape = 4;
|
||||
_animator->animRefreshNPC(0);
|
||||
_animator->animRefreshNPC(_talkingCharNum);
|
||||
|
||||
if (_charSayUnk2 != -1) {
|
||||
_animator->sprites()[_charSayUnk2].active = 0;
|
||||
_sprites->_anims[_charSayUnk2].play = false;
|
||||
_charSayUnk2 = -1;
|
||||
}
|
||||
}
|
||||
|
||||
_timer->update();
|
||||
_sprites->updateSceneAnims();
|
||||
_animator->restoreAllObjectBackgrounds();
|
||||
_animator->preserveAnyChangedBackgrounds();
|
||||
_animator->prepDrawAllObjects();
|
||||
|
||||
if (printText) {
|
||||
currPage = _screen->_curPage;
|
||||
_screen->_curPage = 2;
|
||||
_text->printCharacterText(chatStr, charNum, _characterList[charNum].x1);
|
||||
_screen->_curPage = currPage;
|
||||
}
|
||||
|
||||
_animator->copyChangedObjectsForward(0);
|
||||
updateTextFade();
|
||||
|
||||
if (((chatDuration < (int)(_system->getMillis() - timeAtStart)) && chatDuration != -1 && printText && textOnly) || (!textOnly && !snd_voiceIsPlaying()))
|
||||
break;
|
||||
|
||||
uint32 nextTime = loopStart + _tickLength;
|
||||
|
||||
while (_system->getMillis() < nextTime) {
|
||||
updateInput();
|
||||
|
||||
if (skipFlag()) {
|
||||
runLoop = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextTime - _system->getMillis() >= 10) {
|
||||
_system->delayMillis(10);
|
||||
_screen->updateBackendScreen(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skipFlag()) {
|
||||
resetSkipFlag();
|
||||
snd_stopVoice();
|
||||
}
|
||||
|
||||
_timer->enable(14);
|
||||
_timer->enable(15);
|
||||
_timer->enable(18);
|
||||
_timer->enable(19);
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::endCharacterChat(int8 charNum, int16 convoInitialized) {
|
||||
_talkHeadAnimCharNum = -1;
|
||||
|
||||
if (charNum > 4 && charNum < 11) {
|
||||
_animator->sprites()[_disabledTalkAnimObject].active = 1;
|
||||
_sprites->_anims[_disabledTalkAnimObject].play = true;
|
||||
|
||||
_animator->sprites()[_enabledTalkAnimObject].active = 0;
|
||||
_sprites->_anims[_enabledTalkAnimObject].play = false;
|
||||
}
|
||||
|
||||
if (convoInitialized != 0) {
|
||||
_talkingCharNum = -1;
|
||||
if (_currentCharacter->currentAnimFrame != 88)
|
||||
_currentCharacter->currentAnimFrame = 7;
|
||||
_animator->animRefreshNPC(0);
|
||||
_animator->updateAllObjectShapes();
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::restoreChatPartnerAnimFrame(int8 charNum) {
|
||||
_talkingCharNum = -1;
|
||||
|
||||
if (charNum > 0 && charNum < 5) {
|
||||
_characterList[charNum].currentAnimFrame = _currentChatPartnerBackupFrame;
|
||||
_animator->animRefreshNPC(charNum);
|
||||
}
|
||||
|
||||
if (_currentCharacter->currentAnimFrame != 88)
|
||||
_currentCharacter->currentAnimFrame = 7;
|
||||
|
||||
_animator->animRefreshNPC(0);
|
||||
_animator->updateAllObjectShapes();
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::backupChatPartnerAnimFrame(int8 charNum) {
|
||||
_talkingCharNum = 0;
|
||||
|
||||
if (charNum < 5 && charNum > 0)
|
||||
_currentChatPartnerBackupFrame = _characterList[charNum].currentAnimFrame;
|
||||
|
||||
if (_currentCharacter->currentAnimFrame != 88) {
|
||||
_currentCharacter->currentAnimFrame = 16;
|
||||
if (_scaleMode != 0)
|
||||
_currentCharacter->currentAnimFrame = 7;
|
||||
}
|
||||
|
||||
_animator->animRefreshNPC(0);
|
||||
_animator->updateAllObjectShapes();
|
||||
}
|
||||
|
||||
int8 KyraEngine_LoK::getChatPartnerNum() {
|
||||
uint8 sceneTable[] = {0x2, 0x5, 0x2D, 0x7, 0x1B, 0x8, 0x22, 0x9, 0x30, 0x0A};
|
||||
int pos = 0;
|
||||
int partner = -1;
|
||||
|
||||
for (int i = 1; i < 6; i++) {
|
||||
if (_currentCharacter->sceneId == sceneTable[pos]) {
|
||||
partner = sceneTable[pos + 1];
|
||||
break;
|
||||
}
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
for (int i = 1; i < 5; i++) {
|
||||
if (_characterList[i].sceneId == _currentCharacter->sceneId) {
|
||||
partner = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return partner;
|
||||
}
|
||||
|
||||
int KyraEngine_LoK::initCharacterChat(int8 charNum) {
|
||||
int returnValue = 0;
|
||||
|
||||
if (_talkingCharNum == -1) {
|
||||
returnValue = 1;
|
||||
_talkingCharNum = 0;
|
||||
|
||||
if (_currentCharacter->currentAnimFrame != 88) {
|
||||
_currentCharacter->currentAnimFrame = 16;
|
||||
if (_scaleMode != 0)
|
||||
_currentCharacter->currentAnimFrame = 7;
|
||||
}
|
||||
|
||||
_animator->animRefreshNPC(0);
|
||||
_animator->updateAllObjectShapes();
|
||||
}
|
||||
|
||||
_charSayUnk2 = -1;
|
||||
_animator->flagAllObjectsForBkgdChange();
|
||||
_animator->restoreAllObjectBackgrounds();
|
||||
|
||||
if (charNum > 4 && charNum < 11) {
|
||||
const uint8 animDisableTable[] = { 3, 1, 1, 5, 0, 6 };
|
||||
const uint8 animEnableTable[] = { 4, 2, 5, 6, 1, 7 };
|
||||
|
||||
_disabledTalkAnimObject = animDisableTable[charNum - 5];
|
||||
_enabledTalkAnimObject = animEnableTable[charNum - 5];
|
||||
|
||||
_animator->sprites()[_disabledTalkAnimObject].active = 0;
|
||||
_sprites->_anims[_disabledTalkAnimObject].play = false;
|
||||
|
||||
_animator->sprites()[_enabledTalkAnimObject].active = 1;
|
||||
_sprites->_anims[_enabledTalkAnimObject].play = true;
|
||||
|
||||
_charSayUnk2 = _enabledTalkAnimObject;
|
||||
}
|
||||
|
||||
_animator->flagAllObjectsForRefresh();
|
||||
_animator->flagAllObjectsForBkgdChange();
|
||||
_animator->preserveAnyChangedBackgrounds();
|
||||
_talkHeadAnimCharNum = charNum;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::characterSays(int vocFile, const char *chatStr, int16 charNum, int16 chatDuration) {
|
||||
uint8 startAnimFrames[] = { 0x10, 0x32, 0x56, 0x0, 0x0, 0x0 };
|
||||
|
||||
uint16 chatTicks;
|
||||
int16 convoInitialized;
|
||||
int8 chatPartnerNum;
|
||||
|
||||
if (_currentCharacter->sceneId == 210)
|
||||
return;
|
||||
|
||||
snd_voiceWaitForFinish(true);
|
||||
|
||||
convoInitialized = initCharacterChat(charNum);
|
||||
chatPartnerNum = getChatPartnerNum();
|
||||
|
||||
if (chatPartnerNum >= 0 && chatPartnerNum < 5)
|
||||
backupChatPartnerAnimFrame(chatPartnerNum);
|
||||
|
||||
if (charNum < 5) {
|
||||
if (_flags.isTalkie || _flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98 || _animator->_brandonScaleX == 0x100 || !_scaleMode) {
|
||||
_characterList[charNum].currentAnimFrame = startAnimFrames[charNum];
|
||||
_talkHeadAnimCharNum = charNum;
|
||||
_talkingCharNum = charNum;
|
||||
}
|
||||
_animator->animRefreshNPC(charNum);
|
||||
}
|
||||
|
||||
char *processedString = _text->preprocessString(chatStr);
|
||||
int lineNum = _text->buildMessageSubstrings(processedString);
|
||||
if (_flags.lang == Common::ZH_TWN)
|
||||
lineNum = (strlen(chatStr) + 31) >> 5;
|
||||
|
||||
int16 yPos = _characterList[charNum].y1;
|
||||
yPos -= ((_scaleTable[yPos] * _characterList[charNum].height) >> 8);
|
||||
yPos -= 8;
|
||||
yPos -= (lineNum * _screen->getFontHeight() + (lineNum - 1) * _screen->_lineSpacing);
|
||||
|
||||
_text->_talkMessageY = (_flags.lang == Common::ZH_TWN) ? CLIP<int>(yPos, 10, 80) : CLIP<int>(yPos, 11, 100);
|
||||
_text->_talkMessageH = lineNum * _screen->getFontHeight() + (lineNum - 1) * _screen->_lineSpacing + _text->_langExtraSpacing;
|
||||
|
||||
const bool printText = textEnabled();
|
||||
|
||||
if (printText) {
|
||||
_animator->restoreAllObjectBackgrounds();
|
||||
|
||||
_screen->copyRegion(8, _text->_talkMessageY, 8, 136, 304, _text->_talkMessageH, 2, 2);
|
||||
|
||||
_text->printCharacterText(processedString, charNum, _characterList[charNum].x1);
|
||||
}
|
||||
|
||||
// This happens right at the beginning, when talking to the tree and can be seen in DOSBox, too.
|
||||
// It will make the sentence stay basically forever. We just set it to
|
||||
// the value from the other versions (probably some typo from the translators).
|
||||
if (_flags.lang == Common::KO_KOR && chatDuration == -20)
|
||||
chatDuration = -2;
|
||||
|
||||
if (chatDuration == -2)
|
||||
chatTicks = strlen(processedString) * 9;
|
||||
else
|
||||
chatTicks = (uint16)chatDuration;
|
||||
|
||||
if (!speechEnabled())
|
||||
vocFile = -1;
|
||||
waitForChatToFinish(vocFile, (chatDuration == -1) ? -1 : chatTicks, chatStr, charNum, printText);
|
||||
|
||||
if (printText) {
|
||||
_animator->restoreAllObjectBackgrounds();
|
||||
|
||||
_screen->copyRegion(8, 136, 8, _text->_talkMessageY, 304, _text->_talkMessageH, 2, 2);
|
||||
_animator->preserveAllBackgrounds();
|
||||
_animator->prepDrawAllObjects();
|
||||
|
||||
_screen->copyRegion(8, _text->_talkMessageY, 8, _text->_talkMessageY, 304, _text->_talkMessageH, 2, 0);
|
||||
_animator->flagAllObjectsForRefresh();
|
||||
_animator->copyChangedObjectsForward(0);
|
||||
}
|
||||
|
||||
if (chatPartnerNum != -1 && chatPartnerNum < 5)
|
||||
restoreChatPartnerAnimFrame(chatPartnerNum);
|
||||
|
||||
endCharacterChat(charNum, convoInitialized);
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::drawSentenceCommand(const char *sentence, int color) {
|
||||
int boxY1 = 143;
|
||||
int boxY2 = 152;
|
||||
int col2 = _flags.platform == Common::kPlatformAmiga ? 19 : 12;
|
||||
|
||||
if (_flags.lang == Common::ZH_TWN || _flags.lang == Common::KO_KOR) {
|
||||
boxY1 = 140;
|
||||
boxY2 = _flags.lang == Common::KO_KOR ? 155 : 153;
|
||||
col2 = 0;
|
||||
}
|
||||
|
||||
_screen->fillRect(8, boxY1, 311, boxY2, _flags.platform == Common::kPlatformAmiga ? 19 : 12);
|
||||
|
||||
if (_flags.platform == Common::kPlatformAmiga) {
|
||||
if (color != 19) {
|
||||
_currSentenceColor[0] = 0x3F;
|
||||
_currSentenceColor[1] = 0x3F;
|
||||
_currSentenceColor[2] = 0x3F;
|
||||
|
||||
_screen->setInterfacePalette(_screen->getPalette(1),
|
||||
_currSentenceColor[0], _currSentenceColor[1], _currSentenceColor[2]);
|
||||
}
|
||||
} else if (_startSentencePalIndex != color || _fadeText != false) {
|
||||
_currSentenceColor[0] = _screen->getPalette(0)[765] = _screen->getPalette(0)[color * 3 + 0];
|
||||
_currSentenceColor[1] = _screen->getPalette(0)[766] = _screen->getPalette(0)[color * 3 + 1];
|
||||
_currSentenceColor[2] = _screen->getPalette(0)[767] = _screen->getPalette(0)[color * 3 + 2];
|
||||
|
||||
_screen->setScreenPalette(_screen->getPalette(0));
|
||||
_startSentencePalIndex = color;
|
||||
}
|
||||
|
||||
if (_flags.lang != Common::HE_ISR) {
|
||||
_text->printText(sentence, 8, boxY1, 0xFF, col2, 0);
|
||||
} else {
|
||||
_screen->_charSpacing = -2;
|
||||
_text->printText(sentence, 311 - _screen->getTextWidth(sentence), boxY1, 0xFF, col2, 0);
|
||||
_screen->_charSpacing = 0;
|
||||
}
|
||||
setTextFadeTimerCountdown(15);
|
||||
_fadeText = false;
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::updateSentenceCommand(const char *str1, const char *str2, int color) {
|
||||
char sentenceCommand[500];
|
||||
if (_flags.lang == Common::ZH_TWN) {
|
||||
Common::strlcpy(sentenceCommand, str2 ? str2 : str1, sizeof(sentenceCommand));
|
||||
if (str2)
|
||||
Common::strlcat(sentenceCommand, str1, sizeof(sentenceCommand));
|
||||
} else if (_flags.lang == Common::HE_ISR) {
|
||||
if (str2)
|
||||
Common::strlcpy(sentenceCommand, str2, sizeof(sentenceCommand));
|
||||
Common::strlcat(sentenceCommand, str1, sizeof(sentenceCommand));
|
||||
} else {
|
||||
Common::strlcpy(sentenceCommand, str1, sizeof(sentenceCommand));
|
||||
if (str2)
|
||||
Common::strlcat(sentenceCommand, str2, sizeof(sentenceCommand));
|
||||
}
|
||||
|
||||
drawSentenceCommand(sentenceCommand, color);
|
||||
_screen->updateScreen();
|
||||
}
|
||||
|
||||
void KyraEngine_LoK::updateTextFade() {
|
||||
if (!_fadeText)
|
||||
return;
|
||||
|
||||
bool finished = false;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (_currSentenceColor[i] > 4)
|
||||
_currSentenceColor[i] -= 4;
|
||||
else if (_currSentenceColor[i]) {
|
||||
_currSentenceColor[i] = 0;
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_flags.platform == Common::kPlatformAmiga) {
|
||||
_screen->setInterfacePalette(_screen->getPalette(1),
|
||||
_currSentenceColor[0], _currSentenceColor[1], _currSentenceColor[2]);
|
||||
} else {
|
||||
_screen->getPalette(0)[765] = _currSentenceColor[0];
|
||||
_screen->getPalette(0)[766] = _currSentenceColor[1];
|
||||
_screen->getPalette(0)[767] = _currSentenceColor[2];
|
||||
_screen->setScreenPalette(_screen->getPalette(0));
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
_fadeText = false;
|
||||
_startSentencePalIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
363
engines/kyra/text/text_lol.cpp
Normal file
363
engines/kyra/text/text_lol.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_LOL
|
||||
|
||||
#include "kyra/text/text_lol.h"
|
||||
#include "kyra/engine/lol.h"
|
||||
#include "kyra/graphics/screen_lol.h"
|
||||
#include "kyra/engine/timer.h"
|
||||
#include "kyra/sound/sound.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
TextDisplayer_LoL::TextDisplayer_LoL(LoLEngine *engine, Screen_LoL *screenLoL) : TextDisplayer_rpg(engine, screenLoL),
|
||||
_vm(engine), _screen(screenLoL), _scriptTextParameter(0) {
|
||||
|
||||
memset(_stringParameters, 0, 15 * sizeof(char *));
|
||||
_buffer = new char[600]();
|
||||
|
||||
_waitButtonSpace = 0;
|
||||
}
|
||||
|
||||
TextDisplayer_LoL::~TextDisplayer_LoL() {
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
void TextDisplayer_LoL::setupField(bool mode) {
|
||||
if (_vm->textEnabled()) {
|
||||
|
||||
int y = 142;
|
||||
int h = 37;
|
||||
int stepY = 3;
|
||||
int stepH = 1;
|
||||
|
||||
if (_vm->gameFlags().use16ColorMode) {
|
||||
y = 140;
|
||||
h = 39;
|
||||
stepY = 4;
|
||||
stepH = 2;
|
||||
}
|
||||
|
||||
if (mode) {
|
||||
_screen->copyRegionToBuffer(3, 0, 0, 320, 40, _vm->_pageBuffer1);
|
||||
_screen->copyRegion(80, y, 0, 0, 240, h, 0, 3, Screen::CR_NO_P_CHECK);
|
||||
_screen->copyRegionToBuffer(3, 0, 0, 320, 40, _vm->_pageBuffer2);
|
||||
_screen->copyBlockToPage(3, 0, 0, 320, 40, _vm->_pageBuffer1);
|
||||
} else {
|
||||
_screen->setScreenDim(clearDim(4));
|
||||
int cp = _screen->setCurPage(2);
|
||||
_screen->copyRegionToBuffer(3, 0, 0, 320, 40, _vm->_pageBuffer1);
|
||||
_screen->copyBlockToPage(3, 0, 0, 320, 40, _vm->_pageBuffer2);
|
||||
_screen->copyRegion(0, 0, 80, y, 240, h, 3, _screen->_curPage, Screen::CR_NO_P_CHECK);
|
||||
|
||||
uint32 endTime = _vm->_system->getMillis();
|
||||
for (int i = 177; i > 141; i--) {
|
||||
endTime += _vm->_tickLength;
|
||||
_screen->copyRegion(83, i - stepH + 1, 83, i - stepH, 235, stepY, 0, 0, Screen::CR_NO_P_CHECK);
|
||||
_screen->copyRegion(83, i + 1, 83, i + 1, 235, 1, 2, 0, Screen::CR_NO_P_CHECK);
|
||||
_vm->updateInput();
|
||||
_screen->updateScreen();
|
||||
_vm->delayUntil(endTime);
|
||||
}
|
||||
|
||||
_screen->copyBlockToPage(3, 0, 0, 320, 200, _vm->_pageBuffer1);
|
||||
_screen->setCurPage(cp);
|
||||
|
||||
_vm->_updateFlags &= 0xFFFD;
|
||||
}
|
||||
} else {
|
||||
if (!mode)
|
||||
_screen->setScreenDim(clearDim(4));
|
||||
_vm->toggleSelectedCharacterFrame(1);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDisplayer_LoL::expandField() {
|
||||
uint8 *tmp = _vm->_pageBuffer1 + 13000;
|
||||
|
||||
if (_vm->textEnabled()) {
|
||||
_vm->_fadeText = false;
|
||||
_vm->_textColorFlag = 0;
|
||||
_vm->_timer->disable(11);
|
||||
_screen->setScreenDim(clearDim(3));
|
||||
_screen->copyRegionToBuffer(3, 0, 0, 320, 10, tmp);
|
||||
|
||||
int y = 140;
|
||||
int h = 3;
|
||||
int stepH = 0;
|
||||
|
||||
if (_vm->gameFlags().use16ColorMode) {
|
||||
y = 139;
|
||||
h = 4;
|
||||
stepH = 1;
|
||||
}
|
||||
|
||||
_screen->copyRegion(83, y, 0, 0, 235, h, 0, 2, Screen::CR_NO_P_CHECK);
|
||||
|
||||
uint32 endTime = _vm->_system->getMillis();
|
||||
for (int i = 140; i < 177; i++) {
|
||||
endTime += _vm->_tickLength;
|
||||
_screen->copyRegion(0, 0, 83, i - stepH, 235, h, 2, 0, Screen::CR_NO_P_CHECK);
|
||||
_vm->updateInput();
|
||||
_screen->updateScreen();
|
||||
_vm->delayUntil(endTime);
|
||||
}
|
||||
|
||||
_screen->copyBlockToPage(3, 0, 0, 320, 10, tmp);
|
||||
_vm->_updateFlags |= 2;
|
||||
|
||||
} else {
|
||||
clearDim(3);
|
||||
_vm->toggleSelectedCharacterFrame(0);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDisplayer_LoL::printDialogueText2(int dim, const char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) {
|
||||
int oldDim = 0;
|
||||
|
||||
if (dim == 3) {
|
||||
if (_vm->_updateFlags & 2) {
|
||||
oldDim = clearDim(4);
|
||||
_textDimData[4].color1= _vm->gameFlags().use16ColorMode ? 0x33 : 254;
|
||||
_textDimData[4].color2 = _screen->_curDim->col2;
|
||||
} else {
|
||||
oldDim = clearDim(3);
|
||||
_textDimData[3].color1= _vm->gameFlags().use16ColorMode ? 0x33 : 192;
|
||||
_textDimData[3].color2 = _screen->_curDim->col2;
|
||||
if (!_vm->gameFlags().use16ColorMode)
|
||||
_screen->copyColor(192, 254);
|
||||
_vm->enableTimer(11);
|
||||
_vm->_textColorFlag = 0;
|
||||
_vm->_fadeText = false;
|
||||
}
|
||||
} else {
|
||||
oldDim = _screen->curDimIndex();
|
||||
_screen->setScreenDim(dim);
|
||||
_lineCount = 0;
|
||||
_textDimData[dim].color1= _vm->gameFlags().use16ColorMode ? 0x33 : 254;
|
||||
_textDimData[dim].color2 = _screen->_curDim->col2;
|
||||
}
|
||||
|
||||
int cp = _screen->setCurPage(0);
|
||||
Screen::FontId of = _screen->setFont(_vm->gameFlags().lang == Common::Language::ZH_TWN ? Screen::FID_CHINESE_FNT : _pc98TextMode ? Screen::FID_SJIS_TEXTMODE_FNT : Screen::FID_9_FNT);
|
||||
|
||||
preprocessString(str, script, paramList, paramIndex);
|
||||
_numCharsTotal = Common::strnlen(_dialogueBuffer, 2559);
|
||||
displayText(_dialogueBuffer);
|
||||
|
||||
_screen->setScreenDim(oldDim);
|
||||
_screen->setCurPage(cp);
|
||||
_screen->setFont(of);
|
||||
_lineCount = 0;
|
||||
_vm->_fadeText = false;
|
||||
}
|
||||
|
||||
void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) {
|
||||
static const uint8 textColors256[] = { 0xFE, 0xA2, 0x84, 0x97, 0x9F };
|
||||
static const uint8 textColors16[] = { 0x33, 0xAA, 0x88, 0x55, 0x99 };
|
||||
static const uint8 soundEffect[] = { 0x0B, 0x00, 0x2B, 0x1B, 0x00 };
|
||||
|
||||
const uint8 *textColors = _vm->gameFlags().use16ColorMode ? textColors16 : textColors256;
|
||||
|
||||
if (type & 4)
|
||||
type &= ~4;
|
||||
else
|
||||
_vm->stopPortraitSpeechAnim();
|
||||
|
||||
int index = type & 0x7FFF;
|
||||
assert(index < 5);
|
||||
|
||||
uint16 col = textColors[index];
|
||||
|
||||
int od = _screen->curDimIndex();
|
||||
|
||||
if (_vm->_updateFlags & 2) {
|
||||
clearDim(4);
|
||||
_textDimData[4].color1= col;
|
||||
} else {
|
||||
clearDim(3);
|
||||
if (_vm->gameFlags().use16ColorMode) {
|
||||
_textDimData[3].color1= col;
|
||||
} else {
|
||||
_screen->copyColor(192, col);
|
||||
_textDimData[3].color1= 192;
|
||||
}
|
||||
_vm->enableTimer(11);
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, str);
|
||||
|
||||
vsnprintf((char *)_buffer, 240, str, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
displayText(_buffer);
|
||||
|
||||
_screen->setScreenDim(od);
|
||||
_lineCount = 0;
|
||||
|
||||
if (!(type & 0x8000)) {
|
||||
if (soundEffect[index])
|
||||
_vm->sound()->playSoundEffect(soundEffect[index]);
|
||||
}
|
||||
|
||||
_vm->_textColorFlag = index;
|
||||
_vm->_fadeText = false;
|
||||
}
|
||||
|
||||
void TextDisplayer_LoL::preprocessString(const char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) {
|
||||
char *dst = _dialogueBuffer;
|
||||
|
||||
for (const char *s = str; *s;) {
|
||||
if (_vm->gameFlags().lang == Common::JA_JPN) {
|
||||
uint8 c = *s;
|
||||
if (c >= 0xE0 || (c > 0x80 && c < 0xA0)) {
|
||||
*dst++ = *s++;
|
||||
*dst++ = *s++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (*s != '%') {
|
||||
*dst++ = *s++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char para = *++s;
|
||||
bool eos = false;
|
||||
|
||||
switch (para) {
|
||||
case '\0':
|
||||
eos = true;
|
||||
break;
|
||||
case '#':
|
||||
para = *++s;
|
||||
switch (para) {
|
||||
case 'E':
|
||||
case 'G':
|
||||
case 'X':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 's':
|
||||
case 'u':
|
||||
case 'x':
|
||||
break;
|
||||
default:
|
||||
eos = true;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
case '+':
|
||||
case '-':
|
||||
++s;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (eos)
|
||||
continue;
|
||||
|
||||
para = *s;
|
||||
|
||||
switch (para) {
|
||||
case '\0':
|
||||
eos = true;
|
||||
break;
|
||||
case '0':
|
||||
++s;
|
||||
break;
|
||||
default:
|
||||
while (para && para > 47 && para < 58)
|
||||
para = *++s;
|
||||
break;
|
||||
}
|
||||
if (eos)
|
||||
continue;
|
||||
|
||||
para = *s++;
|
||||
|
||||
switch (para) {
|
||||
case 'a':
|
||||
Common::strlcpy(dst, Common::String::format("%d", _scriptTextParameter).c_str(), 2560 - (dst - _dialogueBuffer));
|
||||
dst += Common::strnlen(dst, 2559 - (dst - _dialogueBuffer));
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
if (script || paramList) {
|
||||
Common::strlcpy(dst, _vm->_characters[script ? script->stack[script->sp + paramIndex] : paramList[paramIndex]].name, 2560 - (dst - _dialogueBuffer));
|
||||
dst += Common::strnlen(dst, 2559 - (dst - _dialogueBuffer));
|
||||
} else {
|
||||
warning("TextDisplayer_LoL::preprocessString(): Missing replacement data for placeholder '%%%c'", para);
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (script || paramList) {
|
||||
Common::strlcpy(dst, _vm->getLangString(script ? script->stack[script->sp + paramIndex] : paramList[paramIndex]), 2560 - (dst - _dialogueBuffer));
|
||||
dst += Common::strnlen(dst, 2559 - (dst - _dialogueBuffer));
|
||||
} else {
|
||||
warning("TextDisplayer_LoL::preprocessString(): Missing replacement data for placeholder '%%%c'", para);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
case 'd':
|
||||
case 'u':
|
||||
case 'x':
|
||||
if (script || paramList) {
|
||||
Common::strlcpy(dst, Common::String::format("%d", script ? script->stack[script->sp + paramIndex] : paramList[paramIndex]).c_str(), 2560 - (dst - _dialogueBuffer));
|
||||
dst += Common::strnlen(dst, 2559 - (dst - _dialogueBuffer));
|
||||
} else {
|
||||
warning("TextDisplayer_LoL::preprocessString(): Missing replacement data for placeholder '%%%c'", para);
|
||||
}
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
KyraRpgEngine *TextDisplayer_LoL::vm() {
|
||||
return _vm;
|
||||
}
|
||||
|
||||
Screen *TextDisplayer_LoL::screen() {
|
||||
return _screen;
|
||||
}
|
||||
|
||||
void TextDisplayer_LoL::textPageBreak() {
|
||||
_pageBreakString = _vm->getLangString(0x4073);
|
||||
TextDisplayer_rpg::textPageBreak();
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif // ENABLE_LOL
|
||||
69
engines/kyra/text/text_lol.h
Normal file
69
engines/kyra/text/text_lol.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KYRA_TEXT_LOL_H
|
||||
#define KYRA_TEXT_LOL_H
|
||||
|
||||
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
|
||||
#include "kyra/text/text_rpg.h"
|
||||
#endif
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#ifdef ENABLE_LOL
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class Screen_LoL;
|
||||
class LoLEngine;
|
||||
struct EMCState;
|
||||
|
||||
class TextDisplayer_LoL : public TextDisplayer_rpg {
|
||||
public:
|
||||
TextDisplayer_LoL(LoLEngine *engine, Screen_LoL *screenLoL);
|
||||
~TextDisplayer_LoL() override;
|
||||
|
||||
void setupField(bool mode);
|
||||
void expandField();
|
||||
|
||||
void printDialogueText2(int dim, const char *str, EMCState *script, const uint16 *paramList, int16 paramIndex);
|
||||
void printMessage(uint16 type, MSVC_PRINTF const char *str, ...) GCC_PRINTF(3, 4);
|
||||
|
||||
int16 _scriptTextParameter;
|
||||
|
||||
private:
|
||||
KyraRpgEngine *vm() override;
|
||||
Screen *screen() override;
|
||||
|
||||
void preprocessString(const char *str, EMCState *script, const uint16 *paramList, int16 paramIndex);
|
||||
void textPageBreak() override;
|
||||
|
||||
char *_stringParameters[15];
|
||||
char *_buffer;
|
||||
|
||||
LoLEngine *_vm;
|
||||
Screen_LoL *_screen;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif // ENABLE_LOL
|
||||
|
||||
#endif
|
||||
925
engines/kyra/text/text_mr.cpp
Normal file
925
engines/kyra/text/text_mr.cpp
Normal file
@@ -0,0 +1,925 @@
|
||||
/* 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 "kyra/text/text_mr.h"
|
||||
#include "kyra/resource/resource.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
TextDisplayer_MR::TextDisplayer_MR(KyraEngine_MR *vm, Screen_MR *screen)
|
||||
: TextDisplayer(vm, screen), _vm(vm), _screen(screen) {
|
||||
}
|
||||
|
||||
char *TextDisplayer_MR::preprocessString(const char *str) {
|
||||
if (_talkBuffer != str) {
|
||||
assert(strlen(str) < sizeof(_talkBuffer) - 1);
|
||||
Common::strlcpy(_talkBuffer, str, sizeof(_talkBuffer));
|
||||
}
|
||||
|
||||
char *p = _talkBuffer;
|
||||
while (*p) {
|
||||
if (*p++ == '\r')
|
||||
return _talkBuffer;
|
||||
}
|
||||
|
||||
p = _talkBuffer;
|
||||
|
||||
if (_vm->_lang == 3) {
|
||||
Screen::FontId curFont = _screen->setFont(Screen::FID_CHINESE_FNT);
|
||||
int textLen = Common::strnlen(p, sizeof(_talkBuffer));
|
||||
|
||||
if (textLen > 68) {
|
||||
int maxTextWidth = ((textLen + 3) / 3) & ~1;
|
||||
for (int i = textLen + 1; i >= maxTextWidth; --i)
|
||||
p[i + 1] = p[i];
|
||||
p[maxTextWidth] = '\r';
|
||||
p += (maxTextWidth + 1);
|
||||
textLen -= maxTextWidth;
|
||||
}
|
||||
|
||||
if (textLen > 34) {
|
||||
int maxTextWidth = ((textLen + 2) / 2) & ~1;
|
||||
for (int i = textLen + 1; i >= maxTextWidth; --i)
|
||||
p[i + 1] = p[i];
|
||||
p[maxTextWidth] = '\r';
|
||||
}
|
||||
|
||||
_screen->setFont(curFont);
|
||||
} else {
|
||||
Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
|
||||
_screen->_charSpacing = -2;
|
||||
|
||||
const int maxTextWidth = (_vm->language() == 0) ? 176 : 240;
|
||||
int textWidth = _screen->getTextWidth(p);
|
||||
|
||||
if (textWidth > maxTextWidth) {
|
||||
int count = 0, offs = 0;
|
||||
if (textWidth > (3*maxTextWidth)) {
|
||||
count = getCharLength(p, textWidth/4);
|
||||
offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
|
||||
p += count + offs;
|
||||
// No update of textWidth here
|
||||
}
|
||||
|
||||
if (textWidth > (2*maxTextWidth)) {
|
||||
count = getCharLength(p, textWidth/3);
|
||||
offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
|
||||
p += count + offs;
|
||||
textWidth = _screen->getTextWidth(p);
|
||||
}
|
||||
|
||||
count = getCharLength(p, textWidth/2);
|
||||
offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
|
||||
p += count + offs;
|
||||
textWidth = _screen->getTextWidth(p);
|
||||
|
||||
if (textWidth > maxTextWidth) {
|
||||
count = getCharLength(p, textWidth/2);
|
||||
offs = dropCRIntoString(p, count, getCharLength(p, maxTextWidth));
|
||||
}
|
||||
}
|
||||
_screen->setFont(curFont);
|
||||
}
|
||||
|
||||
return _talkBuffer;
|
||||
}
|
||||
|
||||
int TextDisplayer_MR::dropCRIntoString(char *str, int minOffs, int maxOffs) {
|
||||
int offset = 0;
|
||||
char *proc = str + minOffs;
|
||||
|
||||
for (int i = minOffs; i < maxOffs; ++i) {
|
||||
if (*proc == ' ') {
|
||||
*proc = '\r';
|
||||
return offset;
|
||||
} else if (*proc == '-') {
|
||||
memmove(proc+1, proc, strlen(proc)+1);
|
||||
*(++proc) = '\r';
|
||||
++offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
++offset;
|
||||
++proc;
|
||||
|
||||
if (!*proc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
proc = str + minOffs;
|
||||
for (int i = minOffs; i >= 0; --i) {
|
||||
if (*proc == ' ') {
|
||||
*proc = '\r';
|
||||
return offset;
|
||||
} else if (*proc == '-') {
|
||||
memmove(proc+1, proc, strlen(proc)+1);
|
||||
*(++proc) = '\r';
|
||||
++offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
--offset;
|
||||
--proc;
|
||||
|
||||
if (!*proc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
*(str + minOffs) = '\r';
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TextDisplayer_MR::printText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
|
||||
if (_vm->_albumChatActive) {
|
||||
c0 = 0xEE;
|
||||
c1 = 0xE3;
|
||||
c2 = 0x00;
|
||||
}
|
||||
|
||||
uint8 colorMap[] = { 0, 255, 240, 240 };
|
||||
colorMap[3] = c1;
|
||||
_screen->setTextColor(colorMap, 0, 3);
|
||||
_screen->_charSpacing = -2;
|
||||
|
||||
Common::String revBuffer;
|
||||
const char *cstr = str.c_str();
|
||||
if (_vm->gameFlags().lang == Common::HE_ISR) {
|
||||
for (int i = str.size() - 1; i >= 0; --i)
|
||||
revBuffer += str[i];
|
||||
cstr = revBuffer.c_str();
|
||||
}
|
||||
|
||||
_screen->printText(cstr, x, y, c0, c2);
|
||||
_screen->_charSpacing = 0;
|
||||
}
|
||||
|
||||
void TextDisplayer_MR::restoreScreen() {
|
||||
_vm->restorePage3();
|
||||
_vm->drawAnimObjects();
|
||||
_screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, 2, 0, Screen::CR_NO_P_CHECK);
|
||||
_vm->flagAnimObjsForRefresh();
|
||||
_vm->refreshAnimObjects(0);
|
||||
}
|
||||
|
||||
void TextDisplayer_MR::calcWidestLineBounds(int &x1, int &x2, int w, int x) {
|
||||
x1 = x;
|
||||
x1 -= (w >> 1);
|
||||
x2 = x1 + w + 1;
|
||||
|
||||
if (x1 + w >= 311)
|
||||
x1 = 311 - w - 1;
|
||||
|
||||
if (x1 < 8)
|
||||
x1 = 8;
|
||||
|
||||
x2 = x1 + w + 1;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
int KyraEngine_MR::chatGetType(const char *str) {
|
||||
while (*str)
|
||||
++str;
|
||||
--str;
|
||||
switch (*str) {
|
||||
case '!':
|
||||
return 2;
|
||||
|
||||
case ')':
|
||||
return 3;
|
||||
|
||||
case '?':
|
||||
return 1;
|
||||
|
||||
case '.':
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int KyraEngine_MR::chatCalcDuration(const char *str) {
|
||||
return MAX<int>(120, strlen(str)*6);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::objectChat(const char *str, int object, int vocHigh, int vocLow) {
|
||||
if (_mainCharacter.animFrame == 87 || _mainCharacter.animFrame == 0xFFFF || _mainCharacter.x1 <= 0 || _mainCharacter.y1 <= 0)
|
||||
return;
|
||||
|
||||
_chatVocLow = _chatVocHigh = -1;
|
||||
objectChatInit(str, object, vocHigh, vocLow);
|
||||
_chatText = str;
|
||||
_chatObject = object;
|
||||
int chatType = chatGetType(str);
|
||||
|
||||
if (_mainCharacter.facing > 7)
|
||||
_mainCharacter.facing = 5;
|
||||
|
||||
static const uint8 talkScriptTable[] = {
|
||||
0x10, 0x11, 0x12, 0x13,
|
||||
0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x04, 0x05, 0x06, 0x07,
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
0x08, 0x09, 0x0A, 0x0B,
|
||||
0x08, 0x09, 0x0A, 0x0B
|
||||
};
|
||||
|
||||
static const char *const talkFilenameTable[] = {
|
||||
"MTFL00S.EMC", "MTFL00Q.EMC", "MTFL00E.EMC", "MTFL00T.EMC",
|
||||
"MTFR00S.EMC", "MTFR00Q.EMC", "MTFR00E.EMC", "MTFR00T.EMC",
|
||||
"MTL00S.EMC", "MTL00Q.EMC", "MTL00E.EMC", "MTL00T.EMC",
|
||||
"MTR00S.EMC", "MTR00Q.EMC", "MTR00E.EMC", "MTR00T.EMC",
|
||||
"MTA00S.EMC", "MTA00Q.EMC", "MTA00E.EMC", "MTA00T.EMC"
|
||||
};
|
||||
|
||||
int chat = talkScriptTable[chatType + _mainCharacter.facing * 4];
|
||||
objectChatProcess(talkFilenameTable[chat]);
|
||||
_text->restoreScreen();
|
||||
// _mainCharacter.facing can not be 0xFF here, so this is safe.
|
||||
_mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
|
||||
updateCharacterAnim(0);
|
||||
_chatText = "";
|
||||
_chatObject = -1;
|
||||
setNextIdleAnimTimer();
|
||||
}
|
||||
|
||||
void KyraEngine_MR::objectChatInit(const char *str, int object, int vocHigh, int vocLow) {
|
||||
str = _text->preprocessString(str);
|
||||
int lineNum = _text->buildMessageSubstrings(str);
|
||||
|
||||
int xPos = 0, yPos = 0;
|
||||
|
||||
if (!object) {
|
||||
int scale = getScale(_mainCharacter.x1, _mainCharacter.y1);
|
||||
yPos = _mainCharacter.y1 - ((_mainCharacter.height * scale) >> 8) - 8;
|
||||
xPos = _mainCharacter.x1;
|
||||
} else {
|
||||
yPos = _talkObjectList[object].y;
|
||||
xPos = _talkObjectList[object].x;
|
||||
}
|
||||
|
||||
_text->_talkMessageH = lineNum * _screen->getFontHeight() + (lineNum - 1) * _screen->_lineSpacing;
|
||||
yPos -= _text->_talkMessageH;
|
||||
yPos = MAX(yPos, 0);
|
||||
_text->_talkMessageY = yPos;
|
||||
|
||||
int width = _text->getWidestLineWidth(lineNum);
|
||||
_text->calcWidestLineBounds(xPos, yPos, width, xPos);
|
||||
_text->_talkCoords.x = xPos;
|
||||
_text->_talkCoords.w = width + 2;
|
||||
|
||||
restorePage3();
|
||||
|
||||
_chatTextEnabled = textEnabled();
|
||||
if (_chatTextEnabled) {
|
||||
objectChatPrintText(str, object);
|
||||
_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
|
||||
} else {
|
||||
_chatEndTime = _system->getMillis();
|
||||
}
|
||||
|
||||
if (speechEnabled()) {
|
||||
_chatVocHigh = vocHigh;
|
||||
_chatVocLow = vocLow;
|
||||
} else {
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_MR::objectChatPrintText(const Common::String &str0, int object) {
|
||||
int c1 = (_lang == 3 && _albumChatActive) ? 0xEE : _talkObjectList[object].color;
|
||||
int c2 = (_lang == 3) ? 0 : 0xF0;
|
||||
Common::String str = _text->preprocessString(str0.c_str());
|
||||
int lineNum = _text->buildMessageSubstrings(str.c_str());
|
||||
int maxWidth = _text->getWidestLineWidth(lineNum);
|
||||
int x = (object == 0) ? _mainCharacter.x1 : _talkObjectList[object].x;
|
||||
int cX1 = 0, cX2 = 0;
|
||||
_text->calcWidestLineBounds(cX1, cX2, maxWidth, x);
|
||||
|
||||
for (int i = 0; i < lineNum; ++i) {
|
||||
str = Common::String(&_text->_talkSubstrings[i * _text->maxSubstringLen()]);
|
||||
int y = _text->_talkMessageY + i * (_screen->getFontHeight() + _screen->_lineSpacing);
|
||||
x = _text->getCenterStringX(str, cX1, cX2);
|
||||
|
||||
_text->printText(str, x, y, c1, c2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_MR::objectChatProcess(const char *script) {
|
||||
memset(&_chatScriptData, 0, sizeof(_chatScriptData));
|
||||
memset(&_chatScriptState, 0, sizeof(_chatScriptState));
|
||||
|
||||
_emc->load(script, &_chatScriptData, &_opcodesAnimation);
|
||||
_emc->init(&_chatScriptState, &_chatScriptData);
|
||||
_emc->start(&_chatScriptState, 0);
|
||||
while (_emc->isValid(&_chatScriptState))
|
||||
_emc->run(&_chatScriptState);
|
||||
|
||||
if (_chatVocHigh >= 0) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
_useFrameTable = true;
|
||||
objectChatWaitToFinish();
|
||||
_useFrameTable = false;
|
||||
|
||||
_emc->unload(&_chatScriptData);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::objectChatWaitToFinish() {
|
||||
int charAnimFrame = _mainCharacter.animFrame;
|
||||
setCharacterAnimDim(_animShapeWidth, _animShapeHeight);
|
||||
|
||||
_emc->init(&_chatScriptState, &_chatScriptData);
|
||||
_emc->start(&_chatScriptState, 1);
|
||||
|
||||
bool running = true;
|
||||
const uint32 endTime = _chatEndTime;
|
||||
resetSkipFlag();
|
||||
|
||||
while (running && !shouldQuit()) {
|
||||
if (!_emc->isValid(&_chatScriptState))
|
||||
_emc->start(&_chatScriptState, 1);
|
||||
|
||||
_animNeedUpdate = false;
|
||||
while (!_animNeedUpdate && _emc->isValid(&_chatScriptState) && !shouldQuit())
|
||||
_emc->run(&_chatScriptState);
|
||||
|
||||
int curFrame = _animNewFrame;
|
||||
uint32 delayTime = _animDelayTime;
|
||||
|
||||
_mainCharacter.animFrame = curFrame;
|
||||
updateCharacterAnim(0);
|
||||
|
||||
uint32 nextFrame = _system->getMillis() + delayTime * _tickLength;
|
||||
|
||||
while (_system->getMillis() < nextFrame && !shouldQuit()) {
|
||||
updateWithText();
|
||||
|
||||
const uint32 curTime = _system->getMillis();
|
||||
if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
|
||||
snd_stopVoice();
|
||||
resetSkipFlag();
|
||||
nextFrame = curTime;
|
||||
running = false;
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
_mainCharacter.animFrame = charAnimFrame;
|
||||
updateCharacterAnim(0);
|
||||
resetCharacterAnimDim();
|
||||
}
|
||||
|
||||
void KyraEngine_MR::badConscienceChat(const char *str, int vocHigh, int vocLow) {
|
||||
if (!_badConscienceShown)
|
||||
return;
|
||||
|
||||
setNextIdleAnimTimer();
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
objectChatInit(str, 1, vocHigh, vocLow);
|
||||
_chatText = str;
|
||||
_chatObject = 1;
|
||||
badConscienceChatWaitToFinish();
|
||||
updateSceneAnim(0x0E, _badConscienceFrameTable[_badConscienceAnim+16]);
|
||||
_text->restoreScreen();
|
||||
update();
|
||||
_chatText = "";
|
||||
_chatObject = -1;
|
||||
}
|
||||
|
||||
void KyraEngine_MR::badConscienceChatWaitToFinish() {
|
||||
if (_chatVocHigh) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
bool running = true;
|
||||
const uint32 endTime = _chatEndTime;
|
||||
resetSkipFlag();
|
||||
|
||||
uint32 nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(4, 8) * _tickLength;
|
||||
|
||||
int frame = _badConscienceFrameTable[_badConscienceAnim+24];
|
||||
while (running && !shouldQuit()) {
|
||||
if (nextFrame < _system->getMillis()) {
|
||||
++frame;
|
||||
if (_badConscienceFrameTable[_badConscienceAnim+32] < frame)
|
||||
frame = _badConscienceFrameTable[_badConscienceAnim+24];
|
||||
|
||||
updateSceneAnim(0x0E, frame);
|
||||
updateWithText();
|
||||
|
||||
nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(4, 8) * _tickLength;
|
||||
}
|
||||
|
||||
updateWithText();
|
||||
|
||||
const uint32 curTime = _system->getMillis();
|
||||
if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
|
||||
snd_stopVoice();
|
||||
resetSkipFlag();
|
||||
nextFrame = curTime;
|
||||
running = false;
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_MR::goodConscienceChat(const char *str, int vocHigh, int vocLow) {
|
||||
if (!_goodConscienceShown)
|
||||
return;
|
||||
|
||||
setNextIdleAnimTimer();
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
objectChatInit(str, 87, vocHigh, vocLow);
|
||||
_chatText = str;
|
||||
_chatObject = 87;
|
||||
goodConscienceChatWaitToFinish();
|
||||
updateSceneAnim(0x0F, _goodConscienceFrameTable[_goodConscienceAnim+10]);
|
||||
_text->restoreScreen();
|
||||
update();
|
||||
_chatText = "";
|
||||
_chatObject = -1;
|
||||
}
|
||||
|
||||
void KyraEngine_MR::goodConscienceChatWaitToFinish() {
|
||||
if (_chatVocHigh) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
bool running = true;
|
||||
const uint32 endTime = _chatEndTime;
|
||||
resetSkipFlag();
|
||||
|
||||
uint32 nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(3, 6) * _tickLength;
|
||||
|
||||
int frame = _goodConscienceFrameTable[_goodConscienceAnim+15];
|
||||
while (running && !shouldQuit()) {
|
||||
if (nextFrame < _system->getMillis()) {
|
||||
++frame;
|
||||
if (_goodConscienceFrameTable[_goodConscienceAnim+20] < frame)
|
||||
frame = _goodConscienceFrameTable[_goodConscienceAnim+15];
|
||||
|
||||
updateSceneAnim(0x0F, frame);
|
||||
updateWithText();
|
||||
|
||||
nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(3, 6) * _tickLength;
|
||||
}
|
||||
|
||||
updateWithText();
|
||||
|
||||
const uint32 curTime = _system->getMillis();
|
||||
if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
|
||||
snd_stopVoice();
|
||||
resetSkipFlag();
|
||||
nextFrame = curTime;
|
||||
running = false;
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_MR::albumChat(const char *str, int vocHigh, int vocLow) {
|
||||
_talkObjectList[1].x = 190;
|
||||
_talkObjectList[1].y = _interfaceCommandLineY1;
|
||||
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
_albumChatActive = true;
|
||||
albumChatInit(str, 1, vocHigh, vocLow);
|
||||
_albumChatActive = false;
|
||||
|
||||
_chatText = str;
|
||||
_chatObject = 1;
|
||||
_screen->hideMouse();
|
||||
albumChatWaitToFinish();
|
||||
_screen->showMouse();
|
||||
|
||||
_chatText = "";
|
||||
_chatObject = -1;
|
||||
}
|
||||
|
||||
void KyraEngine_MR::albumChatInit(const char *str, int object, int vocHigh, int vocLow) {
|
||||
Common::String realString;
|
||||
|
||||
while (*str) {
|
||||
if (str[0] == '\\' && str[1] == 'r') {
|
||||
realString += '\r';
|
||||
++str;
|
||||
} else {
|
||||
realString += *str;
|
||||
}
|
||||
|
||||
++str;
|
||||
}
|
||||
|
||||
str = realString.c_str();
|
||||
|
||||
str = _text->preprocessString(str);
|
||||
int lineNum = _text->buildMessageSubstrings(str);
|
||||
|
||||
int xPos = 0, yPos = 0;
|
||||
|
||||
if (!object) {
|
||||
int scale = getScale(_mainCharacter.x1, _mainCharacter.y1);
|
||||
yPos = _mainCharacter.y1 - ((_mainCharacter.height * scale) >> 8) - 8;
|
||||
xPos = _mainCharacter.x1;
|
||||
} else {
|
||||
yPos = _talkObjectList[object].y;
|
||||
xPos = _talkObjectList[object].x;
|
||||
}
|
||||
|
||||
_text->_talkMessageH = lineNum * _screen->getFontHeight() + (lineNum - 1) * _screen->_lineSpacing;
|
||||
yPos -= _text->_talkMessageH;
|
||||
yPos = MAX(yPos, 0);
|
||||
_text->_talkMessageY = yPos;
|
||||
|
||||
int width = _text->getWidestLineWidth(lineNum);
|
||||
_text->calcWidestLineBounds(xPos, yPos, width, xPos);
|
||||
_text->_talkCoords.x = xPos;
|
||||
_text->_talkCoords.w = width + 2;
|
||||
|
||||
_screen->hideMouse();
|
||||
|
||||
if (textEnabled()) {
|
||||
objectChatPrintText(str, object);
|
||||
_chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength;
|
||||
} else {
|
||||
_chatEndTime = _system->getMillis();
|
||||
}
|
||||
|
||||
if (speechEnabled()) {
|
||||
_chatVocHigh = vocHigh;
|
||||
_chatVocLow = vocLow;
|
||||
} else {
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
_screen->showMouse();
|
||||
}
|
||||
|
||||
void KyraEngine_MR::albumChatWaitToFinish() {
|
||||
if (_chatVocHigh) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
bool running = true;
|
||||
const uint32 endTime = _chatEndTime;
|
||||
resetSkipFlag();
|
||||
|
||||
uint32 nextFrame = 0;
|
||||
int frame = 12;
|
||||
while (running && !shouldQuit()) {
|
||||
if (nextFrame < _system->getMillis()) {
|
||||
++frame;
|
||||
if (frame > 22)
|
||||
frame = 13;
|
||||
|
||||
albumRestoreRect();
|
||||
_album.wsa->displayFrame(frame, 2, -100, 90, 0x4000, nullptr, nullptr);
|
||||
albumUpdateRect();
|
||||
|
||||
nextFrame = _system->getMillis() + _rnd.getRandomNumberRng(4, 8) * _tickLength;
|
||||
}
|
||||
|
||||
if (_album.nextPage != 14)
|
||||
albumUpdateAnims();
|
||||
else
|
||||
_screen->updateScreen();
|
||||
|
||||
const uint32 curTime = _system->getMillis();
|
||||
if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
|
||||
snd_stopVoice();
|
||||
resetSkipFlag();
|
||||
nextFrame = curTime;
|
||||
running = false;
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_MR::malcolmSceneStartupChat() {
|
||||
if (_noStartupChat)
|
||||
return;
|
||||
|
||||
int index = _mainCharacter.sceneId - _chapterLowestScene[_currentChapter];
|
||||
if (_newSceneDlgState[index])
|
||||
return;
|
||||
|
||||
updateDlgBuffer();
|
||||
int vocHighBase = 0, vocHighIndex = 0, index1 = 0, index2 = 0;
|
||||
loadDlgHeader(vocHighBase, vocHighIndex, index1, index2);
|
||||
|
||||
_cnvFile->seek(index1*6, SEEK_CUR);
|
||||
_cnvFile->seek(index2*4, SEEK_CUR);
|
||||
_cnvFile->seek(index*2, SEEK_CUR);
|
||||
_cnvFile->seek(_cnvFile->readUint16LE(), SEEK_SET);
|
||||
|
||||
_isStartupDialog = true;
|
||||
processDialog(vocHighIndex, vocHighBase, 0);
|
||||
_isStartupDialog = false;
|
||||
_newSceneDlgState[index] = true;
|
||||
}
|
||||
|
||||
void KyraEngine_MR::updateDlgBuffer() {
|
||||
if (_cnvFile)
|
||||
_cnvFile->seek(0, SEEK_SET);
|
||||
|
||||
if (_curDlgIndex == _mainCharacter.dlgIndex && _curDlgChapter == _currentChapter && _curDlgLang == _lang)
|
||||
return;
|
||||
|
||||
Common::Path dlgFile(Common::String::format("CH%.02d-S%.02d.%s", _currentChapter, _mainCharacter.dlgIndex, _languageExtension[_lang]));
|
||||
Common::Path cnvFile(Common::String::format("CH%.02d-S%.02d.CNV", _currentChapter, _mainCharacter.dlgIndex));
|
||||
|
||||
delete _cnvFile;
|
||||
delete _dlgBuffer;
|
||||
|
||||
_res->exists(cnvFile, true);
|
||||
_res->exists(dlgFile, true);
|
||||
_cnvFile = _res->createReadStream(cnvFile);
|
||||
_dlgBuffer = _res->createReadStream(dlgFile);
|
||||
assert(_cnvFile);
|
||||
assert(_dlgBuffer);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::loadDlgHeader(int &vocHighBase, int &vocHighIndex, int &index1, int &index2) {
|
||||
assert(_cnvFile);
|
||||
vocHighIndex = _cnvFile->readSint16LE();
|
||||
vocHighBase = _cnvFile->readSint16LE();
|
||||
index1 = _cnvFile->readSint16LE();
|
||||
index2 = _cnvFile->readSint16LE();
|
||||
}
|
||||
|
||||
void KyraEngine_MR::setDlgIndex(int index) {
|
||||
if (_mainCharacter.dlgIndex != index) {
|
||||
memset(_newSceneDlgState, 0, sizeof(_newSceneDlgState));
|
||||
memset(_conversationState, -1, sizeof(_conversationState));
|
||||
_chatAltFlag = false;
|
||||
_mainCharacter.dlgIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
void KyraEngine_MR::updateDlgIndex() {
|
||||
uint16 dlgIndex = _mainCharacter.dlgIndex;
|
||||
|
||||
if (_currentChapter == 1) {
|
||||
static const uint8 dlgIndexMoodNice[] = { 0x0C, 0x0E, 0x10, 0x0F, 0x11 };
|
||||
static const uint8 dlgIndexMoodNormal[] = { 0x00, 0x02, 0x04, 0x03, 0x05 };
|
||||
static const uint8 dlgIndexMoodEvil[] = { 0x06, 0x08, 0x0A, 0x09, 0x0B };
|
||||
|
||||
if (_malcolmsMood == 0)
|
||||
dlgIndex = dlgIndexMoodNice[_characterShapeFile];
|
||||
else if (_malcolmsMood == 1)
|
||||
dlgIndex = dlgIndexMoodNormal[_characterShapeFile];
|
||||
else if (_malcolmsMood == 2)
|
||||
dlgIndex = dlgIndexMoodEvil[_characterShapeFile];
|
||||
} else if (_currentChapter == 2) {
|
||||
if (dlgIndex >= 8)
|
||||
dlgIndex -= 4;
|
||||
if (dlgIndex >= 4)
|
||||
dlgIndex -= 4;
|
||||
|
||||
if (_malcolmsMood == 0)
|
||||
dlgIndex += 8;
|
||||
else if (_malcolmsMood == 2)
|
||||
dlgIndex += 4;
|
||||
} else if (_currentChapter == 4) {
|
||||
if (dlgIndex >= 10)
|
||||
dlgIndex -= 5;
|
||||
if (dlgIndex >= 5)
|
||||
dlgIndex -= 5;
|
||||
|
||||
if (_malcolmsMood == 0)
|
||||
dlgIndex += 10;
|
||||
else if (_malcolmsMood == 2)
|
||||
dlgIndex += 5;
|
||||
}
|
||||
|
||||
_mainCharacter.dlgIndex = dlgIndex;
|
||||
}
|
||||
|
||||
void KyraEngine_MR::processDialog(int vocHighIndex, int vocHighBase, int funcNum) {
|
||||
bool running = true;
|
||||
int script = -1;
|
||||
int vocHigh = -1, vocLow = -1;
|
||||
|
||||
while (running) {
|
||||
uint16 cmd = _cnvFile->readUint16LE();
|
||||
int object = cmd - 12;
|
||||
|
||||
if (cmd == 10) {
|
||||
break;
|
||||
} else if (cmd == 4) {
|
||||
vocHighBase = _cnvFile->readUint16LE();
|
||||
setDlgIndex(vocHighBase);
|
||||
} else if (cmd == 11) {
|
||||
int strSize = _cnvFile->readUint16LE();
|
||||
_cnvFile->readUint16LE();
|
||||
_cnvFile->read(_stringBuffer, strSize);
|
||||
_stringBuffer[strSize] = 0;
|
||||
} else {
|
||||
vocHigh = _vocHighTable[vocHighIndex-1] + vocHighBase;
|
||||
vocLow = _cnvFile->readUint16LE();
|
||||
getTableEntry(_dlgBuffer, vocLow, _stringBuffer);
|
||||
|
||||
if (_isStartupDialog) {
|
||||
delay(60*_tickLength, true);
|
||||
_isStartupDialog = false;
|
||||
}
|
||||
|
||||
if (*_stringBuffer == 0)
|
||||
continue;
|
||||
|
||||
if (cmd != 12) {
|
||||
if (object != script) {
|
||||
if (script >= 0)
|
||||
dialogEndScript(script);
|
||||
|
||||
dialogStartScript(object, funcNum);
|
||||
script = object;
|
||||
}
|
||||
|
||||
npcChatSequence(_stringBuffer, object, vocHigh, vocLow);
|
||||
} else {
|
||||
if (script >= 0) {
|
||||
dialogEndScript(script);
|
||||
script = -1;
|
||||
}
|
||||
|
||||
objectChat(_stringBuffer, 0, vocHigh, vocLow);
|
||||
playStudioSFX(_stringBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (script != -1)
|
||||
dialogEndScript(script);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::dialogStartScript(int object, int funcNum) {
|
||||
_dialogSceneAnim = _talkObjectList[object].sceneAnim;
|
||||
_dialogSceneScript = _talkObjectList[object].sceneScript;
|
||||
if (_dialogSceneAnim >= 0 && _dialogSceneScript >= 0) {
|
||||
_specialSceneScriptStateBackup[_dialogSceneScript] = _specialSceneScriptState[_dialogSceneScript];
|
||||
_specialSceneScriptState[_dialogSceneScript] = true;
|
||||
}
|
||||
|
||||
_emc->init(&_dialogScriptState, &_dialogScriptData);
|
||||
_emc->load(_talkObjectList[object].filename, &_dialogScriptData, &_opcodesDialog);
|
||||
|
||||
_dialogScriptFuncStart = funcNum * 3 + 0;
|
||||
_dialogScriptFuncProc = funcNum * 3 + 1;
|
||||
_dialogScriptFuncEnd = funcNum * 3 + 2;
|
||||
|
||||
_emc->start(&_dialogScriptState, _dialogScriptFuncStart);
|
||||
while (_emc->isValid(&_dialogScriptState))
|
||||
_emc->run(&_dialogScriptState);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::dialogEndScript(int object) {
|
||||
_emc->init(&_dialogScriptState, &_dialogScriptData);
|
||||
_emc->start(&_dialogScriptState, _dialogScriptFuncEnd);
|
||||
|
||||
while (_emc->isValid(&_dialogScriptState))
|
||||
_emc->run(&_dialogScriptState);
|
||||
|
||||
if (_dialogSceneAnim >= 0 && _dialogSceneScript >= 0) {
|
||||
_specialSceneScriptState[_dialogSceneScript] = _specialSceneScriptStateBackup[_dialogSceneScript];
|
||||
_dialogSceneScript = _dialogSceneAnim = -1;
|
||||
}
|
||||
|
||||
_emc->unload(&_dialogScriptData);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::npcChatSequence(const char *str, int object, int vocHigh, int vocLow) {
|
||||
_chatText = str;
|
||||
_chatObject = object;
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
|
||||
objectChatInit(str, object, vocHigh, vocLow);
|
||||
|
||||
if (_chatVocHigh >= 0 && _chatVocLow >= 0) {
|
||||
playVoice(_chatVocHigh, _chatVocLow);
|
||||
_chatVocHigh = _chatVocLow = -1;
|
||||
}
|
||||
|
||||
_emc->init(&_dialogScriptState, &_dialogScriptData);
|
||||
_emc->start(&_dialogScriptState, _dialogScriptFuncProc);
|
||||
|
||||
resetSkipFlag();
|
||||
|
||||
uint32 endTime = _chatEndTime;
|
||||
bool running = true;
|
||||
while (running && !shouldQuit()) {
|
||||
if (!_emc->run(&_dialogScriptState)) {
|
||||
_emc->init(&_dialogScriptState, &_dialogScriptData);
|
||||
_emc->start(&_dialogScriptState, _dialogScriptFuncProc);
|
||||
}
|
||||
|
||||
const uint32 curTime = _system->getMillis();
|
||||
if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) {
|
||||
snd_stopVoice();
|
||||
resetSkipFlag();
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
_text->restoreScreen();
|
||||
_chatText = "";
|
||||
_chatObject= - 1;
|
||||
}
|
||||
|
||||
void KyraEngine_MR::randomSceneChat() {
|
||||
updateDlgBuffer();
|
||||
|
||||
int index = (_mainCharacter.sceneId - _chapterLowestScene[_currentChapter]) * 2;
|
||||
|
||||
int vocHighBase = 0, vocHighIndex = 0, index1 = 0, index2 = 0;
|
||||
loadDlgHeader(vocHighBase, vocHighIndex, index1, index2);
|
||||
|
||||
if (_chatAltFlag)
|
||||
index++;
|
||||
_chatAltFlag = !_chatAltFlag;
|
||||
|
||||
_cnvFile->seek(index1*6, SEEK_CUR);
|
||||
_cnvFile->seek(index*2, SEEK_CUR);
|
||||
_cnvFile->seek(_cnvFile->readUint16LE(), SEEK_SET);
|
||||
|
||||
processDialog(vocHighIndex, vocHighBase, 0);
|
||||
}
|
||||
|
||||
void KyraEngine_MR::doDialog(int dlgIndex, int funcNum) {
|
||||
switch (_currentChapter-2) {
|
||||
case 0:
|
||||
dlgIndex -= 34;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
dlgIndex -= 54;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
dlgIndex -= 55;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
dlgIndex -= 70;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
updateDlgBuffer();
|
||||
|
||||
int vocHighBase = 0, vocHighIndex = 0, index1 = 0, index2 = 0;
|
||||
loadDlgHeader(vocHighBase, vocHighIndex, index1, index2);
|
||||
|
||||
int convState = _conversationState[dlgIndex][vocHighBase];
|
||||
uint32 offset = ((vocHighIndex == 1) ? dlgIndex - 1 : dlgIndex) * 6;
|
||||
if (convState == -1) {
|
||||
_cnvFile->seek(offset, SEEK_CUR);
|
||||
_conversationState[dlgIndex][vocHighBase] = 0;
|
||||
} else if (convState == 0 || convState == 2) {
|
||||
_cnvFile->seek(offset+2, SEEK_CUR);
|
||||
_conversationState[dlgIndex][vocHighBase] = 1;
|
||||
} else {
|
||||
_cnvFile->seek(offset+4, SEEK_CUR);
|
||||
_conversationState[dlgIndex][vocHighBase] = 2;
|
||||
}
|
||||
|
||||
_cnvFile->seek(_cnvFile->readUint16LE(), SEEK_SET);
|
||||
|
||||
processDialog(vocHighIndex, vocHighBase, funcNum);
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
51
engines/kyra/text/text_mr.h
Normal file
51
engines/kyra/text/text_mr.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KYRA_TEXT_MR_H
|
||||
#define KYRA_TEXT_MR_H
|
||||
|
||||
#include "kyra/text/text.h"
|
||||
|
||||
#include "kyra/engine/kyra_mr.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class TextDisplayer_MR : public TextDisplayer {
|
||||
friend class KyraEngine_MR;
|
||||
public:
|
||||
TextDisplayer_MR(KyraEngine_MR *vm, Screen_MR *screen);
|
||||
|
||||
char *preprocessString(const char *str) override;
|
||||
int dropCRIntoString(char *str, int minOffs, int maxOffs);
|
||||
|
||||
void printText(const Common::String &str, int x, int y, uint8 c0, uint8 c1, uint8 c2) override;
|
||||
|
||||
void restoreScreen();
|
||||
|
||||
void calcWidestLineBounds(int &x1, int &x2, int w, int x) override;
|
||||
protected:
|
||||
KyraEngine_MR *_vm;
|
||||
Screen_MR *_screen;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
877
engines/kyra/text/text_rpg.cpp
Normal file
877
engines/kyra/text/text_rpg.cpp
Normal file
@@ -0,0 +1,877 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
|
||||
|
||||
#include "kyra/engine/kyra_rpg.h"
|
||||
#include "kyra/engine/timer.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
enum {
|
||||
kEoBTextBufferSize = 2560
|
||||
};
|
||||
|
||||
TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(engine), _screen(scr),
|
||||
_lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true), _dimCount(scr ? scr->screenDimTableCount() : 0),
|
||||
_numCharsLeft(0), _numCharsPrinted(0), _twoByteLineBreakFlag(false), _waitButtonMode(1),
|
||||
_pc98TextMode(engine->gameFlags().use16ColorMode && engine->game() == GI_LOL),
|
||||
_waitButtonFont(Screen::FID_6_FNT), _isChinese(_vm->gameFlags().lang == Common::Language::ZH_TWN || _vm->gameFlags().lang == Common::Language::ZH_CHN) {
|
||||
|
||||
_dialogueBuffer = new char[kEoBTextBufferSize]();
|
||||
_currentLine = new char[85]();
|
||||
|
||||
if (_pc98TextMode)
|
||||
_waitButtonFont = Screen::FID_SJIS_TEXTMODE_FNT;
|
||||
else if ((_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns))
|
||||
_waitButtonFont = Screen::FID_8_FNT;
|
||||
else if (_vm->gameFlags().platform == Common::kPlatformPC98)
|
||||
_waitButtonFont = Screen::FID_SJIS_FNT;
|
||||
else if ((_vm->game() == GI_LOL && _vm->gameFlags().lang == Common::Language::ZH_TWN))
|
||||
_waitButtonFont = Screen::FID_CHINESE_FNT;
|
||||
|
||||
_textDimData = new TextDimData[_dimCount];
|
||||
memset(_textDimData, 0, sizeof(TextDimData) * _dimCount);
|
||||
|
||||
applySetting(-1, kNoHalfWidthLineEnd, ((_vm->gameFlags().lang == Common::JA_JPN && _vm->game() == GI_EOB1) || (_vm->gameFlags().lang == Common::Language::ZH_TWN && _vm->game() == GI_EOB2)) ? 1 : 0);
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
_colorMap[i] = i;
|
||||
|
||||
for (int i = 0; i < _dimCount; ++i) {
|
||||
const ScreenDim *d = _screen->getScreenDim(i);
|
||||
_textDimData[i].color1= d->col1;
|
||||
_textDimData[i].color2 = d->col2;
|
||||
_textDimData[i].line = d->line;
|
||||
_textDimData[i].column = d->column;
|
||||
}
|
||||
|
||||
_table1 = new char[128]();
|
||||
_table2 = new char[16]();
|
||||
|
||||
_tempString1 = _tempString2 = 0;
|
||||
_ctrl[0] = _ctrl[1] = _ctrl[2] = '\0';
|
||||
_waitButtonSpace = 0;
|
||||
}
|
||||
|
||||
TextDisplayer_rpg::~TextDisplayer_rpg() {
|
||||
setColorMapping(-1, 0 ,0);
|
||||
delete[] _dialogueBuffer;
|
||||
delete[] _currentLine;
|
||||
delete[] _textDimData;
|
||||
delete[] _table1;
|
||||
delete[] _table2;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::setupField(int dim, bool mode) {
|
||||
setPageBreakFlag();
|
||||
|
||||
_textDimData[dim].color2 = _vm->guiSettings()->colors.fill;
|
||||
_screen->setScreenDim(dim);
|
||||
|
||||
if (mode) {
|
||||
_screen->set16bitShadingLevel(4);
|
||||
clearCurDim();
|
||||
_screen->set16bitShadingLevel(0);
|
||||
} else {
|
||||
resetDimTextPositions(dim);
|
||||
}
|
||||
|
||||
_vm->_dialogueFieldAmiga = false;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::resetDimTextPositions(int dim) {
|
||||
_textDimData[dim].column = 0;
|
||||
_textDimData[dim].line = 0;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::resetPageBreakString() {
|
||||
if (_vm->_moreStrings)
|
||||
_pageBreakString = _vm->_moreStrings[0];
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::setPageBreakFlag() {
|
||||
_allowPageBreak = true;
|
||||
_lineCount = 0;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::removePageBreakFlag() {
|
||||
_allowPageBreak = false;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::setColorMapping(int sd, uint8 from, uint8 to) {
|
||||
if (sd < -1 || sd >= _dimCount)
|
||||
error("TextDisplayer_rpg::mapColor(): arg out of range");
|
||||
|
||||
if (sd == -1) {
|
||||
for (int i = 0; i < _dimCount; ++i) {
|
||||
delete[] _textDimData[i].colorMap;
|
||||
_textDimData[i].colorMap = nullptr;
|
||||
}
|
||||
_colorMap[from] = to;
|
||||
} else {
|
||||
if (_textDimData[sd].colorMap == nullptr) {
|
||||
_textDimData[sd].colorMap = new uint8[256];
|
||||
for (int i = 0; i < 256; ++i)
|
||||
_textDimData[sd].colorMap[i] = i;
|
||||
}
|
||||
_textDimData[sd].colorMap[from] = to;
|
||||
}
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::displayText(char *str, ...) {
|
||||
convertString(str);
|
||||
_printFlag = false;
|
||||
|
||||
_lineWidth = 0;
|
||||
_numCharsLeft = 0;
|
||||
_numCharsPrinted = 0;
|
||||
|
||||
_tempString1 = str;
|
||||
_tempString2 = 0;
|
||||
|
||||
_currentLine[0] = 0;
|
||||
|
||||
memset(_ctrl, 0, 3);
|
||||
|
||||
char c = parseCommand();
|
||||
|
||||
va_list args;
|
||||
va_start(args, str);
|
||||
|
||||
const ScreenDim *sd = _screen->_curDim;
|
||||
int sdx = _screen->curDimIndex();
|
||||
|
||||
bool sjisTextMode = (_pc98TextMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false;
|
||||
Screen::FontId of = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns) ? _screen->setFont(Screen::FID_8_FNT) : _screen->_currentFont;
|
||||
|
||||
uint16 charsPerLine = (sd->w << 3) / (_screen->getFontWidth() + _screen->_charSpacing);
|
||||
|
||||
while (c) {
|
||||
char a = tolower((unsigned char)_ctrl[1]);
|
||||
|
||||
if (!_tempString2 && c == '%') {
|
||||
if (a == 'd') {
|
||||
_scriptParaString = Common::String::format("%d", va_arg(args, int));
|
||||
_tempString2 = _scriptParaString.c_str();
|
||||
} else if (a == 's') {
|
||||
_tempString2 = va_arg(args, const char*);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
_ctrl[0] = _ctrl[2];
|
||||
_ctrl[2] = _ctrl[1] = 0;
|
||||
c = parseCommand();
|
||||
}
|
||||
|
||||
if (isTwoByteChar(c)) {
|
||||
char next = parseCommand();
|
||||
int cw = _screen->getCharWidth((uint8)c | (uint8)next << 8) + _textDimData[sdx].charSpacing;
|
||||
if (_textDimData[sdx].column + _lineWidth + cw > (sd->w << 3))
|
||||
printLine(_currentLine);
|
||||
|
||||
_currentLine[_numCharsLeft++] = c;
|
||||
_currentLine[_numCharsLeft++] = next;
|
||||
_currentLine[_numCharsLeft] = '\0';
|
||||
|
||||
_lineWidth += cw;
|
||||
if (_textDimData[sdx].noHalfWidthLineEnd && (_textDimData[sdx].column + _lineWidth + cw >= (sd->w << 3)))
|
||||
printLine(_currentLine);
|
||||
|
||||
c = parseCommand();
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charSpacing);
|
||||
|
||||
switch (c - 1) {
|
||||
case 0:
|
||||
printLine(_currentLine);
|
||||
_screen->updateScreen();
|
||||
textPageBreak();
|
||||
_numCharsPrinted = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
printLine(_currentLine);
|
||||
_textDimData[sdx].color2 = parseCommand();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
printLine(_currentLine);
|
||||
_textDimData[sdx].color1= parseCommand();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
printLine(_currentLine);
|
||||
dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charSpacing);
|
||||
dv = ((dv + 8) & 0xFFF8) - 1;
|
||||
if (dv >= charsPerLine)
|
||||
dv = 0;
|
||||
_textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charSpacing) * dv;
|
||||
break;
|
||||
|
||||
case 12:
|
||||
if (sjisTextMode)
|
||||
_twoByteLineBreakFlag = true;
|
||||
printLine(_currentLine);
|
||||
_twoByteLineBreakFlag = false;
|
||||
//_lineWidth = 0;
|
||||
_lineCount++;
|
||||
_textDimData[sdx].column = 0;
|
||||
_textDimData[sdx].line++;
|
||||
break;
|
||||
|
||||
case 11: case 18: case 23:
|
||||
case 24: case 26: case 28:
|
||||
// These are at the time of writing this comment not known to be
|
||||
// used. In case there is some use of them in some odd version
|
||||
// we display this warning here.
|
||||
warning("TextDisplayer_rpg::displayText: Triggered stub function %d", c - 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_vm->game() == GI_EOB1 || _vm->game() == GI_LOL || (unsigned char)c > 30) {
|
||||
int cw = _screen->getCharWidth((uint8)c) + _textDimData[sdx].charSpacing;
|
||||
_lineWidth += cw;
|
||||
|
||||
_currentLine[_numCharsLeft++] = c;
|
||||
_currentLine[_numCharsLeft] = 0;
|
||||
|
||||
if ((_textDimData[sdx].column + _lineWidth) > (sd->w << 3))
|
||||
printLine(_currentLine);
|
||||
}
|
||||
}
|
||||
|
||||
c = parseCommand();
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
if (_numCharsLeft)
|
||||
printLine(_currentLine);
|
||||
|
||||
_screen->setFont(of);
|
||||
_screen->updateScreen();
|
||||
}
|
||||
|
||||
char TextDisplayer_rpg::parseCommand() {
|
||||
if (!_ctrl[1])
|
||||
readNextPara();
|
||||
|
||||
char res = _ctrl[1];
|
||||
_ctrl[1] = _ctrl[2];
|
||||
_ctrl[2] = 0;
|
||||
|
||||
if (!_ctrl[1])
|
||||
readNextPara();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::readNextPara() {
|
||||
char c = 0;
|
||||
char d = 0;
|
||||
|
||||
if (_tempString2) {
|
||||
if (*_tempString2) {
|
||||
d = *_tempString2++;
|
||||
} else {
|
||||
_tempString2 = 0;
|
||||
d = _ctrl[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!d && _tempString1) {
|
||||
if (*_tempString1)
|
||||
d = *_tempString1++;
|
||||
else
|
||||
_tempString1 = 0;
|
||||
}
|
||||
|
||||
// This seems to be some sort of character conversion mechanism. The original doesn't make any use of it, however.
|
||||
// All necessary conversions take place somewhere else. This code actually causes issues if the character conversions
|
||||
// don't take place before calling displayText(). So we disable it for now. If some (not yet supported) localized
|
||||
// versions depend on this code we'll have to look at this again.
|
||||
#if 0
|
||||
if ((_vm->game() != GI_LOL) && (d & 0x80)) {
|
||||
d &= 0x7F;
|
||||
c = d & 7;
|
||||
d = (d & 0x78) >> 3;
|
||||
uint8 l = d;
|
||||
c = _table1[(l << 3) + c];
|
||||
d = _table2[l];
|
||||
}
|
||||
#endif
|
||||
|
||||
_ctrl[1] = d;
|
||||
_ctrl[2] = c;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::printLine(char *str) {
|
||||
const ScreenDim *sd = _screen->_curDim;
|
||||
int sdx = _screen->curDimIndex();
|
||||
bool sjisTextMode = _pc98TextMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15) ? true : false;
|
||||
|
||||
int fh = _screen->getFontHeight() + _screen->_lineSpacing + _textDimData[sdx].lineSpacing;
|
||||
int lines = (sd->h - _screen->_lineSpacing) / fh;
|
||||
|
||||
// Another hack for Chinese EOB II...The original prints text at the very bottom of the text field,
|
||||
// even if there is a good risk of printing text over the dialogue buttons.
|
||||
if (_isChinese && _allowPageBreak)
|
||||
++lines;
|
||||
|
||||
while (_textDimData[sdx].line >= lines) {
|
||||
if ((lines - _waitButtonSpace) <= _lineCount && _allowPageBreak) {
|
||||
_lineCount = 0;
|
||||
_screen->updateScreen();
|
||||
textPageBreak();
|
||||
_numCharsPrinted = 0;
|
||||
}
|
||||
|
||||
int h1 = ((sd->h / fh) - 1) * fh;
|
||||
int h2 = sd->h - fh;
|
||||
int wOffs = (_textDimData[sdx].shadowColor && sd->sx > 0) ? 1 : 0;
|
||||
|
||||
if (h2)
|
||||
_screen->copyRegion((sd->sx << 3) - wOffs, sd->sy + fh, (sd->sx << 3) - wOffs, sd->sy, (sd->w << 3) + wOffs, h2, _screen->_curPage, _screen->_curPage, Screen::CR_NO_P_CHECK);
|
||||
|
||||
// HACK: In Chinese EOBII some characters overdraw the valid boundaries by one pixel
|
||||
// (at least the ',' does). So, the original redraws the border here. We do the same
|
||||
// since for now I don't have any good idea how to do this less ugly...
|
||||
if (_isChinese && _vm->_flags.gameID == GI_EOB2 && sdx == 7)
|
||||
_screen->drawBox(3, 170, 290, 199, _vm->guiSettings()->colors.fill);
|
||||
|
||||
_screen->set16bitShadingLevel(4);
|
||||
_screen->fillRect((sd->sx << 3) - wOffs, sd->sy + h1, ((sd->sx + sd->w) << 3) - 1, sd->sy + sd->h - 1, remapColor(sdx, _textDimData[sdx].color2));
|
||||
_screen->set16bitShadingLevel(0);
|
||||
|
||||
if (_textDimData[sdx].line)
|
||||
_textDimData[sdx].line--;
|
||||
}
|
||||
|
||||
int x1 = (sd->sx << 3) + _textDimData[sdx].column;
|
||||
int y = sd->sy + (fh + _textDimData[sdx].visualLineSpacingAdjust) * _textDimData[sdx].line;
|
||||
int w = sd->w << 3;
|
||||
int lw = _lineWidth;
|
||||
int s = _numCharsLeft;
|
||||
uint8 twoByteCharOffs = 0;
|
||||
|
||||
if (sjisTextMode) {
|
||||
bool ct = true;
|
||||
|
||||
if ((lw + _textDimData[sdx].column) >= w) {
|
||||
if ((lines - 1 - (_waitButtonSpace << 1)) <= _lineCount)
|
||||
// cut off line to leave space for "MORE" button
|
||||
w -= _vm->guiSettings()->buttons.waitReserve;
|
||||
} else {
|
||||
if (!_twoByteLineBreakFlag || (_lineCount + 1 < lines - 1))
|
||||
ct = false;
|
||||
else
|
||||
// cut off line to leave space for "MORE" button
|
||||
w -= _vm->guiSettings()->buttons.waitReserve;
|
||||
}
|
||||
|
||||
if (ct) {
|
||||
w -= _textDimData[sdx].column;
|
||||
|
||||
int n2 = 0;
|
||||
int n1 = (w / 4) - 1;
|
||||
|
||||
while (n2 < n1 && n2 < s) {
|
||||
if (isTwoByteChar(str[n2]))
|
||||
n2++;
|
||||
n2++;
|
||||
}
|
||||
s = n2;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < s; ++i) {
|
||||
if (isTwoByteChar(str[i]))
|
||||
twoByteCharOffs = (_vm->game() == GI_EOB1 || _isChinese) ? 16 : 8;
|
||||
}
|
||||
|
||||
if (_isChinese) {
|
||||
s = strlen(str);
|
||||
if ((lw + _textDimData[sdx].column) >= w) {
|
||||
s -= ((lw + _textDimData[sdx].column - w) >> 3);
|
||||
w -= _textDimData[sdx].column;
|
||||
}
|
||||
|
||||
} else if ((lw + _textDimData[sdx].column) >= w) {
|
||||
if ((lines - 1) <= _lineCount && _allowPageBreak)
|
||||
// cut off line to leave space for "MORE" button
|
||||
w -= _vm->guiSettings()->buttons.waitReserve;
|
||||
|
||||
w -= _textDimData[sdx].column;
|
||||
|
||||
int lineLastCharPos = 0;
|
||||
int strPos = s - 1;
|
||||
|
||||
if (twoByteCharOffs) {
|
||||
lw = 0;
|
||||
int prevStrPos = 0;
|
||||
|
||||
for (strPos = 0; strPos < s; ++strPos) {
|
||||
uint8 c = str[strPos];
|
||||
if (isTwoByteChar(c))
|
||||
lw += (_screen->getCharWidth(c | (uint8)str[++strPos] << 8) + _textDimData[sdx].charSpacing);
|
||||
else
|
||||
lw += _screen->getCharWidth(c);
|
||||
|
||||
if (!lineLastCharPos && w < lw + twoByteCharOffs)
|
||||
lineLastCharPos = prevStrPos;
|
||||
|
||||
if (lineLastCharPos && c == ' ') {
|
||||
s = strPos;
|
||||
_printFlag = false;
|
||||
break;
|
||||
}
|
||||
prevStrPos = strPos;
|
||||
}
|
||||
|
||||
if (!lineLastCharPos) {
|
||||
lineLastCharPos = s - 1;
|
||||
if (lineLastCharPos && str[lineLastCharPos] == ' ') {
|
||||
s = strPos;
|
||||
_printFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
lw = _lineWidth;
|
||||
|
||||
} else {
|
||||
while (strPos > 0) {
|
||||
//cut off line after last space
|
||||
uint8 c = str[strPos];
|
||||
lw -= _screen->getCharWidth(c);
|
||||
|
||||
if (!lineLastCharPos && lw <= w)
|
||||
lineLastCharPos = strPos;
|
||||
|
||||
if (lineLastCharPos && c == ' ') {
|
||||
s = strPos;
|
||||
_printFlag = false;
|
||||
break;
|
||||
}
|
||||
strPos--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strPos) {
|
||||
if (_textDimData[sdx].column && !_printFlag) {
|
||||
s = lw = 0;
|
||||
_printFlag = true;
|
||||
} else {
|
||||
s = lineLastCharPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char lastChr = str[s];
|
||||
str[s] = 0;
|
||||
|
||||
uint8 col1 = remapColor(sdx, _textDimData[sdx].color1);
|
||||
uint8 col2 = remapColor(sdx, _textDimData[sdx].color2);
|
||||
if (sjisTextMode && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) {
|
||||
x1 &= ~3;
|
||||
y = (y + 8) & ~7;
|
||||
col2 = 0;
|
||||
}
|
||||
|
||||
if (_textDimData[sdx].shadowColor) {
|
||||
_screen->printText(str, x1 - 1, y, _textDimData[sdx].shadowColor, col2);
|
||||
_screen->printText(str, x1, y + 1, _textDimData[sdx].shadowColor, 0);
|
||||
_screen->printText(str, x1 - 1, y + 1, _textDimData[sdx].shadowColor, 0);
|
||||
// Another hack for Chinese EOBII. Due to the reduced line spacing - while still drawing a shadow for the font - the
|
||||
// lines will overdraw by one pixel if we don't clear the bottom line. This will otherwise cause glitches when doing line feeds.
|
||||
for (int i = 0; i < -_textDimData[sdx].lineSpacing && y + fh + i < sd->sy + sd->h; ++i)
|
||||
_screen->drawClippedLine(x1 - 1, y + fh + i, x1 + lw, y + fh + i, col2);
|
||||
col2 = 0;
|
||||
}
|
||||
_screen->printText(str, x1, y, col1, col2);
|
||||
|
||||
_textDimData[sdx].column += lw;
|
||||
_numCharsPrinted += strlen(str);
|
||||
|
||||
str[s] = lastChr;
|
||||
if (lastChr == ' ')
|
||||
s++;
|
||||
if (str[s] == ' ')
|
||||
s++;
|
||||
|
||||
uint32 len = strlen(&str[s]);
|
||||
for (uint32 i = 0; i < len; i++)
|
||||
str[i] = str[s + i];
|
||||
str[len] = 0;
|
||||
|
||||
_numCharsLeft = strlen(str);
|
||||
_lineWidth = _screen->getTextWidth(str) + _textDimData[sdx].charSpacing * _numCharsLeft;
|
||||
|
||||
if (!_numCharsLeft && (_textDimData[sdx].column + twoByteCharOffs) <= (sd->w << 3))
|
||||
return;
|
||||
|
||||
_textDimData[sdx].column = 0;
|
||||
_textDimData[sdx].line++;
|
||||
_lineCount++;
|
||||
|
||||
if (_numCharsLeft || !_isChinese)
|
||||
printLine(str);
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::printDialogueText(int stringId, const char *pageBreakString, const char*) {
|
||||
const char *str = (const char *)(_screen->getCPagePtr(5) + READ_LE_UINT16(&_screen->getCPagePtr(5)[(stringId - 1) << 1]));
|
||||
assert(strlen(str) < kEoBTextBufferSize);
|
||||
Common::strlcpy(_dialogueBuffer, str, kEoBTextBufferSize);
|
||||
|
||||
_screen->set16bitShadingLevel(4);
|
||||
int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleFat) : -1;
|
||||
displayText(_dialogueBuffer);
|
||||
if (cs != -1)
|
||||
_screen->setFontStyles(_screen->_currentFont, cs);
|
||||
_screen->set16bitShadingLevel(0);
|
||||
|
||||
if (pageBreakString) {
|
||||
if (pageBreakString[0]) {
|
||||
_pageBreakString = pageBreakString;
|
||||
displayWaitButton();
|
||||
resetPageBreakString();
|
||||
}
|
||||
}
|
||||
|
||||
_vm->_dialogueFieldAmiga = true;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::printDialogueText(const char *str, bool wait) {
|
||||
assert(Common::strnlen(str, kEoBTextBufferSize) < kEoBTextBufferSize);
|
||||
Common::strlcpy(_dialogueBuffer, str, kEoBTextBufferSize);
|
||||
|
||||
int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleFat) : -1;
|
||||
displayText(_dialogueBuffer);
|
||||
if (cs != -1)
|
||||
_screen->setFontStyles(_screen->_currentFont, cs);
|
||||
if (wait)
|
||||
displayWaitButton();
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::printMessage(const char *str, int textColor, ...) {
|
||||
int tc = _textDimData[_screen->curDimIndex()].color1;
|
||||
|
||||
if (textColor != -1)
|
||||
_textDimData[_screen->curDimIndex()].color1= textColor;
|
||||
|
||||
va_list args;
|
||||
va_start(args, textColor);
|
||||
vsnprintf(_dialogueBuffer, kEoBTextBufferSize - 1, str, args);
|
||||
va_end(args);
|
||||
|
||||
displayText(_dialogueBuffer, textColor);
|
||||
|
||||
if (_vm->game() != GI_EOB1)
|
||||
_textDimData[_screen->curDimIndex()].color1= tc;
|
||||
|
||||
if (!_screen->_curPage)
|
||||
_screen->updateScreen();
|
||||
}
|
||||
|
||||
int TextDisplayer_rpg::clearDim(int dim) {
|
||||
int res = _screen->curDimIndex();
|
||||
_screen->setScreenDim(dim);
|
||||
_textDimData[dim].color1 = _screen->_curDim->col1;
|
||||
_textDimData[dim].color2 = (_vm->game() == GI_LOL || _vm->gameFlags().platform == Common::kPlatformAmiga) ? _screen->_curDim->col2 : _vm->guiSettings()->colors.fill;
|
||||
|
||||
clearCurDim();
|
||||
return res;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::clearCurDim() {
|
||||
int d = _screen->curDimIndex();
|
||||
const ScreenDim *tmp = _screen->getScreenDim(d);
|
||||
|
||||
int xOffs = 0;
|
||||
int wOffs = 0;
|
||||
int hOffs = 0;
|
||||
|
||||
if (_textDimData[d].shadowColor) {
|
||||
if (tmp->sx > 0)
|
||||
xOffs = wOffs = 1;
|
||||
}
|
||||
|
||||
if (_pc98TextMode) {
|
||||
--wOffs;
|
||||
--hOffs;
|
||||
}
|
||||
|
||||
_screen->fillRect((tmp->sx << 3) - xOffs, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1 + wOffs, (tmp->sy + tmp->h) - 1 + hOffs, _textDimData[d].color2);
|
||||
|
||||
_lineCount = 0;
|
||||
_textDimData[d].column = _textDimData[d].line = 0;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::textPageBreak() {
|
||||
if (_vm->game() != GI_LOL)
|
||||
SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2);
|
||||
|
||||
int cp = _screen->setCurPage(0);
|
||||
Screen::FontId cf = _screen->setFont(_waitButtonFont);
|
||||
int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_waitButtonFont, Font::kStyleFat) : -1;
|
||||
|
||||
if (_vm->game() == GI_LOL)
|
||||
_vm->_timer->pauseSingleTimer(11, true);
|
||||
|
||||
_vm->_fadeText = false;
|
||||
int resetPortraitAfterSpeechAnim = 0;
|
||||
int updatePortraitSpeechAnimDuration = 0;
|
||||
|
||||
if (_vm->_updateCharNum != -1) {
|
||||
resetPortraitAfterSpeechAnim = _vm->_resetPortraitAfterSpeechAnim;
|
||||
_vm->_resetPortraitAfterSpeechAnim = 0;
|
||||
updatePortraitSpeechAnimDuration = _vm->_updatePortraitSpeechAnimDuration;
|
||||
if (_vm->_updatePortraitSpeechAnimDuration > 36)
|
||||
_vm->_updatePortraitSpeechAnimDuration = 36;
|
||||
}
|
||||
|
||||
uint32 speechPartTime = 0;
|
||||
if (_vm->speechEnabled() && _vm->_activeVoiceFileTotalTime && _numCharsTotal)
|
||||
speechPartTime = _vm->_system->getMillis() + ((_numCharsPrinted * _vm->_activeVoiceFileTotalTime) / _numCharsTotal);
|
||||
|
||||
int sdx = _screen->curDimIndex();
|
||||
const ScreenDim *dim = _screen->getScreenDim(sdx);
|
||||
|
||||
int x = ((dim->sx + dim->w) << 3) - (_vm->_dialogueButtonWidth + 3);
|
||||
int y = 0;
|
||||
int w = _vm->_dialogueButtonWidth;
|
||||
|
||||
if (_vm->game() == GI_LOL) {
|
||||
if (_isChinese) {
|
||||
y = dim->sy + dim->h - 15;
|
||||
} else if (_vm->_needSceneRestore && (_vm->_updateFlags & 2)) {
|
||||
if (_vm->_currentControlMode || !(_vm->_updateFlags & 2)) {
|
||||
y = dim->sy + dim->h - 5;
|
||||
} else {
|
||||
x += 6;
|
||||
y = dim->sy + dim->h - 2;
|
||||
}
|
||||
} else {
|
||||
y = dim->sy + dim->h - 10;
|
||||
}
|
||||
} else {
|
||||
y = _vm->guiSettings()->buttons.waitY[_waitButtonMode];
|
||||
x = _vm->guiSettings()->buttons.waitX[_waitButtonMode];
|
||||
w = _vm->guiSettings()->buttons.waitWidth[_waitButtonMode];
|
||||
}
|
||||
|
||||
if (_vm->game() == GI_LOL && _vm->gameFlags().use16ColorMode) {
|
||||
_vm->gui_drawBox(x + 8, (y & ~7) - 1, 66, 10, 0xEE, 0xCC, -1);
|
||||
_screen->printText(_pageBreakString.c_str(), (x + 37 - (_pageBreakString.size() << 1) + 4) & ~3, (y + 2) & ~7, 0xC1, 0);
|
||||
} else {
|
||||
_screen->set16bitShadingLevel(4);
|
||||
_vm->gui_drawBox(x, y, w, _vm->guiSettings()->buttons.height, _vm->guiSettings()->colors.frame1, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
|
||||
_screen->set16bitShadingLevel(0);
|
||||
#if defined(ENABLE_EOB)
|
||||
if (_vm->guiSettings()->buttons.labelShadow && _vm->game() != GI_LOL)
|
||||
((Screen_EoB*)screen())->printShadedText(_pageBreakString.c_str(), x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString.c_str()) >> 1), y + _vm->guiSettings()->buttons.txtOffsY, _vm->_dialogueButtonLabelColor1, 0, _vm->guiSettings()->colors.guiColorBlack);
|
||||
else
|
||||
#endif
|
||||
_screen->printText(_pageBreakString.c_str(), x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString.c_str()) >> 1), y + _vm->guiSettings()->buttons.txtOffsY, _vm->_dialogueButtonLabelColor1, 0);
|
||||
}
|
||||
|
||||
_vm->removeInputTop();
|
||||
|
||||
bool loop = true;
|
||||
bool target = false;
|
||||
|
||||
do {
|
||||
int inputFlag = _vm->checkInput(0, false) & 0xFF;
|
||||
_vm->removeInputTop();
|
||||
|
||||
while (!inputFlag && !_vm->shouldQuit()) {
|
||||
_vm->update();
|
||||
|
||||
if (_vm->speechEnabled()) {
|
||||
if (((_vm->_system->getMillis() > speechPartTime) || (_vm->snd_updateCharacterSpeech() != 2)) && speechPartTime) {
|
||||
loop = false;
|
||||
inputFlag = _vm->_keyMap[Common::KEYCODE_RETURN];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inputFlag = _vm->checkInput(0, false) & 0xFF;
|
||||
_vm->removeInputTop();
|
||||
}
|
||||
|
||||
_vm->gui_notifyButtonListChanged();
|
||||
|
||||
if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
|
||||
loop = false;
|
||||
} else if (inputFlag == 199 || inputFlag == 201) {
|
||||
if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, x, y, x + w, y + _vm->guiSettings()->buttons.height)) {
|
||||
if (_vm->game() == GI_LOL)
|
||||
target = true;
|
||||
else
|
||||
loop = false;
|
||||
}
|
||||
} else if (inputFlag == 200 || inputFlag == 202) {
|
||||
if (target)
|
||||
loop = false;
|
||||
}
|
||||
} while (loop && !_vm->shouldQuit());
|
||||
|
||||
_screen->set16bitShadingLevel(4);
|
||||
if (_vm->game() == GI_LOL && _vm->gameFlags().use16ColorMode)
|
||||
_screen->fillRect(x + 8, y, x + 57, y + _vm->guiSettings()->buttons.height, _textDimData[sdx].color2);
|
||||
else
|
||||
_screen->fillRect(x, y, x + w - 1, y + _vm->guiSettings()->buttons.height - 1, _textDimData[sdx].color2);
|
||||
|
||||
// Fix border overdraw glitch
|
||||
if (_vm->game() == GI_EOB2 && _isChinese && y + _vm->guiSettings()->buttons.height == 200)
|
||||
_screen->drawClippedLine(x, 199, x + w - 1, 199, _vm->guiSettings()->colors.frame1);
|
||||
|
||||
clearCurDim();
|
||||
_screen->set16bitShadingLevel(0);
|
||||
_screen->updateScreen();
|
||||
|
||||
if (_vm->game() == GI_LOL)
|
||||
_vm->_timer->pauseSingleTimer(11, false);
|
||||
|
||||
if (_vm->_updateCharNum != -1) {
|
||||
_vm->_resetPortraitAfterSpeechAnim = resetPortraitAfterSpeechAnim;
|
||||
if (updatePortraitSpeechAnimDuration > 36)
|
||||
updatePortraitSpeechAnimDuration -= 36;
|
||||
else
|
||||
updatePortraitSpeechAnimDuration >>= 1;
|
||||
|
||||
_vm->_updatePortraitSpeechAnimDuration = updatePortraitSpeechAnimDuration;
|
||||
}
|
||||
|
||||
if (cs != -1)
|
||||
_screen->setFontStyles(_waitButtonFont, cs);
|
||||
_screen->setFont(cf);
|
||||
_screen->setCurPage(cp);
|
||||
|
||||
if (_vm->game() != GI_LOL)
|
||||
SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2);
|
||||
|
||||
_vm->removeInputTop();
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::displayWaitButton() {
|
||||
_vm->_dialogueNumButtons = 1;
|
||||
_vm->_dialogueButtonString[0] = _pageBreakString.c_str();
|
||||
_vm->_dialogueButtonString[1] = 0;
|
||||
_vm->_dialogueButtonString[2] = 0;
|
||||
_vm->_dialogueHighlightedButton = 0;
|
||||
|
||||
_vm->_dialogueButtonPosX = &_vm->guiSettings()->buttons.waitX[_waitButtonMode];
|
||||
_vm->_dialogueButtonPosY = &_vm->guiSettings()->buttons.waitY[_waitButtonMode];
|
||||
_vm->_dialogueButtonWidth = _vm->guiSettings()->buttons.waitWidth[_waitButtonMode];
|
||||
_vm->_dialogueButtonYoffs = 0;
|
||||
|
||||
SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2);
|
||||
_vm->drawDialogueButtons();
|
||||
|
||||
if (!_vm->shouldQuit())
|
||||
_vm->removeInputTop();
|
||||
|
||||
while (!_vm->processDialogue() && !_vm->shouldQuit()) {}
|
||||
|
||||
_screen->set16bitShadingLevel(4);
|
||||
_screen->fillRect(_vm->_dialogueButtonPosX[0], _vm->_dialogueButtonPosY[0], _vm->_dialogueButtonPosX[0] + _vm->_dialogueButtonWidth - 1, _vm->_dialogueButtonPosY[0] + _vm->guiSettings()->buttons.height - 1, _vm->guiSettings()->colors.fill);
|
||||
clearCurDim();
|
||||
_screen->set16bitShadingLevel(0);
|
||||
_screen->updateScreen();
|
||||
_vm->_dialogueButtonWidth = _vm->guiSettings()->buttons.width;
|
||||
SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2);
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::convertString(char *str) {
|
||||
static const char convertTable_EOB2_Amiga_DE[] = {
|
||||
'\x84', '\x7F', '\x8E', '\x7F', '\x81', '\x7D', '\x9A', '\x7D', '\x94', '\x7E', '\x99', '\x7E', '\0', '\0'
|
||||
};
|
||||
|
||||
const char *table = 0;
|
||||
|
||||
if (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformAmiga && _vm->gameFlags().lang == Common::DE_DEU)
|
||||
table = convertTable_EOB2_Amiga_DE;
|
||||
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
for (; *str; ++str) {
|
||||
for (const char *c = table; *c; c += 2) {
|
||||
if ((*str) == c[0])
|
||||
*str = c[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TextDisplayer_rpg::isTwoByteChar(uint8 c) const {
|
||||
if (_vm->gameFlags().lang == Common::JA_JPN)
|
||||
return (c >= 0xE0 || (c > 0x80 && c < 0xA0));
|
||||
else if (_isChinese)
|
||||
return (c & 0x80);
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextDisplayer_rpg::applySetting(int sd, int ix, int val) {
|
||||
if (sd < -1 || sd >= _dimCount || ix >= kOutOfRange)
|
||||
error("TextDisplayer_rpg::applySetting(): arg out of range");
|
||||
|
||||
const int *memberAddr[] = {
|
||||
&_textDimData[0].lineSpacing,
|
||||
&_textDimData[0].visualLineSpacingAdjust,
|
||||
&_textDimData[0].charSpacing,
|
||||
&_textDimData[0].shadowColor,
|
||||
&_textDimData[0].noHalfWidthLineEnd
|
||||
};
|
||||
|
||||
int offset = (const byte*)memberAddr[ix] - (const byte*)&_textDimData[0];
|
||||
|
||||
if (sd == -1) {
|
||||
for (int i = 0; i < _dimCount; ++i)
|
||||
*(int*)((byte*)&_textDimData[i] + offset) = val;
|
||||
} else {
|
||||
*(int*)((byte*)&_textDimData[sd] + offset) = val;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 TextDisplayer_rpg::remapColor(int sd, uint8 color) const {
|
||||
if (sd < -1 || sd >= _dimCount)
|
||||
error("TextDisplayer_rpg::applySetting(): arg out of range");
|
||||
|
||||
// HACK: Apparently, this needs a better implementation (allowing to set
|
||||
// mappings for col1 and col2 independently). But this will do for now...
|
||||
if (_vm->gameFlags().platform == Common::kPlatformAmiga && sd != 7 && color == _textDimData[sd].color2)
|
||||
return color;
|
||||
|
||||
if (sd != -1 && _textDimData[sd].colorMap != nullptr)
|
||||
return _textDimData[sd].colorMap[color];
|
||||
|
||||
return _colorMap[color];
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif // (ENABLE_EOB || ENABLE_LOL)
|
||||
156
engines/kyra/text/text_rpg.h
Normal file
156
engines/kyra/text/text_rpg.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
|
||||
|
||||
#ifndef KYRA_TEXT_EOB_H
|
||||
#define KYRA_TEXT_EOB_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "kyra/graphics/screen.h"
|
||||
|
||||
namespace Kyra {
|
||||
|
||||
class KyraRpgEngine;
|
||||
|
||||
class TextDisplayer_rpg {
|
||||
public:
|
||||
TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr);
|
||||
virtual ~TextDisplayer_rpg();
|
||||
|
||||
void setupField(int dim, bool mode);
|
||||
|
||||
virtual void printDialogueText(int stringId, const char *pageBreakString, const char *pageBreakString2 = 0);
|
||||
virtual void printDialogueText(const char *str, bool wait = false);
|
||||
void printMessage(const char *str, int textColor = -1, ...);
|
||||
virtual void printShadedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) {}
|
||||
|
||||
virtual int clearDim(int dim);
|
||||
void clearCurDim();
|
||||
|
||||
void resetDimTextPositions(int dim);
|
||||
void resetPageBreakString();
|
||||
void setPageBreakFlag();
|
||||
void removePageBreakFlag();
|
||||
|
||||
void allowPageBreak(bool mode) { _allowPageBreak = mode; }
|
||||
void setWaitButtonMode(int mode) { _waitButtonMode = mode; }
|
||||
int lineCount() const { return _lineCount; }
|
||||
//const uint8 *colorMap() const { return _colorMap; }
|
||||
|
||||
// These methods are ScummVM specific. They are supposed to make necessary modifications
|
||||
// to the text displayer for the various Japanese and Chinese versions without too much
|
||||
// hackery...
|
||||
void setColorMapping(int sd, uint8 from, uint8 to);
|
||||
void setShadowColor(int sd, int col) { applySetting(sd, kShadowColor, col); }
|
||||
void setLineSpacing(int sd, int spacing) { applySetting(sd, kLineSpacing, spacing); }
|
||||
void setVisualLineSpacingAdjust(int sd, int adj) { applySetting(sd, kVisualLineSpacingAdjust, adj); }
|
||||
void setCharSpacing(int sd, int spacing) { applySetting(sd, kCharSpacing, spacing); }
|
||||
|
||||
protected:
|
||||
virtual KyraRpgEngine *vm() { return _vm; }
|
||||
virtual Screen *screen() { return _screen; }
|
||||
|
||||
virtual void displayText(char *str, ...);
|
||||
char parseCommand();
|
||||
void readNextPara();
|
||||
void printLine(char *str);
|
||||
virtual void textPageBreak();
|
||||
void displayWaitButton();
|
||||
|
||||
void convertString(char *str);
|
||||
|
||||
char *_dialogueBuffer;
|
||||
|
||||
const char *_tempString1;
|
||||
const char *_tempString2;
|
||||
char *_currentLine;
|
||||
char _ctrl[3];
|
||||
|
||||
uint16 _lineWidth;
|
||||
uint32 _numCharsTotal;
|
||||
uint32 _numCharsLeft;
|
||||
uint32 _numCharsPrinted;
|
||||
|
||||
bool _printFlag;
|
||||
bool _twoByteLineBreakFlag;
|
||||
const bool _pc98TextMode;
|
||||
|
||||
Common::String _pageBreakString;
|
||||
Common::String _scriptParaString;
|
||||
int _lineCount;
|
||||
|
||||
bool _allowPageBreak;
|
||||
int _waitButtonSpace;
|
||||
int _waitButtonMode;
|
||||
|
||||
static const char _pageBreakDefault[3][5];
|
||||
|
||||
struct TextDimData {
|
||||
uint8 color1;
|
||||
uint8 color2;
|
||||
uint16 column;
|
||||
uint8 line;
|
||||
// These properties don't appear in the original code. The various Japanese and Chinese versions
|
||||
// just had their modifications hacked in in whatever way the devs felt like. These properties
|
||||
// help making the necessary adjustments without too much hackery...
|
||||
int lineSpacing;
|
||||
int visualLineSpacingAdjust; // LOL PC-98 has the worst hack here. The visual line spacing is different than the one that is used to measure the text field space.
|
||||
int charSpacing;
|
||||
int shadowColor;
|
||||
int noHalfWidthLineEnd;
|
||||
uint8 *colorMap;
|
||||
};
|
||||
|
||||
TextDimData *_textDimData;
|
||||
const int _dimCount;
|
||||
KyraRpgEngine *_vm;
|
||||
|
||||
private:
|
||||
bool isTwoByteChar(uint8 c) const;
|
||||
void applySetting(int sd, int ix, int val);
|
||||
uint8 remapColor(int sd, uint8 color) const;
|
||||
|
||||
enum TextFieldVar {
|
||||
kLineSpacing = 0,
|
||||
kVisualLineSpacingAdjust,
|
||||
kCharSpacing,
|
||||
kShadowColor,
|
||||
kNoHalfWidthLineEnd,
|
||||
kOutOfRange
|
||||
};
|
||||
|
||||
Screen *_screen;
|
||||
char *_table1;
|
||||
char *_table2;
|
||||
|
||||
Screen::FontId _waitButtonFont;
|
||||
|
||||
uint8 _colorMap[256];
|
||||
|
||||
bool _isChinese;
|
||||
};
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ENABLE_EOB || ENABLE_LOL
|
||||
Reference in New Issue
Block a user