Browse Source

Fix talk/store text overlap for Chinese and Japanese

We ensure that selectable lines are placed at the same vertical
coordinates but space out unselectable text lines at the cost
of reduced heigh of empty space between the store items.

We also have to move the back button in scrollable lists to the
lower right.

This can definitely be improved further but at least it solves
the problem for now.

Refs #3162
pull/3550/head
Gleb Mazovetskiy 4 years ago
parent
commit
4f64b87330
  1. 230
      Source/stores.cpp
  2. 11
      Source/stores.h
  3. 7
      Source/utils/language.cpp
  4. 3
      Source/utils/language.h

230
Source/stores.cpp

@ -99,6 +99,62 @@ const char *const TownerNames[] = {
N_("Wirt"),
};
constexpr int PaddingTop = 32;
// For most languages, line height is always 12.
// This includes blank lines and divider line.
constexpr int SmallLineHeight = 12;
constexpr int SmallTextHeight = 12;
// For larger small fonts (Chinese and Japanese), text lines are
// taller and overflow.
// We space out blank lines a bit more to give space to 3-line store items.
constexpr int LargeLineHeight = SmallLineHeight + 1;
constexpr int LargeTextHeight = 18;
/**
* The line index with the Back / Leave button.
* This is a special button that is always the last line.
*
* For lists with a scrollbar, it is not selectable (mouse-only).
*/
int BackButtonLine()
{
if (IsSmallFontTall()) {
return stextscrl ? 21 : 20;
}
return 22;
}
int LineHeight()
{
return IsSmallFontTall() ? LargeLineHeight : SmallLineHeight;
}
int TextHeight()
{
return IsSmallFontTall() ? LargeTextHeight : SmallTextHeight;
}
void CalculateLineHeights()
{
stext[0].y = 0;
if (IsSmallFontTall()) {
for (int i = 1; i < STORE_LINES; ++i) {
// Space out consecutive text lines, unless they are both selectable (never the case currently).
if (stext[i].IsText() && stext[i - 1].IsText() && !(stext[i]._ssel && stext[i - 1]._ssel)) {
stext[i].y = stext[i - 1].y + LargeTextHeight;
} else {
stext[i].y = i * LargeLineHeight;
}
}
} else {
for (int i = 1; i < STORE_LINES; ++i) {
stext[i].y = i * SmallLineHeight;
}
}
}
void DrawSTextBack(const Surface &out)
{
CelDrawTo(out, { PANEL_X + 320 + 24, 327 + UI_OFFSET_Y }, *pSTextBoxCels, 1);
@ -122,7 +178,7 @@ void DrawSSlider(const Surface &out, int y1, int y2)
for (; yd3 < yd2; yd3 += 12) {
CelDrawTo(out, { PANEL_X + 601, yd3 }, *pSTextSlidCels, 14);
}
if (stextsel == 22)
if (stextsel == BackButtonLine())
yd3 = stextlhold;
else
yd3 = stextsel;
@ -146,11 +202,6 @@ void AddSTextVal(int y, int val)
stext[y]._sval = val;
}
void OffsetSTextY(int y, int yo)
{
stext[y]._syoff = yo;
}
void AddSText(int x, int y, const char *str, UiFlags flags, bool sel)
{
stext[y]._sx = x;
@ -161,6 +212,26 @@ void AddSText(int x, int y, const char *str, UiFlags flags, bool sel)
stext[y]._ssel = sel;
}
void AddOptionsBackButton()
{
const int line = BackButtonLine();
AddSText(0, line, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
stext[line]._syoff = IsSmallFontTall() ? 0 : 6;
}
void AddItemListBackButton(bool selectable = false)
{
const int line = BackButtonLine();
const char *text = _("Back");
if (!selectable && IsSmallFontTall()) {
AddSText(0, line, text, UiFlags::ColorWhite | UiFlags::AlignRight, selectable);
} else {
AddSLine(line - 1);
AddSText(0, line, text, UiFlags::ColorWhite | UiFlags::AlignCenter, selectable);
stext[line]._syoff = 6;
}
}
void PrintStoreItem(Item *x, int l, UiFlags flags)
{
char sstr[128];
@ -275,7 +346,7 @@ void ScrollSmithBuy(int idx)
}
}
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != 22)
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != BackButtonLine())
stextsel = stextdown;
}
@ -290,10 +361,8 @@ void StartSmithBuy()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollSmithBuy(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
OffsetSTextY(22, 6);
AddItemListBackButton();
storenumh = 0;
for (int i = 0; !smithitem[i].isEmpty(); i++) {
@ -326,7 +395,7 @@ void ScrollSmithPremiumBuy(int boughtitems)
}
idx++;
}
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != 22)
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != BackButtonLine())
stextsel = stextdown;
}
@ -352,9 +421,7 @@ bool StartSmithPremiumBuy()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
OffsetSTextY(22, 6);
AddItemListBackButton();
stextsmax = std::max(storenumh - 4, 0);
@ -475,9 +542,7 @@ void StartSmithSell()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton(/*selectable=*/true);
return;
}
@ -490,10 +555,8 @@ void StartSmithSell()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollSmithSell(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton();
}
bool SmithRepairOk(int i)
@ -565,9 +628,7 @@ void StartSmithRepair()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton(/*selectable=*/true);
return;
}
@ -580,10 +641,9 @@ void StartSmithRepair()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollSmithSell(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton();
}
void FillManaPlayer()
@ -639,7 +699,7 @@ void ScrollWitchBuy(int idx)
}
}
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != 22)
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != BackButtonLine())
stextsel = stextdown;
}
@ -655,10 +715,8 @@ void StartWitchBuy()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollWitchBuy(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
OffsetSTextY(22, 6);
AddItemListBackButton();
storenumh = 0;
for (int i = 0; !witchitem[i].isEmpty(); i++) {
@ -747,9 +805,7 @@ void StartWitchSell()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton(/*selectable=*/true);
return;
}
@ -762,10 +818,8 @@ void StartWitchSell()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollSmithSell(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton();
}
bool WitchRechargeOk(int i)
@ -828,9 +882,7 @@ void StartWitchRecharge()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton(/*selectable=*/true);
return;
}
@ -843,10 +895,8 @@ void StartWitchRecharge()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollSmithSell(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton();
}
void StoreNoMoney()
@ -958,7 +1008,7 @@ void SStartBoyBuy()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
UiFlags itemColor = boyitem.getTextColorWithStatCheck();
if (boyitem._iMagical != ITEM_QUALITY_NORMAL)
@ -971,8 +1021,15 @@ void SStartBoyBuy()
else
AddSTextVal(10, boyitem._iIvalue + (boyitem._iIvalue / 2));
PrintStoreItem(&boyitem, 11, itemColor);
AddSText(0, 22, _("Leave"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
{
// Add a Leave button. Unlike the other item list back buttons,
// this one has different text and different layout in LargerSmallFont locales.
const int line = BackButtonLine();
AddSLine(line - 1);
AddSText(0, line, _("Leave"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
stext[line]._syoff = 6;
}
}
void HealPlayer()
@ -1018,7 +1075,7 @@ void ScrollHealerBuy(int idx)
}
}
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != 22)
if (stextsel != -1 && !stext[stextsel]._ssel && stextsel != BackButtonLine())
stextsel = stextdown;
}
@ -1033,10 +1090,9 @@ void StartHealerBuy()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollHealerBuy(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
OffsetSTextY(22, 6);
AddItemListBackButton();
storenumh = 0;
for (int i = 0; !healitem[i].isEmpty(); i++) {
@ -1150,9 +1206,7 @@ void StartStorytellerIdentify()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton(/*selectable=*/true);
return;
}
@ -1165,10 +1219,9 @@ void StartStorytellerIdentify()
AddSText(0, 1, tempstr, UiFlags::ColorWhitegold | UiFlags::AlignCenter, false);
AddSLine(3);
AddSLine(21);
ScrollSmithSell(stextsval);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
OffsetSTextY(22, 6);
AddItemListBackButton();
}
void StartStorytellerIdentifyShow()
@ -1202,7 +1255,7 @@ void StartTalk()
AddSText(0, 12, _("is not available"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
AddSText(0, 14, _("in the shareware"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
AddSText(0, 16, _("version"), UiFlags::ColorWhite | UiFlags::AlignCenter, false);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
AddOptionsBackButton();
return;
}
@ -1229,7 +1282,7 @@ void StartTalk()
}
}
AddSText(0, sn2, _("Gossip"), UiFlags::ColorBlue | UiFlags::AlignCenter, true);
AddSText(0, 22, _("Back"), UiFlags::ColorWhite | UiFlags::AlignCenter, true);
AddOptionsBackButton();
}
void StartTavern()
@ -1324,7 +1377,7 @@ void SmithBuyItem()
void SmithBuyEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_SMITH);
stextsel = 12;
return;
@ -1384,7 +1437,7 @@ void SmithBuyPItem()
void SmithPremiumBuyEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_SMITH);
stextsel = 14;
return;
@ -1529,7 +1582,7 @@ void StoreSellItem()
void SmithSellEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_SMITH);
stextsel = 16;
return;
@ -1578,7 +1631,7 @@ void SmithRepairItem()
void SmithRepairEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_SMITH);
stextsel = 18;
return;
@ -1656,7 +1709,7 @@ void WitchBuyItem()
void WitchBuyEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_WITCH);
stextsel = 14;
return;
@ -1689,7 +1742,7 @@ void WitchBuyEnter()
void WitchSellEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_WITCH);
stextsel = 16;
return;
@ -1730,7 +1783,7 @@ void WitchRechargeItem()
void WitchRechargeEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_WITCH);
stextsel = 18;
return;
@ -1939,7 +1992,7 @@ void ConfirmEnter()
StartStore(stextshold);
if (stextsel == 22)
if (stextsel == BackButtonLine())
return;
stextsel = stextlhold;
@ -1972,7 +2025,7 @@ void HealerEnter()
void HealerBuyEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_HEALER);
stextsel = 16;
return;
@ -2026,7 +2079,7 @@ void StorytellerEnter()
void StorytellerIdentifyEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(STORE_STORY);
stextsel = 14;
return;
@ -2048,7 +2101,7 @@ void StorytellerIdentifyEnter()
void TalkEnter()
{
if (stextsel == 22) {
if (stextsel == BackButtonLine()) {
StartStore(stextshold);
stextsel = stextlhold;
return;
@ -2252,7 +2305,7 @@ void PrintSString(const Surface &out, int margin, int line, const char *text, Ui
sx += 320;
}
int sy = UI_OFFSET_Y + 32 + line * 12 + stext[line]._syoff;
const int sy = UI_OFFSET_Y + PaddingTop + stext[line].y + stext[line]._syoff;
int width = stextsize ? 575 : 255;
if (stextscrl && line >= 4 && line <= 20) {
@ -2276,7 +2329,7 @@ void PrintSString(const Surface &out, int margin, int line, const char *text, Ui
void DrawSLine(const Surface &out, int y)
{
int sx = 26;
int sy = y * 12;
const int sy = PaddingTop + stext[y].y + TextHeight() / 2;
int width = 587;
if (!stextsize) {
@ -2285,7 +2338,7 @@ void DrawSLine(const Surface &out, int y)
}
BYTE *src = out.at(PANEL_LEFT + sx, UI_OFFSET_Y + 25);
BYTE *dst = out.at(PANEL_X + sx, UI_OFFSET_Y + sy + 38);
BYTE *dst = out.at(PANEL_X + sx, UI_OFFSET_Y + sy);
for (int i = 0; i < 3; i++, src += out.pitch(), dst += out.pitch())
memcpy(dst, src, width);
@ -2453,10 +2506,11 @@ void DrawSText(const Surface &out)
}
}
CalculateLineHeights();
for (int i = 0; i < STORE_LINES; i++) {
if (stext[i]._sline != 0)
if (stext[i].IsDivider())
DrawSLine(out, i);
if (stext[i]._sstr[0] != '\0')
if (stext[i].IsText())
PrintSString(out, stext[i]._sx, i, stext[i]._sstr, stext[i].flags, stext[i]._sval);
}
@ -2745,7 +2799,7 @@ void CheckStoreBtn()
qtextflag = false;
if (leveltype == DTYPE_TOWN)
stream_stop();
} else if (stextsel != -1 && MousePosition.y >= (32 + UI_OFFSET_Y) && MousePosition.y <= (320 + UI_OFFSET_Y)) {
} else if (stextsel != -1 && MousePosition.y >= (PaddingTop + UI_OFFSET_Y) && MousePosition.y <= (320 + UI_OFFSET_Y)) {
if (!stextsize) {
if (MousePosition.x < 344 + PANEL_LEFT || MousePosition.x > 616 + PANEL_LEFT)
return;
@ -2753,8 +2807,12 @@ void CheckStoreBtn()
if (MousePosition.x < 24 + PANEL_LEFT || MousePosition.x > 616 + PANEL_LEFT)
return;
}
int y = (MousePosition.y - (32 + UI_OFFSET_Y)) / 12;
const int relativeY = MousePosition.y - (UI_OFFSET_Y + PaddingTop);
if (stextscrl && MousePosition.x > 600 + PANEL_LEFT) {
// Scroll bar is always measured in terms of the small line height.
int y = relativeY / SmallLineHeight;
if (y == 4) {
if (stextscrlubtn <= 0) {
StoreUp();
@ -2771,17 +2829,29 @@ void CheckStoreBtn()
stextscrldbtn--;
}
}
} else if (y >= 5) {
if (y >= 23)
y = 22;
if (stextscrl && y < 21 && !stext[y]._ssel) {
return;
}
int y = relativeY / LineHeight();
// Large small fonts draw beyond LineHeight. Check if the click was on the overflow text.
if (IsSmallFontTall() && y > 0 && y < STORE_LINES
&& stext[y - 1].IsText() && !stext[y].IsText()
&& relativeY < stext[y - 1].y + LargeTextHeight) {
--y;
}
if (y >= 5) {
if (y >= BackButtonLine() + 1)
y = BackButtonLine();
if (stextscrl && y <= 20 && !stext[y]._ssel) {
if (stext[y - 2]._ssel) {
y -= 2;
} else if (stext[y - 1]._ssel) {
y--;
}
}
if (stext[y]._ssel || (stextscrl && y == 22)) {
if (stext[y]._ssel || (stextscrl && y == BackButtonLine())) {
stextsel = y;
StoreEnter();
}

11
Source/stores.h

@ -52,6 +52,17 @@ struct STextStruct {
int _sline;
bool _ssel;
int _sval;
int y;
[[nodiscard]] bool IsDivider() const
{
return _sline != 0;
}
[[nodiscard]] bool IsText() const
{
return _sstr[0] != '\0';
}
};
/** Shop frame graphics */

7
Source/utils/language.cpp

@ -9,6 +9,7 @@
#include "engine/assets.hpp"
#include "utils/file_util.h"
#include "utils/paths.h"
#include "utils/stdcompat/string_view.hpp"
using namespace devilution;
#define MO_MAGIC 0x950412de
@ -279,6 +280,12 @@ bool HasTranslation(const std::string &locale)
return false;
}
bool IsSmallFontTall()
{
string_view code(sgOptions.Language.szCode, 2);
return code == "zh" || code == "ja" || code == "ko";
}
void LanguageInitialize()
{
const std::string lang = sgOptions.Language.szCode;

3
Source/utils/language.h

@ -14,3 +14,6 @@ const std::string &LanguageParticularTranslate(const char *context, const char *
const std::string &LanguagePluralTranslate(const char *singular, const char *plural, int count);
const std::string &LanguageTranslate(const char *key);
const char *LanguageMetadata(const char *key);
// Chinese and Japanese, and Korean small font is 16px instead of a 12px one for readability.
bool IsSmallFontTall();

Loading…
Cancel
Save