diff --git a/src/GameLogic/IStorage.cs b/src/GameLogic/IStorage.cs index 86e20dd66..03d22060d 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 items that matches the given definition. + /// + /// The item definition to be searched. + /// 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 7d6aa58b2..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,14 +172,40 @@ 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); - return ticketItemDefinition.Equals(ticketItem?.Definition) && ticketItem.Durability > 0 && ticketItem.Level == miniGameDefinition.TicketItemLevel; + if (gameTicketInventoryIndex != UndefinedTicketSlot) + { + ticketItem = player.Inventory?.GetItem(gameTicketInventoryIndex); + if (this.IsValidTicket(ticketItem, ticketItemDefinition, miniGameDefinition)) + { + return true; + } + } + + foreach (var candidateItem in player.Inventory?.FindItemsByDefinition(ticketItemDefinition) ?? Enumerable.Empty()) + { + if (this.IsValidTicket(candidateItem, ticketItemDefinition, miniGameDefinition)) + { + 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) diff --git a/src/GameLogic/Storage.cs b/src/GameLogic/Storage.cs index 0d1bced8c..6f53ccc51 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,20 @@ public async ValueTask TryTakeAllAsync(IStorage anotherStorage) return extension?.GetItem(inventorySlot); } + /// + public IEnumerable FindItemsByDefinition(ItemDefinition definition) + { + var primaryMatches = this.ItemArray + .Where(i => i != null && i.Definition == definition) + .Select(i => i!); + + var extensionMatches = this.Extensions? + .SelectMany(extension => extension.FindItemsByDefinition(definition)) + ?? Enumerable.Empty(); + + return primaryMatches.Concat(extensionMatches); + } + /// public virtual async ValueTask RemoveItemAsync(Item item) {