diff --git a/src/GameLogic/GameMap.cs b/src/GameLogic/GameMap.cs index 159afdc..2a77408 100644 --- a/src/GameLogic/GameMap.cs +++ b/src/GameLogic/GameMap.cs @@ -103,6 +103,23 @@ public class GameMap return result; } + // ADAMU-CUSTOM: remote NPC ("NPC list" mobile feature) — spawn olmuş NPC'yi definition number ile bul. + /// + /// Gets a spawned non-player-character on this map by its definition number. + /// Used to open an NPC's window remotely (mobile "NPC list" feature), independent + /// of the player's position. + /// + /// The . + /// The first matching non-player-character, or null. + public NonPlayerCharacter? GetNpcByNumber(short number) + { + return this._objectsInMap.Values + .OfType() + .FirstOrDefault(npc => npc.Definition.Number == number); + } + + // ADAMU-CUSTOM end + /// /// Gets the attackables in range of the specified coordinates. /// diff --git a/src/GameLogic/PlayerActions/TalkNpcAction.cs b/src/GameLogic/PlayerActions/TalkNpcAction.cs index 05009e2..ca24292 100644 --- a/src/GameLogic/PlayerActions/TalkNpcAction.cs +++ b/src/GameLogic/PlayerActions/TalkNpcAction.cs @@ -57,6 +57,39 @@ public class TalkNpcAction } } + // 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 . /// diff --git a/src/GameServer/MessageHandler/TalkNpcHandlerPlugInBase.cs b/src/GameServer/MessageHandler/TalkNpcHandlerPlugInBase.cs index b5eb052..d2efabc 100644 --- a/src/GameServer/MessageHandler/TalkNpcHandlerPlugInBase.cs +++ b/src/GameServer/MessageHandler/TalkNpcHandlerPlugInBase.cs @@ -29,6 +29,17 @@ internal abstract class TalkNpcHandlerPlugInBase : IPacketHandlerPlugIn public async ValueTask HandlePacketAsync(Player player, Memory packet) { TalkToNpcRequest message = packet; + + // ADAMU-CUSTOM: remote NPC ("NPC list" mobile feature). Object id'leri her zaman <= 0x7FFF + // (GameMap IdGenerator), o yüzden yüksek bit serbest marker. Set edilmişse alt 15 bit bir + // NPC *definition number*'dır — o NPC'nin penceresini konum/harita bağımsız aç. + if ((message.NpcId & 0x8000) != 0) + { + await this.TalkNpcAction.TalkToNpcByNumberAsync(player, (short)(message.NpcId & 0x7FFF)).ConfigureAwait(false); + return; + } + // ADAMU-CUSTOM end + if (player.CurrentMap?.GetObject(message.NpcId) is NonPlayerCharacter npc) { await this.TalkNpcAction.TalkToNpcAsync(player, npc).ConfigureAwait(false);