diff --git a/Source/panels/charpanel.cpp b/Source/panels/charpanel.cpp index 67d0c5c823c..2b959a48bc8 100644 --- a/Source/panels/charpanel.cpp +++ b/Source/panels/charpanel.cpp @@ -37,11 +37,15 @@ struct StyledText { int spacing = 1; }; +struct PanelLabel { + int labelWidth; + int boxWidth; +}; + struct PanelEntry { std::string label; Point position; - int length; - int labelLength; // max label's length - used for line wrapping + PanelLabel width; // max label's length - used for line wrapping std::optional> statDisplayFunc; // function responsible for displaying stat }; @@ -116,28 +120,46 @@ StyledText GetResistInfo(int8_t resist) constexpr int LeftColumnLabelX = 88; constexpr int TopRightLabelX = 211; constexpr int RightColumnLabelX = 253; - -constexpr int LeftColumnLabelWidth = 76; -constexpr int RightColumnLabelWidth = 68; +constexpr int RightColumnNarrowLabelX = 265; // Indices in `panelEntries`. -constexpr unsigned AttributeHeaderEntryIndices[2] = { 5, 6 }; -constexpr unsigned GoldHeaderEntryIndex = 16; +constexpr unsigned PanelHeaderEntryIndices[3] = { 2, 4, 6 }; +constexpr unsigned AttributeHeaderEntryIndices[2] = { 8, 9 }; + +PanelLabel nameLabel = { 0, 150 }; +PanelLabel classLabel = { 0, 149 }; +PanelLabel levelHeaderLabel = { 56, 0 }; +PanelLabel levelLabel = { 0, 57 }; +PanelLabel expHeaderLabel = { 119, 0 }; +PanelLabel expLabel = { 0, 120 }; +PanelLabel attributeHeaderLabel = { 44, 0 }; +PanelLabel goldHeaderLabel = { 98, 0 }; +PanelLabel goldLabel = { 0, 99 }; +PanelLabel veryWideLabel = { 56, 69 }; +PanelLabel wideLabel = { 68, 57 }; +PanelLabel narrowLabel = { 76, 45 }; +PanelLabel noTextNarrowLabel = { 0, 45 }; +PanelLabel resistLabel = { 96, 45 }; PanelEntry panelEntries[] = { - { "", { 9, 14 }, 150, 0, + { "", { 9, 14 }, nameLabel, []() { return StyledText { UiFlags::ColorWhite, InspectPlayer->_pName }; } }, - { "", { 161, 14 }, 149, 0, + { "", { 161, 14 }, classLabel, []() { return StyledText { UiFlags::ColorWhite, std::string(InspectPlayer->getClassName()) }; } }, - { N_("Level"), { 57, 52 }, 57, 45, + { N_("Level"), { 9, /* set dynamically */ 0 }, levelHeaderLabel, {} }, + { N_(""), { 9, 61 }, levelLabel, []() { return StyledText { UiFlags::ColorWhite, StrCat(InspectPlayer->getCharacterLevel()) }; } }, - { N_("Experience"), { TopRightLabelX, 52 }, 99, 91, + + { N_("Experience"), { 9 + levelLabel.boxWidth + 2, /* set dynamically */ 0 }, expHeaderLabel, {} }, + { N_(""), { 9 + levelLabel.boxWidth + 2, 61 }, expLabel, []() { int spacing = ((InspectPlayer->_pExperience >= 1000000000) ? 0 : 1); return StyledText { UiFlags::ColorWhite, FormatInteger(InspectPlayer->_pExperience), spacing }; } }, - { N_("Next level"), { TopRightLabelX, 80 }, 99, 198, + + { N_("Next Level"), { 9 + levelLabel.boxWidth + 2 + expLabel.boxWidth + 2, /* set dynamically */ 0 }, expHeaderLabel, {} }, + { N_(""), { 9 + levelLabel.boxWidth + 2 + expLabel.boxWidth + 2, 61 }, expLabel, []() { if (InspectPlayer->isMaxCharacterLevel()) { return StyledText { UiFlags::ColorWhitegold, std::string(_("None")) }; @@ -147,58 +169,63 @@ PanelEntry panelEntries[] = { return StyledText { UiFlags::ColorWhite, FormatInteger(nextExperienceThreshold), spacing }; } }, - { N_("Base"), { LeftColumnLabelX, /* set dynamically */ 0 }, 0, 44, {} }, - { N_("Now"), { 135, /* set dynamically */ 0 }, 0, 44, {} }, - { N_("Strength"), { LeftColumnLabelX, 135 }, 45, LeftColumnLabelWidth, + { N_("Base"), { LeftColumnLabelX, /* set dynamically */ 0 }, attributeHeaderLabel, {} }, + { N_("Now"), { 135, /* set dynamically */ 0 }, attributeHeaderLabel, {} }, + { N_("Strength"), { LeftColumnLabelX, 108 }, narrowLabel, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Strength), StrCat(InspectPlayer->_pBaseStr) }; } }, - { "", { 135, 135 }, 45, 0, + { "", { 135, 108 }, noTextNarrowLabel, []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Strength), StrCat(InspectPlayer->_pStrength) }; } }, - { N_("Magic"), { LeftColumnLabelX, 163 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetBaseStatColor(CharacterAttribute::Magic), StrCat(InspectPlayer->_pBaseMag) }; } }, - { "", { 135, 163 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Magic), StrCat(InspectPlayer->_pMagic) }; } }, - { N_("Dexterity"), { LeftColumnLabelX, 191 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Dexterity), StrCat(InspectPlayer->_pBaseDex) }; } }, - { "", { 135, 191 }, 45, 0, + { N_("Dexterity"), { LeftColumnLabelX, 150 }, narrowLabel, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Dexterity), StrCat(InspectPlayer->_pBaseDex) }; } }, + { "", { 135, 150 }, noTextNarrowLabel, []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Dexterity), StrCat(InspectPlayer->_pDexterity) }; } }, - { N_("Vitality"), { LeftColumnLabelX, 219 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Vitality), StrCat(InspectPlayer->_pBaseVit) }; } }, - { "", { 135, 219 }, 45, 0, + { N_("Vitality"), { LeftColumnLabelX, 192 }, narrowLabel, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Vitality), StrCat(InspectPlayer->_pBaseVit) }; } }, + { "", { 135, 192 }, noTextNarrowLabel, []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Vitality), StrCat(InspectPlayer->_pVitality) }; } }, - { N_("Points to distribute"), { LeftColumnLabelX, 248 }, 45, LeftColumnLabelWidth, - []() { - InspectPlayer->_pStatPts = std::min(CalcStatDiff(*InspectPlayer), InspectPlayer->_pStatPts); - return StyledText { UiFlags::ColorRed, (InspectPlayer->_pStatPts > 0 ? StrCat(InspectPlayer->_pStatPts) : "") }; - } }, - - { N_("Gold"), { TopRightLabelX, /* set dynamically */ 0 }, 0, 98, {} }, - { "", { TopRightLabelX, 127 }, 99, 0, - []() { return StyledText { UiFlags::ColorWhite, FormatInteger(InspectPlayer->_pGold) }; } }, + { N_("Magic"), { LeftColumnLabelX, 220 }, narrowLabel, + []() { return StyledText { GetBaseStatColor(CharacterAttribute::Magic), StrCat(InspectPlayer->_pBaseMag) }; } }, + { "", { 135, 220 }, noTextNarrowLabel, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Magic), StrCat(InspectPlayer->_pMagic) }; } }, - { N_("Armor class"), { RightColumnLabelX, 163 }, 57, RightColumnLabelWidth, - []() { return StyledText { GetValueColor(InspectPlayer->_pIBonusAC), StrCat(InspectPlayer->GetArmor() + InspectPlayer->getCharacterLevel() * 2) }; } }, - { N_("To hit"), { RightColumnLabelX, 191 }, 57, RightColumnLabelWidth, - []() { return StyledText { GetValueColor(InspectPlayer->_pIBonusToHit), StrCat(InspectPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow ? InspectPlayer->GetRangedToHit() : InspectPlayer->GetMeleeToHit(), "%") }; } }, - { N_("Damage"), { RightColumnLabelX, 219 }, 57, RightColumnLabelWidth, + { N_("Damage"), { RightColumnLabelX, 108 }, wideLabel, []() { const auto [dmgMin, dmgMax] = GetDamage(); int spacing = ((dmgMin >= 100) ? -1 : 1); return StyledText { GetValueColor(InspectPlayer->_pIBonusDam), StrCat(dmgMin, "-", dmgMax), spacing }; } }, + { N_("To hit"), { RightColumnLabelX, 136 }, wideLabel, + []() { return StyledText { GetValueColor(InspectPlayer->_pIBonusToHit), StrCat(InspectPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow ? InspectPlayer->GetRangedToHit() : InspectPlayer->GetMeleeToHit(), "%") }; } }, + { N_("Armor class"), { RightColumnLabelX, 164 }, wideLabel, + []() { return StyledText { GetValueColor(InspectPlayer->_pIBonusAC), StrCat(InspectPlayer->GetArmor() + InspectPlayer->getCharacterLevel() * 2) }; } }, + { N_("Life"), { RightColumnLabelX - 12, 192 }, veryWideLabel, + []() { + int currLife = InspectPlayer->_pHitPoints >> 6; + int maxLife = InspectPlayer->_pMaxHP >> 6; + int spacing = ((maxLife >= 100) ? -1 : 1); + return StyledText { (InspectPlayer->_pHitPoints != InspectPlayer->_pMaxHP ? UiFlags::ColorRed : GetMaxHealthColor()), StrCat(currLife, "/", maxLife), spacing }; + } }, + { N_("Mana"), { RightColumnLabelX - 12, 220 }, veryWideLabel, + []() { + int currMana = HasAnyOf(InspectPlayer->_pIFlags, ItemSpecialEffect::NoMana) ? 0 : InspectPlayer->_pMana >> 6; + int maxMana = HasAnyOf(InspectPlayer->_pIFlags, ItemSpecialEffect::NoMana) ? 0 : InspectPlayer->_pMaxMana >> 6; + int spacing = ((maxMana >= 100) ? -1 : 1); + return StyledText { (InspectPlayer->_pMana != InspectPlayer->_pMaxMana ? UiFlags::ColorRed : GetMaxManaColor()), StrCat(currMana, "/", maxMana), spacing }; + } }, - { N_("Life"), { LeftColumnLabelX, 284 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetMaxHealthColor(), StrCat(InspectPlayer->_pMaxHP >> 6) }; } }, - { "", { 135, 284 }, 45, 0, - []() { return StyledText { (InspectPlayer->_pHitPoints != InspectPlayer->_pMaxHP ? UiFlags::ColorRed : GetMaxHealthColor()), StrCat(InspectPlayer->_pHitPoints >> 6) }; } }, - { N_("Mana"), { LeftColumnLabelX, 312 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetMaxManaColor(), StrCat(HasAnyOf(InspectPlayer->_pIFlags, ItemSpecialEffect::NoMana) ? 0 : InspectPlayer->_pMaxMana >> 6) }; } }, - { "", { 135, 312 }, 45, 0, - []() { return StyledText { (InspectPlayer->_pMana != InspectPlayer->_pMaxMana ? UiFlags::ColorRed : GetMaxManaColor()), StrCat((HasAnyOf(InspectPlayer->_pIFlags, ItemSpecialEffect::NoMana) || (InspectPlayer->_pMana >> 6) <= 0) ? 0 : InspectPlayer->_pMana >> 6) }; } }, - - { N_("Resist magic"), { RightColumnLabelX, 256 }, 57, RightColumnLabelWidth, + { N_("Magic Resistance"), { LeftColumnLabelX + 26, 251 }, resistLabel, []() { return GetResistInfo(InspectPlayer->_pMagResist); } }, - { N_("Resist fire"), { RightColumnLabelX, 284 }, 57, RightColumnLabelWidth, + { N_("Fire Resistance"), { RightColumnNarrowLabelX, 251 }, resistLabel, []() { return GetResistInfo(InspectPlayer->_pFireResist); } }, - { N_("Resist lightning"), { RightColumnLabelX, 313 }, 57, RightColumnLabelWidth, + { N_("Lightning Resistance"), { LeftColumnLabelX + 26, 279 }, resistLabel, []() { return GetResistInfo(InspectPlayer->_pLghtResist); } }, + + { N_("Points to distribute"), { LeftColumnLabelX + 26, 313 }, narrowLabel, + []() { + InspectPlayer->_pStatPts = std::min(CalcStatDiff(*InspectPlayer), InspectPlayer->_pStatPts); + return StyledText { UiFlags::ColorRed, (InspectPlayer->_pStatPts > 0 ? StrCat(InspectPlayer->_pStatPts) : "") }; + } }, + + { N_("Gold"), { TopRightLabelX, 313 }, goldLabel, + []() { return StyledText { UiFlags::ColorWhite, FormatInteger(InspectPlayer->_pGold) }; } }, }; OptionalOwnedClxSpriteList Panel; @@ -227,8 +254,8 @@ void DrawShadowString(const Surface &out, const PanelEntry &entry) const std::string_view textStr = LanguageTranslate(entry.label); std::string_view text; std::string wrapped; - if (entry.labelLength > 0) { - wrapped = WordWrapString(textStr, entry.labelLength, GameFont12, Spacing); + if (entry.width.labelWidth > 0) { + wrapped = WordWrapString(textStr, entry.width.labelWidth, GameFont12, Spacing); text = wrapped; } else { text = textStr; @@ -238,11 +265,11 @@ void DrawShadowString(const Surface &out, const PanelEntry &entry) Point labelPosition = entry.position; - if (entry.length == 0) { + if (entry.width.boxWidth == 0) { style |= UiFlags::AlignCenter; } else { style |= UiFlags::AlignRight; - labelPosition += Displacement { -entry.labelLength - (IsSmallFontTall() ? 2 : 3), 0 }; + labelPosition += Displacement { -entry.width.labelWidth - (IsSmallFontTall() ? 2 : 3), 0 }; } // If the text is less tall than the field, we center it vertically relative to the field. @@ -250,9 +277,9 @@ void DrawShadowString(const Surface &out, const PanelEntry &entry) const int textHeight = static_cast((c_count(wrapped, '\n') + 1) * GetLineHeight(wrapped, GameFont12)); const int labelHeight = std::max(PanelFieldHeight, textHeight); - DrawString(out, text, { labelPosition + Displacement { -2, 2 }, { entry.labelLength, labelHeight } }, + DrawString(out, text, { labelPosition + Displacement { -2, 2 }, { entry.width.labelWidth, labelHeight } }, { .flags = style | UiFlags::ColorBlack, .spacing = Spacing }); - DrawString(out, text, { labelPosition, { entry.labelLength, labelHeight } }, + DrawString(out, text, { labelPosition, { entry.width.labelWidth, labelHeight } }, { .flags = style | UiFlags::ColorWhite, .spacing = Spacing }); } @@ -285,15 +312,21 @@ tl::expected LoadCharPanel() ASSIGN_OR_RETURN(OwnedClxSpriteList boxRight, LoadClxWithStatus("data\\boxrightend.clx")); const bool isSmallFontTall = IsSmallFontTall(); - const int attributeHeadersY = isSmallFontTall ? 112 : 114; + const int panelHeadersY = isSmallFontTall ? 37 : 39; + + for (unsigned i : PanelHeaderEntryIndices) { + panelEntries[i].position.y = panelHeadersY; + } + + const int attributeHeadersY = isSmallFontTall ? 85 : 87; + for (unsigned i : AttributeHeaderEntryIndices) { panelEntries[i].position.y = attributeHeadersY; } - panelEntries[GoldHeaderEntryIndex].position.y = isSmallFontTall ? 105 : 106; for (auto &entry : panelEntries) { if (entry.statDisplayFunc) { - DrawPanelField(out, entry.position, entry.length, boxLeft[0], boxMiddle[0], boxRight[0]); + DrawPanelField(out, entry.position, entry.width.boxWidth, boxLeft[0], boxMiddle[0], boxRight[0]); } DrawShadowString(out, entry); } @@ -318,7 +351,7 @@ void DrawChr(const Surface &out) DrawString( out, tmp.text, - { entry.position + Displacement { pos.x, pos.y + PanelFieldPaddingTop }, { entry.length, PanelFieldInnerHeight } }, + { entry.position + Displacement { pos.x, pos.y + PanelFieldPaddingTop }, { entry.width.boxWidth, PanelFieldInnerHeight } }, { .flags = UiFlags::AlignCenter | UiFlags::VerticalCenter | tmp.style, .spacing = tmp.spacing }); } }