Preserve retail's one-pending-appraisal busy lifetime, parse the complete gated response, and mount the authored examination layout in the shared main-panel host. Keep known 3D preview and inscription-write gaps explicit in AP-110.
192 lines
7 KiB
C#
192 lines
7 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
using AcDream.Core.Combat;
|
|
using AcDream.Core.Items;
|
|
using AcDream.Core.Net.Messages;
|
|
using AcDream.Core.Spells;
|
|
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
public sealed class AppraisalUiControllerTests
|
|
{
|
|
private const uint ObjectId = 0x50000001u;
|
|
|
|
[Fact]
|
|
public void ItemResponse_UsesAuthoredItemSubviewTitleAndScrollbars()
|
|
{
|
|
ImportedLayout layout = FixtureLoader.LoadExamination();
|
|
var objects = new ClientObjectTable();
|
|
objects.AddOrUpdate(new ClientObject
|
|
{
|
|
ObjectId = ObjectId,
|
|
Name = "Atlan Weapon",
|
|
Type = ItemType.MeleeWeapon,
|
|
Value = 1250,
|
|
Burden = 350,
|
|
});
|
|
var sent = new List<uint>();
|
|
using var interaction = NewInteraction(objects, sent);
|
|
var combat = new CombatState();
|
|
int shown = 0;
|
|
int closed = 0;
|
|
using AppraisalUiController controller = AppraisalUiController.Bind(
|
|
layout,
|
|
objects,
|
|
interaction,
|
|
combat,
|
|
new Spellbook(),
|
|
() => shown++,
|
|
() => closed++)!;
|
|
|
|
Assert.True(interaction.ExamineSelectedOrEnterMode(ObjectId));
|
|
var properties = new PropertyBundle();
|
|
properties.Strings[16u] = "A finely balanced weapon.";
|
|
properties.Strings[7u] = "Remember the fallen.";
|
|
properties.Strings[8u] = "Tester";
|
|
|
|
Assert.True(controller.Apply(Parsed(properties)));
|
|
|
|
Assert.Equal(1, shown);
|
|
Assert.Equal(AppraisalView.Item, controller.ActiveView);
|
|
Assert.Equal(0, interaction.BusyCount);
|
|
UiText title = Assert.IsType<UiText>(
|
|
layout.FindElement(AppraisalUiController.TitleId));
|
|
Assert.Equal("Atlan Weapon", Assert.Single(title.LinesProvider()).Text);
|
|
UiText itemText = Assert.IsType<UiText>(
|
|
layout.FindElement(AppraisalUiController.ItemTextId));
|
|
string report = string.Join('\n', itemText.LinesProvider().Select(line => line.Text));
|
|
Assert.Contains("Value: 1,250", report);
|
|
Assert.Contains("Burden: 350", report);
|
|
Assert.Contains("A finely balanced weapon.", report);
|
|
UiScrollbar scrollbar = Assert.IsType<UiScrollbar>(
|
|
layout.FindElement(AppraisalUiController.ItemScrollbarId));
|
|
Assert.Same(itemText.Scroll, scrollbar.Model);
|
|
UiField inscription = Assert.IsType<UiField>(
|
|
layout.FindElement(AppraisalUiController.InscriptionTextId));
|
|
Assert.Contains("Remember the fallen", inscription.Text);
|
|
Assert.False(inscription.Enabled);
|
|
UiText signature = Assert.IsType<UiText>(
|
|
layout.FindElement(AppraisalUiController.SignatureTextId));
|
|
Assert.Equal("Inscribed by Tester", Assert.Single(signature.LinesProvider()).Text);
|
|
|
|
((UiButton)layout.FindElement(AppraisalUiController.CloseId)!).OnClick!.Invoke();
|
|
Assert.Equal(1, closed);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreatureResponse_SelectsCreatureSubviewAndRefreshesInCombatWithoutBusy()
|
|
{
|
|
ImportedLayout layout = FixtureLoader.LoadExamination();
|
|
var objects = new ClientObjectTable();
|
|
objects.AddOrUpdate(new ClientObject
|
|
{
|
|
ObjectId = ObjectId,
|
|
Name = "Drudge Slinker",
|
|
Type = ItemType.Creature,
|
|
});
|
|
var sent = new List<uint>();
|
|
using var interaction = NewInteraction(objects, sent);
|
|
var combat = new CombatState();
|
|
int shown = 0;
|
|
using AppraisalUiController controller = AppraisalUiController.Bind(
|
|
layout,
|
|
objects,
|
|
interaction,
|
|
combat,
|
|
new Spellbook(),
|
|
() => shown++,
|
|
() => { })!;
|
|
|
|
interaction.ExamineSelectedOrEnterMode(ObjectId);
|
|
var properties = new PropertyBundle();
|
|
properties.Ints[25u] = 12;
|
|
var creature = new AppraiseInfoParser.CreatureProfile(
|
|
Flags: 0,
|
|
Health: 80,
|
|
HealthMax: 100,
|
|
Strength: null,
|
|
Endurance: null,
|
|
Quickness: null,
|
|
Coordination: null,
|
|
Focus: null,
|
|
Self: null,
|
|
Stamina: null,
|
|
Mana: null,
|
|
StaminaMax: null,
|
|
ManaMax: null,
|
|
AttributeHighlights: null,
|
|
AttributeColors: null);
|
|
|
|
Assert.True(controller.Apply(Parsed(properties, creature)));
|
|
Assert.Equal(AppraisalView.Creature, controller.ActiveView);
|
|
Assert.Equal(1, shown);
|
|
Assert.False(layout.FindElement(AppraisalUiController.ItemPanelId)!.Visible);
|
|
Assert.True(layout.FindElement(AppraisalUiController.CreaturePanelId)!.Visible);
|
|
|
|
combat.SetCombatMode(CombatMode.Melee);
|
|
controller.Tick(0.74);
|
|
Assert.Single(sent);
|
|
controller.Tick(0.01);
|
|
Assert.Equal(new[] { ObjectId, ObjectId }, sent);
|
|
Assert.Equal(0, interaction.BusyCount);
|
|
|
|
layout.Root.Visible = false;
|
|
controller.Tick(0.75);
|
|
Assert.Equal(new[] { ObjectId, ObjectId }, sent);
|
|
|
|
Assert.True(controller.Apply(Parsed(properties, creature)));
|
|
Assert.Equal(1, shown);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResponseForNeitherPendingNorCurrent_IsIgnored()
|
|
{
|
|
ImportedLayout layout = FixtureLoader.LoadExamination();
|
|
var objects = new ClientObjectTable();
|
|
objects.AddOrUpdate(new ClientObject { ObjectId = ObjectId, Name = "Item" });
|
|
using var interaction = NewInteraction(objects, []);
|
|
using AppraisalUiController controller = AppraisalUiController.Bind(
|
|
layout,
|
|
objects,
|
|
interaction,
|
|
new CombatState(),
|
|
new Spellbook(),
|
|
() => throw new InvalidOperationException("must not show"),
|
|
() => { })!;
|
|
|
|
Assert.False(controller.Apply(Parsed(new PropertyBundle())));
|
|
Assert.Equal(0u, controller.CurrentObjectId);
|
|
}
|
|
|
|
private static ItemInteractionController NewInteraction(
|
|
ClientObjectTable objects,
|
|
List<uint> sent)
|
|
=> new(
|
|
objects,
|
|
playerGuid: () => 0x50000002u,
|
|
sendUse: null,
|
|
sendUseWithTarget: null,
|
|
sendWield: null,
|
|
sendDrop: null,
|
|
sendExamine: sent.Add);
|
|
|
|
private static AppraiseInfoParser.Parsed Parsed(
|
|
PropertyBundle properties,
|
|
AppraiseInfoParser.CreatureProfile? creature = null)
|
|
=> new(
|
|
Guid: ObjectId,
|
|
Flags: creature is null
|
|
? AppraiseInfoParser.IdentifyResponseFlags.IntStatsTable
|
|
: AppraiseInfoParser.IdentifyResponseFlags.CreatureProfile,
|
|
Success: true,
|
|
Properties: properties,
|
|
SpellBook: [],
|
|
ArmorProfile: null,
|
|
CreatureProfile: creature,
|
|
WeaponProfile: null,
|
|
HookProfile: null,
|
|
ArmorLevels: null,
|
|
ArmorEnchantments: null,
|
|
WeaponEnchantments: null,
|
|
ResistEnchantments: null);
|
|
}
|