diff options
Diffstat (limited to 'engines/kyra/text')
-rw-r--r-- | engines/kyra/text/text.cpp | 330 | ||||
-rw-r--r-- | engines/kyra/text/text.h | 81 | ||||
-rw-r--r-- | engines/kyra/text/text_hof.cpp | 672 | ||||
-rw-r--r-- | engines/kyra/text/text_hof.h | 52 | ||||
-rw-r--r-- | engines/kyra/text/text_lok.cpp | 396 | ||||
-rw-r--r-- | engines/kyra/text/text_lol.cpp | 348 | ||||
-rw-r--r-- | engines/kyra/text/text_lol.h | 70 | ||||
-rw-r--r-- | engines/kyra/text/text_mr.cpp | 894 | ||||
-rw-r--r-- | engines/kyra/text/text_mr.h | 52 | ||||
-rw-r--r-- | engines/kyra/text/text_rpg.cpp | 739 | ||||
-rw-r--r-- | engines/kyra/text/text_rpg.h | 115 |
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 |