diff options
Diffstat (limited to 'engines/agi/text.cpp')
-rw-r--r-- | engines/agi/text.cpp | 1422 |
1 files changed, 911 insertions, 511 deletions
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 16c8284ce0..7c8594b62a 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -21,496 +21,1021 @@ */ #include "agi/agi.h" -#include "agi/sprite.h" // for commit_both() +#include "agi/sprite.h" // for commit_both() #include "agi/graphics.h" #include "agi/keyboard.h" +#include "agi/text.h" +#include "agi/systemui.h" +#include "agi/words.h" +#ifdef __DS__ +#include "wordcompletion.h" +#endif namespace Agi { -void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff, - int len, int fg, int bg, bool checkerboard) { - int x1, y1; - int maxx, minx, ofoff; - int update; - // Note: Must be unsigned to use AGDS cyrillic characters! -#ifdef __DS__ - // On the DS, a compiler bug causes the text to render incorrectly, because - // GCC tries to optimisie out writes to this pointer (tested on DevkitARM v19b and v20) - // Making this pointer volatile fixes this. - volatile const unsigned char *m; -#else - const unsigned char *m; -#endif +TextMgr::TextMgr(AgiEngine *vm, Words *words, GfxMgr *gfx) { + _vm = vm; + _words = words; + _gfx = gfx; - // kludge! - update = 1; - if (l == 2) { - update = l = 0; - } + memset(&_messageState, 0, sizeof(_messageState)); + _textPos.row = 0; + _textPos.column = 0; + _reset_Column = 0; - // FR: strings with len == 1 were not printed - if (len == 1) { - _gfx->putTextCharacter(l, xoff + foff, yoff, *msg, fg, bg, checkerboard, _fontData); - maxx = 1; - minx = 0; - ofoff = foff; - y1 = 0; // Check this - } else { - maxx = 0; - minx = GFX_WIDTH; - ofoff = foff; + charAttrib_Set(15, 0); - for (m = (const unsigned char *)msg, x1 = y1 = 0; *m; m++) { + _messageState.wanted_TextPos.row = -1; + _messageState.wanted_TextPos.column = -1; + _messageState.wanted_Text_Width = -1; - // Note: there were extra checks for *m being a cursor character - // here (1, 2 or 3), which have been removed, as the cursor - // character is no longer printed via this function. - if (*m >= 0x20) { - int ypos = (y1 * CHAR_LINES) + yoff; + _textPosArrayCount = 0; + memset(&_textPosArray, 0, sizeof(_textPosArray)); + _textAttribArrayCount = 0; + memset(&_textAttribArray, 0, sizeof(_textAttribArray)); - if ((x1 != (len - 1) || x1 == 39) && (ypos <= (GFX_HEIGHT - CHAR_LINES))) { - int xpos = (x1 * CHAR_COLS) + xoff + foff; + _inputEditEnabled = false; + _inputCursorChar = 0; - if (xpos >= GFX_WIDTH) - continue; + _statusEnabled = false; + _statusRow = 0; - _gfx->putTextCharacter(l, xpos, ypos, *m, fg, bg, checkerboard, _fontData); + _promptRow = 0; + promptDisable(); + promptReset(); - if (x1 > maxx) - maxx = x1; - if (x1 < minx) - minx = x1; - } + _inputStringRow = 0; + _inputStringColumn = 0; + _inputStringEntered = false; + _inputStringMaxLen = 0; + _inputStringCursorPos = 0; + _inputString[0] = 0; - x1++; + configureScreen(2); - // Change line if we've reached the end of this one, unless the next - // character is a new line itself, or the end of the string - if (x1 == len && m[1] != '\n' && m[1] != 0) { - y1++; - x1 = foff = 0; - } + _messageBoxCancelled = false; +} + +TextMgr::~TextMgr() { +} + +void TextMgr::init(SystemUI *systemUI) { + _systemUI = systemUI; +} + +void TextMgr::configureScreen(uint16 row_Min) { + _window_Row_Min = row_Min; + _window_Row_Max = row_Min + 21; + + // forward data to GfxMgr as well + _gfx->setRenderStartOffset(row_Min * FONT_DISPLAY_HEIGHT); +} +uint16 TextMgr::getWindowRowMin() { + return _window_Row_Min; +} + +void TextMgr::dialogueOpen() { + _messageState.dialogue_Open = true; +} + +void TextMgr::dialogueClose() { + _messageState.dialogue_Open = false; +} + +void TextMgr::charPos_Clip(int16 &row, int16 &column) { + row = CLIP<int16>(row, 0, FONT_ROW_CHARACTERS - 1); + column = CLIP<int16>(column, 0, FONT_COLUMN_CHARACTERS - 1); +} + +void TextMgr::charPos_Set(int16 row, int16 column) { + _textPos.row = row; + _textPos.column = column; +} + +void TextMgr::charPos_Set(TextPos_Struct *posPtr) { + _textPos.row = posPtr->row; + _textPos.column = posPtr->column; +} + +void TextMgr::charPos_Get(int16 &row, int16 &column) { + row = _textPos.row; + column = _textPos.column; +} + +void TextMgr::charPos_Get(TextPos_Struct *posPtr) { + posPtr->row = _textPos.row; + posPtr->column = _textPos.column; +} + +void TextMgr::charPos_Push() { + if (_textPosArrayCount < TEXTPOSARRAY_MAX) { + charPos_Get(&_textPosArray[_textPosArrayCount]); + _textPosArrayCount++; + } +} + +void TextMgr::charPos_Pop() { + if (_textPosArrayCount > 0) { + _textPosArrayCount--; + charPos_Set(&_textPosArray[_textPosArrayCount]); + } +} + +void TextMgr::charPos_SetInsideWindow(int16 windowRow, int16 windowColumn) { + if (!_messageState.window_Active) + return; + + _textPos.row = _messageState.textPos.row + windowRow; + _textPos.column = _messageState.textPos.column + windowColumn; +} + +static byte charAttrib_CGA_Conversion[] = { + 0, 1, 1, 1, 2, 2, 2, 3, 3, 1, 1, 1, 2, 2, 2 +}; + +void TextMgr::charAttrib_Set(byte foreground, byte background) { + _textAttrib.foreground = foreground; + _textAttrib.background = calculateTextBackground(background); + + if (!_vm->_game.gfxMode) { + // Text-mode: + // just use the given colors directly + _textAttrib.combinedForeground = foreground; + _textAttrib.combinedBackground = background; + } else { + // Graphics-mode: + switch (_vm->_renderMode) { + case Common::kRenderCGA: + // CGA + if (background) { + _textAttrib.combinedForeground = 3; + _textAttrib.combinedBackground = 8; // enable invert of colors } else { - if (m[1] != 0) { - y1++; - x1 = foff = 0; + if (foreground > 14) { + _textAttrib.combinedForeground = 3; + } else { + _textAttrib.combinedForeground = charAttrib_CGA_Conversion[foreground & 0x0F]; } + _textAttrib.combinedBackground = 0; + } + break; + default: + // EGA-handling: + if (background) { + _textAttrib.combinedForeground = 15; + _textAttrib.combinedBackground = 8; // enable invert of colors + } else { + _textAttrib.combinedForeground = foreground; + _textAttrib.combinedBackground = 0; } + break; } } +} - if (l) - return; - - if (maxx < minx) - return; +byte TextMgr::charAttrib_GetForeground() { + return _textAttrib.foreground; +} +byte TextMgr::charAttrib_GetBackground() { + return _textAttrib.background; +} - maxx *= CHAR_COLS; - minx *= CHAR_COLS; +void TextMgr::charAttrib_Push() { + if (_textAttribArrayCount < TEXTATTRIBARRAY_MAX) { + memcpy(&_textAttribArray[_textAttribArrayCount], &_textAttrib, sizeof(_textAttrib)); + _textAttribArrayCount++; + } +} - if (update) { - _gfx->scheduleUpdate(foff + xoff + minx, yoff, ofoff + xoff + maxx + CHAR_COLS - 1, - yoff + y1 * CHAR_LINES + CHAR_LINES + 1); +void TextMgr::charAttrib_Pop() { + if (_textAttribArrayCount > 0) { + _textAttribArrayCount--; + memcpy(&_textAttrib, &_textAttribArray[_textAttribArrayCount], sizeof(_textAttrib)); + } +} - // Making synchronous text updates reduces CPU load - // when updating status line and input area - _gfx->doUpdate(); +byte TextMgr::calculateTextBackground(byte background) { + if ((_vm->_game.gfxMode) && (background)) { + return 15; // interpreter sets 0xFF, but drawClearCharacter() would use upper 4 bits by shift } + return 0; } -// -// len is in characters, not pixels!! -// -void AgiEngine::blitTextbox(const char *p, int y, int x, int len) { - // if x | y = -1, then center the box - int xoff, yoff, lin, h, w; - char *msg, *m; +void TextMgr::display(int16 textNr, int16 textRow, int16 textColumn) { + const char *logicTextPtr = NULL; + char *processedTextPtr = NULL; - debugC(3, kDebugLevelText, "blitTextbox(): x=%d, y=%d, len=%d", x, y, len); - if (_game.window.active) - closeWindow(); + charPos_Push(); + charPos_Set(textRow, textColumn); - if (x == 0 && y == 0 && len == 0) - x = y = -1; + if (textNr >= 1 && textNr <= _vm->_game._curLogic->numTexts) { + logicTextPtr = _vm->_game._curLogic->texts[textNr - 1]; + processedTextPtr = stringPrintf(logicTextPtr); + processedTextPtr = stringWordWrap(processedTextPtr, 40); + displayText(processedTextPtr); - if (len <= 0) - len = 30; + // Signal, that non-blocking text is shown at the moment + if (textRow > 0) { + // only signal, when it's not the status line (kq3) + _vm->nonBlockingText_IsShown(); + } + } + charPos_Pop(); +} - xoff = x * CHAR_COLS; - yoff = y * CHAR_LINES; +void TextMgr::displayTextInsideWindow(const char *textPtr, int16 windowRow, int16 windowColumn) { + int16 textRow = 0; + int16 textColumn = 0; - m = msg = wordWrapString(agiSprintf(p), &len); + if (!_messageState.window_Active) + return; + + charPos_Push(); + textRow = _messageState.textPos.row + windowRow; + textColumn = _messageState.textPos.column + windowColumn; + charPos_Set(textRow, textColumn); + displayText(textPtr); + charPos_Pop(); +} + +void TextMgr::displayText(const char *textPtr, bool disabledLook) { + const char *curTextPtr = textPtr; + byte curCharacter = 0; + + while (1) { + curCharacter = *curTextPtr; + if (!curCharacter) + break; - for (lin = 1; *m; m++) { - // Test \r for MacOS 8 - if (*m == '\n' || *m == '\r') - lin++; + curTextPtr++; + displayCharacter(curCharacter, disabledLook); } +} - if (lin * CHAR_LINES > GFX_HEIGHT) - lin = (GFX_HEIGHT / CHAR_LINES); +void TextMgr::displayCharacter(byte character, bool disabledLook) { + TextPos_Struct charCurPos; - w = (len + 2) * CHAR_COLS; - h = (lin + 2) * CHAR_LINES; + charPos_Get(&charCurPos); - if (xoff < 0) - xoff = (GFX_WIDTH - w - CHAR_COLS) / 2; - else - xoff -= CHAR_COLS; + switch (character) { + case 0x08: // backspace + if (charCurPos.column) { + charCurPos.column--; + } else if (charCurPos.row > 21) { + charCurPos.column = (FONT_COLUMN_CHARACTERS - 1); + charCurPos.row--; + } + clearBlock(charCurPos.row, charCurPos.column, charCurPos.row, charCurPos.column, _textAttrib.background); + charPos_Set(&charCurPos); + break; - if (yoff < 0) - yoff = (GFX_HEIGHT - 3 * CHAR_LINES - h) / 2; + case 0x0D: + case 0x0A: // CR/LF + if (charCurPos.row < (FONT_ROW_CHARACTERS - 1)) + charCurPos.row++; + charCurPos.column = _reset_Column; + charPos_Set(&charCurPos); + break; + default: + // ch_attrib(state.text_comb, conversion); + _gfx->drawCharacter(charCurPos.row, charCurPos.column, character, _textAttrib.combinedForeground, _textAttrib.combinedBackground, disabledLook); + + charCurPos.column++; + if (charCurPos.column <= (FONT_COLUMN_CHARACTERS - 1)) { + charPos_Set(&charCurPos); + } else { + displayCharacter(0x0D); // go to next line + } + } +} + +void TextMgr::print(int16 textNr) { + const char *logicTextPtr = NULL; + if (textNr >= 1 && textNr <= _vm->_game._curLogic->numTexts) { + logicTextPtr = _vm->_game._curLogic->texts[textNr - 1]; + messageBox(logicTextPtr); + } +} - drawWindow(xoff, yoff, xoff + w - 1, yoff + h - 1); +void TextMgr::printAt(int16 textNr, int16 textPos_Row, int16 textPos_Column, int16 text_Width) { + // Sierra didn't do clipping, we do it for security + charPos_Clip(textPos_Row, textPos_Column); - printText2(2, msg, 0, CHAR_COLS + xoff, CHAR_LINES + yoff, - len + 1, MSG_BOX_TEXT, MSG_BOX_COLOR); + _messageState.wanted_TextPos.row = textPos_Row; + _messageState.wanted_TextPos.column = textPos_Column; + _messageState.wanted_Text_Width = text_Width; - free(msg); + if (_messageState.wanted_Text_Width == 0) { + _messageState.wanted_Text_Width = 30; + } + print(textNr); - _gfx->doUpdate(); + _messageState.wanted_TextPos.row = -1; + _messageState.wanted_TextPos.column = -1; + _messageState.wanted_Text_Width = -1; } -void AgiEngine::eraseTextbox() { - if (!_game.window.active) { - debugC(3, kDebugLevelText, "eraseTextbox(): no window active"); - return; +bool TextMgr::messageBox(const char *textPtr) { + drawMessageBox(textPtr); + + if (_vm->getFlag(VM_FLAG_OUTPUT_MODE)) { + // non-blocking window + _vm->setFlag(VM_FLAG_OUTPUT_MODE, false); + + // Signal, that non-blocking text is shown at the moment + _vm->nonBlockingText_IsShown(); + return true; } - debugC(4, kDebugLevelText, "eraseTextbox(): x1=%d, y1=%d, x2=%d, y2=%d", _game.window.x1, - _game.window.y1, _game.window.x2, _game.window.y2); + // blocking window + _vm->_noSaveLoadAllowed = true; + _vm->nonBlockingText_Forget(); - _gfx->restoreBlock(_game.window.x1, _game.window.y1, - _game.window.x2, _game.window.y2, _game.window.buffer); + // timed window + uint32 windowTimer = _vm->getVar(VM_VAR_WINDOW_AUTO_CLOSE_TIMER); + debugC(3, kDebugLevelText, "blocking window v21=%d", windowTimer); - free(_game.window.buffer); - _game.window.active = false; + windowTimer = windowTimer * 10; // 1 = 0.5 seconds + _messageBoxCancelled = false; - _gfx->doUpdate(); -} + _vm->inGameTimerResetPassedCycles(); + _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_MESSAGEBOX); + do { + _vm->processAGIEvents(); + _vm->inGameTimerUpdate(); -/* - * Public functions - */ + if (windowTimer > 0) { + if (_vm->inGameTimerGetPassedCycles() >= windowTimer) { + // Timer reached, close automatically + _vm->cycleInnerLoopInactive(); + } + } + } while (_vm->cycleInnerLoopIsActive() && !(_vm->shouldQuit() || _vm->_restartGame)); -/** - * Print text in the AGI engine screen. - */ -void AgiEngine::printText(const char *msg, int f, int x, int y, int len, int fg, int bg, bool checkerboard) { - f *= CHAR_COLS; - x *= CHAR_COLS; - y *= CHAR_LINES; + _vm->inGameTimerResetPassedCycles(); - debugC(4, kDebugLevelText, "printText(): %s, %d, %d, %d, %d, %d, %d", msg, f, x, y, len, fg, bg); - printText2(0, agiSprintf(msg), f, x, y, len, fg, bg, checkerboard); -} + _vm->setVar(VM_VAR_WINDOW_AUTO_CLOSE_TIMER, 0); -/** - * Print text in the AGI engine console. - */ -void AgiEngine::printTextConsole(const char *msg, int x, int y, int len, int fg, int bg) { - x *= CHAR_COLS; - y *= 10; + closeWindow(); + _vm->_noSaveLoadAllowed = false; - debugC(4, kDebugLevelText, "printTextConsole(): %s, %d, %d, %d, %d, %d", msg, x, y, len, fg, bg); - printText2(1, msg, 0, x, y, len, fg, bg); + if (_messageBoxCancelled) + return false; + return true; } -/** - * Wrap text line to the specified width. - * @param str String to wrap. - * @param len Length of line. - * - * Based on GBAGI implementation with permission from the author - */ -char *AgiEngine::wordWrapString(const char *s, int *len) { - char *outStr, *msgBuf; - int maxWidth = *len; - const char *pWord; - int lnLen, wLen; +void TextMgr::messageBox_KeyPress(uint16 newKey) { + switch (newKey) { + case AGI_KEY_ENTER: + _vm->cycleInnerLoopInactive(); // exit messagebox-loop + break; + case AGI_KEY_ESCAPE: + _messageBoxCancelled = true; + _vm->cycleInnerLoopInactive(); // exit messagebox-loop + break; + case AGI_MOUSE_BUTTON_LEFT: { + // Check, if mouse cursor is within message box + // If it is, take the click as ENTER. + // That's what AGI on Amiga + Apple IIgs did. + // On Atari ST at least via emulator it seems that the mouse cursor froze when messageboxes were diplayed. + if (isMouseWithinMessageBox()) { + _vm->cycleInnerLoopInactive(); // exit messagebox-loop + } + break; + } + default: + break; + } +} - // Allocate some extra space for the final buffer, as - // outStr may end up being longer than s - // 26 = 200 (screen height) / 8 (font height) + 1 - msgBuf = outStr = (char *)malloc(strlen(s) + 26); +void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 wantedWidth, bool forcedWidth) { + int16 maxWidth = wantedWidth; + int16 startingRow = 0; + char *processedTextPtr; - int msgWidth = 0; + if (_messageState.window_Active) { + closeWindow(); + } + charAttrib_Push(); + charPos_Push(); + charAttrib_Set(0, 15); + + if ((_messageState.wanted_Text_Width == -1) && (maxWidth == 0)) { + maxWidth = 30; + } else if (_messageState.wanted_Text_Width != -1) { + maxWidth = _messageState.wanted_Text_Width; + } - lnLen = 0; + processedTextPtr = stringPrintf(textPtr); - while (*s) { - pWord = s; + int16 calculatedWidth = 0; + int16 calculatedHeight = 0; - while (*s != '\0' && *s != ' ' && *s != '\n' && *s != '\r') - s++; + processedTextPtr = stringWordWrap(processedTextPtr, maxWidth, &calculatedWidth, &calculatedHeight); + _messageState.textSize_Width = calculatedWidth; + _messageState.textSize_Height = calculatedHeight; - wLen = (int)(s - pWord); + _messageState.printed_Height = _messageState.textSize_Height; - if (wLen && *s == '\n' && s[-1] == ' ') - wLen--; + // Caller wants to force specified width/height? set it + if (forcedHeight) + _messageState.textSize_Height = forcedHeight; + + if (forcedWidth) { + if (wantedWidth) + _messageState.textSize_Width = wantedWidth; + } - if (wLen + lnLen >= maxWidth) { - // Check if outStr isn't msgBuf. If this is the case, outStr hasn't advanced - // yet, so no output has been written yet - if (outStr != msgBuf) { - if (outStr[-1] == ' ') - outStr[-1] = '\n'; - else - *outStr++ = '\n'; - } + if (_messageState.wanted_TextPos.row == -1) { + startingRow = ((HEIGHT_MAX - _messageState.textSize_Height - 1) / 2) + 1; + } else { + startingRow = _messageState.wanted_TextPos.row; + } + _messageState.textPos.row = startingRow + _window_Row_Min; + _messageState.textPos_Edge.row = _messageState.textSize_Height + _messageState.textPos.row - 1; + + if (_messageState.wanted_TextPos.column == -1) { + _messageState.textPos.column = (FONT_COLUMN_CHARACTERS - _messageState.textSize_Width) / 2; + } else { + _messageState.textPos.column = _messageState.wanted_TextPos.column; + } + _messageState.textPos_Edge.column = _messageState.textPos.column + _messageState.textSize_Width; + + charPos_Set(_messageState.textPos.row, _messageState.textPos.column); + + _messageState.backgroundSize_Width = (_messageState.textSize_Width * FONT_VISUAL_WIDTH) + 10; + _messageState.backgroundSize_Height = (_messageState.textSize_Height * FONT_VISUAL_HEIGHT) + 10; + _messageState.backgroundPos_x = (_messageState.textPos.column * FONT_VISUAL_WIDTH) - 5; + _messageState.backgroundPos_y = (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4; + + // Hardcoded colors: white background and red lines + _gfx->drawBox(_messageState.backgroundPos_x, _messageState.backgroundPos_y, _messageState.backgroundSize_Width, _messageState.backgroundSize_Height, 15, 4); + + _messageState.window_Active = true; + + _reset_Column = _messageState.textPos.column; + displayText(processedTextPtr); + _reset_Column = 0; + + charPos_Pop(); + charAttrib_Pop(); + + _messageState.dialogue_Open = true; +} - lnLen = 0; +void TextMgr::getMessageBoxInnerDisplayDimensions(int16 &x, int16 &y, int16 &width, int16 &height) { + if (!_messageState.window_Active) + return; + + y = _messageState.textPos.row * FONT_DISPLAY_HEIGHT; + x = _messageState.textPos.column * FONT_DISPLAY_WIDTH; + width = _messageState.textSize_Width * FONT_DISPLAY_WIDTH; + height = _messageState.textSize_Height * FONT_DISPLAY_HEIGHT; +} - while (wLen >= maxWidth) { - msgWidth = maxWidth; +bool TextMgr::isMouseWithinMessageBox() { + // Find out, where current mouse cursor actually is + int16 mouseY = _vm->_mouse.pos.y; + int16 mouseX = _vm->_mouse.pos.x; - memcpy(outStr, pWord, maxWidth); + if (_messageState.window_Active) { + _vm->adjustPosToGameScreen(mouseX, mouseY); - wLen -= maxWidth; - outStr += maxWidth; - pWord += maxWidth; - *outStr++ = '\n'; + if ((mouseX >= _messageState.backgroundPos_x) && (mouseX <= (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) { + if ((mouseY >= _messageState.backgroundPos_y - _messageState.backgroundSize_Height) && (mouseY <= (_messageState.backgroundPos_y))) { + return true; } } + } + return false; +} - if (wLen) { - memcpy(outStr, pWord, wLen); - outStr += wLen; - } - lnLen += wLen+1; +void TextMgr::closeWindow() { + if (_messageState.window_Active) { + _gfx->render_Block(_messageState.backgroundPos_x, _messageState.backgroundPos_y, _messageState.backgroundSize_Width, _messageState.backgroundSize_Height); + } + _messageState.dialogue_Open = false; + _messageState.window_Active = false; +} - if (lnLen > msgWidth) { - msgWidth = lnLen; +void TextMgr::statusRow_Set(int16 row) { + _statusRow = row; +} +int16 TextMgr::statusRow_Get() { + return _statusRow; +} - if (*s == '\0' || *s == ' ' || *s == '\n' || *s == '\r') - msgWidth--; - } +void TextMgr::statusEnable() { + _statusEnabled = true; +} +void TextMgr::statusDisable() { + _statusEnabled = false; +} +bool TextMgr::statusEnabled() { + return _statusEnabled; +} + +void TextMgr::statusDraw() { + char *statusTextPtr = NULL; + + charAttrib_Push(); + charPos_Push(); + + if (_statusEnabled) { + clearLine(_statusRow, 15); - if (*s == '\n') - lnLen = 0; + charAttrib_Set(0, 15); + charPos_Set(_statusRow, 1); + statusTextPtr = stringPrintf(_systemUI->getStatusTextScore()); + displayText(statusTextPtr); - if (*s) - *outStr++ = *s++; + charPos_Set(_statusRow, 30); + if (_vm->getFlag(VM_FLAG_SOUND_ON)) { + statusTextPtr = stringPrintf(_systemUI->getStatusTextSoundOn()); + } else { + statusTextPtr = stringPrintf(_systemUI->getStatusTextSoundOff()); + } + displayText(statusTextPtr); } - *outStr = '\0'; - *len = msgWidth; - return msgBuf; + charPos_Pop(); + charAttrib_Pop(); } -/** - * Remove existing window, if any. - */ -void AgiEngine::closeWindow() { - debugC(4, kDebugLevelText, "closeWindow()"); +void TextMgr::statusClear() { + clearLine(_statusRow, 0); +} - _sprites->eraseBoth(); - eraseTextbox(); // remove window, if any - _sprites->blitBoth(); - _sprites->commitBoth(); // redraw sprites - _game.hasWindow = false; +void TextMgr::clearLine(int16 row, byte color) { + clearLines(row, row, color); } -/** - * Display a message box. - * This function displays the specified message in a text box - * centered in the screen and waits until a key is pressed. - * @param p The text to be displayed - */ -int AgiEngine::messageBox(const char *s) { - int k; - - _sprites->eraseBoth(); - blitTextbox(s, -1, -1, -1); - _sprites->blitBoth(); - k = waitKey(); - debugC(4, kDebugLevelText, "messageBox(): wait_key returned %02x", k); - closeWindow(); +void TextMgr::clearLines(int16 row_Upper, int16 row_Lower, byte color) { + clearBlock(row_Upper, 0, row_Lower, FONT_COLUMN_CHARACTERS - 1, color); +} - return k; +void TextMgr::clearBlock(int16 row_Upper, int16 column_Upper, int16 row_Lower, int16 column_Lower, byte color) { + // Sierra didn't do clipping of the coordinates, we do it for security + // and b/c there actually are some games, that call commands with invalid coordinates + // see cmdClearLines() comments. + charPos_Clip(row_Upper, column_Upper); + charPos_Clip(row_Lower, column_Lower); + + int16 x = column_Upper * FONT_DISPLAY_WIDTH; + int16 y = row_Upper * FONT_DISPLAY_HEIGHT; + int16 width = (column_Lower + 1 - column_Upper) * FONT_DISPLAY_WIDTH; + int16 height = (row_Lower + 1 - row_Upper) * FONT_DISPLAY_HEIGHT; + + y = y + height - 1; // drawDisplayRect wants lower Y-coordinate + _gfx->drawDisplayRect(x, y, width, height, color); } -/** - * Display a message box with buttons. - * This function displays the specified message in a text box - * centered in the screen and waits until a button is pressed. - * @param p The text to be displayed - * @param b NULL-terminated list of button labels - */ -int AgiEngine::selectionBox(const char *m, const char **b) { - int numButtons = 0; - int x, y, i, s; - int bx[5], by[5]; +void TextMgr::clearBlockInsideWindow(int16 windowRow, int16 windowColumn, int16 width, byte color) { + int16 row; + int16 column; + if (!_messageState.window_Active) + return; - _noSaveLoadAllowed = true; + row = _messageState.textPos.row + windowRow; + column = _messageState.textPos.column + windowColumn; + clearBlock(row, column, row, column + width - 1, color); +} - _sprites->eraseBoth(); - blitTextbox(m, -1, -1, -1); +bool TextMgr::inputGetEditStatus() { + return _inputEditEnabled; +} - x = _game.window.x1 + 5 * CHAR_COLS / 2; - y = _game.window.y2 - 5 * CHAR_LINES / 2; - s = _game.window.x2 - _game.window.x1 + 1 - 5 * CHAR_COLS; - debugC(3, kDebugLevelText, "selectionBox(): s = %d", s); +void TextMgr::inputEditOn() { + if (!_inputEditEnabled) { + _inputEditEnabled = true; + if (_inputCursorChar) { + displayCharacter(0x08); // backspace + } + } +} - // Automatically position buttons - for (i = 0; b[i]; i++) { - numButtons++; - s -= CHAR_COLS * strlen(b[i]); +void TextMgr::inputEditOff() { + if (_inputEditEnabled) { + _inputEditEnabled = false; + if (_inputCursorChar) { + displayCharacter(_inputCursorChar); + } } +} + +void TextMgr::inputSetCursorChar(int16 cursorChar) { + _inputCursorChar = cursorChar; +} + +byte TextMgr::inputGetCursorChar() { + return _inputCursorChar; +} + +void TextMgr::promptRow_Set(int16 row) { + _promptRow = row; +} - if (i > 1) { - debugC(3, kDebugLevelText, "selectionBox(): s / %d = %d", i - 1, s / (i - 1)); - s /= (i - 1); +int16 TextMgr::promptRow_Get() { + return _promptRow; +} + +void TextMgr::promptReset() { + _promptCursorPos = 0; + memset(_prompt, 0, sizeof(_prompt)); + memset(_promptPrevious, 0, sizeof(_promptPrevious)); +} + +void TextMgr::promptEnable() { + _promptEnabled = true; +} +void TextMgr::promptDisable() { + _promptEnabled = false; +} +bool TextMgr::promptIsEnabled() { + return _promptEnabled; +} + +void TextMgr::promptKeyPress(uint16 newKey) { + int16 maxChars = 0; + int16 scriptsInputLen = _vm->getVar(VM_VAR_MAX_INPUT_CHARACTERS); + + if (_messageState.dialogue_Open) { + maxChars = TEXT_STRING_MAX_SIZE - 4; } else { - x += s / 2; + maxChars = TEXT_STRING_MAX_SIZE - strlen(_vm->_game.strings[0]); // string 0 is the prompt string prefix } - for (i = 0; b[i]; i++) { - bx[i] = x; - by[i] = y; - x += CHAR_COLS * strlen(b[i]) + s; - } + if (_promptCursorPos) + maxChars--; - _sprites->blitBoth(); + if (scriptsInputLen < maxChars) + maxChars = scriptsInputLen; - clearKeyQueue(); + inputEditOn(); - AllowSyntheticEvents on(this); + switch (newKey) { + case AGI_KEY_BACKSPACE: { + if (_promptCursorPos) { + _promptCursorPos--; + _prompt[_promptCursorPos] = 0; + displayCharacter(newKey); - debugC(4, kDebugLevelText, "selectionBox(): waiting..."); - int key, active = 0; - int rc = -1; - while (rc == -1 && !(shouldQuit() || _restartGame)) { - for (i = 0; b[i]; i++) - _gfx->drawCurrentStyleButton(bx[i], by[i], b[i], i == active, false, i == 0); + promptRememberForAutoComplete(); + } + break; + } + case 0x0A: // LF + break; + case AGI_KEY_ENTER: { + if (_promptCursorPos) { + // something got entered? -> process it and pass it to the scripts + promptRememberForAutoComplete(true); + + memcpy(&_promptPrevious, &_prompt, sizeof(_prompt)); + // parse text + _vm->_words->parseUsingDictionary((char *)&_prompt); + + _promptCursorPos = 0; + _prompt[0] = 0; + promptRedraw(); + } + break; + } + default: + if (maxChars > _promptCursorPos) { + bool acceptableInput = false; + + // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.) + // In text-mode this sort of worked at least with the DOS interpreter + // but as soon as invalid characters were used in graphics mode they weren't properly shown + switch (_vm->getLanguage()) { + case Common::RU_RUS: + if (newKey >= 0x20) + acceptableInput = true; + break; + default: + if ((newKey >= 0x20) && (newKey <= 0x7f)) + acceptableInput = true; + break; + } - pollTimer(); - key = doPollKeyboard(); - switch (key) { - case KEY_ENTER: - rc = active; - debugC(4, kDebugLevelText, "selectionBox(): Button pressed: %d", rc); - break; - case KEY_RIGHT: - active++; - if (active >= numButtons) - active = 0; - break; - case KEY_LEFT: - active--; - if (active < 0) - active = numButtons - 1; - break; - case BUTTON_LEFT: - for (i = 0; b[i]; i++) { - if (_gfx->testButton(bx[i], by[i], b[i])) { - rc = active = i; - debugC(4, kDebugLevelText, "selectionBox(): Button pressed: %d", rc); - break; - } + if (acceptableInput) { + _prompt[_promptCursorPos] = newKey; + _promptCursorPos++; + _prompt[_promptCursorPos] = 0; + displayCharacter(newKey); + + promptRememberForAutoComplete(); } - break; - case 0x09: // Tab - debugC(3, kDebugLevelText, "selectionBox(): Focus change"); - active++; - active %= i; - break; } - _gfx->doUpdate(); + break; + } - if (key == KEY_ESCAPE) - break; + inputEditOff(); +} + +void TextMgr::promptCancelLine() { + while (_promptCursorPos) { + promptKeyPress(0x08); // Backspace until prompt is empty } +} - closeWindow(); - debugC(2, kDebugLevelText, "selectionBox(): Result = %d", rc); +void TextMgr::promptEchoLine() { + int16 previousLen = strlen((char *)_promptPrevious); + + if (_promptCursorPos < previousLen) { + inputEditOn(); - _noSaveLoadAllowed = false; + while (_promptPrevious[_promptCursorPos]) { + promptKeyPress(_promptPrevious[_promptCursorPos]); + } + promptRememberForAutoComplete(); - return rc; + inputEditOff(); + } } -/** - * - */ -int AgiEngine::print(const char *p, int lin, int col, int len) { - if (p == NULL) - return 0; +void TextMgr::promptRedraw() { + char *textPtr = nullptr; - debugC(4, kDebugLevelText, "print(): lin = %d, col = %d, len = %d", lin, col, len); + if (_promptEnabled) { + inputEditOn(); + clearLine(_promptRow, _textAttrib.background); + charPos_Set(_promptRow, 0); + // agi_printf(str_wordwrap(msg, state.string[0], 40) ); - blitTextbox(p, lin, col, len); + textPtr = _vm->_game.strings[0]; + textPtr = stringPrintf(textPtr); + textPtr = stringWordWrap(textPtr, 40); - if (getflag(fOutputMode)) { - // non-blocking window - setflag(fOutputMode, false); - return 1; + displayText(textPtr); + displayText((char *)&_prompt); + inputEditOff(); } +} - // blocking +// for AGI1 +void TextMgr::promptClear() { + clearLine(_promptRow, _textAttrib.background); +} - _noSaveLoadAllowed = true; +void TextMgr::promptRememberForAutoComplete(bool entered) { +#ifdef __DS__ + DS::findWordCompletions((char *)_prompt); +#endif +} - if (_game.vars[vWindowReset] == 0) { - int k; - setvar(vKey, 0); - k = waitKey(); - closeWindow(); +bool TextMgr::stringWasEntered() { + return _inputStringEntered; +} + +void TextMgr::stringSet(const char *text) { + strncpy((char *)_inputString, text, sizeof(_inputString)); + _inputString[sizeof(_inputString) - 1] = 0; // terminator +} + +void TextMgr::stringPos_Get(int16 &row, int16 &column) { + row = _inputStringRow; + column = _inputStringColumn; +} +int16 TextMgr::stringGetMaxLen() { + return _inputStringMaxLen; +} + +void TextMgr::stringEdit(int16 stringMaxLen) { + int16 inputStringLen = strlen((const char *)_inputString); - _noSaveLoadAllowed = false; + // Remember current position for predictive dialog + _inputStringRow = _textPos.row; + _inputStringColumn = _textPos.column; - return k; + // Caller can set the input string + _inputStringCursorPos = 0; + while (_inputStringCursorPos < inputStringLen) { + displayCharacter(_inputString[_inputStringCursorPos]); + _inputStringCursorPos++; } - // timed window + // should never happen unless there is a coding glitch + assert(_inputStringCursorPos <= stringMaxLen); - debugC(3, kDebugLevelText, "f15==0, v21==%d => timed", getvar(21)); - _game.msgBoxTicks = getvar(vWindowReset) * 10; - setvar(vKey, 0); + _inputStringMaxLen = stringMaxLen; + _inputStringEntered = false; - _menuSelected = false; + inputEditOff(); do { - if (getflag(fRestoreJustRan)) - break; + _vm->processAGIEvents(); + } while (_vm->cycleInnerLoopIsActive() && !(_vm->shouldQuit() || _vm->_restartGame)); - if (_menuSelected) - break; + inputEditOn(); - mainCycle(); - if (_game.keypress == KEY_ENTER) { - debugC(4, kDebugLevelText, "KEY_ENTER"); - setvar(vWindowReset, 0); - _game.keypress = 0; - break; + // Forget non-blocking text, user was asked to enter something + _vm->nonBlockingText_Forget(); +} + +void TextMgr::stringKeyPress(uint16 newKey) { + inputEditOn(); + + switch (newKey) { + case 0x3: // ctrl-c + case 0x18: { // ctrl-x + // clear string + while (_inputStringCursorPos) { + _inputStringCursorPos--; + _inputString[_inputStringCursorPos] = 0; + displayCharacter(0x08); } - } while (_game.msgBoxTicks > 0 && !(shouldQuit() || _restartGame)); + break; + } - setvar(vWindowReset, 0); + case AGI_KEY_BACKSPACE: { + if (_inputStringCursorPos) { + _inputStringCursorPos--; + _inputString[_inputStringCursorPos] = 0; + displayCharacter(newKey); - closeWindow(); + stringRememberForAutoComplete(); + } + break; + } - _noSaveLoadAllowed = false; + case AGI_KEY_ENTER: { + stringRememberForAutoComplete(true); - return 0; + _inputStringEntered = true; + + _vm->cycleInnerLoopInactive(); // exit GetString-loop + break; + } + + case AGI_KEY_ESCAPE: { + _inputString[0] = 0; + _inputStringCursorPos = 0; + _inputStringEntered = false; + + _vm->cycleInnerLoopInactive(); // exit GetString-loop + break; + } + + default: + if (_inputStringMaxLen > _inputStringCursorPos) { + bool acceptableInput = false; + + // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.) + // In text-mode this sort of worked at least with the DOS interpreter + // but as soon as invalid characters were used in graphics mode they weren't properly shown + switch (_vm->getLanguage()) { + case Common::RU_RUS: + if (newKey >= 0x20) + acceptableInput = true; + break; + default: + if ((newKey >= 0x20) && (newKey <= 0x7f)) + acceptableInput = true; + break; + } + + if (acceptableInput) { + if ((_vm->_game.cycleInnerLoopType == CYCLE_INNERLOOP_GETSTRING) || ((newKey >= '0') && (newKey <= '9'))) { + // Additionally check for GETNUMBER-mode, if character is a number + // Sierra also did not do this + _inputString[_inputStringCursorPos] = newKey; + _inputStringCursorPos++; + _inputString[_inputStringCursorPos] = 0; + displayCharacter(newKey); + + stringRememberForAutoComplete(); + } + } + } + break; + } + + inputEditOff(); +} + +void TextMgr::stringRememberForAutoComplete(bool entered) { +#ifdef __DS__ + DS::findWordCompletions((char *)_inputString); +#endif } /** - * + * Wraps text line to the specified width. + * @param originalText String to wrap. + * @param maxWidth Length of line. */ -void AgiEngine::printStatus(const char *message, ...) { - va_list args; +char *TextMgr::stringWordWrap(const char *originalText, int16 maxWidth, int16 *calculatedWidthPtr, int16 *calculatedHeightPtr) { + static char resultWrappedBuffer[2000]; + int16 boxWidth = 0; + int16 boxHeight = 0; + int16 lineWidth = 0; // width of current line + + int16 lineWidthLeft = maxWidth; // width left of current line + + int16 wordStartPos = 0; + int16 wordLen = 0; + int16 curReadPos = 0; + int16 curWritePos = 0; + byte wordEndChar = 0; + + //memset(resultWrappedBuffer, 0, sizeof(resultWrappedBuffer)); for debugging + + // Good testcases: + // King's Quest 1 intro: the scrolling text is filled up with spaces, so that old lines are erased + // Apple IIgs restart system UI: spaces used to make the window larger + + while (originalText[curReadPos]) { + // Try to find out length of next word - va_start(args, message); + // If first character is a space, skip it, so that we process at least this space + if (originalText[curReadPos] == ' ') + curReadPos++; - Common::String x = Common::String::vformat(message, args); + while (originalText[curReadPos]) { + if (originalText[curReadPos] == ' ') + break; + if (originalText[curReadPos] == 0x0A) + break; + curReadPos++; + } + wordEndChar = originalText[curReadPos]; + + // Calculate word length + wordLen = curReadPos - wordStartPos; + + if (wordLen >= lineWidthLeft) { + // Not enough space left + if (wordLen > maxWidth) { + // Word way too long, split it in half + curReadPos = curReadPos - (wordLen - maxWidth); + wordLen = maxWidth; + } - va_end(args); + // Add new line + resultWrappedBuffer[curWritePos++] = 0x0A; + if (lineWidth > boxWidth) + boxWidth = lineWidth; + boxHeight++; lineWidth = 0; + lineWidthLeft = maxWidth; - debugC(4, kDebugLevelText, "fg=%d, bg=%d", STATUS_FG, STATUS_BG); - printText(x.c_str(), 0, 0, _game.lineStatus, 40, STATUS_FG, STATUS_BG); + // Reached absolute maximum? -> exit now + if (boxHeight >= HEIGHT_MAX) + break; + + // If first character right after the new line is a space, skip over it + if (wordLen) { + if (originalText[wordStartPos] == ' ') { + wordStartPos++; + wordLen--; + } + } + } + + // Copy current word over + memcpy(&resultWrappedBuffer[curWritePos], &originalText[wordStartPos], wordLen); + lineWidth += wordLen; + lineWidthLeft -= wordLen; + curWritePos += wordLen; + + if (wordEndChar == 0x0A) { + // original text had a new line, so force it + curReadPos++; + + resultWrappedBuffer[curWritePos++] = 0x0A; + if (lineWidth > boxWidth) + boxWidth = lineWidth; + boxHeight++; lineWidth = 0; + lineWidthLeft = maxWidth; + + // Reached absolute maximum? -> exit now + if (boxHeight >= HEIGHT_MAX) + break; + } + + wordStartPos = curReadPos; + } + + resultWrappedBuffer[curWritePos] = 0; + + if (curReadPos > 0) { + if (lineWidth > boxWidth) + boxWidth = lineWidth; + boxHeight++; + } + + if (calculatedWidthPtr) { + *calculatedWidthPtr = boxWidth; + } + if (calculatedHeightPtr) { + *calculatedHeightPtr = boxHeight; + } + return resultWrappedBuffer; } +// =============================================================== + static void safeStrcat(Common::String &p, const char *t) { if (t != NULL) p += t; @@ -523,31 +1048,31 @@ static void safeStrcat(Common::String &p, const char *t) { * @param s string containing the format specifier * @param n logic number */ -char *AgiEngine::agiSprintf(const char *s) { - static char agiSprintf_buf[768]; - Common::String p; +char *TextMgr::stringPrintf(const char *originalText) { + static char resultPrintfBuffer[2000]; + Common::String resultString; char z[16]; - debugC(3, kDebugLevelText, "logic %d, '%s'", _game.lognum, s); + debugC(3, kDebugLevelText, "logic %d, '%s'", _vm->_game.curLogicNr, originalText); - while (*s) { - switch (*s) { + while (*originalText) { + switch (*originalText) { case '%': - s++; - switch (*s++) { + originalText++; + switch (*originalText++) { int i; case 'v': - i = strtoul(s, NULL, 10); - while (*s >= '0' && *s <= '9') - s++; - sprintf(z, "%015i", getvar(i)); + i = strtoul(originalText, NULL, 10); + while (*originalText >= '0' && *originalText <= '9') + originalText++; + sprintf(z, "%015i", _vm->getVar(i)); i = 99; - if (*s == '|') { - s++; - i = strtoul(s, NULL, 10); - while (*s >= '0' && *s <= '9') - s++; + if (*originalText == '|') { + originalText++; + i = strtoul(originalText, NULL, 10); + while (*originalText >= '0' && *originalText <= '9') + originalText++; } if (i == 99) { @@ -558,173 +1083,48 @@ char *AgiEngine::agiSprintf(const char *s) { } else { i = 15 - i; } - safeStrcat(p, z + i); + safeStrcat(resultString, z + i); break; case '0': - i = strtoul(s, NULL, 10) - 1; - safeStrcat(p, objectName(i)); + i = strtoul(originalText, NULL, 10) - 1; + safeStrcat(resultString, _vm->objectName(i)); break; case 'g': - i = strtoul(s, NULL, 10) - 1; - safeStrcat(p, _game.logics[0].texts[i]); + i = strtoul(originalText, NULL, 10) - 1; + safeStrcat(resultString, _vm->_game.logics[0].texts[i]); break; case 'w': - i = strtoul(s, NULL, 10) - 1; - safeStrcat(p, _game.egoWords[i].word); + i = strtoul(originalText, NULL, 10) - 1; + safeStrcat(resultString, _vm->_words->getEgoWord(i)); break; case 's': - i = strtoul(s, NULL, 10); - safeStrcat(p, agiSprintf(_game.strings[i])); + i = strtoul(originalText, NULL, 10); + safeStrcat(resultString, stringPrintf(_vm->_game.strings[i])); break; case 'm': - i = strtoul(s, NULL, 10) - 1; - if (_game.logics[_game.lognum].numTexts > i) - safeStrcat(p, agiSprintf(_game.logics[_game.lognum].texts[i])); + i = strtoul(originalText, NULL, 10) - 1; + if (_vm->_game.logics[_vm->_game.curLogicNr].numTexts > i) + safeStrcat(resultString, stringPrintf(_vm->_game.logics[_vm->_game.curLogicNr].texts[i])); break; } - while (*s >= '0' && *s <= '9') - s++; + while (*originalText >= '0' && *originalText <= '9') + originalText++; break; case '\\': - s++; + originalText++; // FALL THROUGH default: - p += *s++; + resultString += *originalText++; break; } } - assert(p.size() < sizeof(agiSprintf_buf)); - strcpy(agiSprintf_buf, p.c_str()); - return agiSprintf_buf; -} - -/** - * Write the status line. - */ -void AgiEngine::writeStatus() { - char x[64]; - - if (_debug.statusline) { - printStatus("%3d(%03d) %3d,%3d(%3d,%3d) ", - getvar(0), getvar(1), _game.viewTable[0].xPos, - _game.viewTable[0].yPos, WIN_TO_PIC_X(_mouse.x), - WIN_TO_PIC_Y(_mouse.y)); - return; - } - - if (!_game.statusLine) { - clearLines(_game.lineStatus, _game.lineStatus, 0); - flushLines(_game.lineStatus, _game.lineStatus); - -#if 0 - // FIXME: Breaks wrist watch prompt in SQ2 - - // Clear the user input line as well when clearing the status line - // Fixes bug #1893564 - AGI: Texts messed out in Naturette 1 - clearLines(_game.lineUserInput, _game.lineUserInput, 0); - flushLines(_game.lineUserInput, _game.lineUserInput); -#endif - return; - } - - switch (getLanguage()) { - case Common::RU_RUS: - sprintf(x, " \x91\xe7\xa5\xe2: %i \xa8\xa7 %-3i", _game.vars[vScore], _game.vars[vMaxScore]); - printStatus("%-17s \x87\xa2\xe3\xaa:%s", x, getflag(fSoundOn) ? "\xa2\xaa\xab " : "\xa2\xeb\xaa\xab"); - break; - default: - sprintf(x, " Score:%i of %-3i", _game.vars[vScore], _game.vars[vMaxScore]); - printStatus("%-17s Sound:%s ", x, getflag(fSoundOn) ? "on " : "off"); - break; - } -} - -/** - * Print user input prompt. - */ -void AgiEngine::writePrompt() { - int l, fg, bg, pos; - int promptLength = strlen(agiSprintf(_game.strings[0])); - - if (!_game.inputEnabled || _game.inputMode != INPUT_NORMAL) - return; - - l = _game.lineUserInput; - fg = _game.colorFg; - bg = _game.colorBg; - pos = _game.cursorPos; - - debugC(4, kDebugLevelText, "erase line %d", l); - clearLines(l, l, _game.colorBg); - - debugC(4, kDebugLevelText, "prompt = '%s'", agiSprintf(_game.strings[0])); - printText(_game.strings[0], 0, 0, l, promptLength + 1, fg, bg); - printText((char *)_game.inputBuffer, 0, promptLength, l, pos + 1, fg, bg); - _gfx->printCharacter(pos + promptLength, l, _game.cursorChar, fg, bg); - - flushLines(l, l); - _gfx->doUpdate(); -} - -void AgiEngine::clearPrompt(bool useBlackBg) { - int l; - - l = _game.lineUserInput; - clearLines(l, l, useBlackBg ? 0 : _game.colorBg); - flushLines(l, l); - - _gfx->doUpdate(); -} - -/** - * Clear text lines in the screen. - * @param l1 start line - * @param l2 end line - * @param c color - */ -void AgiEngine::clearLines(int l1, int l2, int c) { - // do we need to adjust for +8 on topline? - // inc for endline so it matches the correct num - // ie, from 22 to 24 is 3 lines, not 2 lines. - - debugC(4, kDebugLevelText, "clearLines(%d, %d, %d)", l1, l2, c); - - l1 *= CHAR_LINES; - l2 *= CHAR_LINES; - l2 += CHAR_LINES - 1; - - _gfx->drawRectangle(0, l1, GFX_WIDTH - 1, l2, c); -} - -/** - * - */ -void AgiEngine::flushLines(int l1, int l2) { - l1 *= CHAR_LINES; - l2 *= CHAR_LINES; - l2 += CHAR_LINES - 1; - - _gfx->flushBlock(0, l1, GFX_WIDTH - 1, l2); -} - -/** - * - */ -void AgiEngine::drawWindow(int x1, int y1, int x2, int y2) { - _game.window.active = true; - _game.window.x1 = x1; - _game.window.y1 = y1; - _game.window.x2 = x2; - _game.window.y2 = y2; - _game.window.buffer = (uint8 *)malloc((x2 - x1 + 1) * (y2 - y1 + 1)); - - debugC(4, kDebugLevelText, "x1=%d, y1=%d, x2=%d, y2=%d", x1, y1, x2, y2); - _gfx->saveBlock(x1, y1, x2, y2, _game.window.buffer); - _gfx->drawBox(x1, y1, x2, y2, MSG_BOX_COLOR, MSG_BOX_LINE, 2); + assert(resultString.size() < sizeof(resultPrintfBuffer)); + strcpy(resultPrintfBuffer, resultString.c_str()); + return resultPrintfBuffer; } } // End of namespace Agi |