using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Xna.Framework; using Myra.Graphics2D.Brushes; using Myra.Graphics2D.TextureAtlases; using Myra.Graphics2D.UI; using Myra.Graphics2D.UI.File; using Myra.Graphics2D.UI.Properties; using Newtonsoft.Json; using Sentry; using Sledgemapper.Messages; using Sledgemapper.Shared.Entities; using System; using System.IO; using System.Linq; using TinyMessenger; using Session = Sledgemapper.Shared.Entities.Session; namespace Sledgemapper.UI { public partial class MainWidget { private readonly CommunicationManager CommunicationManager; private readonly TinyMessengerHub _messenger; private readonly GameWindow Window; public MainWidget(CommunicationManager communicationManager, TinyMessengerHub messenger, GameWindow window) { BuildUI(); CommunicationManager = communicationManager; Window = window; _messenger = messenger; MenuConnectLogin.Selected += OnMenuConnectLoginSelected; MenuConnectSync.Selected += OnMenuConnectSyncSelected; MenuFileLoad.Selected += OnMenuFileLoadSelected; MenuFileSave.Selected += OnMenuFileSaveSelected; MenuFileSettings.Selected += OneMenuFileSettingsSelected; //MenuConnectLogin.Selected += OnMenuConnectLoginSelected; //MenuConnectNew.Selected += OnMenuConnectNewSelected; MenuConnectJoin.Selected += OnMenuConnectJoinSelected; MenuConnectUpload.Selected += OnMenuConnectUploadSelected; MenuViewCenterOnSelection.Selected += OnMenuViewCenterOnSelectionSelected; MenuViewShowCellNUmbers.Selected += OnMenuViewShowCellNUmbersSelected; MenuViewShowNotes.Selected += OnMenuViewNotesSelected; MenuCampaingNew.Selected += OnMenuCampaignNew; MenuCampaignOpen.Selected += OnMenuCampaignOpen; MenuCampaignPlayers.Selected += OnMenuCampaignPlayersSelected; MenuMapOpen.Selected += OnMenuMapOpen; MenuMapNew.Selected += OnMenuMapNew; MenuConnectNew.Enabled = false; MenuConnectJoin.Enabled = false; MenuConnectSync.Enabled = false; MenuConnectUpload.Enabled = false; MenuCampaignOpen.Enabled = false; MenuCampaignPlayers.Enabled = false; MenuCampaingNew.Enabled = false; MenuMapNew.Enabled = false; MenuMapOpen.Enabled = false; BtnToolbarLine.Click += OnBtnToolbarLinClicked; BtnToolbarRoom.Click += OnBtnToolbarRoomClicked; BtnToolbarDelete.Click += OnBtnToolbarDeleteClicked; _messenger.Subscribe(OnLoginSuccesfulMessage); _messenger.Subscribe(OnSignalrConnectionUpdateMessage); _messenger.Subscribe(OnMapOpenedMessage); _messenger.Subscribe(OnCenterOnTileMessage); } private void OnCenterOnTileMessage(CenterOnTileMessage obj) { CenterOnTile(obj.X, obj.Y); } private void OnMapOpenedMessage(MapOpenedMessage obj) { lblSessionName.Text = CommunicationManager.SessionData.SessionName; MenuConnectSync.Enabled = true; MenuConnectUpload.Enabled = true; CommunicationManager.SessionData.MapEntityAdded -= OnMapEntityAdded; CommunicationManager.SessionData.MapEntityDeleted -= OnMapEntityDeleted; CommunicationManager.SessionData.MapEntityAdded += OnMapEntityAdded; CommunicationManager.SessionData.MapEntityDeleted += OnMapEntityDeleted; } private async void OnSignalrConnectionUpdateMessage(SignalrConnectionUpdateMessage obj) { lblConnectionStatus.Text = "Connecting"; await CommunicationManager.Connection.StartAsync(); UpdateConnectionState(CommunicationManager.Connection); } private void OnLoginSuccesfulMessage(LoginSuccesfulMessage obj) { MenuConnectNew.Enabled = true; MenuConnectJoin.Enabled = true; MenuCampaignOpen.Enabled = true; MenuCampaignPlayers.Enabled = true; MenuCampaingNew.Enabled = true; lblUsername.Text = $"{obj.UserName} - {obj.Initials}"; } public void ClearSelection() { ClearSelection(GridWalls); ClearSelection(GridOverlays); ClearSelection(Toolbar); } private void ClearSelection(Grid grid) { foreach (var widget in grid.Widgets) { widget.Border = null; } } private void ClearSelection(HorizontalStackPanel grid) { foreach (var widget in grid.Widgets) { widget.Border = null; } } private void OnMenuConnectLoginSelected(object sender, EventArgs e) { Window window = new() { Title = "Login" }; var content = new LoginRegisterWindow(CommunicationManager, window, _messenger); window.Content = content; window.ShowModal(Desktop); } private void OnMenuConnectNewSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "New mapping session" }; var content = new SessionWindow(CommunicationManager, _messenger, window); window.Content = content; window.ShowModal(Desktop); } private void UpdateConnectionState(HubConnection connection) { switch (connection.State) { case HubConnectionState.Connected: lblConnectionStatus.Text = "Connected"; break; case HubConnectionState.Connecting: lblConnectionStatus.Text = "Connecting"; break; case HubConnectionState.Disconnected: lblConnectionStatus.Text = "Disconnected"; break; case HubConnectionState.Reconnecting: lblConnectionStatus.Text = "Reconnecting"; break; } } private void OnMapEntityAdded(object sender, MapEntityAddedEventArgs e) { CommunicationManager.Enqueue(e.MapEntity, TileAction.Add); } private void OnMapEntityDeleted(object sender, MapEntityDeletedEventArgs e) { CommunicationManager.Enqueue(e.MapEntity, TileAction.Delete); } private async void OnMenuConnectSyncSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } var serverMap = await CommunicationManager.Api.Session(CommunicationManager.SessionData.SessionName); CommunicationManager.SessionData.Overlays = serverMap.Overlays; CommunicationManager.SessionData.Map = serverMap.Map; CommunicationManager.SessionData.Walls = serverMap.Walls; CommunicationManager.SessionData.Notes = serverMap.Notes; CommunicationManager.SessionData.Lines = serverMap.Lines; CommunicationManager.SessionData.Rooms = serverMap.Rooms; } private async void OnMenuConnectUploadSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } await CommunicationManager.Api.SaveSnapshot(CommunicationManager.SessionData, CommunicationManager.SessionData.SessionName); } private void OnMenuFileSaveSelected(object sender, EventArgs e) { FileDialog dialog = new(FileDialogMode.SaveFile) { Filter = "*.map" }; dialog.Closed += (s, a) => { if (!dialog.Result) { return; } using StreamWriter file = File.CreateText(dialog.FilePath); JsonSerializer serializer = new(); serializer.Serialize(file, CommunicationManager.SessionData); }; dialog.ShowModal(Desktop); } private void OnMenuFileLoadSelected(object sender, EventArgs e) { var dialog = new FileDialog(FileDialogMode.OpenFile) { Filter = "*.map" }; dialog.Closed += (s, a) => { if (!dialog.Result) { return; } using StreamReader file = File.OpenText(dialog.FilePath); JsonSerializer serializer = new(); var loadData = (Session)serializer.Deserialize(file, typeof(Session)); CommunicationManager.SessionData.Map = loadData.Map; CommunicationManager.SessionData.Overlays = loadData.Overlays; CommunicationManager.SessionData.Walls = loadData.Walls; CommunicationManager.SessionData.Rooms = loadData.Rooms; CommunicationManager.SessionData.Lines = loadData.Lines; }; dialog.ShowModal(Desktop); } private void OneMenuFileSettingsSelected(object sender, EventArgs e) { var propertyGrid = new PropertyGrid { Object = Settings.Instance, Width = 350 }; var _windowEditor = new Window { Title = "Object Editor", Content = propertyGrid }; _windowEditor.ShowModal(Desktop); } private void OnMenuConnectJoinSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Join mapping session" }; var content = new SessionWindow(CommunicationManager, _messenger, window); window.Content = content; window.ShowModal(Desktop); } private void OnMenuViewShowCellNUmbersSelected(object sender, EventArgs e) { State.Instance.ShowCellNumbers = !State.Instance.ShowCellNumbers; } private void OnMenuViewCenterOnSelectionSelected(object sender, EventArgs e) { CenterOnSelectedTile(); } private void OnMenuViewNotesSelected(object sender, EventArgs e) { Window window = new() { Title = "Notes" }; var content = new NoteList(CommunicationManager, _messenger, window); for (var i = 0; i < CommunicationManager.SessionData.Notes.Values.Count; i++) { var note = CommunicationManager.SessionData.Notes.Values.ElementAt(i); var item = new NoteListItem(); item.LblNoteText.Text = $"{note.ToString()} - {note.Text}"; item.BtnNoteCenter.Image = new TextureRegion(CachedContent.Instance.Location); item.BtnNoteView.Image = new TextureRegion(CachedContent.Instance.Eye); item.BtnNoteCenter.Click += (s, e) => { CenterOnTile(note.X, note.Y); }; item.BtnNoteView.Click += (s, e) => { EditNote(note); window.Close(); }; content.StackNotesList.AddChild(item); } window.Content = content; window.ShowModal(Desktop); } private void CenterOnSelectedTile() { CenterOnTile(State.Instance.SelectedTile.X, State.Instance.SelectedTile.Y); } private void CenterOnTile(int x, int y) { var center = new Point((Window.ClientBounds.Width + 200) / 2 - State.Instance.TileSize / 2, Window.ClientBounds.Height / 2 - State.Instance.TileSize / 2); var dx = center.X - x * State.Instance.TileSize - State.Instance.ViewportCenter.X; var dy = center.Y - y * State.Instance.TileSize - State.Instance.ViewportCenter.Y; State.Instance.ViewportCenter = new Vector3(State.Instance.ViewportCenter.X + dx, State.Instance.ViewportCenter.Y + dy, State.Instance.ViewportCenter.Z); } private void OnMenuCampaignNew(object sender, EventArgs e) { if (sender is MenuItem && !((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "New campaign" }; var content = new CampaignWindow(); } private async void OnMenuCampaignOpen(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Campaigns" }; var content = new CampaignList(CommunicationManager, window, _messenger, Settings.Instance, Desktop); await content.LoadCampaigns(); window.Content = content; window.ShowModal(Desktop); } private void OnMapSelected(object sender, EventArgs e) { var item = sender as UI.ListItem; var localContent = item.GetParentContentInWindow(); var list = item.Parent as Myra.Graphics2D.UI.Grid; for (var i = 0; i < list.ChildrenCount; i++) { var currentItem = list.GetChild(i) as HorizontalStackPanel;// UI.ListItem; currentItem.Background = new SolidBrush("#D9D9D9FF"); } item.Background = new SolidBrush(Settings.Instance.OverlayTintColor); } private async void OnMenuCampaignPlayersSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Players" }; var content = new PlayerList(); var players = await CommunicationManager.Api.GetPlayers(State.Instance.CampaignName); foreach (var player in players) { var item = new UI.ListItem(); item.ItemName.Text = player.UserName; content.StackCampaignsList.AddChild(item); } content.BtnInvitePlayer.Click += (s, e) => { window.Close(); ShowAddPLayerWindow(); }; window.Content = content; window.ShowModal(Desktop); } private async void OnMenuMapOpen(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Maps" }; var content = new MapList(); var campaigns = await CommunicationManager.Api.GetMaps(State.Instance.CampaignName); foreach (var campaign in campaigns) { var item = new UI.ListItem(); item.ItemName.Text = campaign.SessionName; item.TouchUp += OnMapSelected; content.StackCampaignsList.AddChild(item); } content.BtnNewCampaign.Click += (s, e) => { window.Close(); OnMenuMapNew(s, e); }; window.Content = content; window.ShowModal(Desktop); } private void ShowAddPLayerWindow() { Window window = new() { Title = "Invite player" }; var content = new PlayerWindow(); //content.BtnNewCampaign.Text = "N"; content.BtnNewCampaign.Click += OnButtonInvitePlayerClicked; window.Content = content; window.ShowModal(Desktop); content.TxtCampaign.SetKeyboardFocus(); } private async void OnButtonJoinSessionClicked(object sender, EventArgs e) { var localContent = ((TextButton)sender).GetParentContentInWindow(); var isValid = localContent.Content.TxtSession.ValidateTextbox(); if (!isValid) { return; } if (CommunicationManager.Connection.State != HubConnectionState.Connected) { lblConnectionStatus.Text = "Connecting"; await CommunicationManager.Connection.StartAsync(); UpdateConnectionState(CommunicationManager.Connection); } var successful = false; try { var result = await CommunicationManager.Connection?.InvokeAsync("JoinSession", localContent.Content.TxtSession.Text); if (result != null) { CommunicationManager.SessionData.Map = result.Map; CommunicationManager.SessionData.Walls = result.Walls; CommunicationManager.SessionData.Overlays = result.Overlays; CommunicationManager.SessionData.MapEntityAdded -= OnMapEntityAdded; CommunicationManager.SessionData.MapEntityDeleted -= OnMapEntityDeleted; CommunicationManager.SessionData.MapEntityAdded += OnMapEntityAdded; CommunicationManager.SessionData.MapEntityDeleted += OnMapEntityDeleted; CommunicationManager.SessionData.SessionId = result.SessionId; CommunicationManager.SessionData.SessionName = result.SessionName; } successful = result != null; } catch (Exception ex) { Console.WriteLine(ex.Message); } if (successful) { CommunicationManager.SessionData.SessionName = localContent.Content.TxtSession.Text; lblSessionName.Text = CommunicationManager.SessionData.SessionName; MenuConnectSync.Enabled = true; MenuConnectUpload.Enabled = true; localContent.Window.Close(); } } private async void OnButtonInvitePlayerClicked(object sender, EventArgs e) { var localContent = ((TextButton)sender).GetParentContentInWindow();// localWindow.Content as PlayerWindow; var isValid = localContent.Content.TxtCampaign.ValidateTextbox(); if (!isValid) { return; } var successful = false; try { await CommunicationManager.Api.InvitePlayer(State.Instance.CampaignName, localContent.Content.TxtCampaign.Text); } catch (Exception ex) { SentrySdk.CaptureException(ex); } localContent.Window.Close(); } private void OnBtnToolbarRoomClicked(object sender, EventArgs e) { State.Instance.InsertMode = InsertMode.NewRoom; ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarLinClicked(object sender, EventArgs e) { State.Instance.InsertMode = InsertMode.NewLine; ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarDeleteClicked(object sender, EventArgs e) { State.Instance.InsertMode = InsertMode.NewDelete; ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnMenuMapNew(object sender, EventArgs e) { if (sender is MenuItem && !((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "New map" }; var content = new MapWindow(); //content.BtnNewCampaign.Text = "N"; content.BtnNewCampaign.Click += OnButtonNewMapClicked; window.Content = content; window.ShowModal(Desktop); content.TxtCampaign.SetKeyboardFocus(); } private async void OnButtonNewMapClicked(object sender, EventArgs e) { Container container = ((TextButton)sender).Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as MapWindow; var isValid = localContent.TxtCampaign.ValidateTextbox(); if (!isValid) { return; } var successful = false; try { await CommunicationManager.Api.NewSession(State.Instance.CampaignName, localContent.TxtCampaign.Text); } catch (Exception ex) { SentrySdk.CaptureException(ex); } localWindow.Close(); } //TODO Refactor private void EditNote(Note note) { State.Instance.SelectedNote = new Note { X = note.X, Y = note.Y, Text = note.Text }; Window window = new() { Title = $" Note on {note.X}:{note.Y}" }; var noteWindow = new NoteWindow(CommunicationManager, window, note); window.Content = noteWindow; window.ShowModal(Desktop); } } }