feat(ui): share indicator detail panels
Port the authored Link Status, Vitae, and Mini Game detail roots and register every indicator page with retail's one-active gmPanelUI owner. Helpful/Harmful and the new pages now replace Inventory, Character, or Magic at one canonical window position while preserving the DAT restore-previous flag. Correct the retail ping wire to its payload-free request/response, publish measured RTT, and port Vitae recovery XP from the live modifier and player properties. Keep transport packet-loss averaging and mini-game gameplay explicitly tracked under AP-110. Release build and all 5,814 tests pass with five intentional skips. Connected visual gate pending. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
52c529be86
commit
a96767ba6d
28 changed files with 6539 additions and 32 deletions
|
|
@ -0,0 +1,166 @@
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Spells;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class IndicatorDetailPanelControllerTests
|
||||
{
|
||||
private const uint Player = 0x50000001u;
|
||||
|
||||
[Fact]
|
||||
public void AuthoredFixtures_AreRetailMainPanelPages()
|
||||
{
|
||||
AssertPage(
|
||||
FixtureLoader.LoadLinkStatusInfos(),
|
||||
LinkStatusUiController.RootId,
|
||||
0x1000001Du,
|
||||
LinkStatusUiController.MainTextId,
|
||||
LinkStatusUiController.CloseId);
|
||||
AssertPage(
|
||||
FixtureLoader.LoadVitaeInfos(),
|
||||
VitaeUiController.RootId,
|
||||
0x10000020u,
|
||||
VitaeUiController.MainTextId,
|
||||
VitaeUiController.CloseId);
|
||||
AssertPage(
|
||||
FixtureLoader.LoadMiniGameInfos(),
|
||||
MiniGameUiController.RootId,
|
||||
0x1000001Eu,
|
||||
0x1000016Eu,
|
||||
MiniGameUiController.CloseId);
|
||||
|
||||
Assert.Equal(8u, RetailPanelCatalog.LinkStatus);
|
||||
Assert.Equal(9u, RetailPanelCatalog.MiniGame);
|
||||
Assert.Equal(15u, RetailPanelCatalog.Vitae);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LinkStatus_RequestsPingOnShow_RefreshesText_AndRepeatsAfter120Seconds()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadLinkStatus();
|
||||
double now = 0d;
|
||||
int pings = 0;
|
||||
int closes = 0;
|
||||
LinkStatusSnapshot snapshot = new(
|
||||
Connected: true,
|
||||
SecondsSinceLastPacket: 0.25d,
|
||||
PacketLossPercentage: 1.25d,
|
||||
RoundTripSeconds: 0.123d);
|
||||
using LinkStatusUiController controller = LinkStatusUiController.Bind(
|
||||
layout,
|
||||
() => snapshot,
|
||||
() => now,
|
||||
() => pings++,
|
||||
LinkStatusStrings.English,
|
||||
() => closes++)!;
|
||||
|
||||
controller.OnShown();
|
||||
|
||||
Assert.Equal(1, pings);
|
||||
string text = VisibleText(layout, LinkStatusUiController.MainTextId);
|
||||
Assert.Contains("Link Indicator", text, StringComparison.Ordinal);
|
||||
Assert.Contains("1.25", text, StringComparison.Ordinal);
|
||||
Assert.Contains("123", text, StringComparison.Ordinal);
|
||||
|
||||
controller.Tick();
|
||||
now = 119d;
|
||||
controller.Tick();
|
||||
Assert.Equal(1, pings);
|
||||
now = 124d;
|
||||
controller.Tick();
|
||||
Assert.Equal(2, pings);
|
||||
|
||||
UiButton close = Assert.IsType<UiButton>(layout.FindElement(LinkStatusUiController.CloseId));
|
||||
close.OnEvent(new UiEvent(0, close, UiEventType.Click));
|
||||
Assert.Equal(1, closes);
|
||||
|
||||
controller.OnHidden();
|
||||
now = 300d;
|
||||
controller.Tick();
|
||||
Assert.Equal(2, pings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Vitae_UsesDeathLevelPoolAndRetailThresholdFormula()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadVitae();
|
||||
var spellbook = new Spellbook();
|
||||
var objects = new ClientObjectTable();
|
||||
objects.AddOrUpdate(new ClientObject { ObjectId = Player });
|
||||
objects.UpdateIntProperty(Player, VitaeUiController.DeathLevelProperty, 10);
|
||||
objects.UpdateIntProperty(Player, VitaeUiController.VitaeCpPoolProperty, 100);
|
||||
using VitaeUiController controller = VitaeUiController.Bind(
|
||||
layout,
|
||||
spellbook,
|
||||
objects,
|
||||
() => Player,
|
||||
VitaeStrings.English)!;
|
||||
|
||||
Assert.Contains(
|
||||
"full strength",
|
||||
VisibleText(layout, VitaeUiController.MainTextId),
|
||||
StringComparison.Ordinal);
|
||||
|
||||
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
|
||||
SpellId: 666u,
|
||||
LayerId: 1u,
|
||||
Duration: -1f,
|
||||
CasterGuid: Player,
|
||||
StatModValue: 0.9f,
|
||||
Bucket: 4u));
|
||||
|
||||
string text = VisibleText(layout, VitaeUiController.MainTextId);
|
||||
Assert.Contains("lost 10 %", text, StringComparison.Ordinal);
|
||||
Assert.Contains("earn 379 more experience", text, StringComparison.Ordinal);
|
||||
Assert.Equal(479, VitaeUiController.VitaeCpPoolThreshold(0.9f, 10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MiniGameFixture_BindsItsAuthoredCloseButton()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadMiniGame();
|
||||
int closes = 0;
|
||||
using MiniGameUiController controller = MiniGameUiController.Bind(
|
||||
layout, () => closes++)!;
|
||||
|
||||
UiButton close = Assert.IsType<UiButton>(layout.FindElement(MiniGameUiController.CloseId));
|
||||
close.OnEvent(new UiEvent(0, close, UiEventType.Click));
|
||||
|
||||
Assert.Equal(1, closes);
|
||||
}
|
||||
|
||||
private static void AssertPage(
|
||||
ElementInfo info,
|
||||
uint rootId,
|
||||
uint type,
|
||||
uint contentId,
|
||||
uint closeId)
|
||||
{
|
||||
Assert.Equal(rootId, info.Id);
|
||||
Assert.Equal(type, info.Type);
|
||||
Assert.Equal((300f, 362f), (info.Width, info.Height));
|
||||
Assert.NotNull(Find(info, contentId));
|
||||
Assert.NotNull(Find(info, closeId));
|
||||
Assert.True(info.TryGetEffectiveBool(
|
||||
RetailPanelUiController.RestorePreviousPropertyId,
|
||||
out bool restorePrevious));
|
||||
Assert.True(restorePrevious);
|
||||
}
|
||||
|
||||
private static string VisibleText(ImportedLayout layout, uint id)
|
||||
{
|
||||
UiText text = Assert.IsType<UiText>(layout.FindElement(id));
|
||||
return string.Join(' ', text.LinesProvider().Select(line => line.Text));
|
||||
}
|
||||
|
||||
private static ElementInfo? Find(ElementInfo root, uint id)
|
||||
{
|
||||
if (root.Id == id) return root;
|
||||
foreach (ElementInfo child in root.Children)
|
||||
if (Find(child, id) is { } found) return found;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue