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