Split textwraps, move more textboxes to new system

This splits the text wrapping functionality of Graphics::textboxwrap to
a new function textboxclass::wrap, and with this new function, some more
text boxes can be moved to the new TEXTTRANSLATE_FUNCTION system.
Namely, Game Saved (specifically the game failing to save text box),
instructional text boxes in Space Station 1, and the Intermission 1
instructional text boxes.
This commit is contained in:
Misa 2024-01-20 21:33:57 -08:00 committed by Misa Elizabeth Kai
parent de00dd4031
commit 94b9722b7b
4 changed files with 233 additions and 143 deletions

View File

@ -806,6 +806,21 @@ void Game::actionprompt_textbox(void)
graphics.textboxapplyposition(); graphics.textboxapplyposition();
} }
static void savetele_textbox_success(textboxclass* THIS)
{
THIS->lines.clear();
THIS->lines.push_back(loc::gettext("Game Saved"));
THIS->pad(3, 3);
}
static void savetele_textbox_fail(textboxclass* THIS)
{
THIS->lines.clear();
THIS->lines.push_back(loc::gettext("ERROR: Could not save game!"));
THIS->wrap(2);
THIS->pad(1, 1);
}
void Game::savetele_textbox(void) void Game::savetele_textbox(void)
{ {
if (inspecial() || map.custommode) if (inspecial() || map.custommode)
@ -815,21 +830,174 @@ void Game::savetele_textbox(void)
if (savetele()) if (savetele())
{ {
graphics.createtextboxflipme(loc::gettext("Game Saved"), -1, 12, TEXT_COLOUR("gray")); graphics.createtextboxflipme("", -1, 12, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxpad(3, 3);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(25); graphics.textboxtimer(25);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, savetele_textbox_success);
} }
else else
{ {
graphics.createtextboxflipme(loc::gettext("ERROR: Could not save game!"), -1, 12, TEXT_COLOUR("red")); graphics.createtextboxflipme("", -1, 12, TEXT_COLOUR("red"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(2);
graphics.textboxpad(1, 1);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(50); graphics.textboxtimer(50);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, savetele_textbox_fail);
} }
graphics.textboxapplyposition();
}
static void wasd_textbox(textboxclass* THIS)
{
THIS->lines.clear();
THIS->lines.push_back(BUTTONGLYPHS_get_wasd_text());
THIS->wrap(4);
THIS->centertext();
THIS->pad(2, 2);
}
static void flip_textbox(textboxclass* THIS)
{
THIS->lines.clear();
char buffer[SCREEN_WIDTH_CHARS*3 + 1];
vformat_buf(
buffer, sizeof(buffer),
loc::gettext("Press {button} to flip"),
"button:but",
vformat_button(ActionSet_InGame, Action_InGame_ACTION)
);
THIS->lines.push_back(buffer);
THIS->wrap(4);
THIS->centertext();
THIS->pad(2, 2);
}
static void arrowkey_textbox(textboxclass* THIS)
{
THIS->lines.clear();
THIS->lines.push_back(loc::gettext("If you prefer, you can press UP or DOWN instead of ACTION to flip."));
THIS->wrap(2);
THIS->centertext();
THIS->pad(1, 1);
}
static void map_textbox(textboxclass* THIS)
{
THIS->lines.clear();
char buffer[SCREEN_WIDTH_CHARS*3 + 1];
vformat_buf(
buffer, sizeof(buffer),
loc::gettext("Press {button} to view map and quicksave"),
"button:but",
vformat_button(ActionSet_InGame, Action_InGame_Map)
);
THIS->lines.push_back(buffer);
THIS->wrap(4);
THIS->centertext();
THIS->pad(2, 2);
}
static void im1_instructions_textbox1(textboxclass* THIS)
{
extern Game game;
THIS->lines.clear();
// Intermission 1 instructional textbox, depends on last saved
const char* floorceiling = graphics.flipmode ? "ceiling" : "floor";
const char* crewmate;
switch (game.lastsaved)
{
case 2:
crewmate = "Vitellary";
break;
case 3:
crewmate = "Vermilion";
break;
case 4:
crewmate = "Verdigris";
break;
case 5:
crewmate = "Victoria";
break;
default:
crewmate = "your companion";
}
char english[SCREEN_WIDTH_TILES*3 + 1]; /* ASCII only */
vformat_buf(english, sizeof(english),
"When you're standing on the {floorceiling}, {crewmate} will try to walk to you.",
"floorceiling:str, crewmate:str",
floorceiling, crewmate
);
THIS->lines.push_back(loc::gettext(english));
THIS->wrap(2);
THIS->padtowidth(36*8);
}
static void im1_instructions_textbox2(textboxclass* THIS)
{
extern Game game;
THIS->lines.clear();
// Intermission 1 instructional textbox, depends on last saved
const char* floorceiling = graphics.flipmode ? "ceiling" : "floor";
const char* crewmate;
switch (game.lastsaved)
{
case 2:
crewmate = "Vitellary";
break;
case 3:
crewmate = "Vermilion";
break;
case 4:
crewmate = "Verdigris";
break;
case 5:
crewmate = "Victoria";
break;
default:
crewmate = "your companion";
}
char english[SCREEN_WIDTH_TILES*3 + 1]; /* ASCII only */
vformat_buf(english, sizeof(english),
"When you're NOT standing on the {floorceiling}, {crewmate} will stop and wait for you.",
"floorceiling:str, crewmate:str",
floorceiling, crewmate
);
THIS->lines.push_back(loc::gettext(english));
THIS->wrap(2);
THIS->padtowidth(36*8);
}
static void im1_instructions_textbox3(textboxclass* THIS)
{
extern Game game;
THIS->lines.clear();
// Intermission 1 instructional textbox, depends on last saved
const char* english;
switch (game.lastsaved)
{
case 2:
case 3:
case 4:
english = "You can't continue to the next room until he is safely across.";
break;
case 5:
english = "You can't continue to the next room until she is safely across.";
break;
default:
english = "You can't continue to the next room until they are safely across.";
}
THIS->lines.push_back(loc::gettext(english));
THIS->wrap(2);
THIS->padtowidth(36*8);
} }
void Game::setstate(const int gamestate) void Game::setstate(const int gamestate)
@ -918,13 +1086,12 @@ void Game::updatestate(void)
break; break;
case 4: case 4:
//End of opening cutscene for now //End of opening cutscene for now
graphics.createtextbox(BUTTONGLYPHS_get_wasd_text(), -1, 195, TEXT_COLOUR("gray")); graphics.createtextbox("", -1, 195, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(4);
graphics.textboxcentertext();
graphics.textboxpad(2, 2);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(60); graphics.textboxtimer(60);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, wasd_textbox);
graphics.textboxapplyposition();
setstate(0); setstate(0);
break; break;
case 5: case 5:
@ -951,21 +1118,12 @@ void Game::updatestate(void)
{ {
obj.flags[13] = true; obj.flags[13] = true;
char buffer[SCREEN_WIDTH_CHARS*3 + 1]; graphics.createtextbox("", -1, 155, TEXT_COLOUR("gray"));
vformat_buf(
buffer, sizeof(buffer),
loc::gettext("Press {button} to view map and quicksave"),
"button:but",
vformat_button(ActionSet_InGame, Action_InGame_Map)
);
graphics.createtextbox(buffer, -1, 155, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(4);
graphics.textboxcentertext();
graphics.textboxpad(2, 2);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(60); graphics.textboxtimer(60);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, map_textbox);
graphics.textboxapplyposition();
} }
setstate(0); setstate(0);
break; break;
@ -1024,70 +1182,27 @@ void Game::updatestate(void)
break; break;
case 11: case 11:
{
//Intermission 1 instructional textbox, depends on last saved
graphics.textboxremovefast(); graphics.textboxremovefast();
const char* floorceiling = graphics.flipmode ? "ceiling" : "floor"; graphics.createtextbox("", -1, 3, TEXT_COLOUR("gray"));
const char* crewmate;
switch (lastsaved)
{
case 2:
crewmate = "Vitellary";
break;
case 3:
crewmate = "Vermilion";
break;
case 4:
crewmate = "Verdigris";
break;
case 5:
crewmate = "Victoria";
break;
default:
crewmate = "your companion";
}
char english[SCREEN_WIDTH_TILES*3 + 1]; /* ASCII only */
vformat_buf(english, sizeof(english),
"When you're NOT standing on the {floorceiling}, {crewmate} will stop and wait for you.",
"floorceiling:str, crewmate:str",
floorceiling, crewmate
);
graphics.createtextbox(loc::gettext(english), -1, 3, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(2);
graphics.textboxpadtowidth(36*8);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(180); graphics.textboxtimer(180);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, im1_instructions_textbox2);
graphics.textboxapplyposition();
setstate(0); setstate(0);
break; break;
}
case 12: case 12:
//Intermission 1 instructional textbox, depends on last saved
obj.removetrigger(12); obj.removetrigger(12);
if (!obj.flags[61]) if (!obj.flags[61])
{ {
obj.flags[61] = true; obj.flags[61] = true;
graphics.textboxremovefast(); graphics.textboxremovefast();
const char* english; graphics.createtextbox("", -1, 3, TEXT_COLOUR("gray"));
switch (lastsaved)
{
case 2:
case 3:
case 4:
english = "You can't continue to the next room until he is safely across.";
break;
case 5:
english = "You can't continue to the next room until she is safely across.";
break;
default:
english = "You can't continue to the next room until they are safely across.";
}
graphics.createtextbox(loc::gettext(english), -1, 3, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(2);
graphics.textboxpadtowidth(36*8);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(120); graphics.textboxtimer(120);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, im1_instructions_textbox3);
graphics.textboxapplyposition();
} }
setstate(0); setstate(0);
break; break;
@ -1098,43 +1213,15 @@ void Game::updatestate(void)
setstate(0); setstate(0);
break; break;
case 14: case 14:
{ graphics.createtextbox("", -1, 3, TEXT_COLOUR("gray"));
//Intermission 1 instructional textbox, depends on last saved
const char* floorceiling = graphics.flipmode ? "ceiling" : "floor";
const char* crewmate;
switch (lastsaved)
{
case 2:
crewmate = "Vitellary";
break;
case 3:
crewmate = "Vermilion";
break;
case 4:
crewmate = "Verdigris";
break;
case 5:
crewmate = "Victoria";
break;
default:
crewmate = "your companion";
}
char english[SCREEN_WIDTH_TILES*3 + 1]; /* ASCII only */
vformat_buf(english, sizeof(english),
"When you're standing on the {floorceiling}, {crewmate} will try to walk to you.",
"floorceiling:str, crewmate:str",
floorceiling, crewmate
);
graphics.createtextbox(loc::gettext(english), -1, 3, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(2);
graphics.textboxpadtowidth(36*8);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(280); graphics.textboxtimer(280);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, im1_instructions_textbox1);
graphics.textboxapplyposition();
setstate(0); setstate(0);
break; break;
}
case 15: case 15:
{ {
//leaving the naughty corner //leaving the naughty corner
@ -1164,13 +1251,12 @@ void Game::updatestate(void)
obj.removetrigger(17); obj.removetrigger(17);
if (BUTTONGLYPHS_keyboard_is_active()) if (BUTTONGLYPHS_keyboard_is_active())
{ {
graphics.createtextbox(loc::gettext("If you prefer, you can press UP or DOWN instead of ACTION to flip."), -1, 187, TEXT_COLOUR("gray")); graphics.createtextbox("", -1, 187, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(2);
graphics.textboxcentertext();
graphics.textboxpad(1, 1);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(100); graphics.textboxtimer(100);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, arrowkey_textbox);
graphics.textboxapplyposition();
} }
setstate(0); setstate(0);
break; break;
@ -1200,20 +1286,12 @@ void Game::updatestate(void)
obj.flags[3] = true; obj.flags[3] = true;
setstate(0); setstate(0);
char buffer[SCREEN_WIDTH_CHARS*3 + 1]; graphics.createtextbox("", -1, 25, TEXT_COLOUR("gray"));
vformat_buf(
buffer, sizeof(buffer),
loc::gettext("Press {button} to flip"),
"button:but",
vformat_button(ActionSet_InGame, Action_InGame_ACTION)
);
graphics.createtextbox(buffer, -1, 25, TEXT_COLOUR("gray"));
graphics.textboxprintflags(PR_FONT_INTERFACE); graphics.textboxprintflags(PR_FONT_INTERFACE);
graphics.textboxwrap(4);
graphics.textboxcentertext();
graphics.textboxpad(2, 2);
graphics.textboxcenterx(); graphics.textboxcenterx();
graphics.textboxtimer(60); graphics.textboxtimer(60);
graphics.textboxtranslate(TEXTTRANSLATE_FUNCTION, flip_textbox);
graphics.textboxapplyposition();
} }
obj.removetrigger(22); obj.removetrigger(22);
break; break;

View File

@ -3240,39 +3240,14 @@ void Graphics::textboxcentery(void)
int Graphics::textboxwrap(int pad) int Graphics::textboxwrap(int pad)
{ {
/* This function just takes a single-line textbox and wraps it... // TODO: delete this function?
* 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.
* Returns the new, total height of the textbox. */
if (!INBOUNDS_VEC(m, textboxes)) if (!INBOUNDS_VEC(m, textboxes))
{ {
vlog_error("textboxwrap() out-of-bounds!"); vlog_error("textboxwrap() out-of-bounds!");
return 16; return 16;
} }
if (textboxes[m].lines.empty())
{
vlog_error("textboxwrap() has no first line!");
return 16;
}
std::string wrapped = font::string_wordwrap_balanced(
textboxes[m].print_flags,
textboxes[m].lines[0],
36 * 8 - pad * 8
);
textboxes[m].lines.clear();
size_t startline = 0; return textboxes[m].wrap(pad);
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 textboxes[m].h;
} }
void Graphics::textboxpad(size_t left_pad, size_t right_pad) void Graphics::textboxpad(size_t left_pad, size_t right_pad)

View File

@ -5,6 +5,7 @@
#include "Font.h" #include "Font.h"
#include "Localization.h" #include "Localization.h"
#include "UTF8.h" #include "UTF8.h"
#include "Vlogging.h"
textboxclass::textboxclass(int gap) textboxclass::textboxclass(int gap)
{ {
@ -255,6 +256,40 @@ void textboxclass::centertext(void)
padtowidth(w-16); 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) void textboxclass::copyoriginaltext(void)
{ {
// Copy the original back, but keep the limit of lines in mind // Copy the original back, but keep the limit of lines in mind

View File

@ -95,6 +95,8 @@ public:
void centertext(void); void centertext(void);
int wrap(int pad);
void copyoriginaltext(void); void copyoriginaltext(void);
void applyoriginalspacing(void); void applyoriginalspacing(void);