//
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
namespace MUnique.OpenMU.GameLogic.PlayerActions;
using MUnique.OpenMU.GameLogic.MiniGames;
using MUnique.OpenMU.GameLogic.NPC;
using MUnique.OpenMU.GameLogic.PlayerActions.Quests;
using MUnique.OpenMU.GameLogic.PlugIns;
using MUnique.OpenMU.GameLogic.Views;
using MUnique.OpenMU.GameLogic.Views.Guild;
using MUnique.OpenMU.GameLogic.Views.NPC;
using MUnique.OpenMU.GameLogic.Views.Quest;
using MUnique.OpenMU.GameLogic.Views.Vault;
using MUnique.OpenMU.Interfaces;
///
/// Action to talk to a npc.
///
public class TalkNpcAction
{
///
/// Talks to the specified Monster.
///
/// The player.
/// The Monster.
public async ValueTask TalkToNpcAsync(Player player, NonPlayerCharacter npc)
{
var npcStats = npc.Definition;
if (this.AdvancePlayerState(npc))
{
if (!await player.PlayerState.TryAdvanceToAsync(PlayerState.NpcDialogOpened).ConfigureAwait(false))
{
return;
}
}
else
{
if (player.PlayerState.CurrentState != PlayerState.EnteredWorld)
{
return;
}
}
player.OpenedNpc = npc;
if (npcStats.MerchantStore != null && npcStats.MerchantStore.Items.Count > 0)
{
await Task.Delay(500).ConfigureAwait(false);
await player.InvokeViewPlugInAsync(p => p.OpenNpcWindowAsync(npcStats.NpcWindow != NpcWindow.Undefined ? npcStats.NpcWindow : NpcWindow.Merchant)).ConfigureAwait(false);
await player.InvokeViewPlugInAsync(p => p.ShowMerchantStoreItemListAsync(npcStats.MerchantStore.Items, StoreKind.Normal)).ConfigureAwait(false);
}
else
{
await this.ShowDialogOfOpenedNpcAsync(player).ConfigureAwait(false);
}
}
// ADAMU-CUSTOM: remote NPC — NPC'ye definition number ile her yerden konuş (mobile "NPC list").
///
/// Talks to an NPC identified by its definition , regardless of the
/// player's current position or map (no proximity required). Used by the mobile "NPC list" to
/// open a merchant store / chaos machine / vault from anywhere. Loads the NPC's home map if it
/// isn't loaded yet, then reuses the normal flow.
///
/// The player.
/// The NPC definition number.
public async ValueTask TalkToNpcByNumberAsync(Player player, short npcNumber)
{
var gameContext = player.GameContext;
// NPC'nin spawn olduğu haritayı bul, (gerekiyorsa) yükle ve instance'ı al.
foreach (var mapDef in gameContext.Configuration.Maps)
{
if (mapDef.MonsterSpawns is null
|| !mapDef.MonsterSpawns.Any(s => s.MonsterDefinition?.Number == npcNumber))
{
continue;
}
var map = await gameContext.GetMapAsync((ushort)mapDef.Number).ConfigureAwait(false);
if (map?.GetNpcByNumber(npcNumber) is { } npc)
{
await this.TalkToNpcAsync(player, npc).ConfigureAwait(false);
return;
}
}
}
// ADAMU-CUSTOM end
///
/// Gets a value indicating whether this action advances the player state to .
///
/// The NPC whose dialog is about to be opened.
/// A value indicating whether this action advances the player state to .
protected virtual bool AdvancePlayerState(NonPlayerCharacter npc) => true;
private async ValueTask ShowDialogOfOpenedNpcAsync(Player player)
{
var npcStats = player.OpenedNpc!.Definition;
switch (npcStats.NpcWindow)
{
case NpcWindow.Undefined:
var eventArgs = new NpcTalkEventArgs();
player.GameContext.PlugInManager.GetPlugInPoint()?.PlayerTalksToNpcAsync(player, player.OpenedNpc, eventArgs);
if (!eventArgs.HasBeenHandled)
{
if (player.CurrentMiniGame is BloodCastleContext bloodCastle && player.OpenedNpc.Definition.Number == 232)
{
await bloodCastle.TalkToNpcArchangelAsync(player).ConfigureAwait(false);
}
else
{
await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.TalkingNotImplementedFormat), npcStats.Number, npcStats.Designation).ConfigureAwait(false);
}
await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
}
else if (!eventArgs.LeavesDialogOpen)
{
player.OpenedNpc = null;
await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
}
else
{
// Leaves dialog opened, so leave the state as it is.
}
break;
case NpcWindow.VaultStorage:
player.Account!.Vault ??= player.PersistenceContext.CreateNew();
var warehouseSize = player.Account.IsVaultExtended ? InventoryConstants.WarehouseSize * 2 : InventoryConstants.WarehouseSize;
player.Vault = new Storage(warehouseSize, player.Account.Vault);
await player.InvokeViewPlugInAsync(p => p.ShowVaultAsync()).ConfigureAwait(false);
break;
case NpcWindow.GuildMaster:
if (await this.IsPlayedAllowedToCreateGuildAsync(player).ConfigureAwait(false))
{
await player.InvokeViewPlugInAsync(p => p.ShowGuildMasterDialogAsync()).ConfigureAwait(false);
}
else
{
player.OpenedNpc = null;
await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
}
break;
case NpcWindow.LegacyQuest:
await this.ShowLegacyQuestDialogAsync(player).ConfigureAwait(false);
break;
case NpcWindow.DoorkeeperTitusDuelWatch:
await player.InvokeViewPlugInAsync(p => p.OpenNpcWindowAsync(npcStats.NpcWindow)).ConfigureAwait(false);
_ = Task.Run(async () =>
{
while (player.IsActive() && player.OpenedNpc?.Definition.NpcWindow == NpcWindow.DoorkeeperTitusDuelWatch)
{
await player.GameContext.DuelRoomManager.ShowRoomsAsync(player).ConfigureAwait(false);
await Task.Delay(5000).ConfigureAwait(false);
}
});
break;
case NpcWindow.ChaosMachine:
case NpcWindow.RemoveJohOption:
await player.InvokeViewPlugInAsync(p => p.OpenNpcWindowAsync(npcStats.NpcWindow)).ConfigureAwait(false);
break;
default:
await player.InvokeViewPlugInAsync(p => p.OpenNpcWindowAsync(npcStats.NpcWindow)).ConfigureAwait(false);
break;
}
if (npcStats.ItemCraftings.Any())
{
player.BackupInventory = new BackupItemStorage(player.Inventory!.ItemStorage);
}
}
private async ValueTask ShowLegacyQuestDialogAsync(Player player)
{
var quests = player.OpenedNpc!.Definition.Quests
.Where(q => q.QualifiedCharacter is null || Equals(q.QualifiedCharacter, player.SelectedCharacter!.CharacterClass));
if (!quests.Any())
{
await player.InvokeViewPlugInAsync(p => p.ShowMessageOfObjectAsync("I have no quests for you.", player.OpenedNpc)).ConfigureAwait(false);
player.OpenedNpc = null;
await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
return;
}
if (quests.All(quest => quest.MinimumCharacterLevel > player.Level))
{
await player.InvokeViewPlugInAsync(p => p.ShowMessageOfObjectAsync(
"I have nothing to do for you. Come back with more power.",
player.OpenedNpc)).ConfigureAwait(false);
player.OpenedNpc = null;
await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
return;
}
var maxQuestNumber = quests.Max(q => q.Number);
var questGroup = quests.FirstOrDefault(q => q.Number == maxQuestNumber)?.Group;
var questState = player.GetQuestState(questGroup ?? 0);
if (questState?.LastFinishedQuest?.Number >= maxQuestNumber)
{
await player.InvokeViewPlugInAsync(p => p.ShowMessageOfObjectAsync(
"I have nothing to do for you. You solved all my quests already.",
player.OpenedNpc)).ConfigureAwait(false);
player.OpenedNpc = null;
await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false);
return;
}
await player.InvokeViewPlugInAsync(p => p.ShowAsync()).ConfigureAwait(false);
}
private async ValueTask IsPlayedAllowedToCreateGuildAsync(Player player)
{
if (player.Level < 100)
{
await player.InvokeViewPlugInAsync(p => p.ShowMessageOfObjectAsync("Your level should be at least level 100", player.OpenedNpc!)).ConfigureAwait(false);
return false;
}
if (player.GuildStatus != null)
{
await player.InvokeViewPlugInAsync(p => p.ShowMessageOfObjectAsync("You already belong to a guild", player.OpenedNpc!)).ConfigureAwait(false);
return false;
}
return true;
}
}