diff --git a/EOLib/Domain/Notifiers/IUserInterfaceNotifier.cs b/EOLib/Domain/Notifiers/IUserInterfaceNotifier.cs new file mode 100644 index 000000000..e12bc6e8d --- /dev/null +++ b/EOLib/Domain/Notifiers/IUserInterfaceNotifier.cs @@ -0,0 +1,16 @@ +using AutomaticTypeMapper; +using System.Collections.Generic; + +namespace EOLib.Domain.Notifiers +{ + public interface IUserInterfaceNotifier + { + void NotifyMessageDialog(string title, IReadOnlyList messages); + } + + [AutoMappedType] + public class NoOpUserInterfaceNotifier : IUserInterfaceNotifier + { + public void NotifyMessageDialog(string title, IReadOnlyList messages) { } + } +} diff --git a/EOLib/PacketHandlers/Message/MessageAcceptHandler.cs b/EOLib/PacketHandlers/Message/MessageAcceptHandler.cs new file mode 100644 index 000000000..c3f23458e --- /dev/null +++ b/EOLib/PacketHandlers/Message/MessageAcceptHandler.cs @@ -0,0 +1,42 @@ +using AutomaticTypeMapper; +using EOLib.Domain.Login; +using EOLib.Domain.Notifiers; +using EOLib.Net; +using EOLib.Net.Handlers; +using System.Collections.Generic; + +namespace EOLib.PacketHandlers.Message +{ + /// + /// Shows a message dialog (ScrollingListDialogSize.Large, ScrollingListDialog.ListItemStyle.Small) + /// + [AutoMappedType] + public class MessageAcceptHandler : InGameOnlyPacketHandler + { + private readonly IEnumerable _userInterfaceNotifiers; + + public override PacketFamily Family => PacketFamily.Message; + + public override PacketAction Action => PacketAction.Accept; + + public MessageAcceptHandler(IPlayerInfoProvider playerInfoProvider, + IEnumerable userInterfaceNotifiers) + : base(playerInfoProvider) + { + _userInterfaceNotifiers = userInterfaceNotifiers; + } + + public override bool HandlePacket(IPacket packet) + { + var title = packet.ReadBreakString(); + var messages = new List(); + while (packet.ReadPosition < packet.Length) + messages.Add(packet.ReadBreakString()); + + foreach (var notifier in _userInterfaceNotifiers) + notifier.NotifyMessageDialog(title, messages); + + return true; + } + } +} diff --git a/EndlessClient/Dialogs/Actions/InGameDialogActions.cs b/EndlessClient/Dialogs/Actions/InGameDialogActions.cs index 083c46e00..37521c394 100644 --- a/EndlessClient/Dialogs/Actions/InGameDialogActions.cs +++ b/EndlessClient/Dialogs/Actions/InGameDialogActions.cs @@ -7,6 +7,8 @@ using EOLib.Domain.Interact.Skill; using Optional; using System; +using System.Collections.Generic; +using System.Linq; namespace EndlessClient.Dialogs.Actions { @@ -26,6 +28,7 @@ public class InGameDialogActions : IInGameDialogActions private readonly IBankAccountDialogFactory _bankAccountDialogFactory; private readonly ISkillmasterDialogFactory _skillmasterDialogFactory; private readonly IBardDialogFactory _bardDialogFactory; + private readonly IScrollingListDialogFactory _scrollingListDialogFactory; private readonly ISfxPlayer _sfxPlayer; private readonly IShopDialogFactory _shopDialogFactory; private readonly IQuestDialogFactory _questDialogFactory; @@ -45,6 +48,7 @@ public class InGameDialogActions : IInGameDialogActions IBankAccountDialogFactory bankAccountDialogFactory, ISkillmasterDialogFactory skillmasterDialogFactory, IBardDialogFactory bardDialogFactory, + IScrollingListDialogFactory scrollingListDialogFactory, ISfxPlayer sfxPlayer) { _friendIgnoreListDialogFactory = friendIgnoreListDialogFactory; @@ -60,6 +64,7 @@ public class InGameDialogActions : IInGameDialogActions _bankAccountDialogFactory = bankAccountDialogFactory; _skillmasterDialogFactory = skillmasterDialogFactory; _bardDialogFactory = bardDialogFactory; + _scrollingListDialogFactory = scrollingListDialogFactory; _sfxPlayer = sfxPlayer; _shopDialogFactory = shopDialogFactory; _questDialogFactory = questDialogFactory; @@ -247,6 +252,35 @@ public void ShowBardDialog() }); } + public void ShowMessageDialog(string title, IReadOnlyList messages) + { + _activeDialogRepository.MessageDialog.MatchNone(() => + { + var dlg = _scrollingListDialogFactory.Create(ScrollingListDialogSize.Large); + dlg.DialogClosed += (_, _) => _activeDialogRepository.MessageDialog = Option.None(); + + dlg.ListItemType = ListDialogItem.ListItemStyle.Small; + dlg.Buttons = ScrollingListDialogButtons.Cancel; + dlg.Title = title; + + var _75spaces = new string(Enumerable.Repeat(' ', 75).ToArray()); + var items = messages + // BU hack - assume that 75 spaces or more indicates an extra line break + .Select(x => x.Replace(_75spaces, " \n")) + // BU hack - assume that 3 spaces or more indicates extra padding and should split the message into multiple lines + .SelectMany(x => x.Split(" ", StringSplitOptions.RemoveEmptyEntries)) + .Select(x => new ListDialogItem(dlg, ListDialogItem.ListItemStyle.Small) { PrimaryText = x == "\n" ? string.Empty : x.Trim() }).ToList(); + + dlg.SetItemList(items); + + _activeDialogRepository.MessageDialog = Option.Some(dlg); + + UseDefaultDialogSounds(dlg); + + dlg.Show(); + }); + } + private void UseDefaultDialogSounds(ScrollingListDialog dialog) { UseDefaultDialogSounds((BaseEODialog)dialog); @@ -298,5 +332,7 @@ public interface IInGameDialogActions void ShowSkillmasterDialog(); void ShowBardDialog(); + + void ShowMessageDialog(string title, IReadOnlyList messages); } } diff --git a/EndlessClient/Dialogs/ActiveDialogRepository.cs b/EndlessClient/Dialogs/ActiveDialogRepository.cs index 7b2405386..cca28ff36 100644 --- a/EndlessClient/Dialogs/ActiveDialogRepository.cs +++ b/EndlessClient/Dialogs/ActiveDialogRepository.cs @@ -31,6 +31,8 @@ public interface IActiveDialogProvider : IDisposable Option BardDialog { get; } + Option MessageDialog { get; } + IReadOnlyList> ActiveDialogs { get; } } @@ -58,6 +60,8 @@ public interface IActiveDialogRepository : IDisposable Option BardDialog { get; set; } + Option MessageDialog { get; set; } + IReadOnlyList> ActiveDialogs { get; } } @@ -86,6 +90,8 @@ public class ActiveDialogRepository : IActiveDialogRepository, IActiveDialogProv public Option BardDialog { get; set; } + public Option MessageDialog { get; set; } + IReadOnlyList> ActiveDialogs { get @@ -103,6 +109,7 @@ IReadOnlyList> ActiveDialogs BankAccountDialog.Map(d => (IXNADialog)d), SkillmasterDialog.Map(d => (IXNADialog)d), BardDialog.Map(d => (IXNADialog)d), + MessageDialog.Map(d => (IXNADialog)d), }.ToList(); } } @@ -127,6 +134,7 @@ public void Dispose() BankAccountDialog = Option.None(); SkillmasterDialog = Option.None(); BardDialog = Option.None(); + MessageDialog = Option.None(); } } } diff --git a/EndlessClient/Dialogs/Factories/ScrollingListDialogFactory.cs b/EndlessClient/Dialogs/Factories/ScrollingListDialogFactory.cs new file mode 100644 index 000000000..93d7ec493 --- /dev/null +++ b/EndlessClient/Dialogs/Factories/ScrollingListDialogFactory.cs @@ -0,0 +1,30 @@ +using AutomaticTypeMapper; +using EndlessClient.Dialogs.Services; +using EOLib.Graphics; + +namespace EndlessClient.Dialogs.Factories +{ + [AutoMappedType] + public class ScrollingListDialogFactory : IScrollingListDialogFactory + { + private readonly INativeGraphicsManager _nativeGraphicsManager; + private readonly IEODialogButtonService _dialogButtonService; + + public ScrollingListDialogFactory(INativeGraphicsManager nativeGraphicsManager, + IEODialogButtonService dialogButtonService) + { + _nativeGraphicsManager = nativeGraphicsManager; + _dialogButtonService = dialogButtonService; + } + + public ScrollingListDialog Create(ScrollingListDialogSize size) + { + return new ScrollingListDialog(_nativeGraphicsManager, _dialogButtonService, size); + } + } + + public interface IScrollingListDialogFactory + { + ScrollingListDialog Create(ScrollingListDialogSize size); + } +} diff --git a/EndlessClient/HUD/ServerMessageActions.cs b/EndlessClient/HUD/ServerMessageActions.cs new file mode 100644 index 000000000..ff63e3f95 --- /dev/null +++ b/EndlessClient/HUD/ServerMessageActions.cs @@ -0,0 +1,23 @@ +using AutomaticTypeMapper; +using EndlessClient.Dialogs.Actions; +using EOLib.Domain.Notifiers; +using System.Collections.Generic; + +namespace EndlessClient.HUD +{ + [AutoMappedType] + public class ServerMessageActions : IUserInterfaceNotifier + { + private readonly IInGameDialogActions _inGameDialogActions; + + public ServerMessageActions(IInGameDialogActions inGameDialogActions) + { + _inGameDialogActions = inGameDialogActions; + } + + public void NotifyMessageDialog(string title, IReadOnlyList messages) + { + _inGameDialogActions.ShowMessageDialog(title, messages); + } + } +}