|
|
|
|
@ -3,6 +3,8 @@
|
|
|
|
|
* |
|
|
|
|
* Implementation of scrolling dialog text. |
|
|
|
|
*/ |
|
|
|
|
#include <string> |
|
|
|
|
#include <vector> |
|
|
|
|
|
|
|
|
|
#include "control.h" |
|
|
|
|
#include "dx.h" |
|
|
|
|
@ -12,6 +14,7 @@
|
|
|
|
|
#include "textdat.h" |
|
|
|
|
#include "utils/language.h" |
|
|
|
|
#include "utils/stdcompat/optional.hpp" |
|
|
|
|
#include "utils/stdcompat/string_view.hpp" |
|
|
|
|
|
|
|
|
|
namespace devilution { |
|
|
|
|
|
|
|
|
|
@ -20,77 +23,36 @@ bool qtextflag;
|
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
/** Current y position of text in px */ |
|
|
|
|
int qtexty; |
|
|
|
|
/** Pointer to the current text being displayed */ |
|
|
|
|
const char *qtextptr; |
|
|
|
|
/** Vertical speed of the scrolling text in ms/px */ |
|
|
|
|
int qtextSpd; |
|
|
|
|
/** Time of last rendering of the text */ |
|
|
|
|
Uint32 sgLastScroll; |
|
|
|
|
/** Start time of scrolling */ |
|
|
|
|
Uint32 ScrollStart; |
|
|
|
|
/** Graphics for the window border */ |
|
|
|
|
std::optional<CelSprite> pTextBoxCels; |
|
|
|
|
|
|
|
|
|
/** Pixels for a line of text and the empty space under it. */ |
|
|
|
|
const int lineHeight = 38; |
|
|
|
|
const int LineHeight = 38; |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Build a single line of text from the given text stream |
|
|
|
|
* @param text The original text |
|
|
|
|
* @param line The buffer to insert the line in to |
|
|
|
|
* @return Indicate that the end of the text was reached |
|
|
|
|
*/ |
|
|
|
|
bool BuildLine(const char *text, char line[128]) |
|
|
|
|
std::vector<std::string> TextLines; |
|
|
|
|
|
|
|
|
|
void LoadText(const char *text) |
|
|
|
|
{ |
|
|
|
|
int lineWidth = 0; |
|
|
|
|
int l = 0; |
|
|
|
|
|
|
|
|
|
while (*text != '\n' && *text != '|' && lineWidth < 543) { |
|
|
|
|
uint8_t c = gbFontTransTbl[(uint8_t)*text]; |
|
|
|
|
text++; |
|
|
|
|
if (c != '\0') { |
|
|
|
|
line[l] = c; |
|
|
|
|
lineWidth += fontkern[GameFontMed][fontframe[GameFontMed][c]] + 2; |
|
|
|
|
} else { |
|
|
|
|
l--; |
|
|
|
|
} |
|
|
|
|
l++; |
|
|
|
|
} |
|
|
|
|
line[l] = '\0'; |
|
|
|
|
if (*text == '|') { |
|
|
|
|
line[l] = '\0'; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
TextLines.clear(); |
|
|
|
|
|
|
|
|
|
if (*text != '\n') { |
|
|
|
|
while (line[l] != ' ' && l > 0) { |
|
|
|
|
line[l] = '\0'; |
|
|
|
|
l--; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
char tempstr[1536]; // Longest test is about 768 chars * 2 for unicode
|
|
|
|
|
strcpy(tempstr, text); |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
WordWrapGameString(tempstr, 543, GameFontMed, 2); |
|
|
|
|
const string_view paragraphs = tempstr; |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Calculate the number of line required by the given text |
|
|
|
|
* @return Number of lines |
|
|
|
|
*/ |
|
|
|
|
int GetLinesInText(const char *text) |
|
|
|
|
{ |
|
|
|
|
char line[128]; |
|
|
|
|
int lines = 0; |
|
|
|
|
|
|
|
|
|
bool doneflag = false; |
|
|
|
|
while (!doneflag) { |
|
|
|
|
doneflag = BuildLine(text, line); |
|
|
|
|
text += strlen(line); |
|
|
|
|
if (*text == '\n') |
|
|
|
|
text++; |
|
|
|
|
lines++; |
|
|
|
|
size_t previous = 0; |
|
|
|
|
while (true) { |
|
|
|
|
size_t next = paragraphs.find("\n", previous); |
|
|
|
|
TextLines.emplace_back(paragraphs.substr(previous, next)); |
|
|
|
|
if (next == std::string::npos) |
|
|
|
|
break; |
|
|
|
|
previous = next + 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return lines; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -98,99 +60,62 @@ int GetLinesInText(const char *text)
|
|
|
|
|
* @param nSFX The index of the sound in the sgSFX table |
|
|
|
|
* @return ms/px |
|
|
|
|
*/ |
|
|
|
|
int CalcTextSpeed(int nSFX) |
|
|
|
|
int CalculateTextSpeed(int nSFX) |
|
|
|
|
{ |
|
|
|
|
int TextHeight; |
|
|
|
|
Uint32 SfxFrames; |
|
|
|
|
|
|
|
|
|
const int numLines = GetLinesInText(qtextptr); |
|
|
|
|
const int numLines = TextLines.size(); |
|
|
|
|
|
|
|
|
|
#ifndef NOSOUND |
|
|
|
|
SfxFrames = GetSFXLength(nSFX); |
|
|
|
|
Uint32 SfxFrames = GetSFXLength(nSFX); |
|
|
|
|
assert(SfxFrames != 0); |
|
|
|
|
#else |
|
|
|
|
// Sound is disabled -- estimate length from the number of lines.
|
|
|
|
|
SfxFrames = numLines * 3000; |
|
|
|
|
Uint32 SfxFrames = numLines * 3000; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
TextHeight = lineHeight * numLines; |
|
|
|
|
TextHeight += lineHeight * 5; // adjust so when speaker is done two line are left
|
|
|
|
|
int textHeight = LineHeight * numLines; |
|
|
|
|
textHeight += LineHeight * 5; // adjust so when speaker is done two line are left
|
|
|
|
|
|
|
|
|
|
return SfxFrames / TextHeight; |
|
|
|
|
return SfxFrames / textHeight; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Print a character |
|
|
|
|
* @param sx Back buffer coordinate |
|
|
|
|
* @param sy Back buffer coordinate |
|
|
|
|
* @param cel CEL sprite |
|
|
|
|
* @param nCel CEL frame number |
|
|
|
|
*/ |
|
|
|
|
void PrintQTextChr(int sx, int sy, const CelSprite &cel, int nCel) |
|
|
|
|
int CalculateTextPosition() |
|
|
|
|
{ |
|
|
|
|
const int start_y = 49 + UI_OFFSET_Y; |
|
|
|
|
const CelOutputBuffer &buf = GlobalBackBuffer().subregionY(start_y, 260); |
|
|
|
|
CelDrawTo(buf, sx, sy - start_y, cel, nCel); |
|
|
|
|
} |
|
|
|
|
Uint32 currTime = SDL_GetTicks(); |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw the current text in the quest dialog window |
|
|
|
|
* @return the start of the text currently being rendered |
|
|
|
|
*/ |
|
|
|
|
void ScrollQTextContent(const char *pnl) |
|
|
|
|
{ |
|
|
|
|
for (Uint32 currTime = SDL_GetTicks(); sgLastScroll + qtextSpd < currTime; sgLastScroll += qtextSpd) { |
|
|
|
|
qtexty--; |
|
|
|
|
if (qtexty <= 49 + UI_OFFSET_Y) { |
|
|
|
|
qtexty += 38; |
|
|
|
|
qtextptr = pnl; |
|
|
|
|
if (*pnl == '|') { |
|
|
|
|
qtextflag = false; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
int y = (currTime - ScrollStart) / qtextSpd - 260; |
|
|
|
|
|
|
|
|
|
int textHeight = LineHeight * TextLines.size(); |
|
|
|
|
if (y >= textHeight) |
|
|
|
|
qtextflag = false; |
|
|
|
|
|
|
|
|
|
return y; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw the current text in the quest dialog window |
|
|
|
|
*/ |
|
|
|
|
static void DrawQTextContent() |
|
|
|
|
void DrawQTextContent(const CelOutputBuffer &out) |
|
|
|
|
{ |
|
|
|
|
// TODO: Draw to the given `out` buffer.
|
|
|
|
|
const char *text, *pnl; |
|
|
|
|
char line[128]; |
|
|
|
|
|
|
|
|
|
text = qtextptr; |
|
|
|
|
pnl = nullptr; |
|
|
|
|
int tx = 48 + PANEL_X; |
|
|
|
|
int ty = qtexty; |
|
|
|
|
|
|
|
|
|
bool doneflag = false; |
|
|
|
|
while (!doneflag) { |
|
|
|
|
doneflag = BuildLine(text, line); |
|
|
|
|
for (int i = 0; line[i]; i++) { |
|
|
|
|
text++; |
|
|
|
|
uint8_t c = fontframe[GameFontMed][gbFontTransTbl[(uint8_t)line[i]]]; |
|
|
|
|
if (*text == '\n') { |
|
|
|
|
text++; |
|
|
|
|
} |
|
|
|
|
if (c != 0) { |
|
|
|
|
PrintQTextChr(tx, ty, *pMedTextCels, c); |
|
|
|
|
} |
|
|
|
|
tx += fontkern[GameFontMed][c] + 2; |
|
|
|
|
} |
|
|
|
|
if (pnl == nullptr) { |
|
|
|
|
pnl = text; |
|
|
|
|
int y = CalculateTextPosition(); |
|
|
|
|
|
|
|
|
|
const int sx = PANEL_X + 48; |
|
|
|
|
const int sy = LineHeight / 2 - (y % LineHeight); |
|
|
|
|
|
|
|
|
|
const unsigned int skipLines = y / LineHeight; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 8; i++) { |
|
|
|
|
const int lineNumber = skipLines + i; |
|
|
|
|
if (lineNumber < 0 || lineNumber >= (int)TextLines.size()) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
tx = 48 + PANEL_X; |
|
|
|
|
ty += lineHeight; |
|
|
|
|
if (ty > 341 + UI_OFFSET_Y) { |
|
|
|
|
doneflag = true; |
|
|
|
|
|
|
|
|
|
const char *line = TextLines[lineNumber].c_str(); |
|
|
|
|
if (line[0] == '\0') { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ScrollQTextContent(pnl); |
|
|
|
|
DrawString(out, line, { sx, sy + i * LineHeight, 543, LineHeight }, UIS_MED, 2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
@ -220,11 +145,10 @@ void InitQTextMsg(_speech_id m)
|
|
|
|
|
{ |
|
|
|
|
if (alltext[m].scrlltxt) { |
|
|
|
|
questlog = false; |
|
|
|
|
qtextptr = _(alltext[m].txtstr); |
|
|
|
|
LoadText(_(alltext[m].txtstr)); |
|
|
|
|
qtextflag = true; |
|
|
|
|
qtexty = 340 + UI_OFFSET_Y; |
|
|
|
|
qtextSpd = CalcTextSpeed(alltext[m].sfxnr); |
|
|
|
|
sgLastScroll = SDL_GetTicks(); |
|
|
|
|
qtextSpd = CalculateTextSpeed(alltext[m].sfxnr); |
|
|
|
|
ScrollStart = SDL_GetTicks(); |
|
|
|
|
} |
|
|
|
|
PlaySFX(alltext[m].sfxnr); |
|
|
|
|
} |
|
|
|
|
@ -238,7 +162,7 @@ void DrawQTextBack(const CelOutputBuffer &out)
|
|
|
|
|
void DrawQText(const CelOutputBuffer &out) |
|
|
|
|
{ |
|
|
|
|
DrawQTextBack(out); |
|
|
|
|
DrawQTextContent(); |
|
|
|
|
DrawQTextContent(out.subregionY(UI_OFFSET_Y + 49, 260)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace devilution
|
|
|
|
|
|