diff --git a/src/DataModel/Configuration/DropItemGroup.cs b/src/DataModel/Configuration/DropItemGroup.cs index 35ec995ea..bb9bbcccd 100644 --- a/src/DataModel/Configuration/DropItemGroup.cs +++ b/src/DataModel/Configuration/DropItemGroup.cs @@ -41,6 +41,11 @@ public enum SpecialItemType /// The money special item type. /// Money, + + /// + /// The jewel special item type. + /// + Jewel, } /// diff --git a/src/GameLogic/DefaultDropGenerator.cs b/src/GameLogic/DefaultDropGenerator.cs index f0d80bf52..5fc1fe2cc 100644 --- a/src/GameLogic/DefaultDropGenerator.cs +++ b/src/GameLogic/DefaultDropGenerator.cs @@ -18,6 +18,8 @@ public class DefaultDropGenerator : IDropGenerator /// public static readonly int BaseMoneyDrop = 7; + private static readonly int DropLevelMaxGap = 12; + private readonly IRandomizer _randomizer; /// @@ -433,7 +435,23 @@ private void AddRandomExcOptions(Item item) else { var monsterLevel = (int)monster[Stats.Level]; - var filteredPossibleItems = selectedGroup.PossibleItems.Where(it => it.DropLevel == 0 || ((it.DropLevel <= monsterLevel) && (it.DropLevel > monsterLevel - 12))).ToArray(); + List filteredPossibleItems; + + if (selectedGroup.ItemType == SpecialItemType.Jewel) + { + filteredPossibleItems = [.. selectedGroup.PossibleItems.Where(it => it.DropLevel <= monsterLevel)]; + + if (monsterLevel > 66) + { + // Jewel of Chaos doesn't drop after a certain monster level + filteredPossibleItems.RemoveAll(it => it.Group == 12 && it.Number == 15); + } + } + else + { + filteredPossibleItems = [.. selectedGroup.PossibleItems.Where(it => it.DropLevel == 0 || (it.DropLevel <= monsterLevel && it.DropLevel > monsterLevel - DropLevelMaxGap))]; + } + return this.GenerateItemDrop(selectedGroup, filteredPossibleItems); } } @@ -490,7 +508,7 @@ private void AddRandomExcOptions(Item item) return this._droppableItemsPerMonsterLevel[monsterLevel] ??= (from it in this._droppableItems where (it.DropLevel <= monsterLevel) - && (it.DropLevel > monsterLevel - 12) + && (it.DropLevel > monsterLevel - DropLevelMaxGap) && (!socketItems || it.MaximumSockets > 0) select it).ToList(); } diff --git a/src/Persistence/Initialization/GameConfigurationInitializerBase.cs b/src/Persistence/Initialization/GameConfigurationInitializerBase.cs index 6c384066c..f62343be1 100644 --- a/src/Persistence/Initialization/GameConfigurationInitializerBase.cs +++ b/src/Persistence/Initialization/GameConfigurationInitializerBase.cs @@ -167,7 +167,7 @@ private void AddItemDropGroups() var jewelsDropItemGroup = this.Context.CreateNew(); jewelsDropItemGroup.SetGuid(4); jewelsDropItemGroup.Chance = 0.001; - jewelsDropItemGroup.ItemType = SpecialItemType.RandomItem; + jewelsDropItemGroup.ItemType = SpecialItemType.Jewel; jewelsDropItemGroup.Description = "The jewels drop item group (0.1 % drop chance)"; this.GameConfiguration.DropItemGroups.Add(jewelsDropItemGroup); BaseMapInitializer.RegisterDefaultDropItemGroup(jewelsDropItemGroup); diff --git a/src/Persistence/Initialization/InitializerBase.cs b/src/Persistence/Initialization/InitializerBase.cs index 2569d90c2..fbbc04969 100644 --- a/src/Persistence/Initialization/InitializerBase.cs +++ b/src/Persistence/Initialization/InitializerBase.cs @@ -195,4 +195,15 @@ protected IncreasableItemOption CreateItemOption(int number, AttributeDefinition return itemOption; } + + /// + /// Register the jewel (or jewel-like item) in the drop item group for jewels. + /// + /// The jewel you want to register. + protected void AddItemToJewelItemDrop(ItemDefinition item) + { + var id = GuidHelper.CreateGuid(4); + var jewelsItemDrop = this.GameConfiguration.DropItemGroups.First(x => x.GetId() == id); + jewelsItemDrop.PossibleItems.Add(item); + } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugIn075.cs b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugIn075.cs new file mode 100644 index 000000000..84c188f98 --- /dev/null +++ b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugIn075.cs @@ -0,0 +1,30 @@ +// +// 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.DataModel.Configuration; +using MUnique.OpenMU.PlugIns; + +/// +/// This update removes the existing drop level gap condition for jewels and similar items that should always drop. +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("CD958BC1-F17A-4C60-B66D-BD29D49B6ADA")] +public class RemoveJewelDropLevelGapPlugIn075 : RemoveJewelDropLevelGapPlugInBase +{ + /// + public override string DataInitializationKey => Version075.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.RemoveJewelDropLevelGap075; + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugIn095d.cs b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugIn095d.cs new file mode 100644 index 000000000..8bd82191a --- /dev/null +++ b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugIn095d.cs @@ -0,0 +1,30 @@ +// +// 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.DataModel.Configuration; +using MUnique.OpenMU.PlugIns; + +/// +/// This update removes the existing drop level gap condition for jewels and similar items that should always drop. +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("6614E91E-5749-478A-96A4-3240E7C1280E")] +public class RemoveJewelDropLevelGapPlugIn095D : RemoveJewelDropLevelGapPlugInBase +{ + /// + public override string DataInitializationKey => Version095d.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.RemoveJewelDropLevelGap095d; + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugInBase.cs b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugInBase.cs new file mode 100644 index 000000000..f383bce2e --- /dev/null +++ b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugInBase.cs @@ -0,0 +1,58 @@ +// +// 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.DataModel.Configuration; +using MUnique.OpenMU.Persistence.Initialization.Items; + +/// +/// This update removes the existing drop level gap condition for jewels and similar items that should always drop. +/// +public abstract class RemoveJewelDropLevelGapPlugInBase : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Remove Jewel Drop Level Gap"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This update removes the existing drop level gap condition for jewels and similar items that should always drop."; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2026, 3, 5, 16, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + var jewelsDropGroupId = new Guid("00000200-0004-0000-0000-000000000000"); + var jewelsDropGroup = gameConfiguration.DropItemGroups.First(x => x.GetId() == jewelsDropGroupId); + jewelsDropGroup.ItemType = SpecialItemType.Jewel; + + Dictionary> itemGroupToItemNumbers = new() + { + { ItemGroups.Misc1, [0, 1, 2] }, // angel, imp, uniria + { ItemGroups.Misc2, [9, 10] }, // alcohol, town portal scroll + }; + foreach (var entry in itemGroupToItemNumbers) + { + var items = gameConfiguration.Items.Where(i => i.Group == (int)entry.Key && entry.Value.Contains(i.Number)); + foreach (var item in items) + { + jewelsDropGroup.PossibleItems.Add(item); + } + } + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugInSeason6.cs b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugInSeason6.cs new file mode 100644 index 000000000..3e2dc2947 --- /dev/null +++ b/src/Persistence/Initialization/Updates/RemoveJewelDropLevelGapPlugInSeason6.cs @@ -0,0 +1,36 @@ +// +// 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.DataModel.Configuration; +using MUnique.OpenMU.PlugIns; + +/// +/// This update removes the existing drop level gap condition for jewels and similar items that should always drop. +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("AB1C9F8B-5E3B-4F2A-BDCD-9C0F1E5A6B7C")] +public class RemoveJewelDropLevelGapPlugInSeason6 : RemoveJewelDropLevelGapPlugInBase +{ + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.RemoveJewelDropLevelGapSeason6; + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false); + + var jewelOfGuardianDropGroupId = new Guid("00000200-001f-0001-0000-000000000000"); + if (gameConfiguration.DropItemGroups.FirstOrDefault(x => x.GetId() == jewelOfGuardianDropGroupId) is { } jewelOfGuardianDropGroup) + { + jewelOfGuardianDropGroup.ItemType = SpecialItemType.Jewel; + } + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 1e771cc39..3c2993923 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -344,4 +344,19 @@ public enum UpdateVersion /// The version of the . /// AddProjectileCountToTripleShot = 67, + + /// + /// The version of the . + /// + RemoveJewelDropLevelGap075 = 68, + + /// + /// The version of the . + /// + RemoveJewelDropLevelGap095d = 69, + + /// + /// The version of the . + /// + RemoveJewelDropLevelGapSeason6 = 70, } \ No newline at end of file diff --git a/src/Persistence/Initialization/Version075/Items/Jewels.cs b/src/Persistence/Initialization/Version075/Items/Jewels.cs index e08b7f0fb..961a4a51f 100644 --- a/src/Persistence/Initialization/Version075/Items/Jewels.cs +++ b/src/Persistence/Initialization/Version075/Items/Jewels.cs @@ -30,17 +30,6 @@ public override void Initialize() this.GameConfiguration.Items.Add(this.CreateJewelOfChaos()); } - /// - /// Register the jewel in the drop item group for jewels. - /// - /// The jewel you want to register. - protected void AddItemToJewelItemDrop(ItemDefinition item) - { - var id = GuidHelper.CreateGuid(4); - var jewelsItemDrop = this.GameConfiguration.DropItemGroups.First(x => x.GetId() == id); - jewelsItemDrop.PossibleItems.Add(item); - } - /// /// Creates an for the 'Jewel of Bless'. /// diff --git a/src/Persistence/Initialization/Version075/Items/Pets.cs b/src/Persistence/Initialization/Version075/Items/Pets.cs index e814c4775..1d16abe57 100644 --- a/src/Persistence/Initialization/Version075/Items/Pets.cs +++ b/src/Persistence/Initialization/Version075/Items/Pets.cs @@ -29,9 +29,12 @@ public Pets(IContext context, GameConfiguration gameConfiguration) /// public override void Initialize() { - this.CreatePet(0, "Guardian Angel", 23, (Stats.DamageReceiveDecrement, 0.8f, AggregateType.Multiplicate), (Stats.MaximumHealth, 50f, AggregateType.AddRaw)); - this.CreatePet(1, "Imp", 28, (Stats.AttackDamageIncrease, 1.3f, AggregateType.Multiplicate)); - this.CreatePet(2, "Horn of Uniria", 25); + var angel = this.CreatePet(0, "Guardian Angel", 23, (Stats.DamageReceiveDecrement, 0.8f, AggregateType.Multiplicate), (Stats.MaximumHealth, 50f, AggregateType.AddRaw)); + this.AddItemToJewelItemDrop(angel); + var imp = this.CreatePet(1, "Imp", 28, (Stats.AttackDamageIncrease, 1.3f, AggregateType.Multiplicate)); + this.AddItemToJewelItemDrop(imp); + var uniria = this.CreatePet(2, "Horn of Uniria", 25); + this.AddItemToJewelItemDrop(uniria); } private ItemDefinition CreatePet(byte number, string name, int dropLevelAndLevelRequirement, params (AttributeDefinition, float, AggregateType)[] basePowerUps) diff --git a/src/Persistence/Initialization/Version075/Items/Potions.cs b/src/Persistence/Initialization/Version075/Items/Potions.cs index fb5f0e090..3a56d4b60 100644 --- a/src/Persistence/Initialization/Version075/Items/Potions.cs +++ b/src/Persistence/Initialization/Version075/Items/Potions.cs @@ -54,6 +54,7 @@ private ItemDefinition CreateAlcohol() alcohol.Height = 2; alcohol.SetGuid(alcohol.Group, alcohol.Number); alcohol.ConsumeEffect = this.GameConfiguration.MagicEffects.First(effect => effect.Number == (short)MagicEffectNumber.Alcohol); + this.AddItemToJewelItemDrop(alcohol); return alcohol; } @@ -230,6 +231,7 @@ private ItemDefinition CreateTownPortalScroll() definition.Width = 1; definition.Height = 2; definition.SetGuid(definition.Group, definition.Number); + this.AddItemToJewelItemDrop(definition); return definition; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Version095d/Items/Pets.cs b/src/Persistence/Initialization/Version095d/Items/Pets.cs index 00650045c..d535ec284 100644 --- a/src/Persistence/Initialization/Version095d/Items/Pets.cs +++ b/src/Persistence/Initialization/Version095d/Items/Pets.cs @@ -30,9 +30,12 @@ public Pets(IContext context, GameConfiguration gameConfiguration) /// public override void Initialize() { - this.CreatePet(0, 0, "Guardian Angel", 23, true, (Stats.DamageReceiveDecrement, 0.8f, AggregateType.Multiplicate), (Stats.MaximumHealth, 50f, AggregateType.AddRaw)); - this.CreatePet(1, 0, "Imp", 28, true, (Stats.AttackDamageIncrease, 1.3f, AggregateType.Multiplicate)); - this.CreatePet(2, 0, "Horn of Uniria", 25, true); + var angel = this.CreatePet(0, 0, "Guardian Angel", 23, true, (Stats.DamageReceiveDecrement, 0.8f, AggregateType.Multiplicate), (Stats.MaximumHealth, 50f, AggregateType.AddRaw)); + this.AddItemToJewelItemDrop(angel); + var imp = this.CreatePet(1, 0, "Imp", 28, true, (Stats.AttackDamageIncrease, 1.3f, AggregateType.Multiplicate)); + this.AddItemToJewelItemDrop(imp); + var uniria = this.CreatePet(2, 0, "Horn of Uniria", 25, true); + this.AddItemToJewelItemDrop(uniria); var dinorant = this.CreatePet(3, SkillNumber.FireBreath, "Horn of Dinorant", 110, false, (Stats.IsDinorantEquipped, 1, AggregateType.AddRaw), (Stats.DamageReceiveDecrement, 0.9f, AggregateType.Multiplicate), (Stats.AttackDamageIncrease, 1.15f, AggregateType.Multiplicate)); this.AddDinorantOptions(dinorant); diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/Pets.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/Pets.cs index facdc13d2..898571207 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/Pets.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/Pets.cs @@ -37,12 +37,15 @@ public Pets(IContext context, GameConfiguration gameConfiguration) public override void Initialize() { #pragma warning disable SA1117 // Parameters should be on same line or separete lines - this.CreatePet(0, 0, 1, 1, "Guardian Angel", 23, true, true, + var angel = this.CreatePet(0, 0, 1, 1, "Guardian Angel", 23, true, true, (Stats.DamageReceiveDecrement, 0.8f, AggregateType.Multiplicate), (Stats.MaximumHealth, 50f, AggregateType.AddRaw)); - this.CreatePet(1, 0, 1, 1, "Imp", 28, true, true, + this.AddItemToJewelItemDrop(angel); + var imp = this.CreatePet(1, 0, 1, 1, "Imp", 28, true, true, (Stats.AttackDamageIncrease, 1.3f, AggregateType.Multiplicate)); - this.CreatePet(2, 0, 1, 1, "Horn of Uniria", 25, true, true); + this.AddItemToJewelItemDrop(imp); + var uniria = this.CreatePet(2, 0, 1, 1, "Horn of Uniria", 25, true, true); + this.AddItemToJewelItemDrop(uniria); var dinorant = this.CreatePet(3, SkillNumber.FireBreath, 1, 1, "Horn of Dinorant", 110, false, true, (Stats.IsDinorantEquipped, 1, AggregateType.AddRaw), diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/Potions.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/Potions.cs index 771d54f66..bfa2d41de 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/Potions.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/Potions.cs @@ -75,6 +75,7 @@ private ItemDefinition CreateAlcohol() alcohol.Height = 2; alcohol.SetGuid(alcohol.Group, alcohol.Number); alcohol.ConsumeEffect = this.GameConfiguration.MagicEffects.First(effect => effect.Number == (short)MagicEffectNumber.Alcohol); + this.AddItemToJewelItemDrop(alcohol); return alcohol; } @@ -375,6 +376,7 @@ private ItemDefinition CreateTownPortalScroll() definition.Width = 1; definition.Height = 2; definition.SetGuid(definition.Group, definition.Number); + this.AddItemToJewelItemDrop(definition); return definition; } diff --git a/src/Persistence/Initialization/VersionSeasonSix/Maps/LandOfTrials.cs b/src/Persistence/Initialization/VersionSeasonSix/Maps/LandOfTrials.cs index e36603364..cf058611e 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Maps/LandOfTrials.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Maps/LandOfTrials.cs @@ -457,7 +457,7 @@ protected override void InitializeDropItemGroups() var jewelsDropItemGroup = this.Context.CreateNew(); jewelsDropItemGroup.SetGuid(this.MapNumber, 1); jewelsDropItemGroup.Chance = 0.001; - jewelsDropItemGroup.ItemType = SpecialItemType.RandomItem; + jewelsDropItemGroup.ItemType = SpecialItemType.Jewel; jewelsDropItemGroup.Description = "Jewel of Guardian"; var jewelOfGuardian = this.GameConfiguration.Items.First(y => y.Group == 14 && y.Number == 31);