// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.Tests; using Microsoft.Extensions.Logging.Abstractions; using Moq; using MUnique.OpenMU.AttributeSystem; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Configuration.Items; using MUnique.OpenMU.DataModel.Entities; using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; /// /// Tests the power up factory which is creating power ups based on the items a player has equipped. /// [TestFixture] public class PowerUpFactoryTest { private const byte UnwearableSlot = 0xFF; private const int PowerUpStrength = 16; private readonly float[] _levelBonus = new float[] { 0, 1, 3, 7, 14 }; /// /// Tests if the item option results in an corresponding power up. /// [Test] public async ValueTask ItemOptionsAsync() { var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false); var factory = this.GetPowerUpFactory(); var item = this.GetItemWithOption(); var result = factory.GetPowerUps(item, player.Attributes!); Assert.That(result.Sum(p => p.Value), Is.EqualTo(PowerUpStrength)); } /// /// Tests if the item option of level 0 results in the corresponding power up. /// [Test] public async ValueTask ItemBasePowerUpLevel0Async() { var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false); var factory = this.GetPowerUpFactory(); var item = this.GetItemWithBasePowerUp(); var result = factory.GetPowerUps(item, player.Attributes!); Assert.That(result.Sum(p => p.Value), Is.EqualTo(PowerUpStrength)); } /// /// Tests if the item option of level 3 results in the corresponding power up. /// [Test] public async ValueTask ItemBasePowerUpLevel3Async() { var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false); var factory = this.GetPowerUpFactory(); var item = this.GetItemWithBasePowerUp(); item.Level = 3; var result = factory.GetPowerUps(item, player.Attributes!); Assert.That(result.Sum(p => p.Value), Is.EqualTo(PowerUpStrength + this._levelBonus[3])); } /// /// Tests if the power ups don't get created if the item has no more durability. /// [Test] public async ValueTask NoPowerUpsWhenItemBrokenAsync() { var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false); var factory = this.GetPowerUpFactory(); var item = this.GetItemWithBasePowerUp(); item.Durability = 0; var result = factory.GetPowerUps(item, player.Attributes!); Assert.That(result.Sum(p => p.Value), Is.EqualTo(0)); } /// /// Tests if the power ups don't get created if the item is not equipped at the player. /// [Test] public async ValueTask NoPowerUpsWhenItemUnwearableAsync() { var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false); var factory = this.GetPowerUpFactory(); var item = this.GetItemWithBasePowerUp(); item.ItemSlot = UnwearableSlot; var result = factory.GetPowerUps(item, player.Attributes!); Assert.That(result.Sum(p => p.Value), Is.EqualTo(0)); } /// /// Tests if the power ups don't get created when the item has no options. /// [Test] public async ValueTask NoPowerUpsInItemAsync() { var player = await PlayerTestHelper.CreatePlayerAsync().ConfigureAwait(false); var factory = this.GetPowerUpFactory(); var item = this.GetItem(); var result = factory.GetPowerUps(item, player.Attributes!); Assert.That(result.Sum(p => p.Value), Is.EqualTo(0)); } /// /// Tests if a complete set of level 11, gives the power up defined for level 11. /// [Test] public void SetCompleteGivesPowerUp() { var factory = this.GetPowerUpFactory(); var items = this.GetDefenseBonusSet(10, 11, 11, 11, 11); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.Not.EqualTo(0)); } /// /// Tests if a complete set of level 11, gives the power up defined for level 11 even if one item has a higher level. /// [Test] public void SetCompleteGivesPowerUpWhenItemIsHigherLevel() { var factory = this.GetPowerUpFactory(); var items = this.GetDefenseBonusSet(10, 11, 12, 11, 11); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.Not.EqualTo(0)); } /// /// Tests if an incomplete set of level 11 gives no power up, because at least one item has not the required level. /// [Test] public void SetIncompleteDueLowerLevelItemsGivesNoPowerUp() { var factory = this.GetPowerUpFactory(); var items = this.GetDefenseBonusSet(10, 11, 11, 15, 9); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.EqualTo(0)); } /// /// Tests if no items give no power ups. /// [Test] public void NoItemsGiveNoSetPowerUps() { var factory = this.GetPowerUpFactory(); var items = Enumerable.Empty(); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.EqualTo(0)); } /// /// Tests if an incomplete set (=not all required items equipped) gives also no power up. /// [Test] public void SetIncompleteGivesNoPowerUp() { var factory = this.GetPowerUpFactory(); var items = this.GetDefenseBonusSet(30, 15, 15, 15); var result = factory.GetSetPowerUps(items.Skip(1), this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.EqualTo(0)); } /// /// Tests if the correct value is set in the power up, as defined. /// [Test] public void SetBonusDefSetValueCorrect() { var factory = this.GetPowerUpFactory(); var items = this.GetDefenseBonusSet(5, 10, 10, 15, 10); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()).ToList(); Assert.That(result.Count, Is.EqualTo(1)); Assert.That(result.First().Value, Is.EqualTo(5.0)); } /// /// Tests if no power up is given for a specific level, when all of the items are of a higher level. /// This is the expected behavior, because otherwise, multiple level-dependent set bonuses would take effect. /// [Test] public void SetBonusNotAppliedWhenFullSetOfHigherLevel() { var factory = this.GetPowerUpFactory(); var items = this.GetDefenseBonusSet(5, 10, 15, 15, 15); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.EqualTo(0)); } /// /// Tests if one ancient item gives just the bonus option. /// [Test] public void OneAncientItemGivesJustBonusOption() { var factory = this.GetPowerUpFactory(); var items = this.GetAncientSet(5, 4).ToList(); var bonusOptions = factory.GetPowerUps(items.First(), this.GetAttributeSystem()); Assert.That(bonusOptions.Count(), Is.EqualTo(1)); var result = factory.GetSetPowerUps(items.Take(1), this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result, Is.Empty); } /// /// Tests if a partially complete ancient set gives just the bonus option plus the number of unlocked options (item count - 1). /// /// The item count. [TestCase(2)] [TestCase(3)] [TestCase(4)] public void AncientSetPartiallyComplete(int setSize) { var factory = this.GetPowerUpFactory(); var setItems = this.GetAncientSet(5, setSize).ToList(); var items = setItems.SkipLast(1).ToList(); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.EqualTo(items.Count - 1)); var bonusOptions = items.SelectMany(item => factory.GetPowerUps(item, this.GetAttributeSystem())); Assert.That(bonusOptions.Count(), Is.EqualTo(items.Count)); } /// /// Tests if a complete ancient set unlocks all options. /// In case of 5 items in a set, there are 9 power ups (4 unlocked options + 5 bonus options). /// [Test] public void AncientSetComplete() { var factory = this.GetPowerUpFactory(); var items = this.GetAncientSet(6, 5).ToList(); var result = factory.GetSetPowerUps(items, this.GetAttributeSystem(), new GameConfiguration()); Assert.That(result.Count(), Is.EqualTo(6)); var bonusOptions = items.SelectMany(item => factory.GetPowerUps(item, this.GetAttributeSystem())); Assert.That(bonusOptions.Count(), Is.EqualTo(5)); } private IItemPowerUpFactory GetPowerUpFactory() { return new ItemPowerUpFactory(new NullLogger()); } private ItemDefinition GetItemDefinition() { var itemDefinition = new Mock(); itemDefinition.Object.Durability = 100; itemDefinition.Setup(d => d.BasePowerUpAttributes).Returns(new List()); itemDefinition.Setup(d => d.PossibleItemSetGroups).Returns(new List()); return itemDefinition.Object; } private Item GetItem() { var item = new Mock(); item.SetupAllProperties(); item.Object.Definition = this.GetItemDefinition(); item.Object.ItemSlot = 0; item.Object.Durability = item.Object.Definition.Durability; item.Setup(i => i.ItemOptions).Returns(new List()); item.Setup(i => i.ItemSetGroups).Returns(new List()); return item.Object; } private Item GetItemWithOption() { var item = this.GetItem(); item.ItemOptions.Add(this.GetOption(Stats.MaximumPhysBaseDmg, PowerUpStrength)); return item; } private Item GetItemWithBasePowerUp() { var item = this.GetItem(); item.Definition!.BasePowerUpAttributes.Add(this.GetBasePowerUpDefinition()); return item; } private ItemBasePowerUpDefinition GetBasePowerUpDefinition() { var resultMock = new Mock(); resultMock.SetupAllProperties(); var bonusTableMock = new Mock(); bonusTableMock.Setup(r => r.BonusPerLevel).Returns(new List()); var result = resultMock.Object; result.BaseValue = PowerUpStrength; result.TargetAttribute = Stats.MaximumPhysBaseDmg; var bonusTable = bonusTableMock.Object; result.BonusPerLevelTable = bonusTable; bonusTable.BonusPerLevel.Add(new LevelBonus(1, this._levelBonus[1])); bonusTable.BonusPerLevel.Add(new LevelBonus(2, this._levelBonus[2])); bonusTable.BonusPerLevel.Add(new LevelBonus(3, this._levelBonus[3])); bonusTable.BonusPerLevel.Add(new LevelBonus(4, this._levelBonus[4])); return result; } private ItemOptionLink GetOption(AttributeDefinition targetAttribute, float value) { var option = new IncreasableItemOption { OptionType = ItemOptionTypes.Option, PowerUpDefinition = new PowerUpDefinition { TargetAttribute = targetAttribute, Boost = new TestPowerUpDefinitionValue(new SimpleElement { Value = value }), }, }; return new ItemOptionLink { ItemOption = option, Level = 1 }; } private IEnumerable GetDefenseBonusSet(float setBonusDefense, byte minimumLevel, params byte[] levels) { var itemOptionDef = new Mock(); itemOptionDef.Setup(a => a.PossibleOptions).Returns(new List()); itemOptionDef.Object.PossibleOptions.Add(this.GetOption(Stats.DefenseBase, setBonusDefense).ItemOption!); var armorSet = new Mock(); armorSet.Setup(a => a.Items).Returns(new List()); armorSet.Setup(a => a.Options).Returns(itemOptionDef.Object); armorSet.Object.MinimumItemCount = levels.Length; armorSet.Object.SetLevel = minimumLevel; foreach (var level in levels) { var item = this.GetItem(); var itemOfItemSet = new ItemOfItemSet { ItemDefinition = item.Definition, ItemSetGroup = armorSet.Object }; item.Definition!.PossibleItemSetGroups.Add(armorSet.Object); item.ItemSetGroups.Add(itemOfItemSet); armorSet.Object.Items.Add(itemOfItemSet); item.Level = level; yield return item; } } private IEnumerable GetAncientSet(int ancientOptionCount, int itemCount) { var itemOptionDef = new Mock(); itemOptionDef.Setup(a => a.PossibleOptions).Returns(new List()); var ancientSet = new Mock(); ancientSet.Setup(a => a.Items).Returns(new List()); ancientSet.Setup(a => a.Options).Returns(itemOptionDef.Object); ancientSet.Object.MinimumItemCount = 2; for (int i = 0; i < ancientOptionCount; i++) { var setOption = this.GetOption(Stats.DefenseBase, i + 10).ItemOption; setOption!.Number = i + 1; itemOptionDef.Object.PossibleOptions.Add(setOption); } var bonusOption = this.GetOption(Stats.TotalStrength, 5).ItemOption; for (int i = 0; i < itemCount; i++) { var item = this.GetItem(); var itemOfSet = new ItemOfItemSet { BonusOption = bonusOption, ItemDefinition = item.Definition, ItemSetGroup = ancientSet.Object }; item.Definition!.PossibleItemSetGroups.Add(ancientSet.Object); item.ItemSetGroups.Add(itemOfSet); item.ItemOptions.Add(new ItemOptionLink { ItemOption = bonusOption, Level = 1 }); ancientSet.Object.Items.Add(itemOfSet); yield return item; } } private AttributeSystem GetAttributeSystem() => new(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty()); private class TestPowerUpDefinitionValue : PowerUpDefinitionValue { public TestPowerUpDefinitionValue(SimpleElement constantValue) { this.ConstantValue = constantValue; } } }