The owner calls the flag tracker important, so the original's pages come across with their tables unchanged: augmentations and luminance auras from the player's own int properties (the object table carries them), recalls from the spellbook with composited spell icons, cantrips matched from the active enchantments to trained skills, attributes and wards, the tracked weapons the original kept data for but never showed, and the quest timers. Read on the tick thread at login and on each page's Refresh; the quest page redraws every five seconds instead of from a thread-pool timer. The root panel's visibility is the settable IsVisible so the main window can open it; the three sections the original left as stubs stay out, and a headless host registers nothing. The fake spell catalog learns IsKnown. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
239 lines
9.8 KiB
C#
239 lines
9.8 KiB
C#
using AcDream.Plugin.Abstractions;
|
|
using OpenAC.MosswartMassacre.FlagTracker;
|
|
using OpenAC.MosswartMassacre.Tests.Fakes;
|
|
|
|
namespace OpenAC.MosswartMassacre.Tests.FlagTracker;
|
|
|
|
public sealed class FlagTrackerTests
|
|
{
|
|
private const uint Player = 0x50000001;
|
|
|
|
private static PluginHarness WithUi(Action<FakeHost>? arrange = null) =>
|
|
new(arrange: host =>
|
|
{
|
|
host.HasUi = true;
|
|
arrange?.Invoke(host);
|
|
});
|
|
|
|
private static void SetPlayerInts(FakeHost host, Dictionary<uint, int> ints) =>
|
|
host.Automation.Objects.Properties[Player] = new PluginItemProperties(
|
|
ints,
|
|
new Dictionary<uint, long>(),
|
|
new Dictionary<uint, bool>(),
|
|
new Dictionary<uint, double>(),
|
|
new Dictionary<uint, string>(),
|
|
new Dictionary<uint, uint>(),
|
|
new Dictionary<uint, uint>());
|
|
|
|
private static FlagTrackerViewModel Panel(PluginHarness harness) =>
|
|
harness.Feature<FlagTrackerFeature>().Panel!;
|
|
|
|
private static int Row(IReadOnlyList<string> names, string name)
|
|
{
|
|
int index = names.ToList().IndexOf(name);
|
|
Assert.True(index >= 0, $"no row {name}");
|
|
return index;
|
|
}
|
|
|
|
[Fact]
|
|
public void A_headless_host_gets_no_window_and_login_does_nothing()
|
|
{
|
|
using var harness = new PluginHarness();
|
|
harness.Login();
|
|
harness.Run(6);
|
|
|
|
FlagTrackerFeature feature = harness.Feature<FlagTrackerFeature>();
|
|
Assert.Null(feature.Panel);
|
|
Assert.Empty(harness.Host.Ui.Panels);
|
|
}
|
|
|
|
[Fact]
|
|
public void A_graphical_host_gets_the_mossy_tracker_window_closed()
|
|
{
|
|
using var harness = WithUi();
|
|
|
|
RegisteredPanel panel = Assert.Single(harness.Host.Ui.Panels);
|
|
Assert.Equal("flagtracker", panel.Descriptor.WindowId);
|
|
Assert.Equal("Mossy Tracker", panel.Descriptor.Title);
|
|
Assert.True(panel.Descriptor.ShowInSidePanel);
|
|
Assert.EndsWith(FlagTrackerFeature.MarkupFile, panel.MarkupPath, StringComparison.Ordinal);
|
|
Assert.Same(Panel(harness), panel.Binding);
|
|
Assert.False(Panel(harness).IsVisible);
|
|
Assert.True(Panel(harness).AugsSelected);
|
|
|
|
Panel(harness).ShowQuests();
|
|
Assert.True(Panel(harness).QuestsSelected);
|
|
Assert.False(Panel(harness).AugsSelected);
|
|
}
|
|
|
|
[Fact]
|
|
public void Augmentations_come_from_the_players_own_properties()
|
|
{
|
|
using var harness = WithUi(host => SetPlayerInts(host, new() { [231] = 3, [230] = 2 }));
|
|
harness.Login();
|
|
FlagTrackerViewModel panel = Panel(harness);
|
|
|
|
Assert.Equal("--- Death Augs ---", panel.AugNames[0]);
|
|
int keepItems = Row(panel.AugNames, "Keep Items");
|
|
Assert.Equal("[MAX]", panel.AugProgress[keepItems]);
|
|
Assert.Equal(0x008000u, panel.AugProgressColors[keepItems]);
|
|
Assert.Equal("Rohula bint Ludun", panel.AugTrainers[keepItems]);
|
|
Assert.Equal("Ayan Baqur", panel.AugLocations[keepItems]);
|
|
|
|
int keepSpells = Row(panel.AugNames, "Keep Spells");
|
|
Assert.Equal("[0/1]", panel.AugProgress[keepSpells]);
|
|
Assert.Equal(0xFF0000u, panel.AugProgressColors[keepSpells]);
|
|
|
|
int carrying = Row(panel.AugNames, "Extra Carrying Capacity");
|
|
Assert.Equal("[2/5]", panel.AugProgress[carrying]);
|
|
Assert.Equal(0xFFFF00u, panel.AugProgressColors[carrying]);
|
|
|
|
// A bought augmentation shows after Refresh.
|
|
SetPlayerInts(harness.Host, new() { [232] = 1 });
|
|
panel.RefreshAugs();
|
|
Assert.Equal("[MAX]", panel.AugProgress[keepSpells]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Luminance_auras_show_progress_and_the_seer_they_belong_to()
|
|
{
|
|
using var harness = WithUi(host => SetPlayerInts(host, new() { [333] = 5, [344] = 2 }));
|
|
harness.Login();
|
|
FlagTrackerViewModel panel = Panel(harness);
|
|
|
|
int damage = Row(panel.LumNames, "+1 Damage Rating");
|
|
Assert.Equal("5/5", panel.LumProgress[damage]);
|
|
Assert.Equal(0x008000u, panel.LumProgressColors[damage]);
|
|
Assert.Equal("Nalicana Auras", panel.LumCategories[damage]);
|
|
|
|
int kahiri = Row(panel.LumNames, "(Ka'hiri) +2 Specialized Skills");
|
|
Assert.Equal("2/5", panel.LumProgress[kahiri]);
|
|
Assert.Equal(0xFFFF00u, panel.LumProgressColors[kahiri]);
|
|
Assert.Equal("LoyalToKahiri", panel.LumCategories[kahiri]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Recalls_show_known_and_unknown_with_their_spell_icons()
|
|
{
|
|
using var harness = WithUi(host => host.Automation.Spells.KnownSpellIds.Add(2023));
|
|
harness.Login();
|
|
FlagTrackerViewModel panel = Panel(harness);
|
|
|
|
Assert.Equal(17, panel.RecallNames.Count);
|
|
Assert.Equal("Recall the Sanctuary", panel.RecallNames[0]);
|
|
Assert.Equal(2023u, panel.RecallSpellIds[0]);
|
|
Assert.Equal("Known", panel.RecallStatus[0]);
|
|
Assert.Equal(0x008000u, panel.RecallStatusColors[0]);
|
|
Assert.Equal("Unknown", panel.RecallStatus[1]);
|
|
Assert.Equal(0xFF0000u, panel.RecallStatusColors[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Cantrips_are_matched_to_trained_skills_attributes_and_wards()
|
|
{
|
|
using var harness = WithUi(host =>
|
|
{
|
|
host.Automation.Character.Skills.Add(new PluginSkillInfo(34, "War Magic", PluginSkillTraining.Specialized, 400));
|
|
host.Automation.Character.Skills.Add(new PluginSkillInfo(6, "Melee Defense", PluginSkillTraining.Trained, 300));
|
|
host.Automation.Character.Skills.Add(new PluginSkillInfo(21, "Healing", PluginSkillTraining.Untrained, 10));
|
|
host.Automation.Spells.All =
|
|
[
|
|
FakeSpellCatalog.Spell(4715, "Epic War Magic Aptitude"),
|
|
FakeSpellCatalog.Spell(2248, "Major Invulnerability"),
|
|
FakeSpellCatalog.Spell(1, "Minor Invulnerability"),
|
|
FakeSpellCatalog.Spell(6099, "Legendary Flame Ward"),
|
|
FakeSpellCatalog.Spell(2091, "Major Strength"),
|
|
FakeSpellCatalog.Spell(2, "Strength Self VIII"),
|
|
];
|
|
host.Automation.Character.ActiveEnchantments =
|
|
[
|
|
new PluginActiveEnchantment(4715, 0, 0, -1),
|
|
new PluginActiveEnchantment(2248, 0, 0, -1),
|
|
new PluginActiveEnchantment(1, 0, 0, -1),
|
|
new PluginActiveEnchantment(6099, 0, 0, -1),
|
|
new PluginActiveEnchantment(2091, 0, 0, -1),
|
|
new PluginActiveEnchantment(2, 0, 0, 3600),
|
|
];
|
|
});
|
|
harness.Login();
|
|
FlagTrackerViewModel panel = Panel(harness);
|
|
|
|
Assert.Equal(
|
|
[
|
|
"--- Specialized Skills ---", "War Magic",
|
|
"--- Trained Skills ---", "Invulnerability",
|
|
"--- Attributes ---", "Strength",
|
|
"--- Protection Auras ---", "Armor", "Bludgeoning Ward", "Piercing Ward",
|
|
"Slashing Ward", "Flame Ward", "Frost Ward", "Acid Ward", "Storm Ward",
|
|
],
|
|
panel.CantripNames);
|
|
|
|
Assert.Equal(FlagTrackerData.Icons.CategoryStar, panel.CantripIcons[0]);
|
|
Assert.Equal("Epic", panel.CantripLevels[1]);
|
|
Assert.Equal(0x800080u, panel.CantripLevelColors[1]);
|
|
Assert.Equal(FlagTrackerData.Icons.GreenCircle, panel.CantripIcons[1]);
|
|
// The higher of two cantrips on one skill wins.
|
|
Assert.Equal("Major", panel.CantripLevels[3]);
|
|
Assert.Equal(0x0000FFu, panel.CantripLevelColors[3]);
|
|
Assert.Equal("Major", panel.CantripLevels[5]);
|
|
Assert.Equal("N/A", panel.CantripLevels[7]);
|
|
Assert.Equal(FlagTrackerData.Icons.RedCircle, panel.CantripIcons[7]);
|
|
int flame = Row(panel.CantripNames, "Flame Ward");
|
|
Assert.Equal("Legendary", panel.CantripLevels[flame]);
|
|
Assert.Equal(0xFFA500u, panel.CantripLevelColors[flame]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Tracked_weapons_are_found_in_the_inventory_by_name()
|
|
{
|
|
using var harness = WithUi(host => host.Automation.Items.Owned.Add(
|
|
FakeItems.Item(0x80000001, "Atlan Sword") with { IconId = 0x06001234 }));
|
|
harness.Login();
|
|
FlagTrackerViewModel panel = Panel(harness);
|
|
|
|
int atlan = Row(panel.WeaponNames, "Atlan Sword");
|
|
Assert.Equal("Acquired", panel.WeaponStatus[atlan]);
|
|
Assert.Equal(0x06001234u, panel.WeaponIcons[atlan]);
|
|
int axe = Row(panel.WeaponNames, "Atlan Axe");
|
|
Assert.Equal("Not Acquired", panel.WeaponStatus[axe]);
|
|
Assert.Equal(FlagTrackerData.Icons.Portal, panel.WeaponIcons[axe]);
|
|
}
|
|
|
|
[Fact]
|
|
public void The_quest_page_hides_maxed_quests_and_sorts_by_solves()
|
|
{
|
|
using var harness = WithUi();
|
|
harness.Login();
|
|
long now = new DateTimeOffset(harness.Clock.UtcNow).ToUnixTimeSeconds();
|
|
var chat = harness.Host.Automation.Chat;
|
|
chat.Deliver($"stipendtimer_0812 - 2 solves ({now - 10})\"x\" -1 3700");
|
|
chat.Deliver($"maxed - 5 solves ({now})\"x\" 5 60");
|
|
chat.Deliver($"aaa - 7 solves ({now - 100})\"x\" -1 50");
|
|
|
|
FlagTrackerViewModel panel = Panel(harness);
|
|
Assert.Equal(["aaa", "Stipend Collection Timer"], panel.QuestRowNames);
|
|
Assert.Equal(["READY", "1h 01m"], panel.QuestCountdowns);
|
|
Assert.Equal([0x008000u, 0xFFFF00u], panel.QuestCountdownColors);
|
|
Assert.Equal(["50s", "1h 1m 40s"], panel.QuestCooldowns);
|
|
Assert.Equal(["7", "2"], panel.QuestSolves);
|
|
|
|
// The countdowns move on every five seconds.
|
|
harness.Run(5.1);
|
|
Assert.Equal("1h 01m", panel.QuestCountdowns[1]);
|
|
harness.Run(120);
|
|
Assert.StartsWith("59m ", panel.QuestCountdowns[1], StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Logoff_empties_the_pages()
|
|
{
|
|
using var harness = WithUi();
|
|
harness.Login();
|
|
Assert.NotEmpty(Panel(harness).AugNames);
|
|
|
|
harness.Plugin.OnLogoff();
|
|
|
|
Assert.Empty(Panel(harness).AugNames);
|
|
Assert.Empty(Panel(harness).QuestRowNames);
|
|
}
|
|
}
|