289 lines
10 KiB
C#
289 lines
10 KiB
C#
using AcDream.Core.Items;
|
|
|
|
namespace AcDream.Core.Tests.Items;
|
|
|
|
public sealed class ItemInteractionPolicyTests
|
|
{
|
|
private const uint Player = 0x50000001u;
|
|
private const uint GroundContainer = 0x70000001u;
|
|
|
|
[Fact]
|
|
public void DetermineUseResult_looseItemAndComponentPack_matchRetailPickupBranch()
|
|
{
|
|
Assert.Equal(ItemPrimaryUseResult.PlaceInBackpack,
|
|
Determine(Obj(0x6001)));
|
|
|
|
var requiresSlot = Obj(0x6002) with
|
|
{
|
|
Flags = PublicWeenieFlags.RequiresPackSlot,
|
|
ItemsCapacity = 12,
|
|
};
|
|
Assert.Equal(ItemPrimaryUseResult.None, Determine(requiresSlot));
|
|
Assert.Equal(ItemPrimaryUseResult.PlaceInBackpack,
|
|
Determine(requiresSlot with { IsComponentPack = true }));
|
|
}
|
|
|
|
[Fact]
|
|
public void DetermineUseResult_onlyAllowsContainedPickupFromViewedGroundContainer()
|
|
{
|
|
var item = Obj(0x6001) with { ContainerId = GroundContainer };
|
|
|
|
Assert.Equal(ItemPrimaryUseResult.None,
|
|
ItemInteractionPolicy.DetermineUseResult(item, Player, 0));
|
|
Assert.Equal(ItemPrimaryUseResult.PlaceInBackpack,
|
|
ItemInteractionPolicy.DetermineUseResult(item, Player, GroundContainer));
|
|
}
|
|
|
|
[Fact]
|
|
public void DetermineUseResult_portsAllOwnedPrimaryClassifications()
|
|
{
|
|
var owned = Obj(0x6001) with
|
|
{
|
|
ContainerId = Player,
|
|
OwnedByPlayer = true,
|
|
Flags = PublicWeenieFlags.RequiresPackSlot,
|
|
};
|
|
|
|
Assert.Equal(ItemPrimaryUseResult.WieldRight,
|
|
Determine(owned with { CombatUse = 1 }));
|
|
Assert.Equal(ItemPrimaryUseResult.WieldLeft,
|
|
Determine(owned with { CombatUse = 1, Flags = owned.Flags | PublicWeenieFlags.WieldLeft }));
|
|
Assert.Equal(ItemPrimaryUseResult.AutoSort,
|
|
Determine(owned with { ValidLocations = EquipMask.HeadWear }));
|
|
Assert.Equal(ItemPrimaryUseResult.OpenSalvage,
|
|
Determine(owned with { Type = ItemType.TinkeringTool }));
|
|
Assert.Equal(ItemPrimaryUseResult.ItemUse,
|
|
Determine(owned with { Useability = ItemUseability.Contained }));
|
|
}
|
|
|
|
[Fact]
|
|
public void DetermineUseResult_nonOwnedGameAndPlayerPaths_areDistinct()
|
|
{
|
|
Assert.Equal(ItemPrimaryUseResult.BeginGame,
|
|
Determine(Obj(0x6001) with
|
|
{
|
|
Type = ItemType.Gameboard,
|
|
Flags = PublicWeenieFlags.RequiresPackSlot,
|
|
}));
|
|
Assert.Equal(ItemPrimaryUseResult.OpenSecureTrade,
|
|
Determine(Obj(0x6002) with
|
|
{
|
|
Flags = PublicWeenieFlags.Player | PublicWeenieFlags.RequiresPackSlot,
|
|
}));
|
|
}
|
|
|
|
[Fact]
|
|
public void UseObject_excludesResultEightFromEarlyClassifiedBound_thenStillWieldsLeft()
|
|
{
|
|
var source = Obj(0x6001) with
|
|
{
|
|
ContainerId = Player,
|
|
OwnedByPlayer = true,
|
|
CombatUse = 1,
|
|
Flags = PublicWeenieFlags.RequiresPackSlot | PublicWeenieFlags.WieldLeft,
|
|
};
|
|
|
|
var decision = ItemInteractionPolicy.DecideUse(Use(source));
|
|
|
|
Assert.Equal(new[] { ItemPolicyActionKind.WieldLeft },
|
|
decision.Actions.Select(a => a.Kind));
|
|
}
|
|
|
|
[Fact]
|
|
public void UseObject_readyVendorTradeAndWieldedSourceGates_areOrdered()
|
|
{
|
|
var direct = OwnedDirect();
|
|
|
|
Assert.Empty(ItemInteractionPolicy.DecideUse(
|
|
Use(direct) with { ReadyForInventoryRequest = false }).Actions);
|
|
Assert.Empty(ItemInteractionPolicy.DecideUse(
|
|
Use(direct) with { ActiveVendorId = 0x7001, Source = direct with { ContainerId = 0x7001 } }).Actions);
|
|
Assert.Equal(ItemPolicyActionKind.Reject,
|
|
Assert.Single(ItemInteractionPolicy.DecideUse(
|
|
Use(direct with { TradeState = 1 })).Actions).Kind);
|
|
Assert.Contains("wield", Assert.Single(ItemInteractionPolicy.DecideUse(
|
|
Use(direct with { Useability = ItemUseability.Wielded })).Actions).Message,
|
|
StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void UseObject_targetedUse_appliesKindAndLocationGate_thenSendsAndMarksBusy()
|
|
{
|
|
var source = OwnedDirect() with
|
|
{
|
|
Useability = 0x00080008u,
|
|
TargetType = (uint)ItemType.Misc,
|
|
};
|
|
var target = Obj(0x6002) with
|
|
{
|
|
ContainerId = Player,
|
|
OwnedByPlayer = true,
|
|
Type = ItemType.Misc,
|
|
};
|
|
|
|
var decision = ItemInteractionPolicy.DecideUse(Use(source) with
|
|
{
|
|
UseCurrentSelection = true,
|
|
SelectedTarget = target,
|
|
});
|
|
|
|
Assert.Equal(new[]
|
|
{
|
|
ItemPolicyActionKind.SendUseWithTarget,
|
|
ItemPolicyActionKind.IncrementBusy,
|
|
}, decision.Actions.Select(a => a.Kind));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(PublicWeenieFlags.PlayerKillerSwitch, ItemPolicyActionKind.ConfirmPlayerKillerSwitch)]
|
|
[InlineData(PublicWeenieFlags.NonPlayerKillerSwitch, ItemPolicyActionKind.ConfirmNonPlayerKillerSwitch)]
|
|
[InlineData(PublicWeenieFlags.VolatileRare, ItemPolicyActionKind.ConfirmVolatileRare)]
|
|
public void UseObject_confirmationFlags_blockWireUse(
|
|
PublicWeenieFlags flag,
|
|
ItemPolicyActionKind expected)
|
|
{
|
|
var decision = ItemInteractionPolicy.DecideUse(
|
|
Use(OwnedDirect() with { Flags = flag | PublicWeenieFlags.RequiresPackSlot }));
|
|
|
|
Assert.Equal(expected, Assert.Single(decision.Actions).Kind);
|
|
}
|
|
|
|
[Fact]
|
|
public void Placement_vendorAndSecureTrade_actButPreserveRetailFalseReturn()
|
|
{
|
|
var item = OwnedDirect() with { StackSize = 10, MaxSplitSize = 10 };
|
|
var vendor = Obj(0x7001) with { Flags = PublicWeenieFlags.Vendor };
|
|
var sale = ItemInteractionPolicy.DecidePlacement(Place(item, vendor));
|
|
Assert.False(sale.ReturnValue);
|
|
Assert.Equal(ItemPolicyActionKind.SellToVendor, Assert.Single(sale.Actions).Kind);
|
|
|
|
var player = Obj(0x7002) with { Flags = PublicWeenieFlags.Player };
|
|
var trade = ItemInteractionPolicy.DecidePlacement(Place(item, player));
|
|
Assert.False(trade.ReturnValue);
|
|
Assert.Equal(ItemPolicyActionKind.StartSecureTrade, Assert.Single(trade.Actions).Kind);
|
|
}
|
|
|
|
[Fact]
|
|
public void Placement_groundStackAndAirborneMatrix_matchesRetail()
|
|
{
|
|
var item = OwnedDirect() with { StackSize = 10, MaxSplitSize = 10 };
|
|
|
|
var airborne = ItemInteractionPolicy.DecidePlacement(PlaceOnGround(item) with
|
|
{
|
|
PlayerOnGround = false,
|
|
});
|
|
Assert.False(airborne.ReturnValue);
|
|
Assert.Equal(ItemPolicyActionKind.Reject, Assert.Single(airborne.Actions).Kind);
|
|
|
|
var split = ItemInteractionPolicy.DecidePlacement(PlaceOnGround(item) with { SplitSize = 4 });
|
|
Assert.True(split.ReturnValue);
|
|
Assert.Equal(ItemPolicyActionKind.SplitToWorld, Assert.Single(split.Actions).Kind);
|
|
|
|
var full = ItemInteractionPolicy.DecidePlacement(PlaceOnGround(item));
|
|
Assert.True(full.ReturnValue);
|
|
Assert.Equal(ItemPolicyActionKind.DropToWorld, Assert.Single(full.Actions).Kind);
|
|
|
|
var alreadyWorld = ItemInteractionPolicy.DecidePlacement(
|
|
PlaceOnGround(item with { IsIn3DView = true }));
|
|
Assert.False(alreadyWorld.ReturnValue);
|
|
Assert.Contains("cancelled", Assert.Single(alreadyWorld.Actions).Message,
|
|
StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void Placement_containerRequiresOpenableAndCurrentGroundObject()
|
|
{
|
|
var item = OwnedDirect();
|
|
var container = Obj(GroundContainer) with { IsContainer = true };
|
|
|
|
var locked = ItemInteractionPolicy.DecidePlacement(Place(item, container) with
|
|
{
|
|
GroundObjectId = GroundContainer,
|
|
});
|
|
Assert.False(locked.ReturnValue);
|
|
|
|
var open = container with { Flags = PublicWeenieFlags.Openable };
|
|
var closedView = ItemInteractionPolicy.DecidePlacement(Place(item, open));
|
|
Assert.False(closedView.ReturnValue);
|
|
|
|
var placed = ItemInteractionPolicy.DecidePlacement(Place(item, open) with
|
|
{
|
|
GroundObjectId = GroundContainer,
|
|
});
|
|
Assert.True(placed.ReturnValue);
|
|
Assert.Equal(ItemPolicyActionKind.PlaceInContainer, Assert.Single(placed.Actions).Kind);
|
|
}
|
|
|
|
private static ItemPrimaryUseResult Determine(ItemPolicyObject item)
|
|
=> ItemInteractionPolicy.DetermineUseResult(item, Player, 0);
|
|
|
|
private static ItemPolicyObject OwnedDirect()
|
|
=> Obj(0x6001) with
|
|
{
|
|
ContainerId = Player,
|
|
OwnedByPlayer = true,
|
|
Flags = PublicWeenieFlags.RequiresPackSlot,
|
|
Useability = ItemUseability.Contained,
|
|
};
|
|
|
|
private static ItemPolicyObject Obj(uint id) => new(
|
|
id,
|
|
ItemType.Misc,
|
|
PublicWeenieFlags.None,
|
|
ContainerId: 0,
|
|
WielderId: 0,
|
|
ValidLocations: EquipMask.None,
|
|
CurrentLocation: EquipMask.None,
|
|
CombatUse: 0,
|
|
ItemsCapacity: 0,
|
|
ContainersCapacity: 0,
|
|
Useability: 0,
|
|
TargetType: 0,
|
|
OwnedByPlayer: false,
|
|
IsContainer: false,
|
|
IsComponentPack: false,
|
|
TradeState: 0,
|
|
StackSize: 1,
|
|
MaxSplitSize: 1,
|
|
IsIn3DView: false);
|
|
|
|
private static ItemUsePolicyInput Use(ItemPolicyObject source) => new(
|
|
source,
|
|
Player,
|
|
GroundObjectId: 0,
|
|
ReadyForInventoryRequest: true,
|
|
ActiveVendorId: 0,
|
|
BypassClassification: false,
|
|
UseCurrentSelection: false,
|
|
SelectedTarget: null,
|
|
ConfirmVolatileRareUses: true,
|
|
InNonCombatMode: false);
|
|
|
|
private static ItemPlacementPolicyInput Place(
|
|
ItemPolicyObject item,
|
|
ItemPolicyObject target) => new(
|
|
item,
|
|
Player,
|
|
GroundObjectId: 0,
|
|
ReadyForInventoryRequest: true,
|
|
TargetId: target.Id,
|
|
Target: target,
|
|
AllowGroundFallback: false,
|
|
MergeAccepted: false,
|
|
DragOnPlayerOpensSecureTrade: true,
|
|
PlayerOnGround: true,
|
|
SplitSize: item.StackSize);
|
|
|
|
private static ItemPlacementPolicyInput PlaceOnGround(ItemPolicyObject item) => new(
|
|
item,
|
|
Player,
|
|
GroundObjectId: 0,
|
|
ReadyForInventoryRequest: true,
|
|
TargetId: 0,
|
|
Target: null,
|
|
AllowGroundFallback: true,
|
|
MergeAccepted: false,
|
|
DragOnPlayerOpensSecureTrade: true,
|
|
PlayerOnGround: true,
|
|
SplitSize: item.StackSize);
|
|
}
|