aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/text
diff options
context:
space:
mode:
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