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)
{