Port the separate creature rating list, layer the animated preview between authored row chrome and text, and follow selection while the examination floaty is visible. Preserve the remaining item-preview and font-state gaps in AP-110. Co-authored-by: Codex <codex@openai.com>
186 lines
6.3 KiB
C#
186 lines
6.3 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
using AcDream.Core.Items;
|
|
using AcDream.Core.Net.Messages;
|
|
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
public sealed class CreatureAppraisalRowsTests
|
|
{
|
|
[Fact]
|
|
public void FailedAssessmentShowsOnlyHealthPercentAndUnknownOtherValues()
|
|
{
|
|
var profile = Profile(
|
|
health: 25u,
|
|
healthMax: 100u,
|
|
highlights: (ushort)0,
|
|
colors: (ushort)0);
|
|
|
|
IReadOnlyList<CreatureAppraisalRow> rows =
|
|
CreatureAppraisalRows.Build(profile, success: false);
|
|
|
|
Assert.All(
|
|
rows,
|
|
row => Assert.Equal(
|
|
CreatureAppraisalValueStyle.Incomplete,
|
|
row.Style));
|
|
Assert.Equal("25 %", rows[6].Value);
|
|
Assert.Equal("???", rows[0].Value);
|
|
Assert.Equal("???", rows[7].Value);
|
|
Assert.Equal("???", rows[8].Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnchantmentBitsSelectRetailPositiveAndNegativeStyles()
|
|
{
|
|
var profile = Profile(
|
|
health: 100u,
|
|
healthMax: 100u,
|
|
highlights: (ushort)((1 << 0) | (1 << 6)),
|
|
colors: (ushort)(1 << 0));
|
|
|
|
IReadOnlyList<CreatureAppraisalRow> rows =
|
|
CreatureAppraisalRows.Build(profile, success: true);
|
|
|
|
Assert.Equal(CreatureAppraisalValueStyle.Positive, rows[0].Style);
|
|
Assert.Equal(CreatureAppraisalValueStyle.Negative, rows[6].Style);
|
|
Assert.Equal(CreatureAppraisalValueStyle.Normal, rows[1].Style);
|
|
}
|
|
|
|
[Fact]
|
|
public void ExtraRatingsFollowRetailGroupingFormattingAndSeparators()
|
|
{
|
|
var properties = new PropertyBundle();
|
|
properties.Ints[0x133u] = 35;
|
|
properties.Ints[0x139u] = 7;
|
|
properties.Ints[0x13Au] = 4;
|
|
properties.Ints[0x134u] = 12;
|
|
properties.Ints[0x13Bu] = 3;
|
|
properties.Ints[0x13Cu] = 2;
|
|
properties.Ints[0x15Eu] = 9;
|
|
properties.Ints[0x15Fu] = 6;
|
|
|
|
IReadOnlyList<CreatureAppraisalRow> rows =
|
|
CreatureAppraisalRows.BuildExtra(properties);
|
|
|
|
Assert.Equal(5, rows.Count);
|
|
Assert.Equal(("", ""), (rows[0].Label, rows[0].Value));
|
|
Assert.Equal(
|
|
("Dmg/CritDmg", "Rating: 35/4"),
|
|
(rows[1].Label, rows[1].Value));
|
|
Assert.Equal(
|
|
("Dmg/CritDmg", "Resist: 12/2"),
|
|
(rows[2].Label, rows[2].Value));
|
|
Assert.Equal(
|
|
("DoT/Life:", "Resist: 9/6"),
|
|
(rows[3].Label, rows[3].Value));
|
|
Assert.Equal(("", ""), (rows[4].Label, rows[4].Value));
|
|
}
|
|
|
|
[Fact]
|
|
public void CritOnlyTriggersRatingRowWhileHealingBoostAloneDoesNot()
|
|
{
|
|
var critOnly = new PropertyBundle();
|
|
critOnly.Ints[0x139u] = 8;
|
|
|
|
IReadOnlyList<CreatureAppraisalRow> rows =
|
|
CreatureAppraisalRows.BuildExtra(critOnly);
|
|
|
|
Assert.Equal(3, rows.Count);
|
|
Assert.Equal("Rating: 0/0", rows[1].Value);
|
|
|
|
var healingOnly = new PropertyBundle();
|
|
healingOnly.Ints[0x143u] = 20;
|
|
Assert.Empty(CreatureAppraisalRows.BuildExtra(healingOnly));
|
|
}
|
|
|
|
[Fact]
|
|
public void AuthoredRowTemplateCarriesRetailOverlappingLabelValueGeometry()
|
|
{
|
|
ElementInfo template =
|
|
FixtureLoader.LoadExaminationRowTemplateInfos();
|
|
|
|
Assert.Equal(CreatureAppraisalRowTemplateFactory.TemplateId, template.Id);
|
|
Assert.Equal((292f, 20f), (template.Width, template.Height));
|
|
ElementInfo label = Assert.Single(
|
|
template.Children,
|
|
child => child.Id == CreatureAppraisalRowTemplateFactory.LabelId);
|
|
ElementInfo value = Assert.Single(
|
|
template.Children,
|
|
child => child.Id == CreatureAppraisalRowTemplateFactory.ValueId);
|
|
Assert.Equal((0f, 128f), (label.X, label.Width));
|
|
Assert.Equal((27f, 256f), (value.X, value.Width));
|
|
Assert.Equal(0x40000001u, label.FontDid);
|
|
Assert.Equal(0x40000001u, value.FontDid);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreatureNameResolverUsesLoadedRetailMappingAndSafeFallback()
|
|
{
|
|
var resolver = new CreatureDisplayNameResolver(
|
|
new Dictionary<uint, string> { [77u] = "Ghost" });
|
|
|
|
Assert.Equal("Ghost", resolver.Resolve(77));
|
|
Assert.Equal(string.Empty, resolver.Resolve(0));
|
|
Assert.Equal(string.Empty, resolver.Resolve(999));
|
|
}
|
|
|
|
[Fact]
|
|
public void LayeredTemplateSeparatesChromeFromForegroundText()
|
|
{
|
|
var templates = new CreatureAppraisalRowTemplateFactory(
|
|
FixtureLoader.LoadExaminationRowTemplateInfos(),
|
|
_ => (0u, 0, 0),
|
|
defaultFont: null);
|
|
var row = new CreatureAppraisalRow(
|
|
"Strength",
|
|
"120",
|
|
CreatureAppraisalValueStyle.Normal);
|
|
|
|
UiTemplateListSlot background = templates.Create(
|
|
row,
|
|
CreatureAppraisalRowLayer.Background);
|
|
UiTemplateListSlot foreground = templates.Create(
|
|
row,
|
|
CreatureAppraisalRowLayer.Foreground);
|
|
|
|
Assert.Empty(((UiText)background.Content.FindElement(
|
|
CreatureAppraisalRowTemplateFactory.LabelId)!).LinesProvider());
|
|
Assert.Empty(((UiText)background.Content.FindElement(
|
|
CreatureAppraisalRowTemplateFactory.ValueId)!).LinesProvider());
|
|
Assert.False(
|
|
Assert.IsType<UiDatElement>(foreground.Content.Root).MediaVisible);
|
|
Assert.Equal(
|
|
"Strength",
|
|
Assert.Single(((UiText)foreground.Content.FindElement(
|
|
CreatureAppraisalRowTemplateFactory.LabelId)!)
|
|
.LinesProvider()).Text);
|
|
Assert.Equal(
|
|
"120",
|
|
Assert.Single(((UiText)foreground.Content.FindElement(
|
|
CreatureAppraisalRowTemplateFactory.ValueId)!)
|
|
.LinesProvider()).Text);
|
|
}
|
|
|
|
private static AppraiseInfoParser.CreatureProfile Profile(
|
|
uint health,
|
|
uint healthMax,
|
|
ushort highlights,
|
|
ushort colors)
|
|
=> new(
|
|
Flags: 0x09u,
|
|
Health: health,
|
|
HealthMax: healthMax,
|
|
Strength: null,
|
|
Endurance: null,
|
|
Quickness: null,
|
|
Coordination: null,
|
|
Focus: null,
|
|
Self: null,
|
|
Stamina: 50u,
|
|
Mana: 75u,
|
|
StaminaMax: 100u,
|
|
ManaMax: 100u,
|
|
AttributeHighlights: highlights,
|
|
AttributeColors: colors);
|
|
}
|