diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index 0014ad91..c31576a3 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -6545,7 +6545,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("audio")); option(loc::gettext("game pad")); option(loc::gettext("accessibility")); - option(loc::gettext("language"), graphics.textboxes.empty()); + option(loc::gettext("language")); option(loc::gettext("return")); menuyoff = 0; maxspacing = 15; diff --git a/desktop_version/src/Graphics.cpp b/desktop_version/src/Graphics.cpp index 3813c162..b6f4272c 100644 --- a/desktop_version/src/Graphics.cpp +++ b/desktop_version/src/Graphics.cpp @@ -3320,6 +3320,50 @@ void Graphics::textboxbuttons(void) textboxes[m].fill_buttons = true; } +void Graphics::textboxcrewmateposition(const TextboxCrewmatePosition* crewmate_position) +{ + if (!INBOUNDS_VEC(m, textboxes)) + { + vlog_error("textboxcrewmateposition() out-of-bounds!"); + return; + } + + textboxes[m].crewmate_position = *crewmate_position; +} + +void Graphics::textboxoriginalcontext(const TextboxOriginalContext* original_context) +{ + if (!INBOUNDS_VEC(m, textboxes)) + { + vlog_error("textboxoriginalcontext() out-of-bounds!"); + return; + } + + textboxes[m].original = *original_context; +} + +void Graphics::textboxcase(char text_case) +{ + if (!INBOUNDS_VEC(m, textboxes)) + { + vlog_error("textboxcase() out-of-bounds!"); + return; + } + + textboxes[m].original.text_case = text_case; +} + +void Graphics::textboxtranslate(void) +{ + if (!INBOUNDS_VEC(m, textboxes)) + { + vlog_error("textboxtranslate() out-of-bounds!"); + return; + } + + textboxes[m].translate(); +} + void Graphics::textboxcommsrelay(void) { // Special treatment for the gamestate textboxes in Comms Relay diff --git a/desktop_version/src/Graphics.h b/desktop_version/src/Graphics.h index 7d02e9d8..3fca3944 100644 --- a/desktop_version/src/Graphics.h +++ b/desktop_version/src/Graphics.h @@ -114,6 +114,14 @@ public: void textboxbuttons(void); + void textboxcrewmateposition(const TextboxCrewmatePosition* crewmate_position); + + void textboxoriginalcontext(const TextboxOriginalContext* original_context); + + void textboxcase(char text_case); + + void textboxtranslate(void); + void textboxcommsrelay(void); void textboxadjust(void); diff --git a/desktop_version/src/Input.cpp b/desktop_version/src/Input.cpp index aaa09e0b..8ecf1e4a 100644 --- a/desktop_version/src/Input.cpp +++ b/desktop_version/src/Input.cpp @@ -1044,19 +1044,12 @@ static void menuactionpress(void) break; case 5: //language options - if (graphics.textboxes.empty()) - { - music.playef(Sound_VIRIDIAN); - loc::loadlanguagelist(); - loc::pre_title_lang_menu = false; - game.createmenu(Menu::language); - game.currentmenuoption = loc::languagelist_curlang; - map.nexttowercolour(); - } - else - { - music.playef(Sound_CRY); - } + music.playef(Sound_VIRIDIAN); + loc::loadlanguagelist(); + loc::pre_title_lang_menu = false; + game.createmenu(Menu::language); + game.currentmenuoption = loc::languagelist_curlang; + map.nexttowercolour(); break; default: /* Return */ @@ -1115,6 +1108,8 @@ static void menuactionpress(void) break; case Menu::language: { + std::string prev_lang = std::string(loc::lang); + music.playef(Sound_VIRIDIAN); if (loc::languagelist.size() != 0 && (unsigned)game.currentmenuoption < loc::languagelist.size()) @@ -1133,6 +1128,17 @@ static void menuactionpress(void) game.menustart = false; loc::pre_title_lang_menu = false; } + + if (prev_lang != loc::lang) + { + /* Retranslate and reposition all text boxes. */ + for (size_t i = 0; i < graphics.textboxes.size(); i++) + { + graphics.textboxes[i].translate(); + graphics.textboxes[i].adjust(); // FIXME: not all textboxes obey the 10-pixel inner border! + } + } + game.returnmenu(); map.nexttowercolour(); game.savestatsandsettings_menu(); diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index 41191eab..2c578c5e 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -385,15 +385,8 @@ static void menurender(void) font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable screen effects, enable slowdown modes or invincibility."), tr, tg, tb); break; case 5: - { font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Language"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the language."), tr, tg, tb); - - if (!graphics.textboxes.empty()) - { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Can not change the language while a textbox is displayed in-game."), tr, tg, tb); - } - } + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the language."), tr, tg, tb); } break; case Menu::graphicoptions: diff --git a/desktop_version/src/Script.cpp b/desktop_version/src/Script.cpp index 10b3ae17..690a2a54 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -42,14 +42,12 @@ scriptclass::scriptclass(void) r = 0; textx = 0; texty = 0; + textcrewmateposition = TextboxCrewmatePosition(); + textoriginalcontext = TextboxOriginalContext(); textbox_colours.clear(); add_default_colours(); textflipme = false; - textcentertext = false; textboxtimer = 0; - textpad_left = 0; - textpad_right = 0; - textpadtowidth = 0; textcase = 1; textbuttons = false; textlarge = false; @@ -525,15 +523,10 @@ void scriptclass::run(void) } } - textcentertext = false; - textpad_left = 0; - textpad_right = 0; - textpadtowidth = 0; textboxtimer = 0; + textcrewmateposition = TextboxCrewmatePosition(); textbox_sprites.clear(); textbox_image = TEXTIMAGE_NONE; - - translate_dialogue(); } else if (words[0] == "position") { @@ -569,35 +562,19 @@ void scriptclass::run(void) } } - int linegap = graphics.getlinegap(); - - //next is whether to position above or below - if (INBOUNDS_VEC(i, obj.entities) && words[2] == "above") + if (INBOUNDS_VEC(i, obj.entities) && (j == 0 || j == 1)) { - if (j == 1) //left + if (words[2] == "above") { - textx = obj.entities[i].xp -10000; //tells the box to be oriented correctly later - texty = obj.entities[i].yp - 16 - (txt.size() * (font::height(PR_FONT_LEVEL) + linegap) - linegap); + textcrewmateposition.text_above = true; + } - } - else if (j == 0) //Right - { - textx = obj.entities[i].xp - 16; - texty = obj.entities[i].yp - 18 - (txt.size() * (font::height(PR_FONT_LEVEL) + linegap) - linegap); - } - } - else if (INBOUNDS_VEC(i, obj.entities)) - { - if (j == 1) //left - { - textx = obj.entities[i].xp -10000; //tells the box to be oriented correctly later - texty = obj.entities[i].yp + 26; - } - else if (j == 0) //Right - { - textx = obj.entities[i].xp - 16; - texty = obj.entities[i].yp + 26; - } + textcrewmateposition.x = obj.entities[i].xp; + textcrewmateposition.override_x = true; + textcrewmateposition.y = obj.entities[i].yp; + textcrewmateposition.override_y = true; + + textcrewmateposition.dir = j; } } else if (words[0] == "customposition") @@ -669,34 +646,19 @@ void scriptclass::run(void) texty = -500; } - int linegap = graphics.getlinegap(); - - //next is whether to position above or below - if (INBOUNDS_VEC(i, obj.entities) && words[2] == "above") + if (INBOUNDS_VEC(i, obj.entities) && (j == 0 || j == 1)) { - if (j == 1) //left + if (words[2] == "above") { - textx = obj.entities[i].xp -10000; //tells the box to be oriented correctly later - texty = obj.entities[i].yp - 16 - (txt.size() * (font::height(PR_FONT_LEVEL) + linegap) - linegap); - } - else if (j == 0) //Right - { - textx = obj.entities[i].xp - 16; - texty = obj.entities[i].yp - 18 - (txt.size() * (font::height(PR_FONT_LEVEL) + linegap) - linegap); - } - } - else if (INBOUNDS_VEC(i, obj.entities)) - { - if (j == 1) //left - { - textx = obj.entities[i].xp -10000; //tells the box to be oriented correctly later - texty = obj.entities[i].yp + 26; - } - else if (j == 0) //Right - { - textx = obj.entities[i].xp - 16; - texty = obj.entities[i].yp + 26; + textcrewmateposition.text_above = true; } + + textcrewmateposition.x = obj.entities[i].xp; + textcrewmateposition.override_x = true; + textcrewmateposition.y = obj.entities[i].xp; + textcrewmateposition.override_y = true; + + textcrewmateposition.dir = j; } } else if (words[0] == "backgroundtext") @@ -769,40 +731,26 @@ void scriptclass::run(void) graphics.setimage(textbox_image); - // Some textbox formatting that can be set by translations... - if (textcentertext) - { - graphics.textboxcentertext(); - } - if (textpad_left > 0 || textpad_right > 0) - { - graphics.textboxpad(textpad_left, textpad_right); - } - if (textpadtowidth > 0) - { - graphics.textboxpadtowidth(textpadtowidth); - } - - //the textbox cannot be outside the screen. Fix if it is. - if (textx <= -1000) - { - //position to the left of the player - textx += 10000; - textx -= graphics.textboxwidth(); - textx += 16; - graphics.textboxmoveto(textx); - } - if (textx == -500 || textx == -1) { graphics.textboxcenterx(); + textcrewmateposition.override_x = false; } if (texty == -500) { graphics.textboxcentery(); + textcrewmateposition.override_y = false; } + TextboxOriginalContext context = TextboxOriginalContext(); + context.text_case = textcase; + context.lines = std::vector(txt); + context.script_name = scriptname; + + graphics.textboxcrewmateposition(&textcrewmateposition); + graphics.textboxoriginalcontext(&context); + graphics.textboxcase(textcase); if (map.custommode) { uint32_t flags = PR_FONT_IDX(font::font_idx_level, cl.rtl); @@ -812,6 +760,7 @@ void scriptclass::run(void) } graphics.textboxprintflags(flags); } + graphics.textboxtranslate(); graphics.textboxadjust(); if (words[0] == "speak_active") @@ -1985,8 +1934,6 @@ void scriptclass::run(void) } break; } - - translate_dialogue(); } else if (words[0] == "trinketbluecontrol") { @@ -2557,68 +2504,6 @@ void scriptclass::run(void) } } -void scriptclass::translate_dialogue(void) -{ - char tc = textcase; - textcase = 1; - - if (!loc::is_cutscene_translated(scriptname)) - { - return; - } - - // English text needs to be un-wordwrapped, translated, and re-wordwrapped - std::string eng; - for (size_t i = 0; i < txt.size(); i++) - { - if (i != 0) - { - eng.append("\n"); - } - eng.append(txt[i]); - } - - eng = font::string_unwordwrap(eng); - const loc::TextboxFormat* format = loc::gettext_cutscene(scriptname, eng, tc); - if (format == NULL || format->text == NULL || format->text[0] == '\0') - { - 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); - } - - textcentertext = format->centertext; - textpad_left = format->pad_left; - textpad_right = format->pad_right; - textpadtowidth = format->padtowidth; - - txt.clear(); - size_t startline = 0; - size_t newline; - do { - newline = tra.find('\n', startline); - txt.push_back(tra.substr(startline, newline-startline)); - startline = newline+1; - } while (newline != std::string::npos); -} - static void gotoerrorloadinglevel(void) { game.quittomenu(); diff --git a/desktop_version/src/Script.h b/desktop_version/src/Script.h index bf9f8d97..aa71220d 100644 --- a/desktop_version/src/Script.h +++ b/desktop_version/src/Script.h @@ -91,8 +91,6 @@ public: void run(void); - void translate_dialogue(void); - void startgamemode(enum StartMode mode); void teleport(void); @@ -114,12 +112,10 @@ public: std::map textbox_colours; int textx; int texty; + TextboxCrewmatePosition textcrewmateposition; + TextboxOriginalContext textoriginalcontext; int r,g,b; bool textflipme; - bool textcentertext; - size_t textpad_left; - size_t textpad_right; - size_t textpadtowidth; char textcase; bool textbuttons; bool textlarge; diff --git a/desktop_version/src/Textbox.cpp b/desktop_version/src/Textbox.cpp index d49f40aa..592090ea 100644 --- a/desktop_version/src/Textbox.cpp +++ b/desktop_version/src/Textbox.cpp @@ -3,6 +3,7 @@ #include #include "Font.h" +#include "Localization.h" #include "UTF8.h" textboxclass::textboxclass(int gap) @@ -33,6 +34,10 @@ textboxclass::textboxclass(int gap) sprites.clear(); image = TEXTIMAGE_NONE; + + crewmate_position = TextboxCrewmatePosition(); + original = TextboxOriginalContext(); + original.text_case = 1; } void textboxclass::addsprite(int x, int y, int tile, int col) @@ -66,6 +71,7 @@ void textboxclass::centery(void) void textboxclass::adjust(void) { resize(); + repositionfromcrewmate(); if (xp < 10) xp = 10; if (yp < 10) yp = 10; if (xp + w > 310) xp = 310 - w; @@ -136,6 +142,42 @@ void textboxclass::resize(void) 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); @@ -192,3 +234,75 @@ void textboxclass::centertext(void) { padtowidth(w-16); } + +void textboxclass::translate(void) +{ + if (!loc::is_cutscene_translated(original.script_name)) + { + // Copy the original back + lines = std::vector(original.lines); + 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') + { + 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); + } +} diff --git a/desktop_version/src/Textbox.h b/desktop_version/src/Textbox.h index 176885b7..4a6ef58f 100644 --- a/desktop_version/src/Textbox.h +++ b/desktop_version/src/Textbox.h @@ -5,6 +5,25 @@ #include #include +/* Position of the crewmate that the text box position is based off of. + * NOT a crewmate sprite inside the text box (that's a TextboxSprite). */ +struct TextboxCrewmatePosition +{ + bool override_x; + bool override_y; + int x; + int y; + int dir; + bool text_above; +}; + +struct TextboxOriginalContext +{ + std::vector lines; + std::string script_name; + char text_case; +}; + struct TextboxSprite { int x; @@ -45,6 +64,8 @@ public: void resize(void); + void repositionfromcrewmate(void); + void addline(const std::string& t); void pad(size_t left_pad, size_t right_pad); @@ -52,6 +73,8 @@ public: void padtowidth(size_t new_w); void centertext(void); + + void translate(void); public: //Fundamentals std::vector lines; @@ -76,6 +99,9 @@ public: std::vector sprites; TextboxImage image; + + TextboxCrewmatePosition crewmate_position; + TextboxOriginalContext original; }; #endif /* TEXTBOX_H */