//
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
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;
///
/// Tests the item consumption action.
///
[TestFixture]
public class ItemConsumptionTest
{
private const int ItemSlot = 12;
///
/// Tests the jewel of bless consume.
///
/// The item level.
/// if set to true, the item consumption is expected.
[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));
}
///
/// Tests the jewel of soul consumption.
///
/// The item level before consuming the jewel of soul.
/// If set to true, the consumption of the jewel of soul is expected.
/// If set to true, the randomizer returns true 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.
/// The expected item level after trying to consume the jewel of soul.
[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();
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));
}
///
/// Test if the jewel of life consumption increases the item option level by 1 until the maximum level is reached.
///
/// The number of options.
/// If set to true, the item consumption is expected; Otherwise, not.
[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));
}
}
///
/// Tests if a failed Jewel of life removes the option at any level.
///
/// The number of options.
[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));
}
///
/// Tests the jewel of harmony consume.
///
public void JewelOfHarmony()
{
Assert.That(true, Is.False);
}
///
/// Tests the refine stone consume.
///
public void RefineStone()
{
// refine stone consume handler is not implemented yet
Assert.That(true, Is.False);
}
///
/// Tests the complex potion consume.
///
public void ComplexPotion()
{
// complex potion consume handler is not implemented yet
Assert.That(true, Is.False);
}
///
/// Tests the shield potion consume.
///
[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));
}
///
/// Tests if the consumption fails because of the player state.
///
[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);
}
///
/// Tests if the consumption of the item decreases its durability by one.
///
[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);
}
///
/// 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 .
///
[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);
}
///
/// Tests if the consumption of the alcohol fails when the item has no durability anymore.
///
[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()!).Verify(view => view!.ConsumeSpecialItemAsync(item, 80), Times.Never);
}
///
/// Tests if the consumption of alcohol works and is forwarded to the player view.
///
[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()!).Verify(view => view!.ConsumeSpecialItemAsync(item, 80), Times.Once);
}
///
/// Tests the health recover by drinking a health potion.
///
[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));
}
///
/// Tests the mana recover by drinking a mana potion.
///
[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 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.SetupAllProperties();
item.Setup(i => i.ItemOptions).Returns(new List());
item.Setup(i => i.ItemSetGroups).Returns(new List());
var definition = new Mock();
definition.SetupAllProperties();
definition.Setup(d => d.PossibleItemOptions).Returns(new List());
definition.Setup(d => d.BasePowerUpAttributes).Returns(new List());
definition.Object.MaximumItemLevel = 15;
var itemSlot = new Mock();
itemSlot.Setup(s => s.ItemSlots).Returns(new List { 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();
option.SetupAllProperties();
option.Setup(o => o.PossibleOptions).Returns(new List());
option.Object.MaximumOptionsPerItem = 4;
option.Object.AddsRandomly = true;
option.Name = "Damage Option";
var possibleOption = new Mock();
possibleOption.SetupAllProperties();
possibleOption.Setup(o => o.LevelDependentOptions).Returns(new List());
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;
}
}