|
|
|
|
/**
|
|
|
|
|
* @file minitext.cpp
|
|
|
|
|
*
|
|
|
|
|
* Implementation of scrolling dialog text.
|
|
|
|
|
*/
|
|
|
|
|
#include "all.h"
|
|
|
|
|
|
|
|
|
|
DEVILUTION_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
|
|
/** Specify if the quest dialog window is being shown */
|
|
|
|
|
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;
|
|
|
|
|
/** Graphics for the medium size font */
|
|
|
|
|
Uint8 *pMedTextCels;
|
|
|
|
|
/** Graphics for the window border */
|
|
|
|
|
Uint8 *pTextBoxCels;
|
|
|
|
|
|
|
|
|
|
/** Maps from font index to medtexts.cel frame number. */
|
|
|
|
|
const Uint8 mfontframe[128] = {
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
0, 0, 0, 37, 49, 38, 0, 39, 40, 47,
|
|
|
|
|
42, 43, 41, 45, 52, 44, 53, 55, 36, 27,
|
|
|
|
|
28, 29, 30, 31, 32, 33, 34, 35, 51, 50,
|
|
|
|
|
48, 46, 49, 54, 0, 1, 2, 3, 4, 5,
|
|
|
|
|
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
|
|
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
|
|
|
|
26, 42, 0, 43, 0, 0, 0, 1, 2, 3,
|
|
|
|
|
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
|
|
|
14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
|
|
|
|
24, 25, 26, 48, 0, 49, 0, 0
|
|
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* Maps from medtexts.cel frame number to character width. Note, the
|
|
|
|
|
* character width may be distinct from the frame width, which is 22 for every
|
|
|
|
|
* medtexts.cel frame.
|
|
|
|
|
*/
|
|
|
|
|
const Uint8 mfontkern[56] = {
|
|
|
|
|
5, 15, 10, 13, 14, 10, 9, 13, 11, 5,
|
|
|
|
|
5, 11, 10, 16, 13, 16, 10, 15, 12, 10,
|
|
|
|
|
14, 17, 17, 22, 17, 16, 11, 5, 11, 11,
|
|
|
|
|
11, 10, 11, 11, 11, 11, 15, 5, 10, 18,
|
|
|
|
|
15, 8, 6, 6, 7, 10, 9, 6, 10, 10,
|
|
|
|
|
5, 5, 5, 5, 11, 12
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Pixels for a line of text and the empty space under it. */
|
|
|
|
|
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])
|
|
|
|
|
{
|
|
|
|
|
int lineWidth = 0;
|
|
|
|
|
int l = 0;
|
|
|
|
|
|
|
|
|
|
while (*text != '\n' && *text != '|' && lineWidth < 543) {
|
|
|
|
|
Uint8 c = gbFontTransTbl[(Uint8)*text];
|
|
|
|
|
text++;
|
|
|
|
|
if (c != '\0') {
|
|
|
|
|
line[l] = c;
|
|
|
|
|
lineWidth += mfontkern[mfontframe[c]] + 2;
|
|
|
|
|
} else {
|
|
|
|
|
l--;
|
|
|
|
|
}
|
|
|
|
|
l++;
|
|
|
|
|
}
|
|
|
|
|
line[l] = '\0';
|
|
|
|
|
if (*text == '|') {
|
|
|
|
|
line[l] = '\0';
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*text != '\n') {
|
|
|
|
|
while (line[l] != ' ' && l > 0) {
|
|
|
|
|
line[l] = '\0';
|
|
|
|
|
l--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Calculate the speed the current text should scroll to match the given audio
|
|
|
|
|
* @param nSFX The index of the sound in the sgSFX table
|
|
|
|
|
* @return ms/px
|
|
|
|
|
*/
|
|
|
|
|
int CalcTextSpeed(int nSFX)
|
|
|
|
|
{
|
|
|
|
|
Uint32 SfxFrames, TextHeight;
|
|
|
|
|
|
|
|
|
|
SfxFrames = GetSFXLength(nSFX);
|
|
|
|
|
assert(SfxFrames != 0);
|
|
|
|
|
|
|
|
|
|
TextHeight = lineHeight * GetLinesInText(qtextptr);
|
|
|
|
|
TextHeight += lineHeight * 5; // adjust so when speaker is done two line are left
|
|
|
|
|
|
|
|
|
|
return SfxFrames / TextHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Print a character
|
|
|
|
|
* @param sx Back buffer coordinate
|
|
|
|
|
* @param sy Back buffer coordinate
|
|
|
|
|
* @param pCelBuff Cel data
|
|
|
|
|
* @param nCel CEL frame number
|
|
|
|
|
*/
|
|
|
|
|
void PrintQTextChr(int sx, int sy, Uint8 *pCelBuff, int nCel)
|
|
|
|
|
{
|
|
|
|
|
CelOutputBuffer buf = GlobalBackBuffer();
|
|
|
|
|
const int start_y = 49 + UI_OFFSET_Y;
|
|
|
|
|
buf = buf.subregionY(start_y, 260);
|
|
|
|
|
CelDrawTo(buf, sx, sy - start_y, pCelBuff, nCel, 22);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw the current text in the quest dialog window
|
|
|
|
|
*/
|
|
|
|
|
static void DrawQTextContent(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 c = mfontframe[gbFontTransTbl[(Uint8)line[i]]];
|
|
|
|
|
if (*text == '\n') {
|
|
|
|
|
text++;
|
|
|
|
|
}
|
|
|
|
|
if (c != 0) {
|
|
|
|
|
PrintQTextChr(tx, ty, pMedTextCels, c);
|
|
|
|
|
}
|
|
|
|
|
tx += mfontkern[c] + 2;
|
|
|
|
|
}
|
|
|
|
|
if (pnl == nullptr) {
|
|
|
|
|
pnl = text;
|
|
|
|
|
}
|
|
|
|
|
tx = 48 + PANEL_X;
|
|
|
|
|
ty += lineHeight;
|
|
|
|
|
if (ty > 341 + UI_OFFSET_Y) {
|
|
|
|
|
doneflag = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScrollQTextContent(pnl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Free the resouces used by the quest dialog window
|
|
|
|
|
*/
|
|
|
|
|
void FreeQuestText()
|
|
|
|
|
{
|
|
|
|
|
MemFreeDbg(pMedTextCels);
|
|
|
|
|
MemFreeDbg(pTextBoxCels);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Load the resouces used by the quest dialog window, and initialize it's state
|
|
|
|
|
*/
|
|
|
|
|
void InitQuestText()
|
|
|
|
|
{
|
|
|
|
|
pMedTextCels = LoadFileInMem("Data\\MedTextS.CEL", nullptr);
|
|
|
|
|
pTextBoxCels = LoadFileInMem("Data\\TextBox.CEL", nullptr);
|
|
|
|
|
qtextflag = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Start the given naration
|
|
|
|
|
* @param m Index of narration from the alltext table
|
|
|
|
|
*/
|
|
|
|
|
void InitQTextMsg(int m)
|
|
|
|
|
{
|
|
|
|
|
if (alltext[m].scrlltxt) {
|
|
|
|
|
questlog = false;
|
|
|
|
|
qtextptr = alltext[m].txtstr;
|
|
|
|
|
qtextflag = true;
|
|
|
|
|
qtexty = 340 + UI_OFFSET_Y;
|
|
|
|
|
qtextSpd = CalcTextSpeed(alltext[m].sfxnr);
|
|
|
|
|
sgLastScroll = SDL_GetTicks();
|
|
|
|
|
}
|
|
|
|
|
PlaySFX(alltext[m].sfxnr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawQTextBack(CelOutputBuffer out)
|
|
|
|
|
{
|
|
|
|
|
CelDrawTo(out, PANEL_X + 24, 327 + UI_OFFSET_Y, pTextBoxCels, 1, 591);
|
|
|
|
|
DrawHalfTransparentRectTo(out, PANEL_X + 27, UI_OFFSET_Y + 28, 585, 297);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawQText(CelOutputBuffer out)
|
|
|
|
|
{
|
|
|
|
|
DrawQTextBack(out);
|
|
|
|
|
DrawQTextContent(out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEVILUTION_END_NAMESPACE
|