feat(mosstank): add VTank-style automation PoC
This commit is contained in:
parent
f6fe0f2a4f
commit
4e6e9bc9d9
212 changed files with 49462 additions and 416 deletions
|
|
@ -0,0 +1,190 @@
|
|||
namespace AcDream.Plugins.MossTank.Tests;
|
||||
|
||||
public sealed class VtankMetaProfileSerializerTests
|
||||
{
|
||||
[Fact]
|
||||
public void LoadsKnownTypedCondActRecord()
|
||||
{
|
||||
const string source = "1\r\nCondAct\r\n5\r\nCType\r\nAType\r\n"
|
||||
+ "CData\r\nAData\r\nState\r\nn\r\nn\r\nn\r\nn\r\nn\r\n1\r\n"
|
||||
+ "i\r\n1\r\ni\r\n2\r\ni\r\n0\r\ns\r\n/say ready\r\n"
|
||||
+ "s\r\nDefault\r\n";
|
||||
|
||||
Assert.True(VtankMetaProfileSerializer.TryLoad(
|
||||
source, out MetaProfile profile, out string error), error);
|
||||
|
||||
MetaRule rule = Assert.Single(profile.Rules);
|
||||
Assert.Equal(MetaConditionKind.Always, rule.Condition.Kind);
|
||||
Assert.Equal(MetaActionKind.ChatCommand, rule.Action.Kind);
|
||||
Assert.Equal("/say ready", rule.Action.Text);
|
||||
Assert.Equal("Default", rule.State);
|
||||
Assert.Equal(source, VtankMetaProfileSerializer.Save(profile));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTripPreservesEveryVtankConditionActionAndEmbeddedNav()
|
||||
{
|
||||
MetaCondition[] conditions = BuildConditions();
|
||||
MetaAction[] actions = BuildActions();
|
||||
var profile = new MetaProfile();
|
||||
for (int index = 0; index < conditions.Length; index++)
|
||||
{
|
||||
profile.Rules.Add(new MetaRule
|
||||
{
|
||||
State = $"State {index}",
|
||||
Condition = conditions[index],
|
||||
Action = actions[index % actions.Length],
|
||||
});
|
||||
}
|
||||
for (int index = conditions.Length; index < actions.Length; index++)
|
||||
{
|
||||
profile.Rules.Add(new MetaRule
|
||||
{
|
||||
State = $"Action {index}",
|
||||
Condition = MetaCondition.Always(),
|
||||
Action = actions[index],
|
||||
});
|
||||
}
|
||||
|
||||
string first = VtankMetaProfileSerializer.Save(profile);
|
||||
Assert.True(VtankMetaProfileSerializer.TryLoad(
|
||||
first, out MetaProfile loaded, out string error), error);
|
||||
string second = VtankMetaProfileSerializer.Save(loaded);
|
||||
|
||||
Assert.Equal(first, second);
|
||||
Assert.Equal(profile.Rules.Count, loaded.Rules.Count);
|
||||
MetaCondition priority = loaded.Rules.Single(rule =>
|
||||
rule.Condition.Kind == MetaConditionKind.MonsterPriorityCountWithinDistance)
|
||||
.Condition;
|
||||
Assert.Equal(2, priority.Number);
|
||||
Assert.Equal(18.5, priority.SecondaryNumber);
|
||||
Assert.Equal(7, priority.TertiaryNumber);
|
||||
MetaAction embedded = loaded.Rules.Select(static rule => rule.Action)
|
||||
.First(action => action.Kind == MetaActionKind.LoadEmbeddedNavigationRoute);
|
||||
Assert.Equal("One point", embedded.SecondaryText);
|
||||
Assert.Equal("uTank2 NAV 1.2\r\n4\r\n1\r\n0\r\n1\r\n2\r\n3\r\n0\r\n",
|
||||
embedded.Text);
|
||||
Assert.Contains("<view width=\"120\" />s\r\n", first, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisabledNativeRuleIsNotExportedAsExecutableLegacyRule()
|
||||
{
|
||||
var profile = new MetaProfile
|
||||
{
|
||||
Rules =
|
||||
[
|
||||
new MetaRule
|
||||
{
|
||||
Enabled = false,
|
||||
Condition = MetaCondition.Always(),
|
||||
Action = new MetaAction
|
||||
{
|
||||
Kind = MetaActionKind.ChatCommand,
|
||||
Text = "/say must not run",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
string source = VtankMetaProfileSerializer.Save(profile);
|
||||
Assert.DoesNotContain("must not run", source, StringComparison.Ordinal);
|
||||
Assert.True(VtankMetaProfileSerializer.TryLoad(
|
||||
source, out MetaProfile loaded, out string error), error);
|
||||
Assert.Empty(loaded.Rules);
|
||||
}
|
||||
|
||||
private static MetaCondition[] BuildConditions() =>
|
||||
[
|
||||
C(MetaConditionKind.Never),
|
||||
C(MetaConditionKind.Always),
|
||||
C(MetaConditionKind.All, children: [C(MetaConditionKind.Always)]),
|
||||
C(MetaConditionKind.Any, children: [C(MetaConditionKind.Never)]),
|
||||
C(MetaConditionKind.ChatMessage, "^ready$"),
|
||||
C(MetaConditionKind.PackSlotsLessThanOrEqual, number: 7),
|
||||
C(MetaConditionKind.SecondsInStateGreaterThanOrEqual, number: 12),
|
||||
C(MetaConditionKind.NavigationRouteEmpty),
|
||||
C(MetaConditionKind.CharacterDeath),
|
||||
C(MetaConditionKind.AnyVendorOpen),
|
||||
C(MetaConditionKind.VendorClosed),
|
||||
C(MetaConditionKind.InventoryItemCountLessThanOrEqual, "Prismatic Taper", 5),
|
||||
C(MetaConditionKind.InventoryItemCountGreaterThanOrEqual, "Pyreal", 10),
|
||||
C(MetaConditionKind.MonsterNameCountWithinDistance, "Olthoi.*", 3, 22.25),
|
||||
C(MetaConditionKind.MonsterPriorityCountWithinDistance,
|
||||
number: 2, secondaryNumber: 18.5, tertiaryNumber: 7),
|
||||
C(MetaConditionKind.NeedToBuff),
|
||||
C(MetaConditionKind.NoMonstersWithinDistance, number: 9.5),
|
||||
C(MetaConditionKind.LandblockEquals, number: unchecked((int)0x8B370000u)),
|
||||
C(MetaConditionKind.LandcellEquals, number: unchecked((int)0x8B37E3A1u)),
|
||||
C(MetaConditionKind.PortalspaceEntered),
|
||||
C(MetaConditionKind.PortalspaceExited),
|
||||
C(MetaConditionKind.Not, children: [C(MetaConditionKind.Never)]),
|
||||
C(MetaConditionKind.PersistentSecondsInStateGreaterThanOrEqual, number: 30),
|
||||
C(MetaConditionKind.TimeLeftOnSpellGreaterThanOrEqual,
|
||||
number: 3179, secondaryNumber: 45),
|
||||
C(MetaConditionKind.BurdenPercentGreaterThanOrEqual, number: 110),
|
||||
C(MetaConditionKind.DistanceFromAnyRoutePointGreaterThanOrEqual, number: 13.75),
|
||||
C(MetaConditionKind.Expression, "getvar['go']==1"),
|
||||
C(MetaConditionKind.ChatMessageCapture, "^(?<who>.+)$", secondary: "2;4"),
|
||||
];
|
||||
|
||||
private static MetaAction[] BuildActions() =>
|
||||
[
|
||||
A(MetaActionKind.None),
|
||||
A(MetaActionKind.SetMetaState, "Hunt"),
|
||||
A(MetaActionKind.ChatCommand, "/say hello"),
|
||||
A(MetaActionKind.All, children:
|
||||
[
|
||||
A(MetaActionKind.ChatCommand, "/say first"),
|
||||
A(MetaActionKind.ExpressionAction, "setvar['done',1]"),
|
||||
]),
|
||||
A(MetaActionKind.LoadEmbeddedNavigationRoute,
|
||||
"uTank2 NAV 1.2\r\n4\r\n1\r\n0\r\n1\r\n2\r\n3\r\n0\r\n",
|
||||
"One point"),
|
||||
A(MetaActionKind.CallMetaState, "Worker", "ReturnHere"),
|
||||
A(MetaActionKind.ReturnFromCall),
|
||||
A(MetaActionKind.ExpressionAction, "setvar['x',2]"),
|
||||
A(MetaActionKind.ChatExpression, "cstr[getvar['x']]"),
|
||||
A(MetaActionKind.SetWatchdog, "Recover", number: 12.5, secondaryNumber: 4.75),
|
||||
A(MetaActionKind.ClearWatchdog),
|
||||
A(MetaActionKind.GetVtankOption, "OpenDoors", "doors"),
|
||||
A(MetaActionKind.SetVtankOption, "OpenDoors", "istrue[1]"),
|
||||
A(MetaActionKind.CreateView, "myview", "<view width=\"120\" />"),
|
||||
A(MetaActionKind.DestroyView, "myview"),
|
||||
A(MetaActionKind.DestroyAllViews),
|
||||
];
|
||||
|
||||
private static MetaCondition C(
|
||||
MetaConditionKind kind,
|
||||
string text = "",
|
||||
double number = 0,
|
||||
double secondaryNumber = 0,
|
||||
double tertiaryNumber = 0,
|
||||
string secondary = "",
|
||||
List<MetaCondition>? children = null) => new()
|
||||
{
|
||||
Kind = kind,
|
||||
Text = text,
|
||||
SecondaryText = secondary,
|
||||
Number = number,
|
||||
SecondaryNumber = secondaryNumber,
|
||||
TertiaryNumber = tertiaryNumber,
|
||||
Children = children ?? [],
|
||||
};
|
||||
|
||||
private static MetaAction A(
|
||||
MetaActionKind kind,
|
||||
string text = "",
|
||||
string secondary = "",
|
||||
double number = 0,
|
||||
double secondaryNumber = 0,
|
||||
List<MetaAction>? children = null) => new()
|
||||
{
|
||||
Kind = kind,
|
||||
Text = text,
|
||||
SecondaryText = secondary,
|
||||
Number = number,
|
||||
SecondaryNumber = secondaryNumber,
|
||||
Children = children ?? [],
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue