From ff9325ede1a43330fddbb9fd7cb620a7b1f3e718 Mon Sep 17 00:00:00 2001 From: Benito Go III Date: Tue, 7 Jan 2025 23:03:54 +0800 Subject: [PATCH 1/4] Updated consumption of minigame ticket by searching the inventory instead of using the client provided index --- src/GameLogic/IStorage.cs | 8 ++++++++ .../PlayerActions/MiniGames/EnterMiniGameAction.cs | 4 +++- src/GameLogic/Storage.cs | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/GameLogic/IStorage.cs b/src/GameLogic/IStorage.cs index 86e20dd66..00bcc8af5 100644 --- a/src/GameLogic/IStorage.cs +++ b/src/GameLogic/IStorage.cs @@ -4,6 +4,7 @@ namespace MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.DataModel.Configuration.Items; using MUnique.OpenMU.PlugIns; using Nito.AsyncEx; @@ -164,6 +165,13 @@ public interface IStorage /// The item from the specified slot. Item? GetItem(byte inventorySlot); + /// + /// Finds an item with the same definition. + /// + /// The item definition to be searched. + /// The item with the same definition. + Item? FindItemByDefinition(ItemDefinition definition); + /// /// Removes the item from this storage. /// diff --git a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs index 7d6aa58b2..eafadb882 100644 --- a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs +++ b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs @@ -175,7 +175,9 @@ private bool CheckTicketItem(MiniGameDefinition miniGameDefinition, Player playe return true; } - ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); + //ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); + ticketItem = player.Inventory?.FindItemByDefinition(ticketItemDefinition); + return ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel; } diff --git a/src/GameLogic/Storage.cs b/src/GameLogic/Storage.cs index 0d1bced8c..54376a486 100644 --- a/src/GameLogic/Storage.cs +++ b/src/GameLogic/Storage.cs @@ -4,6 +4,8 @@ namespace MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.DataModel.Configuration.Items; + /// /// This class wraps the access to the IItemStorage of an character. /// @@ -260,6 +262,12 @@ public async ValueTask TryTakeAllAsync(IStorage anotherStorage) return extension?.GetItem(inventorySlot); } + /// + public virtual Item? FindItemByDefinition(ItemDefinition definition) + { + return this.ItemArray.FirstOrDefault(i => i != null && i.Definition == definition); + } + /// public virtual async ValueTask RemoveItemAsync(Item item) { From d09ae3dbbb3c152cdff58f577b3be82054e9a618 Mon Sep 17 00:00:00 2001 From: Benito Go III Date: Wed, 8 Jan 2025 00:27:28 +0800 Subject: [PATCH 2/4] Consider storage extensions when searching for item --- .../MiniGames/EnterMiniGameAction.cs | 2 +- src/GameLogic/Storage.cs | 20 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs index eafadb882..c75b098b6 100644 --- a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs +++ b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs @@ -175,7 +175,7 @@ private bool CheckTicketItem(MiniGameDefinition miniGameDefinition, Player playe return true; } - //ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); + // find the required ticket instead of using the client provided index for simplicity ticketItem = player.Inventory?.FindItemByDefinition(ticketItemDefinition); return ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel; diff --git a/src/GameLogic/Storage.cs b/src/GameLogic/Storage.cs index 54376a486..ef7abf60f 100644 --- a/src/GameLogic/Storage.cs +++ b/src/GameLogic/Storage.cs @@ -263,9 +263,25 @@ public async ValueTask TryTakeAllAsync(IStorage anotherStorage) } /// - public virtual Item? FindItemByDefinition(ItemDefinition definition) + public Item? FindItemByDefinition(ItemDefinition definition) { - return this.ItemArray.FirstOrDefault(i => i != null && i.Definition == definition); + // Search in the primary storage. + var ticket = this.ItemArray.FirstOrDefault(i => i != null && i.Definition == definition); + if (ticket is not null) + { + return ticket; + } + + // Search in the extensions if they exist. + foreach (var extension in this.Extensions) + { + if (extension.FindItemByDefinition(definition) is { } item) + { + return item; + } + } + + return null; } /// From 5fd9e39f7abdad19bc30c68b07df6d733b679679 Mon Sep 17 00:00:00 2001 From: Benito Go III Date: Wed, 8 Jan 2025 01:02:24 +0800 Subject: [PATCH 3/4] Fixed issue where a ticket of different level is searched first --- src/GameLogic/IStorage.cs | 6 ++--- .../MiniGames/EnterMiniGameAction.cs | 22 ++++++++++++++--- src/GameLogic/Storage.cs | 24 +++++++------------ 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/GameLogic/IStorage.cs b/src/GameLogic/IStorage.cs index 00bcc8af5..03d22060d 100644 --- a/src/GameLogic/IStorage.cs +++ b/src/GameLogic/IStorage.cs @@ -166,11 +166,11 @@ public interface IStorage Item? GetItem(byte inventorySlot); /// - /// Finds an item with the same definition. + /// Finds items that matches the given definition. /// /// The item definition to be searched. - /// The item with the same definition. - Item? FindItemByDefinition(ItemDefinition definition); + /// The items with the same definition. + IEnumerable FindItemsByDefinition(ItemDefinition definition); /// /// Removes the item from this storage. diff --git a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs index c75b098b6..cd9703b83 100644 --- a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs +++ b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs @@ -175,10 +175,26 @@ private bool CheckTicketItem(MiniGameDefinition miniGameDefinition, Player playe return true; } - // find the required ticket instead of using the client provided index for simplicity - ticketItem = player.Inventory?.FindItemByDefinition(ticketItemDefinition); + ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); + if (ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel) + { + return true; + } + + var candidateTicketItems = player.Inventory?.FindItemsByDefinition(ticketItemDefinition); + if (candidateTicketItems is not null && candidateTicketItems.Any()) + { + foreach (var candidateTicketItem in candidateTicketItems) + { + ticketItem = candidateTicketItem; + if (ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel) + { + return true; + } + } + } - return ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel; + return false; } private bool CheckEntranceFee(MiniGameDefinition miniGameDefinition, Player player, out int entranceFee) diff --git a/src/GameLogic/Storage.cs b/src/GameLogic/Storage.cs index ef7abf60f..99d85eff1 100644 --- a/src/GameLogic/Storage.cs +++ b/src/GameLogic/Storage.cs @@ -263,25 +263,17 @@ public async ValueTask TryTakeAllAsync(IStorage anotherStorage) } /// - public Item? FindItemByDefinition(ItemDefinition definition) + public IEnumerable FindItemsByDefinition(ItemDefinition definition) { - // Search in the primary storage. - var ticket = this.ItemArray.FirstOrDefault(i => i != null && i.Definition == definition); - if (ticket is not null) - { - return ticket; - } + var primaryMatches = this.ItemArray + .Where(i => i != null && i.Definition == definition) + .Cast(); - // Search in the extensions if they exist. - foreach (var extension in this.Extensions) - { - if (extension.FindItemByDefinition(definition) is { } item) - { - return item; - } - } + var extensionMatches = this.Extensions? + .SelectMany(extension => extension.FindItemsByDefinition(definition)) + ?? Enumerable.Empty(); - return null; + return primaryMatches.Concat(extensionMatches); } /// From 3db6f68d59557d158a3c5976b4e2ec1d19a1ddf3 Mon Sep 17 00:00:00 2001 From: Benito Go III Date: Wed, 8 Jan 2025 17:17:07 +0800 Subject: [PATCH 4/4] Minor updates for readability --- .../MiniGames/EnterMiniGameAction.cs | 35 ++++++++++++------- src/GameLogic/Storage.cs | 2 +- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs index cd9703b83..28d34d84c 100644 --- a/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs +++ b/src/GameLogic/PlayerActions/MiniGames/EnterMiniGameAction.cs @@ -4,6 +4,7 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.MiniGames; +using MUnique.OpenMU.DataModel.Configuration.Items; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.GuildWar; using MUnique.OpenMU.GameLogic.MiniGames; @@ -16,6 +17,8 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.MiniGames; /// public class EnterMiniGameAction { + private const int UndefinedTicketSlot = 0xFF; + /// /// Tries to enter the mini game event of the specified type and level with the specified ticket item. /// @@ -169,34 +172,42 @@ private async ValueTask ConsumeTicketItemAsync(Item? ticketItem, Player player) private bool CheckTicketItem(MiniGameDefinition miniGameDefinition, Player player, byte gameTicketInventoryIndex, out Item? ticketItem) { + ticketItem = null; + if (miniGameDefinition.TicketItem is not { } ticketItemDefinition) { - ticketItem = null; return true; } - ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); - if (ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel) + if (gameTicketInventoryIndex != UndefinedTicketSlot) { - return true; + ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); + if (this.IsValidTicket(ticketItem, ticketItemDefinition, miniGameDefinition)) + { + return true; + } } - var candidateTicketItems = player.Inventory?.FindItemsByDefinition(ticketItemDefinition); - if (candidateTicketItems is not null && candidateTicketItems.Any()) + foreach (var candidateItem in player.Inventory?.FindItemsByDefinition(ticketItemDefinition) ?? Enumerable.Empty()) { - foreach (var candidateTicketItem in candidateTicketItems) + if (this.IsValidTicket(candidateItem, ticketItemDefinition, miniGameDefinition)) { - ticketItem = candidateTicketItem; - if (ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel) - { - return true; - } + ticketItem = candidateItem; + return true; } } return false; } + private bool IsValidTicket(Item? ticketItem, ItemDefinition requiredItemDefinition, MiniGameDefinition miniGameDefinition) + { + return ticketItem is not null + && requiredItemDefinition.Equals(ticketItem.Definition) + && ticketItem.Durability > 0 + && ticketItem.Level == miniGameDefinition.TicketItemLevel; + } + private bool CheckEntranceFee(MiniGameDefinition miniGameDefinition, Player player, out int entranceFee) { entranceFee = Math.Max(miniGameDefinition.EntranceFee, 0); diff --git a/src/GameLogic/Storage.cs b/src/GameLogic/Storage.cs index 99d85eff1..6f53ccc51 100644 --- a/src/GameLogic/Storage.cs +++ b/src/GameLogic/Storage.cs @@ -267,7 +267,7 @@ public IEnumerable FindItemsByDefinition(ItemDefinition definition) { var primaryMatches = this.ItemArray .Where(i => i != null && i.Definition == definition) - .Cast(); + .Select(i => i!); var extensionMatches = this.Extensions? .SelectMany(extension => extension.FindItemsByDefinition(definition))