diff --git a/EOLib.Localization/DialogResourceID.cs b/EOLib.Localization/DialogResourceID.cs index c39c4a517..1e7161dc2 100644 --- a/EOLib.Localization/DialogResourceID.cs +++ b/EOLib.Localization/DialogResourceID.cs @@ -72,7 +72,8 @@ public enum DialogResourceID TRADE_OTHER_PLAYER_TRICK_YOU = 126, GUILD_CREATE_TAG_FIELD_EMPTY = 128, GUILD_CREATE_NAME_FIELD_EMPTY = 130, - GUILD_CREATE_TAG_TOO_SHORT = 132, + GUILD_WRONG_INPUT = 132, + GUILD_CREATE_TAG_TOO_SHORT = 133, GUILD_CREATE_NAME_TOO_SHORT = 134, GUILD_CREATE_NAME_NOT_APPROVED = 136, GUILD_CREATE_NO_CANDIDATES = 138, diff --git a/EOLib.Localization/EOResourceID.cs b/EOLib.Localization/EOResourceID.cs index a2f112fb2..66fea6a81 100644 --- a/EOLib.Localization/EOResourceID.cs +++ b/EOLib.Localization/EOResourceID.cs @@ -193,21 +193,39 @@ public enum EOResourceID GUILD_WITHDRAW_FUNDS_FROM_GUILD = 201, GUILD_REMOVE_MEMBER = 202, GUILD_REMOVE_A_MEMBER_FROM_GUILD = 203, + GUILD_JOINING_A_GUILD_IS_FREE = 205, + GUILD_PLEASE_CONSIDER_CAREFULLY = 207, GUILD_DESCRIPTION = 219, + GUILD_DO_YOU_ACCEPT = 223, + GUILD_YOUR_ACCOUNT_WILL_BE_CHARGED = 225, GUILD_PLEASE_CONSIDER_CAREFULLY_RECRUIT = 226, + GUILD_TO_VIEW_INFORMATION_ABOUT_A_GUILD_ENTER_ITS_TAG = 227, + + GUILD_SIGNUP_DATE = 228, + + GUILD_RANKING_SYSTEM = 230, + GUILD_LEADERS = 231, + GUILD_ASSIGN_RANK_TO_MEMBER = 233, GUILD_CURRENT_DESCRIPTION = 234, GUILD_CLICK_HERE_TO_CHANGE_THE_DESCRIPTION = 235, + GUILD_BANK_STATUS = 244, + SETTING_KEYBOARD_ENGLISH = 253, SETTING_KEYBOARD_DUTCH = 254, SETTING_KEYBOARD_SWEDISH = 255, SETTING_KEYBOARD_AZERTY = 256, + GUILD_LOOK_UP = 267, + GUILD_VIEW_DETAILS = 268, + GUILD_MEMBERLIST = 269, + GUILD_VIEW_MEMBERS = 270, + BOARD_TOWN_BOARD = 271, BOARD_TOWN_BOARD_NOW_VIEWED = 272, BOARD_LOADING_MESSAGE = 273, diff --git a/EOLib/Domain/Interact/Guild/GuildInfo.cs b/EOLib/Domain/Interact/Guild/GuildInfo.cs new file mode 100644 index 000000000..082f6bf83 --- /dev/null +++ b/EOLib/Domain/Interact/Guild/GuildInfo.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Amadevus.RecordGenerator; +using Moffat.EndlessOnline.SDK.Protocol.Net.Server; + +namespace EOLib.Domain.Interact.Guild +{ + [Record(Features.Default | Features.ObjectEquals | Features.EquatableEquals)] + public sealed partial class GuildInfo + { + public string Name { get; } + + public string Tag { get; } + + public DateTime CreateDate { get; } + + public string Description { get; } + + public string Wealth { get; } + + public IReadOnlyList Ranks { get; } + + public IReadOnlyList Staff { get; } + + public static GuildInfo FromPacket(GuildReportServerPacket packet) + { + return new Builder + { + Name = packet.Name, + Tag = packet.Tag, + CreateDate = DateTime.TryParse(packet.CreateDate, out var created) ? created : new DateTime(0, DateTimeKind.Utc), + Description = packet.Description, + Wealth = packet.Wealth, + Ranks = packet.Ranks, + Staff = packet.Staff + }.ToImmutable(); + } + + } +} diff --git a/EOLib/Domain/Interact/Guild/GuildSessionRepository.cs b/EOLib/Domain/Interact/Guild/GuildSessionRepository.cs index 07fffd3d7..e17a2440f 100644 --- a/EOLib/Domain/Interact/Guild/GuildSessionRepository.cs +++ b/EOLib/Domain/Interact/Guild/GuildSessionRepository.cs @@ -1,4 +1,7 @@ -using AutomaticTypeMapper; +using System.Collections.Generic; +using AutomaticTypeMapper; +using Moffat.EndlessOnline.SDK.Protocol.Net.Server; +using Optional; namespace EOLib.Domain.Interact.Guild { @@ -7,6 +10,10 @@ public interface IGuildSessionProvider int SessionID { get; } string GuildDescription { get; } + + Option GuildInfo { get; } + + IReadOnlyList GuildMembers { get; } } public interface IGuildSessionRepository @@ -14,18 +21,31 @@ public interface IGuildSessionRepository int SessionID { get; set; } string GuildDescription { get; set; } + + OptionGuildInfo { get; set; } + + List GuildMembers { get; set; } } [AutoMappedType(IsSingleton = true)] public class GuildSessionRepository : IGuildSessionRepository, IGuildSessionProvider { public int SessionID { get; set; } + public string GuildDescription { get; set; } + public Option GuildInfo { get; set; } + + public List GuildMembers { get; set; } + + IReadOnlyList IGuildSessionProvider.GuildMembers => GuildMembers; + public GuildSessionRepository() { SessionID = 0; GuildDescription = ""; + GuildInfo = Option.None(); + GuildMembers = new List(); } } } diff --git a/EOLib/PacketHandlers/Guild/GuildReportHandler.cs b/EOLib/PacketHandlers/Guild/GuildReportHandler.cs new file mode 100644 index 000000000..978d3d716 --- /dev/null +++ b/EOLib/PacketHandlers/Guild/GuildReportHandler.cs @@ -0,0 +1,34 @@ +using AutomaticTypeMapper; +using EOLib.Domain.Interact.Guild; +using EOLib.Domain.Login; +using EOLib.Net.Handlers; +using Moffat.EndlessOnline.SDK.Protocol.Net; +using Moffat.EndlessOnline.SDK.Protocol.Net.Server; +using Optional; + +namespace EOLib.PacketHandlers.Guild +{ + [AutoMappedType] + + public class GuildReportHandler : InGameOnlyPacketHandler + { + private readonly IGuildSessionRepository _guildSessionRepository; + + public override PacketFamily Family => PacketFamily.Guild; + + public override PacketAction Action => PacketAction.Report; + + public GuildReportHandler(IPlayerInfoProvider playerInfoProvider, + IGuildSessionRepository guildSessionRepository) + : base(playerInfoProvider) + { + _guildSessionRepository = guildSessionRepository; + } + + public override bool HandlePacket(GuildReportServerPacket packet) + { + _guildSessionRepository.GuildInfo = Option.Some(GuildInfo.FromPacket(packet)); + return true; + } + } +} diff --git a/EOLib/PacketHandlers/Guild/GuildTakeHandler.cs b/EOLib/PacketHandlers/Guild/GuildTakeHandler.cs index a9fc86f82..2e02f7295 100644 --- a/EOLib/PacketHandlers/Guild/GuildTakeHandler.cs +++ b/EOLib/PacketHandlers/Guild/GuildTakeHandler.cs @@ -1,13 +1,9 @@ using AutomaticTypeMapper; -using EOLib.Domain.Interact; using EOLib.Domain.Interact.Guild; using EOLib.Domain.Login; -using EOLib.Domain.Map; using EOLib.Net.Handlers; using Moffat.EndlessOnline.SDK.Protocol.Net; using Moffat.EndlessOnline.SDK.Protocol.Net.Server; -using Optional; -using System.Collections.Generic; namespace EOLib.PacketHandlers.Guild { diff --git a/EOLib/PacketHandlers/Guild/GuildTellHandler.cs b/EOLib/PacketHandlers/Guild/GuildTellHandler.cs new file mode 100644 index 000000000..c2cd2b172 --- /dev/null +++ b/EOLib/PacketHandlers/Guild/GuildTellHandler.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using AutomaticTypeMapper; +using EOLib.Domain.Interact.Guild; +using EOLib.Domain.Login; +using EOLib.Net.Handlers; +using Moffat.EndlessOnline.SDK.Protocol.Net; +using Moffat.EndlessOnline.SDK.Protocol.Net.Server; + +namespace EOLib.PacketHandlers.Guild +{ + [AutoMappedType] + + public class GuildTellHandler : InGameOnlyPacketHandler + { + private readonly IGuildSessionRepository _guildSessionRepository; + + public override PacketFamily Family => PacketFamily.Guild; + + public override PacketAction Action => PacketAction.Tell; + + public GuildTellHandler(IPlayerInfoProvider playerInfoProvider, + IGuildSessionRepository guildSessionRepository) + : base(playerInfoProvider) + { + _guildSessionRepository = guildSessionRepository; + } + + public override bool HandlePacket(GuildTellServerPacket packet) + { + _guildSessionRepository.GuildMembers = new List(packet.Members); + return true; + } + } +} diff --git a/EndlessClient/Dialogs/GuildDialog.cs b/EndlessClient/Dialogs/GuildDialog.cs index 76e441834..75fa6443f 100644 --- a/EndlessClient/Dialogs/GuildDialog.cs +++ b/EndlessClient/Dialogs/GuildDialog.cs @@ -10,6 +10,8 @@ using EOLib.Graphics; using EOLib.Localization; using Microsoft.Xna.Framework; +using Moffat.EndlessOnline.SDK.Protocol.Net.Server; +using MonoGame.Extended.Input.InputListeners; using Optional; using XNAControls; @@ -20,7 +22,18 @@ public class GuildDialog : ScrollingListDialog private enum GuildDialogState { Initial, + + // Initial List Items + Information, + Administration, Management, + BankAccount, + + // Information List Items + Lookup, + ViewMembers, + + // Management List Items Modify, ManageRankings, AssignRank, @@ -36,8 +49,14 @@ private class State public static State Initial => new(GuildDialogState.Initial); + public static State Information => new(GuildDialogState.Information); + public static State Management => new(GuildDialogState.Management); + public static State GuildLookup => new(GuildDialogState.Lookup); + + public static State ViewMembers => new(GuildDialogState.ViewMembers); + public static State Modify => new(GuildDialogState.Modify); public static State ManageRankings => new(GuildDialogState.ManageRankings); @@ -57,6 +76,8 @@ private State(GuildDialogState dialogState) Buttons = ScrollingListDialogButtons.Cancel; break; case GuildDialogState.Modify: + case GuildDialogState.Lookup: + case GuildDialogState.ViewMembers: ListItemStyle = ListDialogItem.ListItemStyle.Small; break; } @@ -88,6 +109,9 @@ public override int GetHashCode() private readonly Stack _stateStack; private State _state; + + private HashSet _cachedMembers; + private Option _cachedGuildInfo; private Option _modifyGuildDescriptionListItem; public GuildDialog(INativeGraphicsManager nativeGraphicsManager, @@ -112,17 +136,14 @@ public GuildDialog(INativeGraphicsManager nativeGraphicsManager, _contentProvider = contentProvider; _stateStack = new Stack(); - _modifyGuildDescriptionListItem = Option.None(); + _cachedMembers = new HashSet(); _stateTransitions = new Dictionary { { State.Initial, SetupInitialState }, + { State.Information, SetupInformationState }, { State.Management, SetupManagementState }, - { State.Modify, SetupModifyState }, - { State.ManageRankings, () => { } }, - { State.AssignRank, () => { } }, - { State.RemoveMember, () => { } }, - { State.Disband, () => { } }, + { State.Modify, SetupModifyState } }; SetState(State.Initial); @@ -134,18 +155,80 @@ public GuildDialog(INativeGraphicsManager nativeGraphicsManager, protected override void OnUpdateControl(GameTime gameTime) { - if (_state.DialogState == GuildDialogState.Modify) + switch (_state.DialogState) { - _modifyGuildDescriptionListItem.MatchSome(item => - { - if (item.PrimaryText != _guildSessionProvider.GuildDescription) + case GuildDialogState.Modify: + _modifyGuildDescriptionListItem.MatchSome(item => { - item.PrimaryText = _guildSessionProvider.GuildDescription; + if (item.PrimaryText != _guildSessionProvider.GuildDescription) + { + item.PrimaryText = _guildSessionProvider.GuildDescription; + } + }); + break; + + case GuildDialogState.Lookup: + _cachedGuildInfo.Match( + some: cachedGuildInfo => + { + _guildSessionProvider.GuildInfo.MatchSome( + some: repoGuildInfo => + { + if (cachedGuildInfo.Equals(repoGuildInfo)) + return; + CacheAndSetGuildInfo(repoGuildInfo); + } + ); + }, + none: () => _guildSessionProvider.GuildInfo.MatchSome(CacheAndSetGuildInfo) + ); + break; + + case GuildDialogState.ViewMembers: + if (!_cachedMembers.SetEquals(_guildSessionProvider.GuildMembers)) + { + ClearItemList(); + + _cachedMembers = _guildSessionProvider.GuildMembers.ToHashSet(); + AddTextAsKeyValueListItems( + _cachedMembers.Select(x => ($"{x.Rank} {x.Name}", char.ToUpper(x.RankName[0]) + x.RankName[1..])).ToArray() + ); } - }); + + break; } base.OnUpdateControl(gameTime); + + void CacheAndSetGuildInfo(GuildInfo guildInfo) + { + _cachedGuildInfo = Option.Some(guildInfo); + + ClearItemList(); + + AddTextAsListItems( + _contentProvider.Fonts[Constants.FontSize08pt5], + false, + new List(), + $"{guildInfo.Name} [{guildInfo.Tag}]", + " ", + _localizedStringFinder.GetString(EOResourceID.GUILD_SIGNUP_DATE), + guildInfo.CreateDate.ToString("yyyy/MM/dd"), + " ", + _localizedStringFinder.GetString(EOResourceID.GUILD_DESCRIPTION), + guildInfo.Description, + " ", + _localizedStringFinder.GetString(EOResourceID.GUILD_BANK_STATUS), + guildInfo.Wealth, + " ", + _localizedStringFinder.GetString(EOResourceID.GUILD_RANKING_SYSTEM), + string.Join("\n", guildInfo.Ranks.Select((x, n) => $"{n + 1} {char.ToUpper(x[0]) + x[1..]}")), + " ", + _localizedStringFinder.GetString(EOResourceID.GUILD_LEADERS), + string.Join("\n", guildInfo.Staff.Select(x => $"{x.Name}{(x.Rank == 0 ? " (founder)" : string.Empty)}")), + " " + ); + } } private void BackButton_Click(object sender, EventArgs e) => GoBack(); @@ -153,6 +236,9 @@ protected override void OnUpdateControl(GameTime gameTime) private void GoBack() { _modifyGuildDescriptionListItem = Option.None(); + _cachedGuildInfo = Option.None(); + _cachedMembers.Clear(); + SetState(_stateStack.Count > 0 ? _stateStack.Pop() : State.Initial, pushState: false); } @@ -169,7 +255,8 @@ private void SetState(State newState, bool pushState = true) ListItemType = _state.ListItemStyle; Buttons = _state.Buttons; - _stateTransitions[_state].Invoke(); + if (_stateTransitions.ContainsKey(_state)) + _stateTransitions[_state].Invoke(); } private void SetupInitialState() @@ -183,6 +270,8 @@ private void SetupInitialState() SubText = _localizedStringFinder.GetString(EOResourceID.GUILD_LEARN_MORE), OffsetY = 45, }; + informationItem.LeftClick += (_, _) => SetState(State.Information); + informationItem.RightClick += (_, _) => SetState(State.Information); var administrationItem = new ListDialogItem(this, ListDialogItem.ListItemStyle.Large, 1) { @@ -219,6 +308,89 @@ private void SetupInitialState() SetItemList(new List { informationItem, administrationItem, managementItem, bankAccountItem }); } + private void SetupInformationState() + { + var guildLookup = new ListDialogItem(this, ListDialogItem.ListItemStyle.Large, 0) + { + ShowIconBackGround = false, + IconGraphic = _dialogIconService.IconSheet, + IconGraphicSource = _dialogIconService.GetDialogIconSource(DialogIcon.GuildLookup), + PrimaryText = _localizedStringFinder.GetString(EOResourceID.GUILD_LOOK_UP), + SubText = _localizedStringFinder.GetString(EOResourceID.GUILD_VIEW_DETAILS), + OffsetY = 45, + }; + guildLookup.LeftClick += GuildLookup_Click; + guildLookup.RightClick += GuildLookup_Click; + + var viewMembers = new ListDialogItem(this, ListDialogItem.ListItemStyle.Large, 0) + { + ShowIconBackGround = false, + IconGraphic = _dialogIconService.IconSheet, + IconGraphicSource = _dialogIconService.GetDialogIconSource(DialogIcon.GuildLookup), + PrimaryText = _localizedStringFinder.GetString(EOResourceID.GUILD_MEMBERLIST), + SubText = _localizedStringFinder.GetString(EOResourceID.GUILD_VIEW_MEMBERS), + OffsetY = 45, + }; + viewMembers.LeftClick += ViewMembers_Click; + viewMembers.RightClick += ViewMembers_Click; + + SetItemList(new List { guildLookup, viewMembers }); + + void GuildLookup_Click(object sender, MouseEventArgs e) + { + var showOnce = false; + var dlg = _textInputDialogFactory.Create(_localizedStringFinder.GetString(EOResourceID.GUILD_TO_VIEW_INFORMATION_ABOUT_A_GUILD_ENTER_ITS_TAG), 3); + dlg.DialogClosing += (_, e) => + { + if (e.Result != XNADialogResult.OK) + return; + + if (dlg.ResponseText.Length < 2 && !showOnce) + { + var invalidGuildTag = _messageBoxFactory.CreateMessageBox( + _localizedStringFinder.GetString(DialogResourceID.GUILD_CREATE_TAG_TOO_SHORT), + _localizedStringFinder.GetString(DialogResourceID.GUILD_WRONG_INPUT), + EODialogButtons.OkCancel); + invalidGuildTag.ShowDialog(); + showOnce = true; + } + else + { + SetState(State.GuildLookup); + _guildActions.Lookup(dlg.ResponseText); + } + }; + dlg.ShowDialog(); + } + + void ViewMembers_Click(object sender, MouseEventArgs e) + { + var showOnce = false; + var dlg = _textInputDialogFactory.Create(_localizedStringFinder.GetString(EOResourceID.GUILD_TO_VIEW_INFORMATION_ABOUT_A_GUILD_ENTER_ITS_TAG), 3); + dlg.DialogClosing += (_, e) => + { + if (e.Result != XNADialogResult.OK) + return; + + if (dlg.ResponseText.Length < 2 && !showOnce) + { + var invalidGuildTag = _messageBoxFactory.CreateMessageBox( + _localizedStringFinder.GetString(DialogResourceID.GUILD_CREATE_TAG_TOO_SHORT), + _localizedStringFinder.GetString(DialogResourceID.GUILD_WRONG_INPUT), + EODialogButtons.OkCancel); + invalidGuildTag.ShowDialog(); + showOnce = true; + } + else + { + SetState(State.ViewMembers); + _guildActions.ViewMembers(dlg.ResponseText); + } + }; + dlg.ShowDialog(); + } + } + private void SetupManagementState() { var modifyGuildItem = new ListDialogItem(this, ListDialogItem.ListItemStyle.Large, 0) @@ -299,6 +471,20 @@ private void SetupModifyState() ); _modifyGuildDescriptionListItem = Option.Some(ChildControls.OfType().ToList()[1]); + + void ShowChangeDescriptionMessageBox() + { + var dlg = _textInputDialogFactory.Create(_localizedStringFinder.GetString(EOResourceID.GUILD_DESCRIPTION), 240); + dlg.DialogClosing += (_, e) => + { + if (e.Result == XNADialogResult.OK) + { + _guildActions.SetGuildDescription(dlg.ResponseText); + GoBack(); + } + }; + dlg.ShowDialog(); + } } private void SetStateIfGuildMember(State state) @@ -312,19 +498,5 @@ private void SetStateIfGuildMember(State state) SetState(state); } - - private void ShowChangeDescriptionMessageBox() - { - var dlg = _textInputDialogFactory.Create(_localizedStringFinder.GetString(EOResourceID.GUILD_DESCRIPTION), 240); - dlg.DialogClosing += (_, e) => - { - if (e.Result == XNADialogResult.OK) - { - _guildActions.SetGuildDescription(dlg.ResponseText); - GoBack(); - } - }; - dlg.ShowDialog(); - } } } diff --git a/EndlessClient/Dialogs/ScrollingListDialog.cs b/EndlessClient/Dialogs/ScrollingListDialog.cs index f135da37a..67d92300d 100644 --- a/EndlessClient/Dialogs/ScrollingListDialog.cs +++ b/EndlessClient/Dialogs/ScrollingListDialog.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using EndlessClient.Dialogs.Factories; using EndlessClient.Dialogs.Services; using EndlessClient.UIControls; using EOLib; using EOLib.Graphics; -using EOLib.Localization; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.BitmapFonts; @@ -408,10 +406,10 @@ public void AddTextAsListItems(BitmapFont font, bool insertLineBreaks, List(); var ts = new TextSplitter(string.Empty, font) { LineLength = 200 }; - foreach (string s in messages) + foreach (var s in messages) { ts.Text = s; - drawStrings.AddRange(ts.NeedsProcessing ? ts.SplitIntoLines() : new[] { s }); + drawStrings.AddRange(ts.SplitIntoLines()); if (insertLineBreaks) { drawStrings.Add(" "); @@ -420,7 +418,7 @@ public void AddTextAsListItems(BitmapFont font, bool insertLineBreaks, List 0 && s[0] == '*'; var nextItem = new ListDialogItem(this, ListDialogItem.ListItemStyle.Small) @@ -438,6 +436,23 @@ public void AddTextAsListItems(BitmapFont font, bool insertLineBreaks, List