fix(ui): preserve retail item titles and spacing
Carry PublicWeenieDesc material type into the live object model so examination titles use the DAT-authored material prefix. Preserve retail AddItemInfo empty appends and embedded armor separator, restoring the deliberate blank rows between appraisal sections. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
d78d09cfbc
commit
6718ee45a0
19 changed files with 341 additions and 46 deletions
|
|
@ -649,11 +649,11 @@ public sealed class AppraisalUiController : IRetainedPanelController
|
|||
: AppraisalView.Creature;
|
||||
}
|
||||
|
||||
private static string BuildTitle(ClientObject obj, PropertyBundle properties)
|
||||
private string BuildTitle(ClientObject obj, PropertyBundle properties)
|
||||
{
|
||||
string name = GetString(properties, DisplayedNameStringProperty);
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
name = obj.GetAppropriateName();
|
||||
name = _itemNames.ResolveAppropriateName(obj);
|
||||
return obj.StackSize > 1
|
||||
? $"{obj.StackSize.ToString(CultureInfo.CurrentCulture)} {name}"
|
||||
: name;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,23 @@ internal sealed class ItemAppraisalReportBuilder
|
|||
ItemAppraisalFontStyle style = ItemAppraisalFontStyle.Normal)
|
||||
=> Add(value, sameParagraph: false, style);
|
||||
|
||||
/// <summary>
|
||||
/// Preserve an empty retail <c>AddItemInfo("", ..., true)</c> append.
|
||||
/// Once text exists this contributes one physical empty row; on an empty
|
||||
/// glyph list retail has no preceding separator to append.
|
||||
/// </summary>
|
||||
public void BlankLine(
|
||||
ItemAppraisalFontStyle style = ItemAppraisalFontStyle.Normal)
|
||||
{
|
||||
if (_fragments.Count == 0)
|
||||
return;
|
||||
|
||||
_fragments.Add(new ItemAppraisalFragment(
|
||||
string.Empty,
|
||||
ItemAppraisalSeparator.Line,
|
||||
style));
|
||||
}
|
||||
|
||||
public ItemAppraisalReport Build()
|
||||
=> _fragments.Count == 0
|
||||
? ItemAppraisalReport.Empty
|
||||
|
|
|
|||
|
|
@ -112,28 +112,33 @@ public static class ItemAppraisalTextFormatter
|
|||
if (!string.IsNullOrWhiteSpace(imbuedBy))
|
||||
report.Line($"Imbued by {imbuedBy}.");
|
||||
|
||||
if (!properties.Ints.TryGetValue(105u, out int workmanship))
|
||||
return;
|
||||
|
||||
if (properties.Ints.TryGetValue(170u, out int salvagedItems)
|
||||
&& salvagedItems > 0)
|
||||
if (properties.Ints.TryGetValue(105u, out int workmanship))
|
||||
{
|
||||
double average = (double)workmanship / salvagedItems;
|
||||
int workmanshipBand = Math.Clamp(
|
||||
(int)Math.Round(average),
|
||||
0,
|
||||
10);
|
||||
report.Line(
|
||||
$"Workmanship: {WorkmanshipAdjective(workmanshipBand)} "
|
||||
+ $"({average.ToString("0.00", CultureInfo.CurrentCulture)})");
|
||||
report.Paragraph(
|
||||
$"Salvaged from {salvagedItems.ToString(CultureInfo.CurrentCulture)} items.");
|
||||
return;
|
||||
if (properties.Ints.TryGetValue(170u, out int salvagedItems)
|
||||
&& salvagedItems > 0)
|
||||
{
|
||||
double average = (double)workmanship / salvagedItems;
|
||||
int workmanshipBand = Math.Clamp(
|
||||
(int)Math.Round(average),
|
||||
0,
|
||||
10);
|
||||
report.Line(
|
||||
$"Workmanship: {WorkmanshipAdjective(workmanshipBand)} "
|
||||
+ $"({average.ToString("0.00", CultureInfo.CurrentCulture)})");
|
||||
report.Paragraph(
|
||||
$"Salvaged from {salvagedItems.ToString(CultureInfo.CurrentCulture)} items.");
|
||||
}
|
||||
else
|
||||
{
|
||||
report.Line(
|
||||
$"Workmanship: {WorkmanshipAdjective(Math.Clamp(workmanship, 0, 10))} "
|
||||
+ $"({workmanship.ToString(CultureInfo.CurrentCulture)})");
|
||||
}
|
||||
}
|
||||
|
||||
report.Line(
|
||||
$"Workmanship: {WorkmanshipAdjective(Math.Clamp(workmanship, 0, 10))} "
|
||||
+ $"({workmanship.ToString(CultureInfo.CurrentCulture)})");
|
||||
// Appraisal_ShowTinkeringInfo @ 0x004B1090 appends an empty
|
||||
// AddItemInfo entry unconditionally, establishing the next section.
|
||||
report.BlankLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -144,11 +149,12 @@ public static class ItemAppraisalTextFormatter
|
|||
RetailReportBuilder report,
|
||||
PropertyBundle properties)
|
||||
{
|
||||
if (properties.Ints.TryGetValue(265u, out int setId)
|
||||
&& EquipmentSetName(setId) is { } setName)
|
||||
{
|
||||
string? setName = properties.Ints.TryGetValue(265u, out int setId)
|
||||
? EquipmentSetName(setId)
|
||||
: null;
|
||||
bool setShown = setName is not null;
|
||||
if (setShown)
|
||||
report.Line($"Set: {setName}");
|
||||
}
|
||||
|
||||
(uint Property, string Label)[] ratingProperties =
|
||||
[
|
||||
|
|
@ -168,11 +174,23 @@ public static class ItemAppraisalTextFormatter
|
|||
.Select(pair =>
|
||||
$"{pair.Label} {properties.GetInt(pair.Property).ToString(CultureInfo.CurrentCulture)}")
|
||||
.ToArray();
|
||||
if (ratings.Length != 0)
|
||||
bool ratingsShown = ratings.Length != 0;
|
||||
if (ratingsShown)
|
||||
report.Line($"Ratings: {string.Join(", ", ratings)}");
|
||||
if (properties.GetInt(379u) is int vitality && vitality > 0)
|
||||
{
|
||||
report.Line(
|
||||
$"This item adds {vitality.ToString(CultureInfo.CurrentCulture)} Vitality.");
|
||||
ratingsShown = true;
|
||||
}
|
||||
|
||||
// Appraisal_ShowRatings terminates its emitted block with an empty
|
||||
// append; SetAppraiseInfo adds another when either Set or Ratings
|
||||
// contributed content.
|
||||
if (ratingsShown)
|
||||
report.BlankLine();
|
||||
if (setShown || ratingsShown)
|
||||
report.BlankLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -341,7 +359,9 @@ public static class ItemAppraisalTextFormatter
|
|||
return;
|
||||
}
|
||||
|
||||
report.Line(
|
||||
// Retail's first armor string begins with a literal '\n' before
|
||||
// AddItemInfo adds its ordinary separator.
|
||||
report.Paragraph(
|
||||
$"Armor Level: {armorLevel.ToString(CultureInfo.CurrentCulture)}",
|
||||
EnchantmentStyle(appraisal.ArmorEnchantments, 0x0001u));
|
||||
ShowProtection(
|
||||
|
|
@ -492,23 +512,34 @@ public static class ItemAppraisalTextFormatter
|
|||
PropertyBundle properties,
|
||||
RetailAppraisalNameResolver names)
|
||||
{
|
||||
// Appraisal_ShowSpecialProperties @ 0x004B0159.
|
||||
report.BlankLine();
|
||||
|
||||
int carryLimit = properties.GetInt(279u);
|
||||
if (carryLimit > 0)
|
||||
{
|
||||
report.BlankLine();
|
||||
report.Line(
|
||||
$"You can only carry "
|
||||
+ $"{carryLimit.ToString("N0", CultureInfo.CurrentCulture)} "
|
||||
+ "of these items.");
|
||||
}
|
||||
|
||||
if (properties.Floats.TryGetValue(167u, out double cooldown)
|
||||
&& cooldown > 0d)
|
||||
{
|
||||
report.Line($"Cooldown When Used: {FormatDeltaTime(cooldown)}");
|
||||
if (properties.Ints.ContainsKey(280u))
|
||||
report.BlankLine();
|
||||
}
|
||||
|
||||
int cleave = properties.GetInt(292u);
|
||||
if (cleave > 1)
|
||||
{
|
||||
report.Line(
|
||||
$"Cleave: {cleave.ToString(CultureInfo.CurrentCulture)} enemies in front arc.");
|
||||
report.BlankLine();
|
||||
}
|
||||
|
||||
var special = new List<string>();
|
||||
int slayer = properties.GetInt(166u);
|
||||
|
|
@ -745,16 +776,23 @@ public static class ItemAppraisalTextFormatter
|
|||
PropertyBundle properties)
|
||||
{
|
||||
int level = properties.GetInt(369u);
|
||||
int skill = properties.GetInt(366u);
|
||||
int difficulty = properties.GetInt(367u);
|
||||
int specializedSkill = properties.GetInt(368u);
|
||||
if (level > 0
|
||||
|| (skill > 0 && difficulty > 0)
|
||||
|| specializedSkill > 0)
|
||||
{
|
||||
report.BlankLine();
|
||||
}
|
||||
|
||||
if (level > 0)
|
||||
report.Line(
|
||||
$"Use requires level {level.ToString(CultureInfo.CurrentCulture)}.");
|
||||
int skill = properties.GetInt(366u);
|
||||
int difficulty = properties.GetInt(367u);
|
||||
if (skill > 0 && difficulty > 0)
|
||||
report.Line(
|
||||
$"Use requires {UsageSkillName(skill)} of at least "
|
||||
+ $"{difficulty.ToString(CultureInfo.CurrentCulture)}.");
|
||||
int specializedSkill = properties.GetInt(368u);
|
||||
if (specializedSkill > 0)
|
||||
report.Line(
|
||||
$"Use requires specialized {UsageSkillName(specializedSkill)}.");
|
||||
|
|
@ -794,11 +832,13 @@ public static class ItemAppraisalTextFormatter
|
|||
report.Line(
|
||||
$"Item XP: {experience.ToString("N0", CultureInfo.CurrentCulture)} / "
|
||||
+ $"{nextExperience.ToString("N0", CultureInfo.CurrentCulture)}");
|
||||
report.BlankLine();
|
||||
|
||||
if (properties.GetInt(352u) == 2)
|
||||
{
|
||||
report.Paragraph(
|
||||
report.Line(
|
||||
"This cloak has a chance to reduce an incoming attack by 200 damage.");
|
||||
report.BlankLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1776,6 +1816,10 @@ public static class ItemAppraisalTextFormatter
|
|||
ItemAppraisalFontStyle style = ItemAppraisalFontStyle.Normal)
|
||||
=> _report.Paragraph(value, style);
|
||||
|
||||
public void BlankLine(
|
||||
ItemAppraisalFontStyle style = ItemAppraisalFontStyle.Normal)
|
||||
=> _report.BlankLine(style);
|
||||
|
||||
public ItemAppraisalReport Build() => _report.Build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using AcDream.Content;
|
||||
using AcDream.Core.Items;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Types;
|
||||
|
|
@ -86,6 +87,33 @@ public sealed class RetailAppraisalNameResolver
|
|||
? name
|
||||
: string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Port of the material-decoration tail in
|
||||
/// <c>ACCWeenieObject::GetObjectName @ 0x0058E6E0</c>. Retail first
|
||||
/// chooses the singular/plural base name, removes an already-authored
|
||||
/// occurrence of the resolved material, trims it, then prefixes the
|
||||
/// material exactly once.
|
||||
/// </summary>
|
||||
public string ResolveAppropriateName(ClientObject obj)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(obj);
|
||||
|
||||
string baseName = obj.GetAppropriateName();
|
||||
if (obj.MaterialType is not { } materialType)
|
||||
return baseName;
|
||||
|
||||
string material = ResolveMaterial(unchecked((int)materialType));
|
||||
if (string.IsNullOrEmpty(material))
|
||||
return baseName;
|
||||
|
||||
string remainder = baseName
|
||||
.Replace(material, string.Empty, StringComparison.Ordinal)
|
||||
.Trim();
|
||||
return string.IsNullOrEmpty(remainder)
|
||||
? material
|
||||
: $"{material} {remainder}";
|
||||
}
|
||||
|
||||
private static string Normalize(string value)
|
||||
=> value.Replace('_', ' ');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue