feat: port retail magic lifecycle and retained spell UI
Complete the retail cast-intent, target, component, enchantment, and busy-state paths; mount the DAT-authored spell bar, spellbook, component book, effects panels, and shared panel lifecycle; and add scoped input plus conformance coverage. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
7b7ffcd278
commit
07be994d97
84 changed files with 17822 additions and 1051 deletions
|
|
@ -0,0 +1,102 @@
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Spells;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class EffectsIndicatorControllerTests
|
||||
{
|
||||
private static (uint, int, int) NoTex(uint _) => (0u, 0, 0);
|
||||
|
||||
[Fact]
|
||||
public void AuthoredFixture_BuildsRetailIndicatorButtons()
|
||||
{
|
||||
ElementInfo info = FixtureLoader.LoadIndicatorsInfos();
|
||||
Assert.Equal(0x10000610u, info.Id);
|
||||
Assert.Equal(150f, info.Width);
|
||||
Assert.Equal(30f, info.Height);
|
||||
|
||||
ImportedLayout layout = LayoutImporter.Build(info, NoTex, datFont: null);
|
||||
Assert.IsType<UiButton>(layout.FindElement(EffectsIndicatorController.HelpfulButtonId));
|
||||
Assert.IsType<UiButton>(layout.FindElement(EffectsIndicatorController.HarmfulButtonId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enchantments_GhostAndEnableMatchingIndicator_ThenClickCorrectPanel()
|
||||
{
|
||||
var table = SpellTable.LoadFromReader(new StringReader(
|
||||
"Spell ID,Name,Flags [Hex]\n42,Boon,0x4\n43,Bane,0x0\n"));
|
||||
var spellbook = new Spellbook(table);
|
||||
ImportedLayout layout = BuildSyntheticLayout();
|
||||
var toggled = new List<uint>();
|
||||
using EffectsIndicatorController controller = EffectsIndicatorController.Bind(
|
||||
layout, spellbook, toggled.Add)!;
|
||||
UiButton helpful = Assert.IsType<UiButton>(
|
||||
layout.FindElement(EffectsIndicatorController.HelpfulButtonId));
|
||||
UiButton harmful = Assert.IsType<UiButton>(
|
||||
layout.FindElement(EffectsIndicatorController.HarmfulButtonId));
|
||||
|
||||
Assert.False(helpful.Enabled);
|
||||
Assert.False(harmful.Enabled);
|
||||
|
||||
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
|
||||
42u, 10u, 60f, 1u, Bucket: 4u, SpellCategory: 42u));
|
||||
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
|
||||
43u, 11u, 60f, 2u, Bucket: 8u, SpellCategory: 43u));
|
||||
Assert.False(helpful.Enabled);
|
||||
Assert.False(harmful.Enabled);
|
||||
|
||||
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
|
||||
42u, 1u, 60f, 1u, Bucket: 1u, SpellCategory: 99u, PowerLevel: 1u));
|
||||
Assert.True(helpful.Enabled);
|
||||
Assert.False(harmful.Enabled);
|
||||
helpful.OnEvent(new UiEvent(0, helpful, UiEventType.Click));
|
||||
Assert.Equal([RetailPanelCatalog.PositiveEffects], toggled);
|
||||
|
||||
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
|
||||
43u, 1u, 60f, 2u, Bucket: 2u, SpellCategory: 99u, PowerLevel: 7u));
|
||||
// The harmful spell wins the shared category in the effects list, but
|
||||
// retail's raw registry counters keep both indicator types enabled.
|
||||
Assert.True(helpful.Enabled);
|
||||
Assert.True(harmful.Enabled);
|
||||
harmful.OnEvent(new UiEvent(0, harmful, UiEventType.Click));
|
||||
Assert.Equal(
|
||||
[RetailPanelCatalog.PositiveEffects, RetailPanelCatalog.NegativeEffects],
|
||||
toggled);
|
||||
|
||||
spellbook.OnEnchantmentRemoved(1u, 42u);
|
||||
Assert.False(helpful.Enabled);
|
||||
Assert.True(harmful.Enabled);
|
||||
}
|
||||
|
||||
private static ImportedLayout BuildSyntheticLayout()
|
||||
{
|
||||
var root = new ElementInfo { Id = 1u, Type = 3u, Width = 150f, Height = 30f };
|
||||
var helpful = ButtonInfo(EffectsIndicatorController.HelpfulButtonId);
|
||||
var harmful = ButtonInfo(EffectsIndicatorController.HarmfulButtonId);
|
||||
return LayoutImporter.BuildFromInfos(root, [helpful, harmful], NoTex, null);
|
||||
}
|
||||
|
||||
private static ElementInfo ButtonInfo(uint id)
|
||||
{
|
||||
var info = new ElementInfo
|
||||
{
|
||||
Id = id,
|
||||
Type = EffectsIndicatorController.RetailClassId,
|
||||
Width = 20f,
|
||||
Height = 20f,
|
||||
DefaultStateId = UiButtonStateMachine.Normal,
|
||||
};
|
||||
info.States[UiButtonStateMachine.Normal] = new UiStateInfo
|
||||
{
|
||||
Id = UiButtonStateMachine.Normal,
|
||||
Name = "Normal",
|
||||
};
|
||||
info.States[UiButtonStateMachine.Ghosted] = new UiStateInfo
|
||||
{
|
||||
Id = UiButtonStateMachine.Ghosted,
|
||||
Name = "Ghosted",
|
||||
};
|
||||
return info;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue