From 6c1005a80d4219db46044177bd40fadb9977ce9a Mon Sep 17 00:00:00 2001 From: Zin <62830952+Zintixx@users.noreply.github.com> Date: Sun, 9 Feb 2025 10:22:12 -0800 Subject: [PATCH 1/5] Offense Stat Line Limitations --- .../Util/ItemStatsCalculator.cs | 91 +++++++++++++++++-- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/Maple2.Server.Game/Util/ItemStatsCalculator.cs b/Maple2.Server.Game/Util/ItemStatsCalculator.cs index 236cf34b6..faa858658 100644 --- a/Maple2.Server.Game/Util/ItemStatsCalculator.cs +++ b/Maple2.Server.Game/Util/ItemStatsCalculator.cs @@ -19,6 +19,59 @@ public sealed class ItemStatsCalculator { // ReSharper restore All #endregion + private const float OFFENSE_LINE_MAX_THRESHOLD = 0.5f; // 50% threshold for offense lines + private static readonly IList offenseBasicAttributes = [ + BasicAttribute.AttackSpeed, + BasicAttribute.CriticalRate, + BasicAttribute.CriticalDamage, + BasicAttribute.PhysicalAtk, + BasicAttribute.MagicalAtk, + BasicAttribute.MinWeaponAtk, + BasicAttribute.MaxWeaponAtk, + BasicAttribute.Damage, + BasicAttribute.Piercing, + BasicAttribute.BonusAtk, + BasicAttribute.PetBonusAtk, + ]; + private static readonly IList offenseSpecialAttributes = [ + SpecialAttribute.TotalDamage, + SpecialAttribute.CriticalDamage, + SpecialAttribute.NormalNpcDamage, + SpecialAttribute.LeaderNpcDamage, + SpecialAttribute.EliteNpcDamage, + SpecialAttribute.BossNpcDamage, + SpecialAttribute.IceDamage, + SpecialAttribute.FireDamage, + SpecialAttribute.DarkDamage, + SpecialAttribute.HolyDamage, + SpecialAttribute.PoisonDamage, + SpecialAttribute.ElectricDamage, + SpecialAttribute.MeleeDamage, + SpecialAttribute.RangedDamage, + SpecialAttribute.PhysicalPiercing, + SpecialAttribute.MagicalPiercing, + SpecialAttribute.MeleeSplashDamage, + SpecialAttribute.RangedSplashDamage, + SpecialAttribute.PvpDamage, + SpecialAttribute.SkillLevelUpTier1, + SpecialAttribute.SkillLevelUpTier2, + SpecialAttribute.SkillLevelUpTier3, + SpecialAttribute.SkillLevelUpTier4, + SpecialAttribute.SkillLevelUpTier5, + SpecialAttribute.SkillLevelUpTier6, + SpecialAttribute.SkillLevelUpTier7, + SpecialAttribute.SkillLevelUpTier8, + SpecialAttribute.SkillLevelUpTier9, + SpecialAttribute.SkillLevelUpTier10, + SpecialAttribute.SkillLevelUpTier11, + SpecialAttribute.SkillLevelUpTier12, + SpecialAttribute.SkillLevelUpTier13, + SpecialAttribute.SkillLevelUpTier14, + SpecialAttribute.DarkStreamDamage, + SpecialAttribute.MaxWeaponAttack, + SpecialAttribute.ChaosRaidAttack, + ]; + public ItemStats? GetStats(Item item, bool rollMax = false) { if (item.Metadata.Option == null) { return null; @@ -32,13 +85,13 @@ public sealed class ItemStatsCalculator { stats[ItemStats.Type.Constant] = constantOption; } - if (GetStaticOption(item, job, pick, out ItemStats.Option? staticOption)) { + if (GetStaticOption(item, job, ItemStats.Type.Static, pick, out ItemStats.Option? staticOption)) { stats[ItemStats.Type.Static] = staticOption; } if (TableMetadata.ItemOptionRandomTable.Options.TryGetValue(item.Metadata.Option.RandomId, item.Rarity, out ItemOption? itemOption)) { - ItemStats.Option option = GetRandomOption(itemOption); + ItemStats.Option option = GetRandomOption(itemOption, item.Type, ItemStats.Type.Random); RandomizeValues(item, itemOption, ref option, rollMax); stats[ItemStats.Type.Random] = option; } @@ -83,7 +136,7 @@ public bool UpdateRandomOption(ref Item item, params LockOption[] presets) { if (!TableMetadata.ItemOptionRandomTable.Options.TryGetValue(item.Metadata.Option.RandomId, item.Rarity, out ItemOption? itemOption)) { return false; } - ItemStats.Option randomOption = GetRandomOption(itemOption, option.Count, presets); + ItemStats.Option randomOption = GetRandomOption(itemOption, item.Type, ItemStats.Type.Random, option.Count, presets); if (!RandomizeValues(item, itemOption, ref randomOption)) { return false; @@ -309,7 +362,7 @@ private bool GetConstantOption(Item item, int job, ItemOptionPickTable.Option? p } // Used to calculate the default static attributes for a given item. - private bool GetStaticOption(Item item, int job, ItemOptionPickTable.Option? pick, [NotNullWhen(true)] out ItemStats.Option? option) { + private bool GetStaticOption(Item item, int job, ItemStats.Type statsType, ItemOptionPickTable.Option? pick, [NotNullWhen(true)] out ItemStats.Option? option) { option = null; if (item.Metadata.Option == null) { return false; @@ -317,7 +370,7 @@ private bool GetStaticOption(Item item, int job, ItemOptionPickTable.Option? pic if (TableMetadata.ItemOptionStaticTable.Options.TryGetValue(item.Metadata.Option.StaticId, item.Rarity, out ItemOption? itemOption)) { // We're using RandomItemOption here considering the logic is the same. - option = RandomItemOption(itemOption); + option = RandomItemOption(itemOption, item.Type, statsType); } if (item.Metadata.Option.StaticType == ItemOptionMakeType.Lua && pick != null) { @@ -338,8 +391,8 @@ private bool GetStaticOption(Item item, int job, ItemOptionPickTable.Option? pic } // Used to calculate the default random attributes for a given item. - private ItemStats.Option GetRandomOption(ItemOption itemOption, int count = -1, params LockOption[] presets) { - return RandomItemOption(itemOption, count, presets); + private ItemStats.Option GetRandomOption(ItemOption itemOption, in ItemType itemType, ItemStats.Type statsType, int count = -1, params LockOption[] presets) { + return RandomItemOption(itemOption, itemType, statsType, count, presets); } private ItemEquipVariationTable? GetVariationTable(in ItemType type) { @@ -416,7 +469,7 @@ private static ItemStats.Option ConstantItemOption(ItemOptionConstant option) { return new ItemStats.Option(statResult, specialResult); } - private static ItemStats.Option RandomItemOption(ItemOption option, int count = -1, params LockOption[] presets) { + private static ItemStats.Option RandomItemOption(ItemOption option, in ItemType itemType, ItemStats.Type statsType, int count = -1, params LockOption[] presets) { var statResult = new Dictionary(); var specialResult = new Dictionary(); @@ -442,6 +495,11 @@ private static ItemStats.Option RandomItemOption(ItemOption option, int count = while (statResult.Count + specialResult.Count < total) { ItemOption.Entry entry = option.Entries.Random(); + if (statsType == ItemStats.Type.Random && + itemType.IsArmor && // Only check armor + !IsValidStat(total, statResult, specialResult, entry)) { + continue; + } if (!AddResult(entry, statResult, specialResult)) { Log.Error("Failed to select random item option: {Entry}", entry); // Invalid entry } @@ -483,6 +541,23 @@ bool AddResult(ItemOption.Entry entry, IDictionary } } + /// + /// Verifies if the new attribute being added meets the offense line threshold. + /// + /// + private static bool IsValidStat(int statLineCount, IDictionary statDict, IDictionary specialDict, ItemOption.Entry entry) { + int offenseStatCount = statDict.Keys.Count(stat => offenseBasicAttributes.Contains(stat)); + offenseStatCount += specialDict.Keys.Count(stat => offenseSpecialAttributes.Contains(stat)); + + if (entry.BasicAttribute != null && offenseBasicAttributes.Contains((BasicAttribute) entry.BasicAttribute)) { + offenseStatCount++; + } else if (entry.SpecialAttribute != null && offenseSpecialAttributes.Contains((SpecialAttribute) entry.SpecialAttribute)) { + offenseStatCount++; + } + + return (float) offenseStatCount / statLineCount < OFFENSE_LINE_MAX_THRESHOLD; + } + private int ConstValue(BasicAttribute attribute, int statValue, int deviation, int type, int job, int levelFactor, int rarity, ushort level) { (float, float) range = attribute switch { BasicAttribute.Strength => Lua.ConstantValueStr(statValue, deviation, type, job, levelFactor, rarity, level), From 63d4839cbee530232f44cd4f6a531a232aef788b Mon Sep 17 00:00:00 2001 From: Zin <62830952+Zintixx@users.noreply.github.com> Date: Sun, 9 Feb 2025 11:54:02 -0800 Subject: [PATCH 2/5] more offense stats, elemental damage stats --- .../Util/ItemStatsCalculator.cs | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/Maple2.Server.Game/Util/ItemStatsCalculator.cs b/Maple2.Server.Game/Util/ItemStatsCalculator.cs index faa858658..2773b7bb3 100644 --- a/Maple2.Server.Game/Util/ItemStatsCalculator.cs +++ b/Maple2.Server.Game/Util/ItemStatsCalculator.cs @@ -21,6 +21,11 @@ public sealed class ItemStatsCalculator { private const float OFFENSE_LINE_MAX_THRESHOLD = 0.5f; // 50% threshold for offense lines private static readonly IList offenseBasicAttributes = [ + BasicAttribute.Strength, + BasicAttribute.Dexterity, + BasicAttribute.Intelligence, + BasicAttribute.Luck, + BasicAttribute.Accuracy, BasicAttribute.AttackSpeed, BasicAttribute.CriticalRate, BasicAttribute.CriticalDamage, @@ -71,6 +76,14 @@ public sealed class ItemStatsCalculator { SpecialAttribute.MaxWeaponAttack, SpecialAttribute.ChaosRaidAttack, ]; + private static readonly IList elementalDamageAttributes = [ + SpecialAttribute.IceDamage, + SpecialAttribute.FireDamage, + SpecialAttribute.DarkDamage, + SpecialAttribute.HolyDamage, + SpecialAttribute.PoisonDamage, + SpecialAttribute.ElectricDamage, + ]; public ItemStats? GetStats(Item item, bool rollMax = false) { if (item.Metadata.Option == null) { @@ -496,8 +509,7 @@ private static ItemStats.Option RandomItemOption(ItemOption option, in ItemType while (statResult.Count + specialResult.Count < total) { ItemOption.Entry entry = option.Entries.Random(); if (statsType == ItemStats.Type.Random && - itemType.IsArmor && // Only check armor - !IsValidStat(total, statResult, specialResult, entry)) { + !IsValidStat(itemType, total, statResult, specialResult, entry)) { continue; } if (!AddResult(entry, statResult, specialResult)) { @@ -545,17 +557,29 @@ bool AddResult(ItemOption.Entry entry, IDictionary /// Verifies if the new attribute being added meets the offense line threshold. /// /// - private static bool IsValidStat(int statLineCount, IDictionary statDict, IDictionary specialDict, ItemOption.Entry entry) { - int offenseStatCount = statDict.Keys.Count(stat => offenseBasicAttributes.Contains(stat)); - offenseStatCount += specialDict.Keys.Count(stat => offenseSpecialAttributes.Contains(stat)); + private static bool IsValidStat(ItemType itemType, int statLineCount, IDictionary statDict, IDictionary specialDict, ItemOption.Entry entry) { + if (itemType.IsAccessory) { + int currentElementalStats = specialDict.Keys.Count(stat => elementalDamageAttributes.Contains(stat)); + if (entry.SpecialAttribute != null && elementalDamageAttributes.Contains((SpecialAttribute) entry.SpecialAttribute)) { + currentElementalStats++; + } + return currentElementalStats < 2; + } + + if (itemType.IsArmor) { + int offenseStatCount = statDict.Keys.Count(stat => offenseBasicAttributes.Contains(stat)); + offenseStatCount += specialDict.Keys.Count(stat => offenseSpecialAttributes.Contains(stat)); - if (entry.BasicAttribute != null && offenseBasicAttributes.Contains((BasicAttribute) entry.BasicAttribute)) { - offenseStatCount++; - } else if (entry.SpecialAttribute != null && offenseSpecialAttributes.Contains((SpecialAttribute) entry.SpecialAttribute)) { - offenseStatCount++; + if (entry.BasicAttribute != null && offenseBasicAttributes.Contains((BasicAttribute) entry.BasicAttribute)) { + offenseStatCount++; + } else if (entry.SpecialAttribute != null && offenseSpecialAttributes.Contains((SpecialAttribute) entry.SpecialAttribute)) { + offenseStatCount++; + } + + return (float) offenseStatCount / statLineCount < OFFENSE_LINE_MAX_THRESHOLD; } - return (float) offenseStatCount / statLineCount < OFFENSE_LINE_MAX_THRESHOLD; + return true; } private int ConstValue(BasicAttribute attribute, int statValue, int deviation, int type, int job, int levelFactor, int rarity, ushort level) { From cd3fe24aea770cacc31ff50090b79a5c5f5a8d37 Mon Sep 17 00:00:00 2001 From: Zin <62830952+Zintixx@users.noreply.github.com> Date: Sun, 9 Feb 2025 12:38:11 -0800 Subject: [PATCH 3/5] oops --- Maple2.Server.Game/Util/ItemStatsCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Maple2.Server.Game/Util/ItemStatsCalculator.cs b/Maple2.Server.Game/Util/ItemStatsCalculator.cs index 2773b7bb3..e9c6b0e19 100644 --- a/Maple2.Server.Game/Util/ItemStatsCalculator.cs +++ b/Maple2.Server.Game/Util/ItemStatsCalculator.cs @@ -576,7 +576,7 @@ private static bool IsValidStat(ItemType itemType, int statLineCount, IDictionar offenseStatCount++; } - return (float) offenseStatCount / statLineCount < OFFENSE_LINE_MAX_THRESHOLD; + return (float) offenseStatCount / statLineCount <= OFFENSE_LINE_MAX_THRESHOLD; } return true; From 1937826db7de585d6090aee7325cd82efd74a6e9 Mon Sep 17 00:00:00 2001 From: Zin <62830952+Zintixx@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:22:26 -0800 Subject: [PATCH 4/5] more changes.. --- .../Util/ItemStatsCalculator.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Maple2.Server.Game/Util/ItemStatsCalculator.cs b/Maple2.Server.Game/Util/ItemStatsCalculator.cs index e9c6b0e19..1e2f08706 100644 --- a/Maple2.Server.Game/Util/ItemStatsCalculator.cs +++ b/Maple2.Server.Game/Util/ItemStatsCalculator.cs @@ -76,13 +76,22 @@ public sealed class ItemStatsCalculator { SpecialAttribute.MaxWeaponAttack, SpecialAttribute.ChaosRaidAttack, ]; - private static readonly IList elementalDamageAttributes = [ + private static readonly IList damageTypeAttributes = [ SpecialAttribute.IceDamage, SpecialAttribute.FireDamage, SpecialAttribute.DarkDamage, SpecialAttribute.HolyDamage, SpecialAttribute.PoisonDamage, SpecialAttribute.ElectricDamage, + SpecialAttribute.MeleeDamage, + SpecialAttribute.RangedDamage, + SpecialAttribute.BossNpcDamage, + SpecialAttribute.NormalNpcDamage, + SpecialAttribute.LeaderNpcDamage, + SpecialAttribute.EliteNpcDamage, + SpecialAttribute.TotalDamage, + SpecialAttribute.PvpDamage, + SpecialAttribute.DarkStreamDamage, ]; public ItemStats? GetStats(Item item, bool rollMax = false) { @@ -558,15 +567,13 @@ bool AddResult(ItemOption.Entry entry, IDictionary /// /// private static bool IsValidStat(ItemType itemType, int statLineCount, IDictionary statDict, IDictionary specialDict, ItemOption.Entry entry) { - if (itemType.IsAccessory) { - int currentElementalStats = specialDict.Keys.Count(stat => elementalDamageAttributes.Contains(stat)); - if (entry.SpecialAttribute != null && elementalDamageAttributes.Contains((SpecialAttribute) entry.SpecialAttribute)) { - currentElementalStats++; - } - return currentElementalStats < 2; + int damageTypeStatCount = specialDict.Keys.Count(stat => damageTypeAttributes.Contains(stat)); + if (entry.SpecialAttribute != null && damageTypeAttributes.Contains((SpecialAttribute) entry.SpecialAttribute)) { + // cannot have more than 1 damage type attribute + return damageTypeStatCount < 1; } - if (itemType.IsArmor) { + if (!itemType.IsCombatPet || !itemType.IsWeapon || !itemType.IsAccessory) { int offenseStatCount = statDict.Keys.Count(stat => offenseBasicAttributes.Contains(stat)); offenseStatCount += specialDict.Keys.Count(stat => offenseSpecialAttributes.Contains(stat)); From 13f716958db068bbe5f0a63950677ab68284ffff Mon Sep 17 00:00:00 2001 From: Zin <62830952+Zintixx@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:26:50 -0800 Subject: [PATCH 5/5] OOPS --- Maple2.Server.Game/Util/ItemStatsCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Maple2.Server.Game/Util/ItemStatsCalculator.cs b/Maple2.Server.Game/Util/ItemStatsCalculator.cs index 1e2f08706..c677a3a7b 100644 --- a/Maple2.Server.Game/Util/ItemStatsCalculator.cs +++ b/Maple2.Server.Game/Util/ItemStatsCalculator.cs @@ -573,7 +573,7 @@ private static bool IsValidStat(ItemType itemType, int statLineCount, IDictionar return damageTypeStatCount < 1; } - if (!itemType.IsCombatPet || !itemType.IsWeapon || !itemType.IsAccessory) { + if (itemType is { IsCombatPet: false, IsWeapon: false, IsAccessory: false }) { int offenseStatCount = statDict.Keys.Count(stat => offenseBasicAttributes.Contains(stat)); offenseStatCount += specialDict.Keys.Count(stat => offenseSpecialAttributes.Contains(stat));