Automatic Line Breaks, somewhat even lines (#5689)
Co-authored-by: Hedara <hedara90@gmail.com> Co-authored-by: Eduardo Quezada <eduardo602002@gmail.com>
This commit is contained in:
parent
5da1f322d2
commit
e4ef3a440f
6 changed files with 331 additions and 50 deletions
|
@ -10,6 +10,7 @@
|
||||||
max(POKEMON_NAME_LENGTH + 1, \
|
max(POKEMON_NAME_LENGTH + 1, \
|
||||||
ABILITY_NAME_LENGTH + 1)))
|
ABILITY_NAME_LENGTH + 1)))
|
||||||
#define BATTLE_MSG_MAX_WIDTH 208
|
#define BATTLE_MSG_MAX_WIDTH 208
|
||||||
|
#define BATTLE_MSG_MAX_LINES 2
|
||||||
|
|
||||||
// for 0xFD
|
// for 0xFD
|
||||||
#define B_TXT_BUFF1 0x0
|
#define B_TXT_BUFF1 0x0
|
||||||
|
|
33
include/line_break.h
Normal file
33
include/line_break.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef GUARD_LINE_BREAK_H
|
||||||
|
#define GUARD_LINE_BREAK_H
|
||||||
|
|
||||||
|
#define BADNESS_UNFILLED 1 // Badness added per pixel diff from max width
|
||||||
|
#define BADNESS_JAGGED 1 // Badness added per pixel diff from longest, squared per line
|
||||||
|
#define BADNESS_RUNT 100 // Badness added if there's a runt
|
||||||
|
#define BADNESS_OVERFLOW 100 // Badness added per pixel overflow, squared per line (not used)
|
||||||
|
#define BADNESS_WIDE_SPACE 1 // Badness added per extra pixel width (not used)
|
||||||
|
#define MAX_SPACE_WIDTH 5
|
||||||
|
|
||||||
|
struct StringWord {
|
||||||
|
u32 startIndex:16;
|
||||||
|
u32 length:8;
|
||||||
|
u32 width:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StringLine {
|
||||||
|
struct StringWord *words;
|
||||||
|
u16 numWords;
|
||||||
|
u8 spaceWidth;
|
||||||
|
u8 extraSpaceWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
void StripLineBreaks(u8 *src);
|
||||||
|
void BreakStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId);
|
||||||
|
void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId);
|
||||||
|
|
||||||
|
bool32 IsWordSplittingChar(const u8 *src, u32 index);
|
||||||
|
u32 GetStringBadness(struct StringLine *stringLines, u32 numLines, u32 maxWidth);
|
||||||
|
void BuildNewString(struct StringLine *stringLines, u32 numLines, u32 maxLines, u8 *str);
|
||||||
|
bool32 StringHasManualBreaks(u8 *src);
|
||||||
|
|
||||||
|
#endif // GUARD_LINE_BREAK_H
|
|
@ -31,6 +31,7 @@
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
#include "line_break.h"
|
||||||
#include "constants/battle_anim.h"
|
#include "constants/battle_anim.h"
|
||||||
#include "constants/battle_move_effects.h"
|
#include "constants/battle_move_effects.h"
|
||||||
#include "constants/battle_partner.h"
|
#include "constants/battle_partner.h"
|
||||||
|
@ -2044,6 +2045,7 @@ static void PlayerHandleChooseAction(u32 battler)
|
||||||
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
|
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
|
||||||
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
|
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
|
||||||
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo);
|
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo);
|
||||||
|
BreakStringAutomatic(gDisplayedStringBattle, WindowWidthPx(B_WIN_ACTION_PROMPT), 2, FONT_NORMAL);
|
||||||
|
|
||||||
if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(B_POSITION_PLAYER_RIGHT))
|
if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(B_POSITION_PLAYER_RIGHT))
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
#include "line_break.h"
|
||||||
#include "constants/battle_anim.h"
|
#include "constants/battle_anim.h"
|
||||||
#include "constants/songs.h"
|
#include "constants/songs.h"
|
||||||
#include "constants/trainers.h"
|
#include "constants/trainers.h"
|
||||||
|
@ -298,6 +299,7 @@ static void SafariHandleChooseAction(u32 battler)
|
||||||
|
|
||||||
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
|
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
|
||||||
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo2);
|
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo2);
|
||||||
|
BreakStringAutomatic(gDisplayedStringBattle, WindowWidthPx(B_WIN_ACTION_PROMPT), 2, FONT_NORMAL);
|
||||||
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT);
|
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
#include "trainer_hill.h"
|
#include "trainer_hill.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
#include "line_break.h"
|
||||||
#include "constants/abilities.h"
|
#include "constants/abilities.h"
|
||||||
#include "constants/battle_dome.h"
|
#include "constants/battle_dome.h"
|
||||||
#include "constants/battle_string_ids.h"
|
#include "constants/battle_string_ids.h"
|
||||||
|
@ -165,6 +166,11 @@ const u8 gText_drastically[] = _("drastically ");
|
||||||
const u8 gText_severely[] = _("severely ");
|
const u8 gText_severely[] = _("severely ");
|
||||||
static const u8 sText_TerrainReturnedToNormal[] = _("The terrain returned to normal!"); // Unused
|
static const u8 sText_TerrainReturnedToNormal[] = _("The terrain returned to normal!"); // Unused
|
||||||
|
|
||||||
|
// Remove these when done testing
|
||||||
|
static const u8 sTest_TempTestText1[] = _("This is a text for testing stuff.");
|
||||||
|
static const u8 sTest_TempTestText2[] = _("This is a text for testing stuff that should be two lines.");
|
||||||
|
static const u8 sTest_TempTestText3[] = _("This is a text for testing stuff that should be three lines so it has to have some extra text.");
|
||||||
|
|
||||||
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
|
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
|
||||||
{
|
{
|
||||||
[STRINGID_TRAINER1LOSETEXT] = COMPOUND_STRING("{B_TRAINER1_LOSE_TEXT}"),
|
[STRINGID_TRAINER1LOSETEXT] = COMPOUND_STRING("{B_TRAINER1_LOSE_TEXT}"),
|
||||||
|
@ -1402,8 +1408,8 @@ const u8 gText_PkmnIsEvolving[] = _("What?\n{STR_VAR_1} is evolving!");
|
||||||
const u8 gText_CongratsPkmnEvolved[] = _("Congratulations! Your {STR_VAR_1}\nevolved into {STR_VAR_2}!{WAIT_SE}\p");
|
const u8 gText_CongratsPkmnEvolved[] = _("Congratulations! Your {STR_VAR_1}\nevolved into {STR_VAR_2}!{WAIT_SE}\p");
|
||||||
const u8 gText_PkmnStoppedEvolving[] = _("Huh? {STR_VAR_1}\nstopped evolving!\p");
|
const u8 gText_PkmnStoppedEvolving[] = _("Huh? {STR_VAR_1}\nstopped evolving!\p");
|
||||||
const u8 gText_EllipsisQuestionMark[] = _("……?\p");
|
const u8 gText_EllipsisQuestionMark[] = _("……?\p");
|
||||||
const u8 gText_WhatWillPkmnDo[] = _("What will\n{B_BUFF1} do?");
|
const u8 gText_WhatWillPkmnDo[] = _("What will {B_BUFF1} do?");
|
||||||
const u8 gText_WhatWillPkmnDo2[] = _("What will\n{B_PLAYER_NAME} do?");
|
const u8 gText_WhatWillPkmnDo2[] = _("What will {B_PLAYER_NAME} do?");
|
||||||
const u8 gText_WhatWillWallyDo[] = _("What will\nWALLY do?");
|
const u8 gText_WhatWillWallyDo[] = _("What will\nWALLY do?");
|
||||||
const u8 gText_LinkStandby[] = _("{PAUSE 16}Link standby…");
|
const u8 gText_LinkStandby[] = _("{PAUSE 16}Link standby…");
|
||||||
const u8 gText_BattleMenu[] = _("Battle{CLEAR_TO 56}Bag\nPokémon{CLEAR_TO 56}Run");
|
const u8 gText_BattleMenu[] = _("Battle{CLEAR_TO 56}Bag\nPokémon{CLEAR_TO 56}Run");
|
||||||
|
@ -2421,8 +2427,7 @@ static void GetBattlerNick(u32 battler, u8 *dst)
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
GetBattlerNick(battler, text); \
|
GetBattlerNick(battler, text); \
|
||||||
toCpy = text; \
|
toCpy = text;
|
||||||
dstWidth = GetStringLineWidth(fontId, dst, letterSpacing, lineNum, dstSize);
|
|
||||||
|
|
||||||
#define HANDLE_NICKNAME_STRING_LOWERCASE(battler) \
|
#define HANDLE_NICKNAME_STRING_LOWERCASE(battler) \
|
||||||
if (GetBattlerSide(battler) != B_SIDE_PLAYER) \
|
if (GetBattlerSide(battler) != B_SIDE_PLAYER) \
|
||||||
|
@ -2439,8 +2444,7 @@ static void GetBattlerNick(u32 battler, u8 *dst)
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
GetBattlerNick(battler, text); \
|
GetBattlerNick(battler, text); \
|
||||||
toCpy = text; \
|
toCpy = text;
|
||||||
dstWidth = GetStringLineWidth(fontId, dst, letterSpacing, lineNum, dstSize);
|
|
||||||
|
|
||||||
static const u8 *BattleStringGetOpponentNameByTrainerId(u16 trainerId, u8 *text, u8 multiplayerId, u8 battler)
|
static const u8 *BattleStringGetOpponentNameByTrainerId(u16 trainerId, u8 *text, u8 multiplayerId, u8 battler)
|
||||||
{
|
{
|
||||||
|
@ -2589,17 +2593,10 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
|
||||||
{
|
{
|
||||||
u32 dstID = 0; // if they used dstID, why not use srcID as well?
|
u32 dstID = 0; // if they used dstID, why not use srcID as well?
|
||||||
const u8 *toCpy = NULL;
|
const u8 *toCpy = NULL;
|
||||||
u32 lastValidSkip = 0;
|
|
||||||
u32 toCpyWidth = 0;
|
|
||||||
u32 dstWidth = 0;
|
|
||||||
// This buffer may hold either the name of a trainer, Pokémon, or item.
|
|
||||||
u8 text[max(max(max(32, TRAINER_NAME_LENGTH + 1), POKEMON_NAME_LENGTH + 1), ITEM_NAME_LENGTH)];
|
u8 text[max(max(max(32, TRAINER_NAME_LENGTH + 1), POKEMON_NAME_LENGTH + 1), ITEM_NAME_LENGTH)];
|
||||||
u8 *textStart = &text[0];
|
u8 *textStart = &text[0];
|
||||||
u8 multiplayerId;
|
u8 multiplayerId;
|
||||||
u8 fontId = FONT_NORMAL;
|
u8 fontId = FONT_NORMAL;
|
||||||
s16 letterSpacing = 0;
|
|
||||||
u32 lineNum = 1;
|
|
||||||
u32 displayedLineNums = 1;
|
|
||||||
|
|
||||||
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK)
|
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK)
|
||||||
multiplayerId = gRecordedBattleMultiplayerId;
|
multiplayerId = gRecordedBattleMultiplayerId;
|
||||||
|
@ -2617,7 +2614,6 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
|
||||||
while (*src != EOS)
|
while (*src != EOS)
|
||||||
{
|
{
|
||||||
toCpy = NULL;
|
toCpy = NULL;
|
||||||
dstWidth = GetStringLineWidth(fontId, dst, letterSpacing, lineNum, dstSize);
|
|
||||||
|
|
||||||
if (*src == PLACEHOLDER_BEGIN)
|
if (*src == PLACEHOLDER_BEGIN)
|
||||||
{
|
{
|
||||||
|
@ -3122,18 +3118,6 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
|
||||||
|
|
||||||
if (toCpy != NULL)
|
if (toCpy != NULL)
|
||||||
{
|
{
|
||||||
toCpyWidth = GetStringLineWidth(fontId, toCpy, letterSpacing, 1, dstSize);
|
|
||||||
|
|
||||||
if (dstWidth + toCpyWidth > BATTLE_MSG_MAX_WIDTH)
|
|
||||||
{
|
|
||||||
dst[lastValidSkip] = displayedLineNums == 1 ? CHAR_NEWLINE : CHAR_PROMPT_SCROLL;
|
|
||||||
dstWidth = GetStringLineWidth(fontId, dst, letterSpacing, lineNum, dstSize);
|
|
||||||
if (displayedLineNums == 1)
|
|
||||||
displayedLineNums++;
|
|
||||||
else
|
|
||||||
displayedLineNums = 1;
|
|
||||||
lineNum++;
|
|
||||||
}
|
|
||||||
while (*toCpy != EOS)
|
while (*toCpy != EOS)
|
||||||
{
|
{
|
||||||
dst[dstID] = *toCpy;
|
dst[dstID] = *toCpy;
|
||||||
|
@ -3153,31 +3137,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
toCpyWidth = GetGlyphWidth(*src, FALSE, fontId);
|
|
||||||
dst[dstID] = *src;
|
dst[dstID] = *src;
|
||||||
if (dstWidth + toCpyWidth > BATTLE_MSG_MAX_WIDTH)
|
|
||||||
{
|
|
||||||
dst[lastValidSkip] = displayedLineNums == 1 ? CHAR_NEWLINE : CHAR_PROMPT_SCROLL;
|
|
||||||
if (displayedLineNums == 1)
|
|
||||||
displayedLineNums++;
|
|
||||||
else
|
|
||||||
displayedLineNums = 1;
|
|
||||||
lineNum++;
|
|
||||||
dstWidth = 0;
|
|
||||||
}
|
|
||||||
switch (*src)
|
|
||||||
{
|
|
||||||
case CHAR_PROMPT_CLEAR:
|
|
||||||
case CHAR_PROMPT_SCROLL:
|
|
||||||
displayedLineNums = 1;
|
|
||||||
case CHAR_NEWLINE:
|
|
||||||
lineNum++;
|
|
||||||
dstWidth = 0;
|
|
||||||
//fallthrough
|
|
||||||
case CHAR_SPACE:
|
|
||||||
lastValidSkip = dstID;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dstID++;
|
dstID++;
|
||||||
}
|
}
|
||||||
src++;
|
src++;
|
||||||
|
@ -3186,6 +3146,8 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
|
||||||
dst[dstID] = *src;
|
dst[dstID] = *src;
|
||||||
dstID++;
|
dstID++;
|
||||||
|
|
||||||
|
BreakStringAutomatic(dst, BATTLE_MSG_MAX_WIDTH, BATTLE_MSG_MAX_WIDTH, fontId);
|
||||||
|
|
||||||
return dstID;
|
return dstID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
281
src/line_break.c
Normal file
281
src/line_break.c
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
#include "global.h"
|
||||||
|
#include "line_break.h"
|
||||||
|
#include "text.h"
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
void StripLineBreaks(u8 *src)
|
||||||
|
{
|
||||||
|
u32 currIndex = 0;
|
||||||
|
while (src[currIndex] != EOS)
|
||||||
|
{
|
||||||
|
if (src[currIndex] == CHAR_PROMPT_SCROLL || src[currIndex] == CHAR_NEWLINE)
|
||||||
|
src[currIndex] = CHAR_SPACE;
|
||||||
|
currIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId)
|
||||||
|
{
|
||||||
|
u32 currIndex = 0;
|
||||||
|
u8 *currSrc = src;
|
||||||
|
while (src[currIndex] != EOS)
|
||||||
|
{
|
||||||
|
if (src[currIndex] == CHAR_PROMPT_CLEAR)
|
||||||
|
{
|
||||||
|
u8 replacedChar = src[currIndex + 1];
|
||||||
|
src[currIndex + 1] = EOS;
|
||||||
|
BreakSubStringAutomatic(currSrc, maxWidth, screenLines, fontId);
|
||||||
|
src[currIndex + 1] = replacedChar;
|
||||||
|
currSrc = &src[currIndex + 1];
|
||||||
|
}
|
||||||
|
currIndex++;
|
||||||
|
}
|
||||||
|
BreakSubStringAutomatic(currSrc, maxWidth, screenLines, fontId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId)
|
||||||
|
{
|
||||||
|
// If the string already has line breaks, don't interfere with them
|
||||||
|
if (StringHasManualBreaks(src))
|
||||||
|
return;
|
||||||
|
// Sanity check
|
||||||
|
if (src[0] == EOS)
|
||||||
|
return;
|
||||||
|
u32 numChars = 1;
|
||||||
|
u32 numWords = 1;
|
||||||
|
u32 currWordIndex = 0;
|
||||||
|
u32 currWordLength = 1;
|
||||||
|
bool32 isPrevCharSplitting = FALSE;
|
||||||
|
bool32 isCurrCharSplitting;
|
||||||
|
// Get numbers of chars in string and count words
|
||||||
|
while (src[numChars] != EOS)
|
||||||
|
{
|
||||||
|
isCurrCharSplitting = IsWordSplittingChar(src, numChars);
|
||||||
|
if (isCurrCharSplitting && !isPrevCharSplitting)
|
||||||
|
numWords++;
|
||||||
|
isPrevCharSplitting = isCurrCharSplitting;
|
||||||
|
numChars++;
|
||||||
|
}
|
||||||
|
// Allocate enough space for word data
|
||||||
|
struct StringWord *allWords = Alloc(numWords*sizeof(struct StringWord));
|
||||||
|
|
||||||
|
allWords[currWordIndex].startIndex = 0;
|
||||||
|
allWords[currWordIndex].width = 0;
|
||||||
|
isPrevCharSplitting = FALSE;
|
||||||
|
// Fill in word begin index and lengths
|
||||||
|
for (u32 i = 1; i < numChars; i++)
|
||||||
|
{
|
||||||
|
isCurrCharSplitting = IsWordSplittingChar(src, i);
|
||||||
|
if (isCurrCharSplitting && !isPrevCharSplitting)
|
||||||
|
{
|
||||||
|
allWords[currWordIndex].length = currWordLength;
|
||||||
|
currWordIndex++;
|
||||||
|
currWordLength = 0;
|
||||||
|
}
|
||||||
|
else if (!isCurrCharSplitting && isPrevCharSplitting)
|
||||||
|
{
|
||||||
|
allWords[currWordIndex].startIndex = i;
|
||||||
|
allWords[currWordIndex].width = 0;
|
||||||
|
currWordLength++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currWordLength++;
|
||||||
|
}
|
||||||
|
isPrevCharSplitting = isCurrCharSplitting;
|
||||||
|
}
|
||||||
|
allWords[currWordIndex].length = currWordLength;
|
||||||
|
|
||||||
|
// Fill in individual word widths
|
||||||
|
for (u32 i = 0; i < numWords; i++)
|
||||||
|
{
|
||||||
|
for (u32 j = 0; j < allWords[i].length; j++)
|
||||||
|
allWords[i].width += GetGlyphWidth(src[allWords[i].startIndex + j], FALSE, fontId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Does it all fit one one line? Then no break
|
||||||
|
// Step 2: Try to split across minimum number of lines
|
||||||
|
u32 spaceWidth = GetGlyphWidth(0, FALSE, fontId);
|
||||||
|
u32 totalWidth = allWords[0].width;
|
||||||
|
// Calculate total widths without any line breaks
|
||||||
|
for (u32 i = 1; i < numWords; i++)
|
||||||
|
totalWidth += allWords[i].width + spaceWidth;
|
||||||
|
|
||||||
|
// If it doesn't fit on 1 line, do fancy line break calculation
|
||||||
|
// NOTE: Currently the line break calculation isn't fancy
|
||||||
|
if (totalWidth > maxWidth)
|
||||||
|
{
|
||||||
|
// Figure out how many lines are needed with naive method
|
||||||
|
u32 currLineWidth = 0;
|
||||||
|
u32 totalLines = 1;
|
||||||
|
bool32 shouldTryAgain;
|
||||||
|
for (currWordIndex = 0; currWordIndex < numWords; currWordIndex++)
|
||||||
|
{
|
||||||
|
if (currLineWidth + allWords[currWordIndex].length > maxWidth)
|
||||||
|
{
|
||||||
|
totalLines++;
|
||||||
|
currLineWidth = allWords[currWordIndex].width;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currLineWidth += allWords[currWordIndex].width + spaceWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LINE LAYOUT STARTS HERE
|
||||||
|
struct StringLine *stringLines;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
shouldTryAgain = FALSE;
|
||||||
|
u16 targetLineWidth = totalWidth/totalLines;
|
||||||
|
stringLines = Alloc(totalLines*sizeof(struct StringLine));
|
||||||
|
for (u32 lineIndex = 0; lineIndex < totalLines; lineIndex++)
|
||||||
|
{
|
||||||
|
stringLines[lineIndex].numWords = 0;
|
||||||
|
stringLines[lineIndex].spaceWidth = spaceWidth;
|
||||||
|
stringLines[lineIndex].extraSpaceWidth = 0;
|
||||||
|
}
|
||||||
|
currWordIndex = 0;
|
||||||
|
u16 currLineIndex = 0;
|
||||||
|
stringLines[currLineIndex].words = &allWords[currWordIndex];
|
||||||
|
stringLines[currLineIndex].numWords = 1;
|
||||||
|
currLineWidth = allWords[currWordIndex].width;
|
||||||
|
currWordIndex++;
|
||||||
|
while (currWordIndex < numWords)
|
||||||
|
{
|
||||||
|
if (currLineWidth + spaceWidth + allWords[currWordIndex].width > maxWidth)
|
||||||
|
{
|
||||||
|
// go to next line
|
||||||
|
currLineIndex++;
|
||||||
|
if (currLineIndex == totalLines)
|
||||||
|
{
|
||||||
|
totalLines++;
|
||||||
|
Free(stringLines);
|
||||||
|
shouldTryAgain = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stringLines[currLineIndex].words = &allWords[currWordIndex];
|
||||||
|
stringLines[currLineIndex].numWords = 1;
|
||||||
|
currLineWidth = allWords[currWordIndex].width;
|
||||||
|
currWordIndex++;
|
||||||
|
}
|
||||||
|
else if (currLineWidth > targetLineWidth)
|
||||||
|
{
|
||||||
|
// go to next line
|
||||||
|
currLineIndex++;
|
||||||
|
if (currLineIndex == totalLines)
|
||||||
|
{
|
||||||
|
totalLines++;
|
||||||
|
Free(stringLines);
|
||||||
|
shouldTryAgain = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stringLines[currLineIndex].words = &allWords[currWordIndex];
|
||||||
|
stringLines[currLineIndex].numWords = 1;
|
||||||
|
currLineWidth = allWords[currWordIndex].width;
|
||||||
|
currWordIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// continue on current line
|
||||||
|
// add word and space width
|
||||||
|
currLineWidth += spaceWidth + allWords[currWordIndex].width;
|
||||||
|
stringLines[currLineIndex].numWords++;
|
||||||
|
currWordIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (shouldTryAgain);
|
||||||
|
//u32 currBadness = GetStringBadness(stringLines, totalLines, maxWidth);
|
||||||
|
BuildNewString(stringLines, totalLines, screenLines, src);
|
||||||
|
Free(stringLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(allWords);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow word splitting on allowed chars
|
||||||
|
bool32 IsWordSplittingChar(const u8 *src, u32 index)
|
||||||
|
{
|
||||||
|
switch (src[index])
|
||||||
|
{
|
||||||
|
case CHAR_SPACE:
|
||||||
|
return TRUE;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Badness calculation
|
||||||
|
// unfilled lines scale linerarly
|
||||||
|
// jagged lines scales by the square
|
||||||
|
// runts scale linearly
|
||||||
|
// numbers not final
|
||||||
|
// ISN'T ACTUALLY USED RIGHT NOW
|
||||||
|
u32 GetStringBadness(struct StringLine *stringLines, u32 numLines, u32 maxWidth)
|
||||||
|
{
|
||||||
|
u32 badness = 0;
|
||||||
|
u32 *lineWidths = Alloc(numLines*4);
|
||||||
|
u32 widestWidth = 0;
|
||||||
|
for (u32 i = 0; i < numLines; i++)
|
||||||
|
{
|
||||||
|
lineWidths[i] = 0;
|
||||||
|
for (u32 j = 0; j < stringLines[i].numWords; j++)
|
||||||
|
lineWidths[i] += stringLines[i].words[j].width;
|
||||||
|
lineWidths[i] += (stringLines[i].numWords-1)*stringLines[i].spaceWidth;
|
||||||
|
if (lineWidths[i] > widestWidth)
|
||||||
|
widestWidth = lineWidths[i];
|
||||||
|
if (stringLines[i].numWords == 1)
|
||||||
|
badness += BADNESS_RUNT;
|
||||||
|
}
|
||||||
|
for (u32 i = 0; i < numLines; i++)
|
||||||
|
{
|
||||||
|
u32 extraSpaceWidth = 0;
|
||||||
|
if (lineWidths[i] != widestWidth)
|
||||||
|
{
|
||||||
|
// Not the best way to do this, ideally a line should be allowed to get longer than current widest
|
||||||
|
// line. But then the widest line has to be recalculated.
|
||||||
|
while (lineWidths[i] + (extraSpaceWidth + 1) * (stringLines[i].numWords - 1) < widestWidth && extraSpaceWidth < MAX_SPACE_WIDTH)
|
||||||
|
extraSpaceWidth++;
|
||||||
|
lineWidths[i] += extraSpaceWidth*(stringLines[i].numWords-1);
|
||||||
|
}
|
||||||
|
badness += (maxWidth - lineWidths[i]) * BADNESS_UNFILLED;
|
||||||
|
u32 baseBadness = (widestWidth - lineWidths[i]) * BADNESS_JAGGED;
|
||||||
|
badness += baseBadness*baseBadness;
|
||||||
|
stringLines[i].extraSpaceWidth = extraSpaceWidth;
|
||||||
|
}
|
||||||
|
Free(lineWidths);
|
||||||
|
return badness;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the new string from the data stored in the StringLine structs
|
||||||
|
void BuildNewString(struct StringLine *stringLines, u32 numLines, u32 maxLines, u8 *str)
|
||||||
|
{
|
||||||
|
u32 srcCharIndex = 0;
|
||||||
|
for (u32 lineIndex = 0; lineIndex < numLines; lineIndex++)
|
||||||
|
{
|
||||||
|
srcCharIndex += stringLines[lineIndex].words[0].length;
|
||||||
|
for (u32 wordIndex = 1; wordIndex < stringLines[lineIndex].numWords; wordIndex++)
|
||||||
|
// Add length of word and a space
|
||||||
|
srcCharIndex += stringLines[lineIndex].words[wordIndex].length + 1;
|
||||||
|
if (lineIndex + 1 < numLines)
|
||||||
|
{
|
||||||
|
// Add the appropriate line break depending on line number
|
||||||
|
if (lineIndex >= maxLines - 1 && numLines > maxLines)
|
||||||
|
str[srcCharIndex] = CHAR_PROMPT_SCROLL;
|
||||||
|
else
|
||||||
|
str[srcCharIndex] = CHAR_NEWLINE;
|
||||||
|
srcCharIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool32 StringHasManualBreaks(u8 *src)
|
||||||
|
{
|
||||||
|
u32 charIndex = 0;
|
||||||
|
while (src[charIndex] != EOS)
|
||||||
|
{
|
||||||
|
if (src[charIndex] == CHAR_PROMPT_SCROLL || src[charIndex] == CHAR_NEWLINE)
|
||||||
|
return TRUE;
|
||||||
|
charIndex++;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
Loading…
Reference in a new issue