Skip to content
8 changes: 8 additions & 0 deletions src/DataModel/Configuration/Items/ItemDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ public partial class ItemDefinition
/// </summary>
public bool IsBoundToCharacter { get; set; }

/// <summary>
/// Gets or sets a value indicating whether items of this kind are quest items.
/// Quest items may additionally only be picked up from the ground by a character
/// which has an active quest that requires them, even if the character is a member
/// of the party which killed the monster that dropped it.
/// </summary>
public bool IsQuestItem { get; set; }

/// <summary>
/// Gets or sets the storage limit per character which is checked on pick-up.
/// A value of 0 means, that there is no limit.
Expand Down
12 changes: 11 additions & 1 deletion src/GameLogic/DroppedItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,17 @@ private async void DisposeAndDelete(object? state)
}
}

private bool IsPlayerAnOwner(Player player)
/// <summary>
/// Gets a value indicating whether the owner-pickup priority period is still active.
/// </summary>
public bool IsOwnerPickupPriorityActive => DateTime.UtcNow < this._dropTimestamp.Add(TimeUntilDropIsFree);

/// <summary>
/// Determines whether the specified player is an owner of this dropped item.
/// </summary>
/// <param name="player">The player.</param>
/// <returns><c>true</c> if the player is an owner; otherwise, <c>false</c>.</returns>
public bool IsPlayerAnOwner(Player player)
{
return this._owners?.Contains(player) ?? true;
}
Expand Down
39 changes: 39 additions & 0 deletions src/GameLogic/PlayerActions/Items/PickupItemAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace MUnique.OpenMU.GameLogic.PlayerActions.Items;

using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.DataModel.Configuration.Quests;
using MUnique.OpenMU.GameLogic.Views;
using MUnique.OpenMU.GameLogic.Views.Inventory;
using MUnique.OpenMU.Interfaces;
Expand Down Expand Up @@ -61,6 +62,12 @@ public async ValueTask PickupItemAsync(Player player, ushort dropId)
}
}

private static async ValueTask<(bool Success, Item? StackTarget)> RejectAsync(Player player, string messageKey)
{
await player.ShowLocalizedBlueMessageAsync(messageKey).ConfigureAwait(false);
return (false, null);
}

private static bool CanPickup(Player player, ILocateable droppedLocateable)
{
if (!player.IsAlive)
Expand Down Expand Up @@ -92,6 +99,21 @@ private static bool IsLimitReached(Player player, ItemDefinition? itemDefinition
return player.Inventory?.Items.Count(item => item.Definition == itemDefinition) >= itemDefinition.StorageLimitPerCharacter;
}

private static bool PlayerHasActiveQuestForItem(Player player, Item item)
{
var questStates = player.SelectedCharacter?.QuestStates;
if (questStates is null)
{
return false;
}

return questStates
.Select(q => q.ActiveQuest)
.OfType<QuestDefinition>()
.SelectMany(q => q.RequiredItems)
.Any(r => r.Item == item.Definition && (r.DropItemGroup?.ItemLevel is null || r.DropItemGroup.ItemLevel == item.Level));
Comment thread
eduardosmaniotto marked this conversation as resolved.
}
Comment thread
eduardosmaniotto marked this conversation as resolved.

private static async ValueTask<(bool Success, Item? StackTarget)> TryPickupItemAsync(Player player, DroppedItem droppedItem)
{
if (!CanPickup(player, droppedItem))
Expand Down Expand Up @@ -119,6 +141,23 @@ private static bool IsLimitReached(Player player, ItemDefinition? itemDefinition
return (false, null);
}

if (droppedItem.Item.Definition?.IsQuestItem == true
&& !PlayerHasActiveQuestForItem(player, droppedItem.Item))
{
return await RejectAsync(player, nameof(PlayerMessage.ItemDoesNotBelongToYou)).ConfigureAwait(false);
}

if (droppedItem.Item.Definition?.IsBoundToCharacter == true
&& !droppedItem.IsPlayerAnOwner(player))
{
return await RejectAsync(player, nameof(PlayerMessage.ItemDoesNotBelongToYou)).ConfigureAwait(false);
}

if (!droppedItem.IsPlayerAnOwner(player) && droppedItem.IsOwnerPickupPriorityActive)
{
return await RejectAsync(player, nameof(PlayerMessage.ItemDoesNotBelongToYou)).ConfigureAwait(false);
}

var result = await droppedItem.TryPickUpByAsync(player).ConfigureAwait(false);
if (result.Success)
{
Expand Down
11 changes: 10 additions & 1 deletion src/GameLogic/Properties/PlayerMessage.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/GameLogic/Properties/PlayerMessage.resx
Original file line number Diff line number Diff line change
Expand Up @@ -699,4 +699,7 @@
<data name="PkClearYouAreNotPlayerKiller" xml:space="preserve">
<value>You are not a Player Killer.</value>
</data>
<data name="ItemDoesNotBelongToYou" xml:space="preserve">
<value>This item doesn't belong to you.</value>
</data>
</root>
Loading