diff --git a/src/GameLogic/DroppedItem.cs b/src/GameLogic/DroppedItem.cs index 7e8ce8f21..4c00f0f1e 100644 --- a/src/GameLogic/DroppedItem.cs +++ b/src/GameLogic/DroppedItem.cs @@ -250,6 +250,11 @@ private async ValueTask TryStackOnItemAsync(Player player, Item stackTarge player.Logger.LogInformation("Item '{0}' got picked up by player '{1}'. Durability of available stack {2} increased to {3}", this, player, stackTarget, stackTarget.Durability); this.DisposeAndDelete(null); + if (player.GameContext.PlugInManager.GetPlugInPoint() is { } itemStackedPlugIn) + { + await itemStackedPlugIn.ItemStackedAsync(player, this.Item, stackTarget).ConfigureAwait(false); + } + return true; } diff --git a/src/GameLogic/PlayerActions/Items/MoveItemAction.cs b/src/GameLogic/PlayerActions/Items/MoveItemAction.cs index cf9b94a1f..9dc0a14a3 100644 --- a/src/GameLogic/PlayerActions/Items/MoveItemAction.cs +++ b/src/GameLogic/PlayerActions/Items/MoveItemAction.cs @@ -80,11 +80,18 @@ public async ValueTask MoveItemAsync(Player player, byte fromSlot, Storages from break; } - if (movement != Movement.None + if (movement is not Movement.None && player.GameContext.PlugInManager.GetPlugInPoint() is { } itemMovedPlugIn) { await itemMovedPlugIn.ItemMovedAsync(player, item).ConfigureAwait(false); } + + if (movement is not (Movement.None or Movement.Normal) + && toItemStorage?.GetItem(toSlot) is { } target + && player.GameContext.PlugInManager.GetPlugInPoint() is { } itemStackedPlugIn) + { + await itemStackedPlugIn.ItemStackedAsync(player, item, target).ConfigureAwait(false); + } } private async ValueTask FullStackAsync(Player player, Item sourceItem, Item targetItem) diff --git a/src/GameLogic/PlugIns/ChatCommands/ItemChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/ItemChatCommandPlugIn.cs index e0a68bd6d..27e88a72b 100644 --- a/src/GameLogic/PlugIns/ChatCommands/ItemChatCommandPlugIn.cs +++ b/src/GameLogic/PlugIns/ChatCommands/ItemChatCommandPlugIn.cs @@ -43,7 +43,7 @@ private static Item CreateItem(Player gameMaster, ItemChatCommandArgs arguments) { var item = new TemporaryItem(); item.Definition = GetItemDefination(gameMaster, arguments); - item.Durability = item.Definition.Durability; + item.Durability = item.IsStackable() ? 1 : item.Definition.Durability; item.HasSkill = item.Definition.Skill != null && arguments.Skill; item.Level = GetItemLevel(item.Definition, arguments); item.SocketCount = item.Definition.MaximumSockets; diff --git a/src/GameLogic/PlugIns/IItemStackedPlugIn.cs b/src/GameLogic/PlugIns/IItemStackedPlugIn.cs new file mode 100644 index 000000000..51c273268 --- /dev/null +++ b/src/GameLogic/PlugIns/IItemStackedPlugIn.cs @@ -0,0 +1,24 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.PlugIns; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.PlugIns; + +/// +/// A plugin interface which is called when an item got moved by the player. +/// +[Guid("FCDE60E9-6183-4833-BF59-CE907F9EECCD")] +[PlugInPoint("Item stacked", "Plugins which are called when an item has been stacked by the player.")] +public interface IItemStackedPlugIn +{ + /// + /// Is called when an item has been moved by the player. + /// + /// The player. + /// The source item. + /// The target item. + ValueTask ItemStackedAsync(Player player, Item sourceItem, Item targetItem); +} \ No newline at end of file diff --git a/src/GameLogic/PlugIns/SymbolOfKundunStackedPlugIn.cs b/src/GameLogic/PlugIns/SymbolOfKundunStackedPlugIn.cs new file mode 100644 index 000000000..f95244fba --- /dev/null +++ b/src/GameLogic/PlugIns/SymbolOfKundunStackedPlugIn.cs @@ -0,0 +1,47 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.PlugIns; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.PlugIns; + +/// +/// This plugin transforms a stack of symbol of kundun into a lost map. +/// +[PlugIn(nameof(SymbolOfKundunStackedPlugIn), "This plugin transforms a stack of symbol of kundun into a lost map.")] +[Guid("F07A9CED-F43E-4824-9587-F5C3C3187A13")] +public sealed class SymbolOfKundunStackedPlugIn : IItemStackedPlugIn +{ + private const byte SymbolOfKundunGroup = 14; + private const byte SymbolOfKundunNumber = 29; + private const byte LostMapGroup = 14; + private const byte LostMapNumber = 28; + + /// + public async ValueTask ItemStackedAsync(Player player, Item sourceItem, Item targetItem) + { + if (targetItem.Definition is not { Group: SymbolOfKundunGroup, Number: SymbolOfKundunNumber }) + { + return; + } + + if (targetItem.Durability() < targetItem.Definition.Durability) + { + return; + } + + var lostMap = player.GameContext.Configuration.Items.FirstOrDefault(item => item is { Group: LostMapGroup, Number: LostMapNumber }); + if (lostMap is null) + { + player.Logger.LogWarning("Lost map definition not found."); + return; + } + + await player.InvokeViewPlugInAsync(p => p.RemoveItemAsync(targetItem.ItemSlot)).ConfigureAwait(false); + targetItem.Definition = lostMap; + targetItem.Durability = 1; + await player.InvokeViewPlugInAsync(p => p.ItemAppearAsync(targetItem)).ConfigureAwait(false); + } +} diff --git a/src/Persistence/Initialization/Updates/AddKalimaPlugIn.cs b/src/Persistence/Initialization/Updates/AddKalimaPlugIn.cs new file mode 100644 index 000000000..6f7e43229 --- /dev/null +++ b/src/Persistence/Initialization/Updates/AddKalimaPlugIn.cs @@ -0,0 +1,87 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +using MUnique.OpenMU.DataModel.Configuration.Items; + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.PlugIns; + +/// +/// This adds the items required to enter the kalima map. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("0C99155F-1289-4E73-97F0-47CB67C3716F")] +public class AddKalimaPlugIn : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Add Kalima"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This adds the items required to enter the kalima map."; + + /// + public override UpdateVersion Version => UpdateVersion.AddKalima; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2024, 06, 09, 18, 0, 0, DateTimeKind.Utc); + + /// +#pragma warning disable CS1998 + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) +#pragma warning restore CS1998 + { + this.CreateLostMap(context, gameConfiguration); + this.CreateSymbolOfKundun(context, gameConfiguration); + } + + private void CreateLostMap(IContext context, GameConfiguration gameConfiguration) + { + var itemDefinition = context.CreateNew(); + itemDefinition.Name = "Lost Map"; + itemDefinition.Number = 28; + itemDefinition.Group = 14; + itemDefinition.DropsFromMonsters = false; + itemDefinition.Durability = 1; + itemDefinition.Width = 1; + itemDefinition.Height = 1; + itemDefinition.MaximumItemLevel = 7; + itemDefinition.SetGuid(itemDefinition.Group, itemDefinition.Number); + gameConfiguration.Items.Add(itemDefinition); + } + + private void CreateSymbolOfKundun(IContext context, GameConfiguration gameConfiguration) + { + var itemDefinition = context.CreateNew(); + itemDefinition.Name = "Symbol of Kundun"; + itemDefinition.Number = 29; + itemDefinition.Group = 14; + itemDefinition.DropLevel = 0; + itemDefinition.DropsFromMonsters = true; + itemDefinition.Durability = 5; + itemDefinition.Width = 1; + itemDefinition.Height = 1; + itemDefinition.MaximumItemLevel = 7; + itemDefinition.SetGuid(itemDefinition.Group, itemDefinition.Number); + gameConfiguration.Items.Add(itemDefinition); + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 4e7a53394..fea089606 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -88,5 +88,10 @@ public enum UpdateVersion /// /// The version of the . /// - AddPointsPerResetByClassAttribute = 16 + AddPointsPerResetByClassAttribute = 16, + + /// + /// The version of the . + /// + AddKalima = 17, } \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/Misc.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/Misc.cs index ea97df119..e1a77dd16 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/Misc.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/Misc.cs @@ -27,6 +27,39 @@ public override void Initialize() { this.CreateLifeStone(); this.CreateGoldenCherryBlossomBranch(); + this.CreateLostMap(); + this.CreateSymbolOfKundun(); + } + + private void CreateLostMap() + { + var itemDefinition = this.Context.CreateNew(); + itemDefinition.Name = "Lost Map"; + itemDefinition.Number = 28; + itemDefinition.Group = 14; + itemDefinition.DropsFromMonsters = false; + itemDefinition.Durability = 1; + itemDefinition.Width = 1; + itemDefinition.Height = 1; + itemDefinition.MaximumItemLevel = 7; + itemDefinition.SetGuid(itemDefinition.Group, itemDefinition.Number); + this.GameConfiguration.Items.Add(itemDefinition); + } + + private void CreateSymbolOfKundun() + { + var itemDefinition = this.Context.CreateNew(); + itemDefinition.Name = "Symbol of Kundun"; + itemDefinition.Number = 29; + itemDefinition.Group = 14; + itemDefinition.DropLevel = 0; + itemDefinition.DropsFromMonsters = true; + itemDefinition.Durability = 5; + itemDefinition.Width = 1; + itemDefinition.Height = 1; + itemDefinition.MaximumItemLevel = 7; + itemDefinition.SetGuid(itemDefinition.Group, itemDefinition.Number); + this.GameConfiguration.Items.Add(itemDefinition); } private void CreateLifeStone()