// // 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; } }