416 lines
9.0 KiB
C++
416 lines
9.0 KiB
C++
#include "Textbox.h"
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "Font.h"
|
|
#include "Localization.h"
|
|
#include "UTF8.h"
|
|
#include "Vlogging.h"
|
|
|
|
textboxclass::textboxclass(int gap)
|
|
{
|
|
w = 0;
|
|
h = 0;
|
|
tl = 0;
|
|
prev_tl = 0;
|
|
tm = 0;
|
|
timer = 0;
|
|
|
|
xp = 0;
|
|
yp = 0;
|
|
r = 0;
|
|
g = 0;
|
|
b = 0;
|
|
linegap = gap;
|
|
|
|
flipme = false;
|
|
|
|
rand = 0;
|
|
|
|
large = false;
|
|
|
|
should_centerx = false;
|
|
should_centery = false;
|
|
|
|
print_flags = PR_FONT_LEVEL;
|
|
translate = TEXTTRANSLATE_NONE;
|
|
function = NULL;
|
|
fill_buttons = false;
|
|
|
|
sprites.clear();
|
|
|
|
image = TEXTIMAGE_NONE;
|
|
|
|
crewmate_position = TextboxCrewmatePosition();
|
|
original = TextboxOriginalContext();
|
|
original.text_case = 1;
|
|
spacing = TextboxSpacing();
|
|
}
|
|
|
|
void textboxclass::addsprite(int x, int y, int tile, int col)
|
|
{
|
|
TextboxSprite sprite;
|
|
sprite.x = x;
|
|
sprite.y = y;
|
|
sprite.tile = tile;
|
|
sprite.col = col;
|
|
sprites.push_back(sprite);
|
|
}
|
|
|
|
void textboxclass::setimage(TextboxImage new_image)
|
|
{
|
|
image = new_image;
|
|
}
|
|
|
|
void textboxclass::centerx(void)
|
|
{
|
|
resize();
|
|
xp = 160 - (w / 2);
|
|
resize();
|
|
}
|
|
void textboxclass::centery(void)
|
|
{
|
|
resize();
|
|
yp = 120 - (h / 2);
|
|
resize();
|
|
}
|
|
|
|
void textboxclass::applyposition(void)
|
|
{
|
|
resize();
|
|
repositionfromcrewmate();
|
|
if (should_centerx)
|
|
{
|
|
centerx();
|
|
}
|
|
if (should_centery)
|
|
{
|
|
centery();
|
|
}
|
|
if (translate == TEXTTRANSLATE_CUTSCENE)
|
|
{
|
|
adjust();
|
|
}
|
|
}
|
|
|
|
void textboxclass::adjust(void)
|
|
{
|
|
resize();
|
|
if (xp < 10) xp = 10;
|
|
if (yp < 10) yp = 10;
|
|
if (xp + w > 310) xp = 310 - w;
|
|
if (yp + h > 230) yp = 230 - h;
|
|
resize();
|
|
}
|
|
|
|
void textboxclass::initcol(int rr, int gg, int bb)
|
|
{
|
|
r = rr;
|
|
g = gg;
|
|
b = bb;
|
|
tl = 0.5;
|
|
}
|
|
|
|
void textboxclass::update(void)
|
|
{
|
|
prev_tl = tl;
|
|
if (tm == 0)
|
|
{
|
|
tl += .1f;
|
|
if (tl >= 1)
|
|
{
|
|
tl = 1;
|
|
tm = 1;
|
|
}
|
|
}
|
|
else if (tm == 2)
|
|
{
|
|
tl -= .1f;
|
|
if (tl <= 0.5)
|
|
{
|
|
tl = 0.5;
|
|
//this textbox will be removed by updatetextboxes() later
|
|
}
|
|
}
|
|
if (timer > 0)
|
|
{
|
|
timer--;
|
|
if (timer == 0) tm = 2;
|
|
}
|
|
}
|
|
|
|
void textboxclass::remove(void)
|
|
{
|
|
tm = 2;
|
|
tl = 1.0f; //Remove mode
|
|
}
|
|
|
|
void textboxclass::removefast(void)
|
|
{
|
|
tm = 2;
|
|
tl = 0.4f; //Remove mode
|
|
}
|
|
|
|
void textboxclass::resize(void)
|
|
{
|
|
//Set the width and height to the correct sizes
|
|
int max = 0;
|
|
for (size_t iter = 0; iter < lines.size(); iter++)
|
|
{
|
|
int len = font::len(print_flags, lines[iter].c_str());
|
|
if (len > max) max = len;
|
|
}
|
|
|
|
// 16 for the borders
|
|
w = max + 16;
|
|
h = lines.size()*(font::height(print_flags) + linegap) + 16 - linegap;
|
|
}
|
|
|
|
void textboxclass::repositionfromcrewmate(void)
|
|
{
|
|
const int font_height = font::height(print_flags);
|
|
|
|
// Reposition based off crewmate position, if applicable
|
|
if (crewmate_position.override_x)
|
|
{
|
|
if (crewmate_position.dir == 1) // left
|
|
{
|
|
xp = crewmate_position.x - w + 16;
|
|
}
|
|
else if (crewmate_position.dir == 0) // right
|
|
{
|
|
xp = crewmate_position.x - 16;
|
|
}
|
|
}
|
|
if (crewmate_position.override_y)
|
|
{
|
|
if (crewmate_position.text_above)
|
|
{
|
|
if (crewmate_position.dir == 1) // left
|
|
{
|
|
yp = crewmate_position.y - 16 - (lines.size() * (font_height + linegap) - linegap);
|
|
}
|
|
else if (crewmate_position.dir == 0) // right
|
|
{
|
|
yp = crewmate_position.y - 18 - (lines.size() * (font_height + linegap) - linegap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
yp = crewmate_position.y + 26;
|
|
}
|
|
}
|
|
}
|
|
|
|
void textboxclass::addline(const std::string& t)
|
|
{
|
|
lines.push_back(t);
|
|
resize();
|
|
if ((int)lines.size() > (large ? 26 : 11))
|
|
{
|
|
lines.clear();
|
|
}
|
|
}
|
|
|
|
void textboxclass::pad(size_t left_pad, size_t right_pad)
|
|
{
|
|
// Pad the current text with a certain number of spaces on the left and right
|
|
if (font::is_rtl(print_flags))
|
|
{
|
|
// Swap left and right, because left will end up on the right and vice versa...
|
|
size_t old_left_pad = left_pad;
|
|
left_pad = right_pad;
|
|
right_pad = old_left_pad;
|
|
}
|
|
|
|
for (size_t iter = 0; iter < lines.size(); iter++)
|
|
{
|
|
lines[iter] = std::string(left_pad, ' ') + lines[iter] + std::string(right_pad, ' ');
|
|
}
|
|
resize();
|
|
}
|
|
|
|
void textboxclass::padtowidth(size_t new_w)
|
|
{
|
|
/* Pad the current text so that each line is new_w pixels wide.
|
|
* Each existing line is centered in that width. */
|
|
resize();
|
|
uint8_t glyph_w = 8;
|
|
font::glyph_dimensions(print_flags, &glyph_w, NULL);
|
|
size_t chars_w = SDL_max(w-16, new_w) / glyph_w;
|
|
for (size_t iter = 0; iter < lines.size(); iter++)
|
|
{
|
|
size_t n_glyphs = font::len(print_flags, lines[iter].c_str()) / glyph_w;
|
|
signed int padding_needed = chars_w - n_glyphs;
|
|
if (padding_needed < 0)
|
|
{
|
|
continue;
|
|
}
|
|
size_t left_pad = padding_needed / 2;
|
|
size_t right_pad = padding_needed - left_pad;
|
|
|
|
lines[iter] = std::string(left_pad, ' ') + lines[iter] + std::string(right_pad, ' ');
|
|
}
|
|
resize();
|
|
}
|
|
|
|
void textboxclass::centertext(void)
|
|
{
|
|
padtowidth(w-16);
|
|
}
|
|
|
|
int textboxclass::wrap(int pad)
|
|
{
|
|
/* This function just takes a single-line textbox and wraps it...
|
|
* pad = the total number of characters we are going to pad this textbox.
|
|
* (or how many characters we should stay clear of 288 pixels width in general)
|
|
* Only to be used after a manual graphics.createtextbox[flipme] call,
|
|
* or the retranslation of a text box created with said call.
|
|
* Returns the new, total height of the textbox. */
|
|
if (lines.empty())
|
|
{
|
|
vlog_error("textboxclass::wrap() has no first line!");
|
|
return 16;
|
|
}
|
|
|
|
std::string wrapped = font::string_wordwrap_balanced(
|
|
print_flags,
|
|
lines[0],
|
|
36 * 8 - pad * 8
|
|
);
|
|
lines.clear();
|
|
|
|
size_t startline = 0;
|
|
size_t newline;
|
|
do {
|
|
size_t pos_n = wrapped.find('\n', startline);
|
|
size_t pos_p = wrapped.find('|', startline);
|
|
newline = SDL_min(pos_n, pos_p);
|
|
addline(wrapped.substr(startline, newline-startline));
|
|
startline = newline + 1;
|
|
} while (newline != std::string::npos);
|
|
|
|
return h;
|
|
}
|
|
|
|
void textboxclass::copyoriginaltext(void)
|
|
{
|
|
// Copy the original back, but keep the limit of lines in mind
|
|
lines.clear();
|
|
for (size_t i = 0; i < original.lines.size(); i++)
|
|
{
|
|
addline(original.lines[i]);
|
|
}
|
|
}
|
|
|
|
void textboxclass::applyoriginalspacing(void)
|
|
{
|
|
if (spacing.centertext)
|
|
{
|
|
centertext();
|
|
}
|
|
if (spacing.pad_left > 0 || spacing.pad_right > 0)
|
|
{
|
|
pad(spacing.pad_left, spacing.pad_right);
|
|
}
|
|
if (spacing.padtowidth > 0)
|
|
{
|
|
padtowidth(spacing.padtowidth);
|
|
}
|
|
}
|
|
|
|
void textboxclass::updatetext(void)
|
|
{
|
|
switch (translate)
|
|
{
|
|
case TEXTTRANSLATE_NONE:
|
|
copyoriginaltext();
|
|
applyoriginalspacing();
|
|
break;
|
|
case TEXTTRANSLATE_CUTSCENE:
|
|
translatecutscene();
|
|
break;
|
|
case TEXTTRANSLATE_FUNCTION:
|
|
if (function == NULL)
|
|
{
|
|
SDL_assert(0 && "function is NULL!");
|
|
break;
|
|
}
|
|
function(this);
|
|
}
|
|
}
|
|
|
|
void textboxclass::translatecutscene(void)
|
|
{
|
|
if (!loc::is_cutscene_translated(original.script_name))
|
|
{
|
|
copyoriginaltext();
|
|
applyoriginalspacing();
|
|
return;
|
|
}
|
|
|
|
// English text needs to be un-wordwrapped, translated, and re-wordwrapped
|
|
std::string eng;
|
|
for (size_t i = 0; i < original.lines.size(); i++)
|
|
{
|
|
if (i != 0)
|
|
{
|
|
eng.append("\n");
|
|
}
|
|
eng.append(original.lines[i]);
|
|
}
|
|
|
|
eng = font::string_unwordwrap(eng);
|
|
const loc::TextboxFormat* format = loc::gettext_cutscene(original.script_name, eng, original.text_case);
|
|
if (format == NULL || format->text == NULL || format->text[0] == '\0')
|
|
{
|
|
copyoriginaltext();
|
|
applyoriginalspacing();
|
|
return;
|
|
}
|
|
|
|
std::string tra;
|
|
if (format->tt)
|
|
{
|
|
tra = std::string(format->text);
|
|
size_t pipe;
|
|
while (true)
|
|
{
|
|
pipe = tra.find('|', 0);
|
|
if (pipe == std::string::npos)
|
|
{
|
|
break;
|
|
}
|
|
tra.replace(pipe, 1, "\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tra = font::string_wordwrap_balanced(PR_FONT_LEVEL, format->text, format->wraplimit);
|
|
}
|
|
|
|
lines.clear();
|
|
size_t startline = 0;
|
|
size_t newline;
|
|
do
|
|
{
|
|
newline = tra.find('\n', startline);
|
|
lines.push_back(tra.substr(startline, newline - startline));
|
|
startline = newline + 1;
|
|
}
|
|
while (newline != std::string::npos);
|
|
|
|
if (format->centertext)
|
|
{
|
|
centertext();
|
|
}
|
|
if (format->pad_left > 0 || format->pad_right > 0)
|
|
{
|
|
pad(format->pad_left, format->pad_right);
|
|
}
|
|
if (format->padtowidth > 0)
|
|
{
|
|
padtowidth(format->padtowidth);
|
|
}
|
|
}
|