The original kept VTank's profile folder in step with the club's metas repository; the port does the same against host.VtankProfiles, dropping .met files under mosstank/metas and .nav files under mosstank/navs where MossTank reads dropped profiles, flattened to their bare names as the original flattened them. The Gitea tree and raw endpoints are the original's; the repository address is the metas_repository_url setting, defaulting to the original repository. An overwritten file keeps its old copy as .bak (MossTank's pickers skip .bak side files). Storage is text-only, so the SHA-256 compare hashes the text a stored read gives back (UTF-8, BOM dropped); for the plain-text files in the repo that equals the original's byte compare. Network work runs on the thread pool, one operation after another; chat lines and window updates are applied on the tick. The four /mm verbs keep their chat lines, and the Metas tab is its own markup window with IsVisible for the main window to open. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
239 lines
9.9 KiB
C#
239 lines
9.9 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, registered => registered.Descriptor.WindowId == "flagtracker");
|
|
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);
|
|
}
|
|
}
|