diff --git a/EOLib/PacketHandlers/NPC/NPCDialogHandler.cs b/EOLib/PacketHandlers/NPC/NPCDialogHandler.cs
new file mode 100644
index 000000000..072f2c527
--- /dev/null
+++ b/EOLib/PacketHandlers/NPC/NPCDialogHandler.cs
@@ -0,0 +1,71 @@
+using AutomaticTypeMapper;
+using EOLib.Domain.Chat;
+using EOLib.Domain.Login;
+using EOLib.Domain.Map;
+using EOLib.Domain.Notifiers;
+using EOLib.IO.Repositories;
+using EOLib.Net;
+using EOLib.Net.Handlers;
+using Optional;
+using Optional.Collections;
+using System.Collections.Generic;
+
+namespace EOLib.PacketHandlers.NPC
+{
+ ///
+ /// Special dialog packet for NPC speech. Sent by GameServer when the priest talks.
+ ///
+ [AutoMappedType]
+ public class NPCDialogHandler : InGameOnlyPacketHandler
+ {
+ private readonly ICurrentMapStateRepository _currentMapStateRepository;
+ private readonly IENFFileProvider _enfFileProvider;
+ private readonly IChatRepository _chatRepository;
+ private readonly IEnumerable _npcActionNotifiers;
+
+ public override PacketFamily Family => PacketFamily.NPC;
+
+ public override PacketAction Action => PacketAction.Dialog;
+
+ public NPCDialogHandler(IPlayerInfoProvider playerInfoProvider,
+ ICurrentMapStateRepository currentMapStateRepository,
+ IENFFileProvider enfFileProvider,
+ IChatRepository chatRepository,
+ IEnumerable npcActionNotifiers)
+ : base(playerInfoProvider)
+ {
+ _currentMapStateRepository = currentMapStateRepository;
+ _enfFileProvider = enfFileProvider;
+ _chatRepository = chatRepository;
+ _npcActionNotifiers = npcActionNotifiers;
+ }
+
+ // note: this is the same implementation as NPCPlayerHandler::HandleNPCTalk
+ public override bool HandlePacket(IPacket packet)
+ {
+ var index = packet.ReadShort();
+ var message = packet.ReadEndString();
+
+ var npc = GetNPC(index);
+ npc.Match(
+ some: n =>
+ {
+ var npcData = _enfFileProvider.ENFFile[n.ID];
+
+ var chatData = new ChatData(ChatTab.Local, npcData.Name, message, ChatIcon.Note, filter: false);
+ _chatRepository.AllChat[ChatTab.Local].Add(chatData);
+
+ foreach (var notifier in _npcActionNotifiers)
+ notifier.ShowNPCSpeechBubble(index, message);
+ },
+ none: () => _currentMapStateRepository.UnknownNPCIndexes.Add(index));
+
+ return true;
+ }
+
+ private Option GetNPC(int index)
+ {
+ return _currentMapStateRepository.NPCs.SingleOrNone(n => n.Index == index);
+ }
+ }
+}