diff --git a/src/AttributeSystem/AggregateType.cs b/src/AttributeSystem/AggregateType.cs index 6f5a6fa8c..660211fff 100644 --- a/src/AttributeSystem/AggregateType.cs +++ b/src/AttributeSystem/AggregateType.cs @@ -23,4 +23,9 @@ public enum AggregateType /// Adds the value to the final value. /// AddFinal, + + /// + /// Adds only the highest available value to the raw base value (jewelry element resistance). + /// + Maximum, } \ No newline at end of file diff --git a/src/AttributeSystem/ComposableAttribute.cs b/src/AttributeSystem/ComposableAttribute.cs index 4423689e1..b8907c5c0 100644 --- a/src/AttributeSystem/ComposableAttribute.cs +++ b/src/AttributeSystem/ComposableAttribute.cs @@ -61,6 +61,8 @@ private float GetAndCacheValue() var rawValues = this.Elements.Where(e => e.AggregateType == AggregateType.AddRaw).Sum(e => e.Value); var multiValues = this.Elements.Where(e => e.AggregateType == AggregateType.Multiplicate).Select(e => e.Value).Concat(Enumerable.Repeat(1.0F, 1)).Aggregate((a, b) => a * b); var finalValues = this.Elements.Where(e => e.AggregateType == AggregateType.AddFinal).Sum(e => e.Value); + var maxValues = this.Elements.Where(e => e.AggregateType == AggregateType.Maximum).MaxBy(e => e.Value)?.Value ?? 0; + rawValues += maxValues; if (multiValues == 0 && this.Elements.All(e => e.AggregateType != AggregateType.Multiplicate)) { @@ -75,7 +77,7 @@ private float GetAndCacheValue() // nothing to do } - this._cachedValue = (rawValues * multiValues + finalValues); + this._cachedValue = (rawValues * multiValues) + finalValues; return this._cachedValue.Value; } diff --git a/src/GameLogic/InventoryStorage.cs b/src/GameLogic/InventoryStorage.cs index a0805a57b..12fb47c2d 100644 --- a/src/GameLogic/InventoryStorage.cs +++ b/src/GameLogic/InventoryStorage.cs @@ -8,7 +8,7 @@ namespace MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.Views.World; using MUnique.OpenMU.PlugIns; -using static OpenMU.DataModel.InventoryConstants; +using static MUnique.OpenMU.DataModel.InventoryConstants; /// /// The storage of an inventory of a player, which also contains equippable slots. This class also manages the powerups which get created by equipped items. @@ -166,8 +166,9 @@ await this._player.ForEachWorldObserverAsync( this._player.Attributes.ItemPowerUps.Add(item, factory.GetPowerUps(item, this._player.Attributes).ToList()); // reset player equipped ammunition amount - if (this.EquippedAmmunitionItem is { } ammoItem) { - this._player.Attributes[Stats.AmmunitionAmount] = (float) ammoItem.Durability; + if (this.EquippedAmmunitionItem is { } ammoItem) + { + this._player.Attributes[Stats.AmmunitionAmount] = (float)ammoItem.Durability; } } } diff --git a/src/GameLogic/ItemExtensions.cs b/src/GameLogic/ItemExtensions.cs index a8cbb23bd..6a6f63441 100644 --- a/src/GameLogic/ItemExtensions.cs +++ b/src/GameLogic/ItemExtensions.cs @@ -124,13 +124,25 @@ public static bool IsShield(this Item item) return item.Definition?.Group == ShieldItemGroup; } + /// + /// Determines whether this item is a jewelry (pendant or ring) item. + /// + /// The item. + /// + /// true if the specified item is jewelry; otherwise, false. + /// + public static bool IsJewelry(this Item item) + { + return item.ItemSlot >= InventoryConstants.PendantSlot && item.ItemSlot <= InventoryConstants.Ring2Slot; + } + /// /// Determines whether this instance is a is weapon which deals physical damage. /// /// The item. /// The minimum physical damage of the weapon. /// - /// true if this instance is a is weapon which deals physical damage; otherwise, false. + /// true if this instance is a weapon which deals physical damage; otherwise, false. /// public static bool IsPhysicalWeapon(this Item item, [NotNullWhen(true)] out float? minimumDmg) { @@ -144,7 +156,7 @@ public static bool IsPhysicalWeapon(this Item item, [NotNullWhen(true)] out floa /// The item. /// The staff rise percentage of the weapon. /// - /// true if this instance is a is weapon which deals wizardry damage; otherwise, false. + /// true if this instance is a weapon which deals wizardry damage; otherwise, false. /// public static bool IsWizardryWeapon(this Item item, [NotNullWhen(true)] out float? staffRise) { diff --git a/src/GameLogic/ItemPowerUpFactory.cs b/src/GameLogic/ItemPowerUpFactory.cs index 63af2ff0a..7159e8f73 100644 --- a/src/GameLogic/ItemPowerUpFactory.cs +++ b/src/GameLogic/ItemPowerUpFactory.cs @@ -184,12 +184,20 @@ private IEnumerable GetBasePowerUpWrappers(Item item, AttributeS attribute.ThrowNotInitializedProperty(attribute.BaseValueElement is null, nameof(attribute.BaseValueElement)); attribute.ThrowNotInitializedProperty(attribute.TargetAttribute is null, nameof(attribute.TargetAttribute)); - yield return new PowerUpWrapper(attribute.BaseValueElement, attribute.TargetAttribute, attributeHolder); + var levelBonusElmt = (attribute.BonusPerLevelTable?.BonusPerLevel ?? Enumerable.Empty()) + .FirstOrDefault(bonus => bonus.Level == item.Level)? + .GetAdditionalValueElement(attribute.AggregateType); - var levelBonus = (attribute.BonusPerLevelTable?.BonusPerLevel ?? Enumerable.Empty()).FirstOrDefault(bonus => bonus.Level == item.Level); - if (levelBonus is not null) + if (levelBonusElmt is null) { - yield return new PowerUpWrapper(levelBonus.GetAdditionalValueElement(attribute.AggregateType), attribute.TargetAttribute, attributeHolder); + yield return new PowerUpWrapper(attribute.BaseValueElement, attribute.TargetAttribute, attributeHolder); + } + else + { + yield return new PowerUpWrapper( + new CombinedElement(attribute.BaseValueElement, levelBonusElmt), + attribute.TargetAttribute, + attributeHolder); } } @@ -253,7 +261,7 @@ private IEnumerable CreateExcellentAndAncientBasePowerUpWrappers var baseDropLevel = item.Definition!.DropLevel; var ancientDropLevel = item.Definition!.CalculateDropLevel(true, false, 0); - if (InventoryConstants.IsDefenseItemSlot(item.ItemSlot)) + if (InventoryConstants.IsDefenseItemSlot(item.ItemSlot) && !item.IsJewelry()) { var baseDefense = (int)(item.Definition?.BasePowerUpAttributes.FirstOrDefault(a => a.TargetAttribute == Stats.DefenseBase)?.BaseValue ?? 0); var additionalDefense = (baseDefense * 12 / baseDropLevel) + (baseDropLevel / 5) + 4; diff --git a/src/Persistence/Initialization/Items/OptionExtensions.cs b/src/Persistence/Initialization/Items/OptionExtensions.cs index 7ff273a35..651cd89ce 100644 --- a/src/Persistence/Initialization/Items/OptionExtensions.cs +++ b/src/Persistence/Initialization/Items/OptionExtensions.cs @@ -80,6 +80,6 @@ public static ItemOptionDefinition ExcellentPhysicalAttackOptions(this GameConfi /// The excellent wizardry attack options. public static ItemOptionDefinition ExcellentWizardryAttackOptions(this GameConfiguration gameConfiguration) { - return gameConfiguration.ItemOptions.First(o => o.Name == ExcellentOptions.PhysicalAttackOptionsName); + return gameConfiguration.ItemOptions.First(o => o.Name == ExcellentOptions.WizardryAttackOptionsName); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugIn075.cs b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugIn075.cs new file mode 100644 index 000000000..6992be509 --- /dev/null +++ b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugIn075.cs @@ -0,0 +1,24 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes the wings damage absorption and increase bonus level tables values for a (sum) calculation, instead of a compound calculation +/// for version 075. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("3821267A-9C37-40E5-B023-BAB1A8E4DAB7")] +public class FixWingsDmgRatesUpdatePlugIn075 : FixWingsDmgRatesUpdatePlugInBase +{ + /// + public override string DataInitializationKey => Version075.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.FixWingsDmgRatesPlugIn075; +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugIn095d.cs b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugIn095d.cs new file mode 100644 index 000000000..9c3192a95 --- /dev/null +++ b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugIn095d.cs @@ -0,0 +1,24 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes the wings damage absorption and increase bonus level tables values for a (sum) calculation, instead of a compound calculation +/// for version 095d. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("F45FA4D0-B19B-48E2-9592-A37F3B36348A")] +public class FixWingsDmgRatesUpdatePlugIn095D : FixWingsDmgRatesUpdatePlugInBase +{ + /// + public override string DataInitializationKey => Version095d.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.FixWingsDmgRatesPlugIn095d; +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugInBase.cs b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugInBase.cs new file mode 100644 index 000000000..8a8c0d58b --- /dev/null +++ b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugInBase.cs @@ -0,0 +1,59 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel.Configuration; + +/// +/// This update fixes the wings damage absorption and increase bonus level tables values for a (sum) calculation, instead of a compound calculation. +/// +public abstract class FixWingsDmgRatesUpdatePlugInBase : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Fix Wings Damage Rates"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This update fixes the wings damage absorption and increase bonus level tables values."; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2023, 10, 8, 16, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + string dmgAbsorbCommonName = "Wing absorb"; + string dmgIncreaseCommonName = "Damage Increase (1st and 3rd Wings)"; + string dmgIncrease2ndWingsName = "Damage Increase (2nd Wings)"; + + string[] wingDmgTableNames = [dmgAbsorbCommonName, dmgIncrease2ndWingsName, dmgIncreaseCommonName]; + + foreach (var tableName in wingDmgTableNames) + { + var bonusEntries = gameConfiguration.ItemLevelBonusTables.FirstOrDefault(ilbt => ilbt.Name == tableName)?.BonusPerLevel; + + if (bonusEntries is not null) + { + foreach (var bonusEntry in bonusEntries) + { + bonusEntry.AdditionalValue -= 1.0f; + } + } + } + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugInSeason6.cs b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugInSeason6.cs new file mode 100644 index 000000000..8adabb9ca --- /dev/null +++ b/src/Persistence/Initialization/Updates/FixWingsDmgRatesUpdatePlugInSeason6.cs @@ -0,0 +1,24 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes the wings damage absorption and increase bonus level tables values for a (sum) calculation, instead of a compound calculation +/// for season 6. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("03F49890-CB0E-40B7-A590-174BBA1962F4")] +public class FixWingsDmgRatesUpdatePlugInSeason6 : FixWingsDmgRatesUpdatePlugInBase +{ + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.FixWingsDmgRatesPlugInSeason6; +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index b66aae1d0..923421ea1 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -164,4 +164,19 @@ public enum UpdateVersion /// The version of the . /// FixMaxManaAndAbilityJewelryOptionsSeason6 = 31, + + /// + /// The version of the . + /// + FixWingsDmgRatesPlugIn075 = 32, + + /// + /// The version of the . + /// + FixWingsDmgRatesPlugIn095d = 33, + + /// + /// The version of the . + /// + FixWingsDmgRatesPlugInSeason6 = 34, } \ No newline at end of file diff --git a/src/Persistence/Initialization/Version075/Items/Jewelery.cs b/src/Persistence/Initialization/Version075/Items/Jewelery.cs index 407d6db53..60b584ea0 100644 --- a/src/Persistence/Initialization/Version075/Items/Jewelery.cs +++ b/src/Persistence/Initialization/Version075/Items/Jewelery.cs @@ -5,7 +5,6 @@ namespace MUnique.OpenMU.Persistence.Initialization.Version075.Items; using MUnique.OpenMU.AttributeSystem; -using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Configuration.Items; using MUnique.OpenMU.GameLogic.Attributes; @@ -29,6 +28,9 @@ public Jewelery(IContext context, GameConfiguration gameConfiguration) { } + /// + protected override int MaximumOptionLevel => 3; + /// public sealed override void Initialize() { @@ -162,8 +164,9 @@ protected ItemDefinition CreateJewelery(byte number, int slot, bool dropsFromMon if (resistanceAttribute != null) { - var powerUp = this.CreateItemBasePowerUpDefinition(resistanceAttribute, 0.1f, AggregateType.AddRaw); + var powerUp = this.CreateItemBasePowerUpDefinition(resistanceAttribute, 0.1f, AggregateType.Maximum); powerUp.BonusPerLevelTable = this._resistancesBonusTable; + item.BasePowerUpAttributes.Add(powerUp); } foreach (var characterClass in this.GameConfiguration.CharacterClasses) diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs index 18e0abbe4..53415b041 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs @@ -166,7 +166,7 @@ private ItemDefinition CreateWing(byte number, byte width, byte height, string n if (damageIncreaseInitial > 0) { - var powerUp = this.CreateItemBasePowerUpDefinition(Stats.AttackDamageIncrease, 1f + damageIncreaseInitial / 100f, AggregateType.Multiplicate); + var powerUp = this.CreateItemBasePowerUpDefinition(Stats.AttackDamageIncrease, 1f + (damageIncreaseInitial / 100f), AggregateType.Multiplicate); powerUp.BonusPerLevelTable = damageIncreasePerLevel; wing.BasePowerUpAttributes.Add(powerUp); } diff --git a/src/Persistence/Initialization/WingsInitializerBase.cs b/src/Persistence/Initialization/WingsInitializerBase.cs index 0749b3538..09de38071 100644 --- a/src/Persistence/Initialization/WingsInitializerBase.cs +++ b/src/Persistence/Initialization/WingsInitializerBase.cs @@ -81,10 +81,9 @@ protected ItemLevelBonusTable CreateAbsorbBonusPerLevel() { IEnumerable Generate() { - yield return 1f; - for (int level = 1; level <= this.MaximumItemLevel; level++) + for (int level = 0; level <= this.MaximumItemLevel; level++) { - yield return 1f - (0.02f * level); + yield return -0.02f * level; } } @@ -95,10 +94,9 @@ protected ItemLevelBonusTable CreateDamageIncreaseBonusPerLevelFirstAndThirdWing { IEnumerable Generate() { - yield return 1f; - for (int level = 1; level <= this.MaximumItemLevel; level++) + for (int level = 0; level <= this.MaximumItemLevel; level++) { - yield return 1f + (0.02f * level); + yield return 0.02f * level; } } @@ -109,10 +107,9 @@ protected ItemLevelBonusTable CreateDamageIncreaseBonusPerLevelSecondWings() { IEnumerable Generate() { - yield return 1; - for (int level = 1; level <= this.MaximumItemLevel; level++) + for (int level = 0; level <= this.MaximumItemLevel; level++) { - yield return 1 + (0.01f * level); + yield return 0.01f * level; } }