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
|
|
@ -40,6 +40,7 @@ public sealed class ItemInteractionControllerTests
|
|||
public readonly List<(uint VendorGuid, IReadOnlyList<(int Amount, uint ItemGuid)> Items, uint AlternateCurrencyId)> BuyAlls = new();
|
||||
public bool SendBuyAllSucceeds = true;
|
||||
public readonly List<(uint VendorGuid, IReadOnlyList<(int Amount, uint ItemGuid)> Items)> Sells = new();
|
||||
public readonly List<(uint ToolGuid, IReadOnlyList<uint> ItemGuids)> Salvages = new();
|
||||
public bool SendSellSucceeds = true;
|
||||
public readonly List<string> Toasts = new();
|
||||
public readonly List<string> SystemMessages = new();
|
||||
|
|
@ -133,7 +134,12 @@ public sealed class ItemInteractionControllerTests
|
|||
},
|
||||
interfaceText: (text, type) => InterfaceTexts.Add((text, type)),
|
||||
sendStackableMerge: (source, target, amount) =>
|
||||
Merges.Add((source, target, amount)));
|
||||
Merges.Add((source, target, amount)),
|
||||
sendSalvage: (tool, items) =>
|
||||
{
|
||||
Salvages.Add((tool, items.ToArray()));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public ItemInteractionController Controller { get; }
|
||||
|
|
@ -171,6 +177,86 @@ public sealed class ItemInteractionControllerTests
|
|||
Assert.Empty(h.UseWithTarget);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationApply_dispatchesDirectlyWithoutInstallingTargetMode()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint source = 0x50000A21u;
|
||||
h.AddContained(source, item =>
|
||||
{
|
||||
item.Useability = HealthKitUseability;
|
||||
item.TargetType = (uint)ItemType.Creature;
|
||||
});
|
||||
|
||||
Assert.True(h.Controller.TryApplyItem(source, Player));
|
||||
|
||||
Assert.Equal(new[] { (source, Player) }, h.UseWithTarget);
|
||||
Assert.False(h.Controller.IsAnyTargetModeActive);
|
||||
Assert.Equal(1, h.Controller.BusyCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationApply_reportsRefusalWhenTargetIsIncompatible()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint source = 0x50000A21u;
|
||||
const uint coat = 0x50000A22u;
|
||||
h.AddContained(source, item =>
|
||||
{
|
||||
item.Useability = HealthKitUseability;
|
||||
item.TargetType = (uint)ItemType.Creature;
|
||||
});
|
||||
h.AddContained(coat, item => item.Type = ItemType.Armor);
|
||||
|
||||
Assert.False(h.Controller.TryApplyItem(source, coat));
|
||||
|
||||
Assert.Empty(h.UseWithTarget);
|
||||
Assert.Equal(0, h.Controller.BusyCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationUse_dispatchesOnlyAnOrdinaryWireUse()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint item = 0x50000A23u;
|
||||
h.AddContained(item, candidate =>
|
||||
candidate.Useability = ItemUseability.Contained);
|
||||
|
||||
Assert.True(h.Controller.TryUseItemForAutomation(item));
|
||||
|
||||
Assert.Equal(new[] { item }, h.Uses);
|
||||
Assert.Equal(1, h.Controller.BusyCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationUse_refusesTargetedItemInsteadOfOpeningModalCursor()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint item = 0x50000A24u;
|
||||
h.AddContained(item, candidate =>
|
||||
candidate.Useability = HealthKitUseability);
|
||||
|
||||
Assert.False(h.Controller.TryUseItemForAutomation(item));
|
||||
|
||||
Assert.Empty(h.Uses);
|
||||
Assert.False(h.Controller.IsAnyTargetModeActive);
|
||||
Assert.Equal(0, h.Controller.BusyCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationAppraisalUsesCanonicalOwnerWithoutChangingSelectionMode()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint item = 0x50000A25u;
|
||||
h.AddContained(item);
|
||||
|
||||
Assert.True(h.Controller.TryAppraiseForAutomation(item));
|
||||
|
||||
Assert.Equal(new[] { item }, h.Examines);
|
||||
Assert.False(h.Controller.IsAnyTargetModeActive);
|
||||
Assert.Equal(1, h.Controller.BusyCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetSession_RetryNotifiesEveryStateObserver()
|
||||
{
|
||||
|
|
@ -2085,6 +2171,110 @@ public sealed class ItemInteractionControllerTests
|
|||
Assert.False(h.Controller.TryGetPendingInventoryRequest(out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationMoveUsesWholeOrExactPartialRetailRequest()
|
||||
{
|
||||
var whole = new Harness();
|
||||
const uint wholeItem = 0x50000A32u;
|
||||
whole.AddContained(wholeItem, item => item.StackSize = 10);
|
||||
|
||||
Assert.True(whole.Controller.TryMoveItemForAutomation(
|
||||
wholeItem, Player, amount: 0u, placement: 7));
|
||||
Assert.Equal(new[] { (wholeItem, Player, 7) }, whole.Puts);
|
||||
Assert.True(whole.Controller.TryGetPendingInventoryRequest(out var put));
|
||||
Assert.Equal(InventoryRequestKind.PutInContainer, put.Kind);
|
||||
|
||||
var partial = new Harness();
|
||||
const uint partialItem = 0x50000A33u;
|
||||
partial.AddContained(partialItem, item => item.StackSize = 10);
|
||||
|
||||
Assert.True(partial.Controller.TryMoveItemForAutomation(
|
||||
partialItem, Player, amount: 2u, placement: 3));
|
||||
Assert.Equal(
|
||||
new[] { (partialItem, Player, 3u, 2u) },
|
||||
partial.SplitPuts);
|
||||
Assert.True(partial.Controller.TryGetPendingInventoryRequest(out var split));
|
||||
Assert.Equal(InventoryRequestKind.SplitToContainer, split.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationMergeUsesRetailPlannerAndSharedGate()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint source = 0x50000A34u;
|
||||
const uint target = 0x50000A35u;
|
||||
h.AddContained(source, item =>
|
||||
{
|
||||
item.WeenieClassId = 77u;
|
||||
item.StackSize = 8;
|
||||
item.StackSizeMax = 10;
|
||||
});
|
||||
h.AddContained(target, item =>
|
||||
{
|
||||
item.WeenieClassId = 77u;
|
||||
item.StackSize = 7;
|
||||
item.StackSizeMax = 10;
|
||||
});
|
||||
|
||||
Assert.True(h.Controller.TryMergeItemsForAutomation(source, target));
|
||||
|
||||
Assert.Equal(new[] { (source, target, 3u) }, h.Merges);
|
||||
Assert.True(h.Controller.TryGetPendingInventoryRequest(out var pending));
|
||||
Assert.Equal(InventoryRequestKind.Merge, pending.Kind);
|
||||
Assert.False(h.Controller.TryMoveItemForAutomation(source, Player));
|
||||
Assert.Single(h.Merges);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationDropAndGivePreserveExactStackAmounts()
|
||||
{
|
||||
var drop = new Harness();
|
||||
const uint dropItem = 0x50000A36u;
|
||||
drop.AddContained(dropItem, item => item.StackSize = 10);
|
||||
|
||||
Assert.True(drop.Controller.TryDropItemForAutomation(dropItem, 2u));
|
||||
Assert.Equal(new[] { (dropItem, 2u) }, drop.SplitDrops);
|
||||
Assert.Empty(drop.Drops);
|
||||
|
||||
var give = new Harness();
|
||||
const uint giveItem = 0x50000A37u;
|
||||
const uint recipient = 0x70000A38u;
|
||||
give.AddContained(giveItem, item => item.StackSize = 10);
|
||||
give.Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = recipient,
|
||||
Name = "Recipient",
|
||||
Type = ItemType.Creature,
|
||||
});
|
||||
|
||||
Assert.True(give.Controller.TryGiveItemForAutomation(
|
||||
giveItem, recipient, 4u));
|
||||
Assert.Equal(new[] { (recipient, giveItem, 4u) }, give.Gives);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationSalvageRequiresRetailToolAndSuitableOwnedItems()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint tool = 0x50000A40u;
|
||||
const uint source = 0x50000A41u;
|
||||
h.AddContained(tool, item => item.Type = ItemType.TinkeringTool);
|
||||
h.AddContained(source, item =>
|
||||
{
|
||||
item.MaterialType = 12u;
|
||||
item.Structure = 50;
|
||||
});
|
||||
|
||||
Assert.True(h.Controller.TrySalvageItemsForAutomation(tool, [source]));
|
||||
Assert.Single(h.Salvages);
|
||||
Assert.Equal(tool, h.Salvages[0].ToolGuid);
|
||||
Assert.Equal(new[] { source }, h.Salvages[0].ItemGuids);
|
||||
|
||||
h.Objects.Get(source)!.Structure = 100;
|
||||
Assert.False(h.Controller.TrySalvageItemsForAutomation(tool, [source]));
|
||||
Assert.Single(h.Salvages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchingInventoryFailureReleasesGlobalRequest()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue