aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/text
diff options
context:
space:
mode:
authorathrxx2019-01-26 01:31:34 +0100
committerathrxx2019-03-06 20:48:15 +0100
commit1dfdcc7252ac83643cae7a7447c025da2af63843 (patch)
treeb6736d006bf67d5264dd171c336f0915695d1f88 /engines/kyra/text
parent8b53d20b51771680c3d31aa02c0285b7a8be4e85 (diff)
downloadscummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.gz
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.tar.bz2
scummvm-rg350-1dfdcc7252ac83643cae7a7447c025da2af63843.zip
KYRA: cleanup dir
Reorganize all files in sub directories. The file placement isn't as intuitive as it might be for other engines, which is probably the reason why this hasn't been done before.
Diffstat (limited to 'engines/kyra/text')
-rw-r--r--engines/kyra/text/text.cpp330
-rw-r--r--engines/kyra/text/text.h81
-rw-r--r--engines/kyra/text/text_hof.cpp672
-rw-r--r--engines/kyra/text/text_hof.h52
-rw-r--r--engines/kyra/text/text_lok.cpp396
-rw-r--r--engines/kyra/text/text_lol.cpp348
-rw-r--r--engines/kyra/text/text_lol.h70
-rw-r--r--engines/kyra/text/text_mr.cpp894
-rw-r--r--engines/kyra/text/text_mr.h52
-rw-r--r--engines/kyra/text/text_rpg.cpp739
-rw-r--r--engines/kyra/text/text_rpg.h115
11 files changed, 3749 insertions, 0 deletions
diff --git a/engines/kyra/text/text.cpp b/engines/kyra/text/text.cpp
new file mode 100644
index 0000000000..36af67e6aa
--- /dev/null
+++ b/engines/kyra/text/text.cpp
@@ -0,0 +1,330 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "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;
+ memset(_talkSubstrings, 0, sizeof(_talkSubstrings));
+ memset(_talkBuffer, 0, sizeof(_talkBuffer));
+}
+
+void TextDisplayer::setTalkCoords(uint16 y) {
+ _talkCoords.y = y;
+}
+
+int TextDisplayer::getCenterStringX(const char *str, int x1, int x2) {
+ _screen->_charWidth = -2;
+ int strWidth = _screen->getTextWidth(str);
+ _screen->_charWidth = 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->_charWidth = -2;
+ int i = 0;
+ while (i <= len && *str) {
+ uint c = *str++;
+ c &= 0xFF;
+ if (c >= 0x7F && _vm->gameFlags().lang == Common::JA_JPN) {
+ c = READ_LE_UINT16(str - 1);
+ ++str;
+ }
+ i += _screen->getCharWidth(c);
+ ++charsCount;
+ }
+ _screen->_charWidth = 0;
+ }
+ return charsCount;
+}
+
+int TextDisplayer::dropCRIntoString(char *str, int offs) {
+ int pos = 0;
+ str += offs;
+ while (*str) {
+ if (*str == ' ') {
+ *str = '\r';
+ return pos;
+ }
+ ++str;
+ ++pos;
+ }
+ return 0;
+}
+
+char *TextDisplayer::preprocessString(const char *str) {
+ if (str != _talkBuffer) {
+ assert(strlen(str) < sizeof(_talkBuffer) - 1);
+ strcpy(_talkBuffer, str);
+ }
+ char *p = _talkBuffer;
+ while (*p) {
+ if (*p == '\r') {
+ return _talkBuffer;
+ }
+ ++p;
+ }
+ p = _talkBuffer;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ int textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 0;
+ if (textWidth > 176) {
+ if (textWidth > 352) {
+ int count = getCharLength(p, textWidth / 3);
+ int offs = dropCRIntoString(p, count);
+ p += count + offs;
+ _screen->_charWidth = -2;
+ textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 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 == '\r') {
+ 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 (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->_charWidth = -2;
+ for (int l = 0; l < linesCount; ++l) {
+ int w = _screen->getTextWidth(&_talkSubstrings[l * TALK_SUBSTRING_LEN]);
+ if (maxWidth < w) {
+ maxWidth = w;
+ }
+ }
+ _screen->_charWidth = 0;
+ return maxWidth;
+}
+
+void TextDisplayer::calcWidestLineBounds(int &x1, int &x2, int w, int cx) {
+ x1 = cx - w / 2;
+ if (x1 + w >= Screen::SCREEN_W - 12) {
+ x1 = Screen::SCREEN_W - 12 - w - 1;
+ } else if (x1 < 12) {
+ x1 = 12;
+ }
+ 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);
+ int top = y - lineCount * 10;
+ if (top < 0) {
+ top = 0;
+ }
+ _talkMessageY = top;
+ _talkMessageH = lineCount * 10;
+ int w = getWidestLineWidth(lineCount);
+ int x1, x2;
+ 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);
+
+ for (int i = 0; i < lineCount; ++i) {
+ top = i * 10 + _talkMessageY;
+ char *msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
+ int left = getCenterStringX(msg, x1, x2);
+ printText(msg, left, top, color, 0xC, 0);
+ }
+ _screen->_curPage = curPage;
+ _talkMessagePrinted = true;
+}
+
+void TextDisplayer::printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
+ uint8 colorMap[] = { 0, 15, 12, 12 };
+ colorMap[3] = c1;
+ _screen->setTextColor(colorMap, 0, 3);
+ _screen->_charWidth = -2;
+ _screen->printText(str, x, y, c0, c2);
+ _screen->_charWidth = 0;
+}
+
+void TextDisplayer::printCharacterText(const char *text, int8 charNum, int charX) {
+ int top, left, x1, x2, w, x;
+ char *msg;
+
+ text = preprocessString(text);
+ int lineCount = buildMessageSubstrings(text);
+ w = getWidestLineWidth(lineCount);
+ 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];
+ }
+
+ for (int i = 0; i < lineCount; ++i) {
+ top = i * 10 + _talkMessageY;
+ msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
+ left = getCenterStringX(msg, x1, x2);
+ printText(msg, left, top, color, 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
diff --git a/engines/kyra/text/text.h b/engines/kyra/text/text.h
new file mode 100644
index 0000000000..3ea51c389c
--- /dev/null
+++ b/engines/kyra/text/text.h
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 char *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 char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
+ void printCharacterText(const char *text, int8 charNum, int charX);
+
+ uint16 _talkMessageY;
+ uint16 _talkMessageH;
+ 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;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/text/text_hof.cpp b/engines/kyra/text/text_hof.cpp
new file mode 100644
index 0000000000..2d6332a0a2
--- /dev/null
+++ b/engines/kyra/text/text_hof.cpp
@@ -0,0 +1,672 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "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 * 10;
+ y = MAX(0, y - (lineCount * 10));
+ 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];
+ printText(msg, getCenterStringX(msg, x1, x2), i * 10 + _talkMessageY, c1, 0xCF, 0);
+ }
+ }
+
+ _screen->_curPage = curPageBackUp;
+}
+
+char *TextDisplayer_HoF::preprocessString(const char *str) {
+ if (str != _talkBuffer) {
+ assert(strlen(str) < sizeof(_talkBuffer) - 1);
+ strcpy(_talkBuffer, str);
+ }
+
+ char *p = _talkBuffer;
+ while (*p) {
+ if (*p == '\r')
+ return _talkBuffer;
+ ++p;
+ }
+
+ p = _talkBuffer;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ int textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 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->_charWidth = -2;
+ textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 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 char *str) {
+ static const uint8 durationMultiplicator[] = { 16, 14, 12, 10, 8, 8, 7, 6, 5, 4 };
+
+ int duration = strlen(str);
+ duration *= _flags.isTalkie ? 8 : durationMultiplicator[(_configTextspeed / 10)];
+ return MAX<int>(duration, 120);
+}
+
+void KyraEngine_HoF::objectChat(const char *str, int object, int vocHigh, int vocLow) {
+ setNextIdleAnimTimer();
+
+ _chatVocHigh = _chatVocLow = -1;
+
+ objectChatInit(str, object, vocHigh, vocLow);
+ _chatText = str;
+ _chatObject = object;
+ int chatType = chatGetType(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 = 0;
+ _chatObject = -1;
+
+ setNextIdleAnimTimer();
+}
+
+void KyraEngine_HoF::objectChatInit(const char *str, int object, int vocHigh, int vocLow) {
+ str = _text->preprocessString(str);
+ int lineNum = _text->buildMessageSubstrings(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 * 10;
+ yPos = MAX(yPos, 0);
+ _text->_talkMessageY = yPos;
+ _text->_talkMessageH = lineNum*10;
+
+ 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 char *str, int object) {
+ int c1 = _talkObjectList[object].color;
+ str = _text->preprocessString(str);
+ int lineNum = _text->buildMessageSubstrings(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 = &_text->_talkSubstrings[i*_text->maxSubstringLen()];
+
+ int y = _text->_talkMessageY + i * 10;
+ 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, 0);
+ 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(), 0);
+}
+
+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 {
+ 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;
+ }
+ memcpy(_unkBuf500Bytes, _dlgBuffer + offs, len);
+ _unkBuf500Bytes[len] = 0;
+ 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;
+ memcpy(_unkBuf500Bytes, _dlgBuffer + offs, len);
+ _unkBuf500Bytes[len] = 0;
+ offs += len;
+ }
+
+ if (_unkBuf500Bytes[0]) {
+ if ((!_flags.isTalkie && cmd == 11) || (_flags.isTalkie && cmd == 12)) {
+ if (activeTimSequence > -1) {
+ deinitTalkObject(activeTimSequence);
+ activeTimSequence = -1;
+ }
+ objectChat((const char *)_unkBuf500Bytes, 0, vocHi, vocLo);
+ } else {
+ if (activeTimSequence != nextTimSequence) {
+ if (activeTimSequence > -1) {
+ deinitTalkObject(activeTimSequence);
+ activeTimSequence = -1;
+ }
+ initTalkObject(nextTimSequence);
+ activeTimSequence = nextTimSequence;
+ }
+ npcChatSequence((const char *)_unkBuf500Bytes, nextTimSequence, vocHi, vocLo);
+ }
+ }
+ }
+ }
+
+ if (activeTimSequence > -1)
+ deinitTalkObject(activeTimSequence);
+
+ _screen->showMouse();
+}
+
+void KyraEngine_HoF::initTalkObject(int index) {
+ TalkObject &object = _talkObjectList[index];
+
+ char STAFilename[13];
+ char ENDFilename[13];
+
+ strcpy(STAFilename, object.filename);
+ strcpy(_TLKFilename, object.filename);
+ strcpy(ENDFilename, object.filename);
+
+ strcat(STAFilename + 4, "_STA.TIM");
+ strcat(_TLKFilename + 4, "_TLK.TIM");
+ strcat(ENDFilename + 4, "_END.TIM");
+
+ _currentTalkSections.STATim = _tim->load(STAFilename, &_timOpcodes);
+ _currentTalkSections.TLKTim = _tim->load(_TLKFilename, &_timOpcodes);
+ _currentTalkSections.ENDTim = _tim->load(ENDFilename, &_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)
+ 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)
+ 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 char *str, int objectId, int vocHigh, int vocLow) {
+ _chatText = str;
+ _chatObject = objectId;
+ objectChatInit(str, objectId, vocHigh, vocLow);
+
+ if (!_currentTalkSections.TLKTim)
+ _currentTalkSections.TLKTim = _tim->load(_TLKFilename, &_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 = 0;
+ _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
diff --git a/engines/kyra/text/text_hof.h b/engines/kyra/text/text_hof.h
new file mode 100644
index 0000000000..cced5bf536
--- /dev/null
+++ b/engines/kyra/text/text_hof.h
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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);
+ void restoreScreen();
+
+ void printCustomCharacterText(const char *src, int x, int y, uint8 c1, int srcPage, int dstPage);
+
+ char *preprocessString(const char *str);
+ void calcWidestLineBounds(int &x1, int &x2, int w, int x);
+private:
+ KyraEngine_HoF *_vm;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/text/text_lok.cpp b/engines/kyra/text/text_lok.cpp
new file mode 100644
index 0000000000..c45f0410e3
--- /dev/null
+++ b/engines/kyra/text/text_lok.cpp
@@ -0,0 +1,396 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "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, int16 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();
+
+ if (textEnabled() && !speechEnabled() && chatDuration != -1) {
+ switch (_configTextspeed) {
+ case 0:
+ chatDuration *= 2;
+ break;
+ case 2:
+ chatDuration /= 4;
+ break;
+ case 3:
+ chatDuration = -1;
+ 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 < (int16)(_system->getMillis() - timeAtStart)) && chatDuration != -1 && printText) || (!printText && !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);
+ _system->updateScreen();
+ }
+ }
+ }
+
+ 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) {
+ _charSayUnk3 = -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();
+ _charSayUnk3 = charNum;
+
+ return returnValue;
+}
+
+void KyraEngine_LoK::characterSays(int vocFile, const char *chatStr, int8 charNum, int8 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) {
+ _characterList[charNum].currentAnimFrame = startAnimFrames[charNum];
+ _charSayUnk3 = charNum;
+ _talkingCharNum = charNum;
+ _animator->animRefreshNPC(charNum);
+ }
+
+ char *processedString = _text->preprocessString(chatStr);
+ int lineNum = _text->buildMessageSubstrings(processedString);
+
+ int16 yPos = _characterList[charNum].y1;
+ yPos -= ((_scaleTable[yPos] * _characterList[charNum].height) >> 8);
+ yPos -= 8;
+ yPos -= lineNum * 10;
+
+ if (yPos < 11)
+ yPos = 11;
+
+ if (yPos > 100)
+ yPos = 100;
+
+ _text->_talkMessageY = yPos;
+ _text->_talkMessageH = lineNum * 10;
+
+ const bool printText = textEnabled();
+
+ if (printText) {
+ _animator->restoreAllObjectBackgrounds();
+
+ _screen->copyRegion(12, _text->_talkMessageY, 12, 136, 296, _text->_talkMessageH, 2, 2);
+
+ _text->printCharacterText(processedString, charNum, _characterList[charNum].x1);
+ }
+
+ if (chatDuration == -2)
+ chatTicks = strlen(processedString) * 9;
+ else
+ chatTicks = chatDuration;
+
+ if (!speechEnabled())
+ vocFile = -1;
+ waitForChatToFinish(vocFile, chatTicks, chatStr, charNum, printText);
+
+ if (printText) {
+ _animator->restoreAllObjectBackgrounds();
+
+ _screen->copyRegion(12, 136, 12, _text->_talkMessageY, 296, _text->_talkMessageH, 2, 2);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+
+ _screen->copyRegion(12, _text->_talkMessageY, 12, _text->_talkMessageY, 296, _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) {
+ _screen->fillRect(8, 143, 311, 152, _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;
+ }
+
+ _text->printText(sentence, 8, 143, 0xFF, _flags.platform == Common::kPlatformAmiga ? 19 : 12, 0);
+ setTextFadeTimerCountdown(15);
+ _fadeText = false;
+}
+
+void KyraEngine_LoK::updateSentenceCommand(const char *str1, const char *str2, int color) {
+ char sentenceCommand[500];
+ 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
diff --git a/engines/kyra/text/text_lol.cpp b/engines/kyra/text/text_lol.cpp
new file mode 100644
index 0000000000..7c724e3b8e
--- /dev/null
+++ b/engines/kyra/text/text_lol.cpp
@@ -0,0 +1,348 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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];
+ memset(_buffer, 0, 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);
+
+ for (int i = 177; i > 141; i--) {
+ uint32 endTime = _vm->_system->getMillis() + _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);
+
+ for (int i = 140; i < 177; i++) {
+ uint32 endTime = _vm->_system->getMillis() + _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::printDialogueText(int dim, 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->unkA;
+ } else {
+ oldDim = clearDim(3);
+ _textDimData[3].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 192;
+ _textDimData[3].color2 = _screen->_curDim->unkA;
+ 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->unkA;
+ }
+
+ int cp = _screen->setCurPage(0);
+ Screen::FontId of = _screen->setFont((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT);
+
+ preprocessString(str, script, paramList, paramIndex);
+ _numCharsTotal = strlen(_dialogueBuffer);
+ 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();
+
+ uint16 col = textColors[type & 0x7FFF];
+
+ 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[type])
+ _vm->sound()->playSoundEffect(soundEffect[type]);
+ }
+
+ _vm->_textColorFlag = type & 0x7FFF;
+ _vm->_fadeText = false;
+}
+
+void TextDisplayer_LoL::preprocessString(char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) {
+ char *dst = _dialogueBuffer;
+
+ for (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':
+ strcpy(dst, Common::String::format("%d", _scriptTextParameter).c_str());
+ dst += strlen(dst);
+ break;
+
+ case 'n':
+ strcpy(dst, _vm->_characters[script ? script->stack[script->sp + paramIndex] : paramList[paramIndex]].name);
+ dst += strlen(dst);
+ break;
+
+ case 's':
+ strcpy(dst, _vm->getLangString(script ? script->stack[script->sp + paramIndex] : paramList[paramIndex]));
+ dst += strlen(dst);
+ break;
+
+ case 'X':
+ case 'd':
+ case 'u':
+ case 'x':
+ strcpy(dst, Common::String::format("%d", script ? script->stack[script->sp + paramIndex] : paramList[paramIndex]).c_str());
+ dst += strlen(dst);
+ break;
+
+ case '\0':
+ default:
+ continue;
+ }
+ }
+ *dst = 0;
+}
+
+KyraRpgEngine *TextDisplayer_LoL::vm() {
+ return _vm;
+}
+
+Screen *TextDisplayer_LoL::screen() {
+ return _screen;
+}
+
+void TextDisplayer_LoL::textPageBreak() {
+ strcpy(_pageBreakString, _vm->getLangString(0x4073));
+ TextDisplayer_rpg::textPageBreak();
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_LOL
diff --git a/engines/kyra/text/text_lol.h b/engines/kyra/text/text_lol.h
new file mode 100644
index 0000000000..00428971ca
--- /dev/null
+++ b/engines/kyra/text/text_lol.h
@@ -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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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();
+
+ void setupField(bool mode);
+ void expandField();
+
+ void printDialogueText(int dim, char *str, EMCState *script, const uint16 *paramList, int16 paramIndex);
+ void printMessage(uint16 type, const char *str, ...) GCC_PRINTF(3, 4);
+
+ int16 _scriptTextParameter;
+
+private:
+ virtual KyraRpgEngine *vm();
+ virtual Screen *screen();
+
+ void preprocessString(char *str, EMCState *script, const uint16 *paramList, int16 paramIndex);
+ void textPageBreak();
+
+ char *_stringParameters[15];
+ char *_buffer;
+
+ LoLEngine *_vm;
+ Screen_LoL *_screen;
+};
+
+} // End of namespace Kyra
+
+#endif // ENABLE_LOL
+
+#endif
diff --git a/engines/kyra/text/text_mr.cpp b/engines/kyra/text/text_mr.cpp
new file mode 100644
index 0000000000..8400700503
--- /dev/null
+++ b/engines/kyra/text/text_mr.cpp
@@ -0,0 +1,894 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "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);
+ strcpy(_talkBuffer, str);
+ }
+
+ char *p = _talkBuffer;
+ while (*p) {
+ if (*p++ == '\r')
+ return _talkBuffer;
+ }
+
+ p = _talkBuffer;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -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 char *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->_charWidth = -2;
+ _screen->printText(str, x, y, c0, c2);
+ _screen->_charWidth = 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.animFrame = _characterFrameTable[_mainCharacter.facing];
+ updateCharacterAnim(0);
+ _chatText = 0;
+ _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;
+ }
+
+ yPos -= lineNum * 10;
+ yPos = MAX(yPos, 0);
+ _text->_talkMessageY = yPos;
+ _text->_talkMessageH = lineNum*10;
+
+ 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 char *str, int object) {
+ int c1 = _talkObjectList[object].color;
+ str = _text->preprocessString(str);
+ int lineNum = _text->buildMessageSubstrings(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 = &_text->_talkSubstrings[i*_text->maxSubstringLen()];
+
+ int y = _text->_talkMessageY + i * 10;
+ x = _text->getCenterStringX(str, cX1, cX2);
+
+ _text->printText(str, x, y, c1, 0xF0, 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 = 0;
+ _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 = 0;
+ _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 = 188;
+
+ _chatVocHigh = _chatVocLow = -1;
+ _albumChatActive = true;
+ albumChatInit(str, 1, vocHigh, vocLow);
+ _albumChatActive = false;
+
+ _chatText = str;
+ _chatObject = 1;
+ _screen->hideMouse();
+ albumChatWaitToFinish();
+ _screen->showMouse();
+
+ _chatText = 0;
+ _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;
+ }
+
+ yPos -= lineNum * 10;
+ yPos = MAX(yPos, 0);
+ _text->_talkMessageY = yPos;
+ _text->_talkMessageH = lineNum*10;
+
+ 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, 0, 0);
+ 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::String dlgFile = Common::String::format("CH%.02d-S%.02d.%s", _currentChapter, _mainCharacter.dlgIndex, _languageExtension[_lang]);
+ Common::String cnvFile = Common::String::format("CH%.02d-S%.02d.CNV", _currentChapter, _mainCharacter.dlgIndex);
+
+ delete _cnvFile;
+ delete _dlgBuffer;
+
+ _res->exists(cnvFile.c_str(), true);
+ _res->exists(dlgFile.c_str(), 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();
+ vocLow = _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);
+ script = -1;
+ }
+
+ 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 = 0;
+ _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
diff --git a/engines/kyra/text/text_mr.h b/engines/kyra/text/text_mr.h
new file mode 100644
index 0000000000..339aba691d
--- /dev/null
+++ b/engines/kyra/text/text_mr.h
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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);
+ int dropCRIntoString(char *str, int minOffs, int maxOffs);
+
+ void printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
+
+ void restoreScreen();
+
+ void calcWidestLineBounds(int &x1, int &x2, int w, int x);
+protected:
+ KyraEngine_MR *_vm;
+ Screen_MR *_screen;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
new file mode 100644
index 0000000000..10e1a4409f
--- /dev/null
+++ b/engines/kyra/text/text_rpg.cpp
@@ -0,0 +1,739 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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),
+ _numCharsLeft(0), _numCharsPrinted(0), _sjisTextModeLineBreak(false), _waitButtonMode(1) {
+
+ _dialogueBuffer = new char[kEoBTextBufferSize];
+ memset(_dialogueBuffer, 0, kEoBTextBufferSize);
+
+ _currentLine = new char[85];
+ memset(_currentLine, 0, 85);
+
+ _textDimData = new TextDimData[_screen->screenDimTableCount()];
+
+ for (int i = 0; i < _screen->screenDimTableCount(); i++) {
+ const ScreenDim *d = _screen->getScreenDim(i);
+ _textDimData[i].color1 = d->unk8;
+ _textDimData[i].color2 = d->unkA;
+ _textDimData[i].line = d->unkC;
+ _textDimData[i].column = d->unkE;
+ }
+
+ _table1 = new char[128];
+ memset(_table1, 0, 128);
+ _table2 = new char[16];
+ memset(_table2, 0, 16);
+
+ _waitButtonSpace = 0;
+}
+
+TextDisplayer_rpg::~TextDisplayer_rpg() {
+ 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);
+ }
+}
+
+void TextDisplayer_rpg::resetDimTextPositions(int dim) {
+ _textDimData[dim].column = 0;
+ _textDimData[dim].line = 0;
+}
+
+void TextDisplayer_rpg::resetPageBreakString() {
+ if (_vm->_moreStrings)
+ strcpy(_pageBreakString, _vm->_moreStrings[0]);
+}
+
+void TextDisplayer_rpg::setPageBreakFlag() {
+ _allowPageBreak = true;
+ _lineCount = 0;
+}
+
+void TextDisplayer_rpg::removePageBreakFlag() {
+ _allowPageBreak = false;
+}
+
+void TextDisplayer_rpg::displayText(char *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 = (_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false;
+ int sjisOffs = (sjisTextMode || _vm->game() == GI_EOB2) ? 8 : 9;
+ 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->_charWidth);
+
+ while (c) {
+ char a = tolower((unsigned char)_ctrl[1]);
+
+ if (!_tempString2 && c == '%') {
+ if (a == 'd') {
+ strcpy(_scriptParaString, Common::String::format("%d", va_arg(args, int)).c_str());
+ _tempString2 = _scriptParaString;
+ } else if (a == 's') {
+ _tempString2 = va_arg(args, char *);
+ } else {
+ break;
+ }
+
+ _ctrl[0] = _ctrl[2];
+ _ctrl[2] = _ctrl[1] = 0;
+ c = parseCommand();
+ }
+
+ if (_vm->gameFlags().lang == Common::JA_JPN) {
+ uint8 cu = (uint8) c;
+ if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
+ if ((_textDimData[sdx].column + _lineWidth + sjisOffs) > (sd->w << 3))
+ printLine(_currentLine);
+
+ _currentLine[_numCharsLeft++] = c;
+ _currentLine[_numCharsLeft++] = parseCommand();
+ _currentLine[_numCharsLeft] = '\0';
+
+ _lineWidth += sjisOffs;
+ c = parseCommand();
+ continue;
+ }
+ }
+
+ uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth);
+
+ switch (c - 1) {
+ case 0:
+ printLine(_currentLine);
+ 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->_charWidth);
+ dv = ((dv + 8) & 0xFFF8) - 1;
+ if (dv >= charsPerLine)
+ dv = 0;
+ _textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charWidth) * dv;
+ break;
+
+ case 12:
+ if (sjisTextMode)
+ _sjisTextModeLineBreak = true;
+ printLine(_currentLine);
+ _sjisTextModeLineBreak = false;
+ _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) {
+ _lineWidth += (sjisTextMode ? 4 : (_screen->_currentFont == Screen::FID_SJIS_FNT ? 9 : _screen->getCharWidth((uint8)c)));
+ _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);
+}
+
+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 = _vm->gameFlags().lang == Common::JA_JPN && (_vm->gameFlags().use16ColorMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false;
+
+ int fh = (_screen->_currentFont == Screen::FID_SJIS_FNT) ? 9 : (_screen->getFontHeight() + _screen->_charOffset);
+ int lines = (sd->h - _screen->_charOffset) / fh;
+
+ while (_textDimData[sdx].line >= lines) {
+ if ((lines - _waitButtonSpace) <= _lineCount && _allowPageBreak) {
+ _lineCount = 0;
+ textPageBreak();
+ _numCharsPrinted = 0;
+ }
+
+ int h1 = ((sd->h / fh) - 1) * fh;
+ int h2 = sd->h - fh;
+
+ if (h2)
+ _screen->copyRegion(sd->sx << 3, sd->sy + fh, sd->sx << 3, sd->sy, sd->w << 3, h2, _screen->_curPage, _screen->_curPage, Screen::CR_NO_P_CHECK);
+
+ _screen->set16bitShadingLevel(4);
+ _screen->fillRect(sd->sx << 3, sd->sy + h1, ((sd->sx + sd->w) << 3) - 1, sd->sy + sd->h - 1, _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 + (sjisTextMode ? (_textDimData[sdx].line << 3) : (fh * _textDimData[sdx].line));
+ int w = sd->w << 3;
+ int lw = _lineWidth;
+ int s = _numCharsLeft;
+ char c = 0;
+ 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 (!_sjisTextModeLineBreak || (_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) {
+ c = str[n2];
+ uint8 cu = (uint8) c;
+ if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0))
+ n2++;
+ n2++;
+ }
+ s = n2;
+ }
+ } else {
+ if (_vm->gameFlags().lang == Common::JA_JPN) {
+ for (int i = 0; i < s; ++i) {
+ uint8 cu = (uint8) str[i];
+ if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0))
+ twoByteCharOffs = 8;
+ }
+ }
+
+ 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;
+ c = str[0];
+
+ for (strPos = 0; strPos < s; ++strPos) {
+ uint8 cu = (uint8) str[strPos];
+ if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
+ lw += 9;
+ strPos++;
+ } else {
+ lw += _screen->getCharWidth((uint8)c);
+ }
+
+ if (!lineLastCharPos && w < lw + twoByteCharOffs)
+ lineLastCharPos = prevStrPos;
+
+ if (lineLastCharPos && c == ' ') {
+ s = strPos;
+ _printFlag = false;
+ break;
+ }
+ prevStrPos = strPos;
+ c = (char) cu;
+ }
+
+ 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
+ c = str[strPos];
+
+ lw -= _screen->getCharWidth((uint8)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;
+ }
+ }
+
+ }
+ }
+
+ c = str[s];
+ str[s] = 0;
+
+ uint8 col = _textDimData[sdx].color1;
+ if (sjisTextMode && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) {
+ switch (_textDimData[sdx].color1) {
+ case 0x88:
+ col = 0x41;
+ break;
+ case 0x55:
+ col = 0x81;
+ break;
+ case 0xAA:
+ col = 0x21;
+ break;
+ case 0x99:
+ col = 0xA1;
+ break;
+ case 0x33:
+ col = 0xE1;
+ break;
+ case 0x18:
+ col = 0x61;
+ break;
+ default:
+ col = 1;
+ break;
+ }
+ _screen->printText(str, x1 & ~3, (y + 8) & ~7, col, 0);
+ } else {
+ _screen->printText(str, x1, y, col, _textDimData[sdx].color2);
+ _screen->updateScreen();
+ }
+
+ _textDimData[sdx].column += lw;
+ _numCharsPrinted += strlen(str);
+
+ str[s] = c;
+
+ if (c == ' ')
+ 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 = sjisTextMode ? (_numCharsLeft << 2) : (_screen->_currentFont == Screen::FID_SJIS_FNT ? _numCharsLeft * 9 : _screen->getTextWidth(str));
+
+ if (!_numCharsLeft && (_textDimData[sdx].column + twoByteCharOffs) <= (sd->w << 3))
+ return;
+
+ _textDimData[sdx].column = 0;
+ _textDimData[sdx].line++;
+ _lineCount++;
+
+ printLine(str);
+}
+
+void TextDisplayer_rpg::printDialogueText(int stringId, const char *pageBreakString) {
+ 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);
+ displayText(_dialogueBuffer);
+ _screen->set16bitShadingLevel(0);
+
+ if (pageBreakString) {
+ if (pageBreakString[0]) {
+ strcpy(_pageBreakString, pageBreakString);
+ displayWaitButton();
+ resetPageBreakString();
+ }
+ }
+}
+
+void TextDisplayer_rpg::printDialogueText(const char *str, bool wait) {
+ assert(strlen(str) < kEoBTextBufferSize);
+ Common::strlcpy(_dialogueBuffer, str, kEoBTextBufferSize);
+
+ strcpy(_dialogueBuffer, str);
+ displayText(_dialogueBuffer);
+ 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);
+
+ 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->unk8;
+ _textDimData[dim].color2 = _vm->game() == GI_LOL ? _screen->_curDim->unkA : _vm->guiSettings()->colors.fill;
+ clearCurDim();
+ return res;
+}
+
+void TextDisplayer_rpg::clearCurDim() {
+ int d = _screen->curDimIndex();
+ const ScreenDim *tmp = _screen->getScreenDim(d);
+ if (_vm->gameFlags().use16ColorMode) {
+ _screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 2, (tmp->sy + tmp->h) - 2, _textDimData[d].color2);
+ } else
+ _screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1, (tmp->sy + tmp->h) - 1, _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((_vm->gameFlags().lang == Common::JA_JPN && _vm->gameFlags().use16ColorMode) ? Screen::FID_SJIS_FNT : ((_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns) ? Screen::FID_8_FNT : Screen::FID_6_FNT));
+
+ 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);
+
+ const ScreenDim *dim = _screen->getScreenDim(_screen->curDimIndex());
+
+ int x = ((dim->sx + dim->w) << 3) - (_vm->_dialogueButtonWidth + 3);
+ int y = 0;
+ int w = _vm->_dialogueButtonWidth;
+
+ if (_vm->game() == GI_LOL) {
+ 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->gameFlags().use16ColorMode) {
+ _vm->gui_drawBox(x + 8, (y & ~7) - 1, 66, 10, 0xEE, 0xCC, -1);
+ _screen->printText(_pageBreakString, (x + 37 - (strlen(_pageBreakString) << 1) + 4) & ~3, (y + 2) & ~7, 0xC1, 0);
+ } else {
+ int yOffs = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns) ? 1 : 2;
+ _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);
+ _screen->printText(_pageBreakString, x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString) >> 1), y + yOffs, _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 + 9)) {
+ 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->gameFlags().use16ColorMode)
+ _screen->fillRect(x + 8, y, x + 57, y + 9, _textDimData[_screen->curDimIndex()].color2);
+ else
+ _screen->fillRect(x, y, x + w - 1, y + _vm->guiSettings()->buttons.height - 1, _textDimData[_screen->curDimIndex()].color2);
+
+ 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;
+ }
+
+ _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;
+ _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 = 95;
+ SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2);
+}
+
+} // End of namespace Kyra
+
+#endif // (ENABLE_EOB || ENABLE_LOL)
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
new file mode 100644
index 0000000000..30d3463726
--- /dev/null
+++ b/engines/kyra/text/text_rpg.h
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
+
+#ifndef KYRA_TEXT_EOB_H
+#define KYRA_TEXT_EOB_H
+
+#include "common/scummsys.h"
+
+namespace Kyra {
+
+class Screen;
+class KyraRpgEngine;
+
+class TextDisplayer_rpg {
+public:
+ TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr);
+ virtual ~TextDisplayer_rpg();
+
+ void setupField(int dim, bool mode);
+
+ void printDialogueText(int stringId, const char *pageBreakString);
+ void printDialogueText(const char *str, bool wait = false);
+ void printMessage(const char *str, int textColor = -1, ...);
+
+ 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() { return _lineCount; }
+
+protected:
+ virtual KyraRpgEngine *vm() { return _vm; }
+ virtual Screen *screen() { return _screen; }
+
+ void displayText(char *str, ...);
+ char parseCommand();
+ void readNextPara();
+ void printLine(char *str);
+ virtual void textPageBreak();
+ void displayWaitButton();
+
+ char *_dialogueBuffer;
+
+ char *_tempString1;
+ char *_tempString2;
+ char *_currentLine;
+ char _ctrl[3];
+
+ uint16 _lineWidth;
+ uint32 _numCharsTotal;
+ uint32 _numCharsLeft;
+ uint32 _numCharsPrinted;
+
+ bool _printFlag;
+ bool _sjisTextModeLineBreak;
+
+ char _pageBreakString[20];
+ char _scriptParaString[11];
+ int _lineCount;
+
+ bool _allowPageBreak;
+ int _waitButtonSpace;
+ int _waitButtonMode;
+
+ static const char _pageBreakDefault[3][5];
+
+ struct TextDimData {
+ uint8 color1;
+ uint8 color2;
+ uint16 column;
+ uint8 line;
+ };
+
+ TextDimData *_textDimData;
+
+private:
+ KyraRpgEngine *_vm;
+ Screen *_screen;
+
+ char *_table1;
+ char *_table2;
+};
+
+} // End of namespace Kyra
+
+#endif
+
+#endif // ENABLE_EOB || ENABLE_LOL