Files
AdamuSw/tests/MUnique.OpenMU.Tests/ItemConsumptionTest.cs
2026-07-14 19:00:35 +03:00

418 lines
18 KiB
C#

// <copyright file="ItemConsumptionTest.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Tests;
using Moq;
using MUnique.OpenMU.AttributeSystem;
using MUnique.OpenMU.DataModel;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.GameLogic.Attributes;
using MUnique.OpenMU.GameLogic.PlayerActions.ItemConsumeActions;
using MUnique.OpenMU.GameLogic.Views;
/// <summary>
/// Tests the item consumption action.
/// </summary>
[TestFixture]
public class ItemConsumptionTest
{
private const int ItemSlot = 12;
/// <summary>
/// Tests the jewel of bless consume.
/// </summary>
/// <param name="itemLevel">The item level.</param>
/// <param name="consumptionExpectation">if set to <c>true</c>, the item consumption is expected.</param>
[TestCase(0, true)]
[TestCase(1, true)]
[TestCase(2, true)]
[TestCase(3, true)]
[TestCase(4, true)]
[TestCase(5, true)]
[TestCase(6, false)]
[TestCase(7, false)]
public async ValueTask JewelOfBlessAsync(byte itemLevel, bool consumptionExpectation)
{
var consumeHandler = new BlessJewelConsumeHandlerPlugIn();
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var upgradeableItem = this.GetItemWithPossibleOption();
upgradeableItem.Level = itemLevel;
var upgradableItemSlot = (byte)(ItemSlot + 1);
await player.Inventory!.AddItemAsync(upgradableItemSlot, upgradeableItem).ConfigureAwait(false);
var bless = this.GetItem();
await player.Inventory.AddItemAsync(ItemSlot, bless).ConfigureAwait(false);
bless.Durability = 1;
var consumed = await consumeHandler.ConsumeItemAsync(player, bless, upgradeableItem, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(consumed, Is.EqualTo(consumptionExpectation));
Assert.That(upgradeableItem.Level, consumed ? Is.EqualTo(itemLevel + 1) : Is.EqualTo(itemLevel));
}
/// <summary>
/// Tests the jewel of soul consumption.
/// </summary>
/// <param name="itemLevel">The item level before consuming the jewel of soul.</param>
/// <param name="consumptionExpectation">If set to <c>true</c>, the consumption of the jewel of soul is expected.</param>
/// <param name="success">If set to <c>true</c>, the randomizer returns <c>true</c> when asked about wether the item level should be increased. However, it doesn't have any effect if the item is already level 9 or higher.</param>
/// <param name="expectedItemLevel">The expected item level after trying to consume the jewel of soul.</param>
[TestCase(0, true, true, 1)]
[TestCase(1, true, true, 2)]
[TestCase(2, true, true, 3)]
[TestCase(3, true, true, 4)]
[TestCase(4, true, true, 5)]
[TestCase(5, true, true, 6)]
[TestCase(6, true, true, 7)]
[TestCase(7, true, true, 8)]
[TestCase(8, true, true, 9)]
[TestCase(9, false, true, 9)]
[TestCase(10, false, true, 10)]
[TestCase(11, false, true, 11)]
[TestCase(12, false, true, 12)]
[TestCase(13, false, true, 13)]
[TestCase(14, false, true, 14)]
[TestCase(15, false, true, 15)]
[TestCase(0, true, false, 0)]
[TestCase(1, true, false, 0)]
[TestCase(2, true, false, 1)]
[TestCase(3, true, false, 2)]
[TestCase(4, true, false, 3)]
[TestCase(5, true, false, 4)]
[TestCase(6, true, false, 5)]
[TestCase(7, true, false, 0)]
[TestCase(8, true, false, 0)]
public async ValueTask JewelOfSoulAsync(byte itemLevel, bool consumptionExpectation, bool success, byte expectedItemLevel)
{
var randomizer = new Mock<IRandomizer>();
randomizer.Setup(r => r.NextRandomBool(50)).Returns(success);
var consumeHandler = new SoulJewelConsumeHandlerPlugIn(randomizer.Object);
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var upgradeableItem = this.GetItemWithPossibleOption();
upgradeableItem.Level = itemLevel;
var upgradableItemSlot = (byte)(ItemSlot + 1);
await player.Inventory!.AddItemAsync(upgradableItemSlot, upgradeableItem).ConfigureAwait(false);
var soul = this.GetItem();
await player.Inventory.AddItemAsync(ItemSlot, soul).ConfigureAwait(false);
soul.Durability = 1;
var consumed = await consumeHandler.ConsumeItemAsync(player, soul, upgradeableItem, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(consumed, Is.EqualTo(consumptionExpectation));
Assert.That(upgradeableItem.Level, Is.EqualTo(expectedItemLevel));
}
/// <summary>
/// Test if the jewel of life consumption increases the item option level by 1 until the maximum level is reached.
/// </summary>
/// <param name="numberOfOptions">The number of options.</param>
/// <param name="consumptionExpectation">If set to <c>true</c>, the item consumption is expected; Otherwise, not.</param>
[TestCase(1, true)]
[TestCase(2, true)]
[TestCase(3, true)]
[TestCase(4, true)]
[TestCase(5, false)]
public async ValueTask JewelOfLifeAsync(int numberOfOptions, bool consumptionExpectation)
{
var consumeHandler = new LifeJewelConsumeHandlerPlugIn();
consumeHandler.Configuration.SuccessChance = 1;
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var upgradeableItem = this.GetItemWithPossibleOption();
var upgradableItemSlot = (byte)(ItemSlot + 1);
await player.Inventory!.AddItemAsync(upgradableItemSlot, upgradeableItem).ConfigureAwait(false);
bool jolConsumed = false;
for (int i = 0; i < numberOfOptions; i++)
{
var item = this.GetItem();
await player.Inventory.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
item.Durability = 1;
jolConsumed = await consumeHandler.ConsumeItemAsync(player, item, upgradeableItem, FruitUsage.Undefined).ConfigureAwait(false);
}
Assert.That(jolConsumed, Is.EqualTo(consumptionExpectation));
if (jolConsumed)
{
Assert.That(upgradeableItem.ItemOptions.Count, Is.EqualTo(1));
Assert.That(upgradeableItem.ItemOptions.First().Level, Is.EqualTo(numberOfOptions));
}
}
/// <summary>
/// Tests if a failed Jewel of life removes the option at any level.
/// </summary>
/// <param name="numberOfOptions">The number of options.</param>
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public async ValueTask JewelOfLifeFailRemovesOptionAsync(int numberOfOptions)
{
var consumeHandler = new LifeJewelConsumeHandlerPlugIn();
consumeHandler.Configuration.SuccessChance = 1;
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var upgradeableItem = this.GetItemWithPossibleOption();
var upgradableItemSlot = (byte)(ItemSlot + 1);
await player.Inventory!.AddItemAsync(upgradableItemSlot, upgradeableItem).ConfigureAwait(false);
for (int i = 0; i < numberOfOptions; i++)
{
var jol1 = this.GetItem();
await player.Inventory.AddItemAsync(ItemSlot, jol1).ConfigureAwait(false);
jol1.Durability = 1;
await consumeHandler.ConsumeItemAsync(player, jol1, upgradeableItem, FruitUsage.Undefined).ConfigureAwait(false);
}
Assert.That(upgradeableItem.ItemOptions.Count, Is.EqualTo(1));
// then adding fails, so option needs to be removed
consumeHandler.Configuration.SuccessChance = 0;
var jol2 = this.GetItem();
await player.Inventory.AddItemAsync(ItemSlot, jol2).ConfigureAwait(false);
jol2.Durability = 1;
var jolConsumed = await consumeHandler.ConsumeItemAsync(player, jol2, upgradeableItem, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(jolConsumed, Is.True);
Assert.That(upgradeableItem.ItemOptions.Count, Is.EqualTo(0));
}
/// <summary>
/// Tests the jewel of harmony consume.
/// </summary>
public void JewelOfHarmony()
{
Assert.That(true, Is.False);
}
/// <summary>
/// Tests the refine stone consume.
/// </summary>
public void RefineStone()
{
// refine stone consume handler is not implemented yet
Assert.That(true, Is.False);
}
/// <summary>
/// Tests the complex potion consume.
/// </summary>
public void ComplexPotion()
{
// complex potion consume handler is not implemented yet
Assert.That(true, Is.False);
}
/// <summary>
/// Tests the shield potion consume.
/// </summary>
[Test]
public async ValueTask ShieldPotionAsync()
{
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
var consumeHandler = new LargeShieldPotionConsumeHandlerPlugIn();
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.True);
Assert.That(player.Attributes!.GetValueOfAttribute(Stats.CurrentShield), Is.GreaterThan(0.0f));
}
/// <summary>
/// Tests if the consumption fails because of the player state.
/// </summary>
[Test]
public async ValueTask FailByWrongPlayerStateAsync()
{
var consumeHandler = new AlcoholConsumeHandlerPlugIn();
var player = await this.GetPlayerAsync().ConfigureAwait(false);
await player.PlayerState.TryAdvanceToAsync(PlayerState.TradeRequested).ConfigureAwait(false);
var item = this.GetItem();
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.False);
}
/// <summary>
/// Tests if the consumption of the item decreases its durability by one.
/// </summary>
[Test]
public async ValueTask ItemDurabilityDecreaseAsync()
{
var consumeHandler = new LargeShieldPotionConsumeHandlerPlugIn();
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
item.Durability = 3;
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.True);
Assert.That(item.Durability, Is.EqualTo(2));
Assert.That(player.Inventory.Items.Any(), Is.True);
}
/// <summary>
/// Tests if the consumption of the item not causes the removal of the item, when the durability reaches 0.
/// The removal is handled in the <see cref="ItemConsumeAction"/>.
/// </summary>
[Test]
public async ValueTask ItemRemovalAsync()
{
var consumeHandler = new LargeShieldPotionConsumeHandlerPlugIn();
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.True);
Assert.That(item.Durability, Is.EqualTo(0));
Assert.That(player.Inventory.Items.Any(), Is.True);
}
/// <summary>
/// Tests if the consumption of the alcohol fails when the item has no durability anymore.
/// </summary>
[Test]
public async ValueTask DrinkAlcoholFailAsync()
{
var consumeHandler = new AlcoholConsumeHandlerPlugIn();
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
item.Durability = 0;
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.False);
Mock.Get(player.ViewPlugIns.GetPlugIn<IConsumeSpecialItemPlugIn>()!).Verify(view => view!.ConsumeSpecialItemAsync(item, 80), Times.Never);
}
/// <summary>
/// Tests if the consumption of alcohol works and is forwarded to the player view.
/// </summary>
[Test]
public async ValueTask DrinkAlcoholSuccessAsync()
{
var consumeHandler = new AlcoholConsumeHandlerPlugIn();
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
item.Definition!.ConsumeEffect = new Persistence.BasicModel.MagicEffectDefinition
{
Duration = new Persistence.BasicModel.PowerUpDefinitionValue
{
ConstantValue = { Value = 80 }
},
PowerUpDefinitions =
{
new Persistence.BasicModel.PowerUpDefinition
{
TargetAttribute = Stats.AttackSpeedAny,
Boost = new Persistence.BasicModel.PowerUpDefinitionValue
{
ConstantValue = { Value = 20 }
}
}
}
};
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.True);
Mock.Get(player.ViewPlugIns.GetPlugIn<IConsumeSpecialItemPlugIn>()!).Verify(view => view!.ConsumeSpecialItemAsync(item, 80), Times.Once);
}
/// <summary>
/// Tests the health recover by drinking a health potion.
/// </summary>
[Test]
public async ValueTask HealthRecoverAsync()
{
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
var consumeHandler = new LargeHealthPotionConsumeHandlerPlugIn();
consumeHandler.Configuration = consumeHandler.CreateDefaultConfig() as RecoverConsumeHandlerConfiguration;
consumeHandler.Configuration!.RecoverSteps.Clear(); // When there are no steps, we recover all immediately.
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.True);
Assert.That(player.Attributes!.GetValueOfAttribute(Stats.CurrentHealth), Is.GreaterThan(0.0f));
}
/// <summary>
/// Tests the mana recover by drinking a mana potion.
/// </summary>
[Test]
public async ValueTask ManaRecoverAsync()
{
var player = await this.GetPlayerAsync().ConfigureAwait(false);
var item = this.GetItem();
await player.Inventory!.AddItemAsync(ItemSlot, item).ConfigureAwait(false);
var consumeHandler = new LargeManaPotionConsumeHandler();
var success = await consumeHandler.ConsumeItemAsync(player, item, null, FruitUsage.Undefined).ConfigureAwait(false);
Assert.That(success, Is.True);
Assert.That(player.Attributes!.GetValueOfAttribute(Stats.CurrentMana), Is.GreaterThan(0.0f));
}
private Item GetItem()
{
return new()
{
Definition = new DataModel.Configuration.Items.ItemDefinition { Width = 1, Height = 1 },
Durability = 1,
};
}
private async ValueTask<Player> GetPlayerAsync()
{
var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false);
player.SelectedCharacter!.Attributes.Add(new StatAttribute(Stats.Level, 100));
player.SelectedCharacter.Attributes.Add(new StatAttribute(Stats.CurrentHealth, 0));
player.SelectedCharacter.Attributes.Add(new StatAttribute(Stats.CurrentMana, 0));
player.SelectedCharacter.Attributes.Add(new StatAttribute(Stats.CurrentShield, 0));
return player;
}
private Item GetItemWithPossibleOption()
{
var item = new Mock<Item>();
item.SetupAllProperties();
item.Setup(i => i.ItemOptions).Returns(new List<ItemOptionLink>());
item.Setup(i => i.ItemSetGroups).Returns(new List<ItemOfItemSet>());
var definition = new Mock<ItemDefinition>();
definition.SetupAllProperties();
definition.Setup(d => d.PossibleItemOptions).Returns(new List<ItemOptionDefinition>());
definition.Setup(d => d.BasePowerUpAttributes).Returns(new List<ItemBasePowerUpDefinition>());
definition.Object.MaximumItemLevel = 15;
var itemSlot = new Mock<ItemSlotType>();
itemSlot.Setup(s => s.ItemSlots).Returns(new List<int> { InventoryConstants.LeftHandSlot });
definition.Setup(d => d.ItemSlot).Returns(itemSlot.Object);
item.Object.Definition = definition.Object;
item.Object.Durability = 1;
item.Object.Definition.Width = 1;
item.Object.Definition.Height = 2;
var option = new Mock<ItemOptionDefinition>();
option.SetupAllProperties();
option.Setup(o => o.PossibleOptions).Returns(new List<IncreasableItemOption>());
option.Object.MaximumOptionsPerItem = 4;
option.Object.AddsRandomly = true;
option.Name = "Damage Option";
var possibleOption = new Mock<IncreasableItemOption>();
possibleOption.SetupAllProperties();
possibleOption.Setup(o => o.LevelDependentOptions).Returns(new List<ItemOptionOfLevel>());
possibleOption.Object.OptionType = ItemOptionTypes.Option;
option.Object.PossibleOptions.Add(possibleOption.Object);
for (int level = 1; level <= 4; level++)
{
var levelDependentOption = new ItemOptionOfLevel();
levelDependentOption.Level = level;
possibleOption.Object.LevelDependentOptions.Add(levelDependentOption);
}
item.Object.Definition.PossibleItemOptions.Add(option.Object);
return item.Object;
}
}