Files
devilutionX/Source/minitext.cpp
T
Gleb Mazovetskiy 8ca71272b8 Load all CEL as CL2
Convert CEL files to CL2 at load time. CL2 format is more efficient and is about as fast to render.

CEL vs CL2 sizes, on dLvl 5: https://gist.github.com/glebm/9bbdd76962abcd4fd2405ecd3379af97

Memory:

* Peak memory (while loading): -300 KiB
* Memory in-game (dLvl5): -700 KiB
* RG99 binary size: -15 KiB (1333096 -> 1317192)

Performance on rg99:

* On average, -1 FPS in town.
* Same FPS in dungeon (20 FPS on dLvl 1).
2022-07-19 10:59:35 +02:00

158 lines
3.6 KiB
C++

/**
* @file minitext.cpp
*
* Implementation of scrolling dialog text.
*/
#include <string>
#include <vector>
#include "DiabloUI/ui_flags.hpp"
#include "control.h"
#include "engine.h"
#include "engine/cel_sprite.hpp"
#include "engine/dx.h"
#include "engine/load_cel.hpp"
#include "engine/render/cl2_render.hpp"
#include "engine/render/text_render.hpp"
#include "textdat.h"
#include "utils/language.h"
#include "utils/stdcompat/optional.hpp"
#include "utils/stdcompat/string_view.hpp"
namespace devilution {
bool qtextflag;
namespace {
/** Vertical speed of the scrolling text in ms/px */
int qtextSpd;
/** Start time of scrolling */
Uint32 ScrollStart;
/** Graphics for the window border */
OptionalOwnedCelSprite pTextBoxCels;
/** Pixels for a line of text and the empty space under it. */
const int LineHeight = 38;
std::vector<std::string> TextLines;
void LoadText(string_view text)
{
TextLines.clear();
const std::string paragraphs = WordWrapString(text, 543, GameFont30);
size_t previous = 0;
while (true) {
size_t next = paragraphs.find('\n', previous);
TextLines.emplace_back(paragraphs.substr(previous, next - previous));
if (next == std::string::npos)
break;
previous = next + 1;
}
}
/**
* @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
*/
uint32_t CalculateTextSpeed(int nSFX)
{
const int numLines = TextLines.size();
#ifndef NOSOUND
Uint32 sfxFrames = GetSFXLength(nSFX);
#else
// Sound is disabled -- estimate length from the number of lines.
Uint32 sfxFrames = numLines * 3000;
#endif
assert(sfxFrames != 0);
uint32_t textHeight = LineHeight * numLines;
textHeight += LineHeight * 5; // adjust so when speaker is done two line are left
assert(textHeight != 0);
return sfxFrames / textHeight;
}
int CalculateTextPosition()
{
uint32_t currTime = SDL_GetTicks();
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
*/
void DrawQTextContent(const Surface &out)
{
int y = CalculateTextPosition();
const int sx = GetUIRectangle().position.x + 48;
const int sy = 0 - (y % LineHeight);
const unsigned int skipLines = y / LineHeight;
for (int i = 0; i < 8; i++) {
const unsigned int lineNumber = skipLines + i;
if (lineNumber >= TextLines.size()) {
continue;
}
const std::string &line = TextLines[lineNumber];
if (line.empty()) {
continue;
}
DrawString(out, line, { { sx, sy + i * LineHeight }, { 543, LineHeight } }, UiFlags::FontSize30 | UiFlags::ColorGold);
}
}
} // namespace
void FreeQuestText()
{
pTextBoxCels = std::nullopt;
}
void InitQuestText()
{
pTextBoxCels = LoadCelAsCl2("Data\\TextBox.CEL", 591);
}
void InitQTextMsg(_speech_id m)
{
if (Speeches[m].scrlltxt) {
QuestLogIsOpen = false;
LoadText(_(Speeches[m].txtstr));
qtextflag = true;
qtextSpd = CalculateTextSpeed(Speeches[m].sfxnr);
ScrollStart = SDL_GetTicks();
}
PlaySFX(Speeches[m].sfxnr);
}
void DrawQTextBack(const Surface &out)
{
const Point uiPosition = GetUIRectangle().position;
Cl2Draw(out, uiPosition + Displacement { 24, 327 }, CelSprite { *pTextBoxCels }, 0);
DrawHalfTransparentRectTo(out, uiPosition.x + 27, uiPosition.y + 28, 585, 297);
}
void DrawQText(const Surface &out)
{
DrawQTextBack(out);
DrawQTextContent(out.subregionY(GetUIRectangle().position.y + 49, 260));
}
} // namespace devilution