### Summary * Removes player constructed as a requirement for BODs. * When two items stack and they don't match player constructed flags, the resulting stack loses the flag.
95 lines
3 KiB
C#
95 lines
3 KiB
C#
using Xunit;
|
|
|
|
namespace Server.Tests;
|
|
|
|
[Collection("Sequential Server Tests")]
|
|
public class PlayerConstructedStackingTests
|
|
{
|
|
// PlayerConstructed is per-instance provenance, and stack operations were written when no
|
|
// item carried any. Merging keeps the receiver's copy of a field and splitting rebuilds one
|
|
// half from a fixed list of fields, so a flag that is not accounted for in both places is
|
|
// one that ordinary stacking can launder or erase.
|
|
|
|
// Stands in for a real stackable type. LiftItemDupe builds the remainder through the
|
|
// parameterless constructor and copies only a fixed list of fields onto it -- Stackable is
|
|
// not on that list -- so the remainder is only stackable if the type restores it the way
|
|
// every genuine stackable does.
|
|
private class StackableItem : Item
|
|
{
|
|
public StackableItem() => Stackable = true;
|
|
|
|
public StackableItem(Serial serial) : base(serial) => Stackable = true;
|
|
}
|
|
|
|
private static StackableItem MakeStack(Serial serial, int amount, bool playerConstructed) =>
|
|
new(serial) { Amount = amount, PlayerConstructed = playerConstructed };
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void CanStackWith_IsTrueWhenProvenanceMatches(bool playerConstructed)
|
|
{
|
|
var first = MakeStack((Serial)0x1, 5, playerConstructed);
|
|
var second = MakeStack((Serial)0x2, 7, playerConstructed);
|
|
|
|
try
|
|
{
|
|
Assert.True(first.CanStackWith(second));
|
|
}
|
|
finally
|
|
{
|
|
first.Delete();
|
|
second.Delete();
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void LiftItemDupe_CopiesPlayerConstructedToRemainder(bool playerConstructed)
|
|
{
|
|
var stack = MakeStack((Serial)0x1, 10, playerConstructed);
|
|
Item remainder = null;
|
|
|
|
try
|
|
{
|
|
remainder = Mobile.LiftItemDupe(stack, 4);
|
|
|
|
Assert.NotNull(remainder);
|
|
Assert.NotSame(stack, remainder);
|
|
Assert.Equal(4, stack.Amount);
|
|
Assert.Equal(6, remainder.Amount);
|
|
Assert.Equal(playerConstructed, remainder.PlayerConstructed);
|
|
}
|
|
finally
|
|
{
|
|
stack.Delete();
|
|
remainder?.Delete();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void SplitHalvesRemainStackableWithEachOther()
|
|
{
|
|
// The two halves of a split must still be one pile's worth: if the split dropped the
|
|
// flag, the remainder would no longer stack back onto what it came from.
|
|
var stack = MakeStack((Serial)0x1, 10, true);
|
|
Item remainder = null;
|
|
|
|
try
|
|
{
|
|
remainder = Mobile.LiftItemDupe(stack, 4);
|
|
Assert.NotNull(remainder);
|
|
|
|
Assert.True(stack.CanStackWith(remainder));
|
|
Assert.True(stack.StackWith(null, remainder, false));
|
|
Assert.Equal(10, stack.Amount);
|
|
Assert.True(stack.PlayerConstructed);
|
|
}
|
|
finally
|
|
{
|
|
stack.Delete();
|
|
remainder?.Delete();
|
|
}
|
|
}
|
|
}
|