Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion BH/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

#define CODE_PAGE CP_UTF8 // 65001 - UTF-8: supports all Unicode languages

#define MAX_ITEM_NAME_SIZE 56
#define MAX_ITEM_NAME_LINE_SIZE 56 // Visible characters on one line of an item name (color codes not counted)
#define MAX_ITEM_NAME_TOTAL_SIZE 72 // Visible characters in a whole item name, line breaks included
#define MAX_ITEM_TEXT_SIZE 512 // Half of the maximum in-game limit
#define BOOK_NAME_SIZE_LIMIT 127 // TP & ID Tomes have a smaller limit for some reason
#define ITEM_TEXT_SIZE_LIMIT 1023
Expand Down
64 changes: 40 additions & 24 deletions BH/Modules/Item/ItemDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3023,6 +3023,14 @@ bool IsWhitespaceEquivalent(wchar_t ch)
return iswspace(ch) || ch == L'\b';
}

// A color code at position i: \xFF, 'c', then one of 0-9 ; : or \x01-\x1F (3 wchar_t)
bool IsColorCode(const wstring& text, size_t i)
{
if (i + 2 >= text.size() || text[i] != L'\xFF' || text[i + 1] != L'c') { return false; }
const wchar_t c = text[i + 2];
return (c >= L'0' && c <= L'9') || c == L';' || c == L':' || (c >= L'\x01' && c <= L'\x1F');
}

void TrimItemText(UnitItemInfo* uInfo,
wstring& name,
BOOL bLimit)
Expand Down Expand Up @@ -3065,39 +3073,47 @@ void TrimItemText(UnitItemInfo* uInfo,
}
name.resize(offset);

int nColorCodesSize = 0;
int lengthLimit = 0;
if (bLimit)
{
// Calc the extra size from colors
// In wide strings: color code is \xFF followed by 'c' followed by digit = 3 wchar_t
std::wregex color_reg(L"\\xFFc[0-9;:\\x01-\\x1F]", std::regex_constants::ECMAScript);
auto color_matches = std::wsregex_iterator(name.begin(), name.end(), color_reg);
auto color_end = std::wsregex_iterator();
auto match_count = std::distance(color_matches, color_end);
nColorCodesSize += 3 * match_count;

bool inShop = (uInfo->item->pItemData->pOwnerInventory != 0 && // Skip on ground items
uInfo->item->pItemData->pOwnerInventory->pOwner != 0 &&
find(begin(ShopNPCs), end(ShopNPCs), uInfo->item->pItemData->pOwnerInventory->pOwner->dwTxtFileNo) != end(ShopNPCs));

// Increase limit for shop items
lengthLimit = inShop ? MAX_ITEM_TEXT_SIZE : MAX_ITEM_NAME_SIZE;

int nColorsToKeep = 0;
for (std::wsregex_iterator k = color_matches; k != color_end; ++k)
// Count visible characters only (color codes take no room). One line may hold up to
// MAX_ITEM_NAME_LINE_SIZE, so a label is never wider than before; a name with several
// lines may hold up to MAX_ITEM_NAME_TOTAL_SIZE in all. Shop items keep their larger limit.
const int lineLimit = inShop ? MAX_ITEM_TEXT_SIZE : MAX_ITEM_NAME_LINE_SIZE;
const int totalLimit = inShop ? MAX_ITEM_TEXT_SIZE : MAX_ITEM_NAME_TOTAL_SIZE;

wstring trimmed;
trimmed.reserve(name.size());
int lineChars = 0;
int totalChars = 0;
size_t keepSize = 0; // trimmed size up to the last visible character kept
bool cut = false;
for (size_t i = 0; i < name.size(); ++i)
{
std::wsmatch match = *k;
auto pos = match.position();
if (pos - (nColorsToKeep) > lengthLimit) { break; }
nColorsToKeep += 3;
}

// Truncate if too long
if (name.size() - nColorCodesSize > lengthLimit)
if (IsColorCode(name, i))
{
// Keep color codes, even inside a cut part of a line: they color the lines after it
trimmed.append(name, i, 3);
i += 2;
continue;
}
if (totalChars >= totalLimit) { cut = true; break; }
const wchar_t ch = name[i];
if (ch == L'\n') { lineChars = 0; }
else if (lineChars >= lineLimit) { cut = true; continue; } // rest of this line: drop up to the next line break
else { ++lineChars; }
trimmed += ch;
++totalChars;
keepSize = trimmed.size();
}
if (cut)
{
int max_size = lengthLimit + nColorsToKeep;
name.resize(max_size);
trimmed.resize(keepSize); // drop color codes left after the last visible character
name = trimmed;
}
}

Expand Down