using Exceptionless; using Exceptionless.Models; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MonoGame.Extended; using MonoGame.Extended.VectorDraw; using Myra; using Myra.Graphics2D.Brushes; using Myra.Graphics2D.TextureAtlases; using Myra.Graphics2D.UI; using Myra.Graphics2D.UI.File; using Myra.Graphics2D.UI.Properties; using Myra.Utility; using Newtonsoft.Json; using Sledgemapper.Shared.Entities; using Sledgemapper.UI; using System; using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using AsyncAwaitBestPractices; namespace Sledgemapper { public class Sledgemapper : Game { private readonly CommunicationManager _communicationManager; private readonly State _state; private readonly GraphicsDeviceManager _graphics; private SpriteBatch _spriteBatch; private readonly Desktop _desktop; private KeyboardState oldState; private MouseState oldMouseState; private Vector3 _viewportCenter = new(0, 0, 0); private bool _isDraggin; private Dictionary _fonts; private Texture2D _eye; private Texture2D _location; private Texture2D _comment; private readonly Session _sessionData; private AuthenticateResponse _authResponse; private RenderTarget2D rendertarget; private MainWidget _mainWidget; private bool _showCellNumbers; private readonly Settings _settings; public Sledgemapper() { _graphics = new GraphicsDeviceManager(this); _graphics.GraphicsProfile = GraphicsProfile.Reach; _graphics.PreferMultiSampling = false; Content.RootDirectory = "Content"; _desktop = new Desktop(); MyraEnvironment.Game = this; _sessionData = new Session(); IsFixedTimeStep = true; TargetElapsedTime = TimeSpan.FromSeconds(1d / 30d); _communicationManager = new CommunicationManager(_sessionData); _communicationManager.Connection.Reconnected += OnHubReconnected; _communicationManager.Connection.Reconnecting += OnHubReconnecting; _communicationManager.Connection.Closed += OnHubDisconnected; _state = new State(); _settings = new Settings(); _vector2Pool = ArrayPool.Create(); } private async Task OnHubDisconnected(Exception arg) { ExceptionlessClient.Default.SubmitEvent(new Event { Message = "Hub disconnected", Type = "SignalR Client Events", Source = _settings.MachineName }); _mainWidget.lblConnectionStatus.Text = "Disconnected"; await Task.Yield(); } private async Task OnHubReconnecting(Exception arg) { ExceptionlessClient.Default.SubmitEvent(new Event { Message = "Reconnecting Hub", Type = "SignalR Client Events", Source = _settings.MachineName }); _mainWidget.lblConnectionStatus.Text = "Reconnecting"; await Task.Yield(); } private async Task OnHubReconnected(string arg) { ExceptionlessClient.Default.SubmitEvent(new Event { Message = "Hub reconnected", Type = "SignalR Client Events", Source = _settings.MachineName }); _mainWidget.lblConnectionStatus.Text = "Connected"; await Task.Yield(); } protected override void Initialize() { ExceptionlessClient.Default.SubmitEvent(new Event { Message = "Initialize", Type = "AppLifecycle", Source = _settings.MachineName }); IsMouseVisible = true; Window.AllowUserResizing = true; Window.ClientSizeChanged += OnClientSizeChanged; base.Initialize(); } private void ResetRenderTarget() { rendertarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color, DepthFormat.None, 0, RenderTargetUsage.DiscardContents); } private void OnClientSizeChanged(object sender, EventArgs e) { rendertarget?.Dispose(); try { ResetRenderTarget(); } catch { System.Console.WriteLine("rendertarget dispose exception"); } } private void AddItemToToolGrid(Grid grid, EventHandler eventAction, Dictionary tilesFolderContent) { var indexX = 0; var indexY = 0; foreach (var item in tilesFolderContent) { var tileButton = new ImageButton { Image = new TextureRegion(item.Value), GridColumn = indexY, GridRow = indexX, Id = item.Key, Width = 40, Height = 40 }; tileButton.Click += eventAction; grid.Widgets.Add(tileButton); indexY++; if (indexY == 4) { indexY = 0; indexX++; } } } private void AddItemToToolGrid(Grid grid, EventHandler eventAction, SpriteSheet spriteSheet, string e = "") { var indexX = 0; var indexY = 0; grid.Widgets.Clear(); _mainWidget.ScrOverlay.ResetScroll(); foreach (var item in spriteSheet.index.Where(t => String.IsNullOrWhiteSpace(e) || t.Key.ToLower().Contains(e.ToLower()))) { var tileButton = new ImageButton { Image = new TextureRegion(spriteSheet.Texture, item.Value), GridColumn = indexY, GridRow = indexX, Id = item.Key, Width = 40, Height = 40 }; tileButton.Click += eventAction; tileButton.MouseMoved += OnTileButtonTouchEntered; tileButton.MouseLeft += OnTileButtonTouchLeft; grid.Widgets.Add(tileButton); indexY++; if (indexY == 4) { indexY = 0; indexX++; } } } private void OnTileButtonTouchLeft(object sender, EventArgs e) { _desktop.HideContextMenu(); _lblOverlayName.Visible = false; } private SpriteSheet _rippleSpriteSheet; Label _lblOverlayName; private void OnTileButtonTouchEntered(object sender, EventArgs e) { var mouseState = Mouse.GetState().Position; mouseState.X += 10; mouseState.Y += 10; _lblOverlayName.Visible = true; _lblOverlayName.Text = ((ImageButton)sender).Id; _desktop.ShowContextMenu(_lblOverlayName, mouseState); } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); outlineShader = Content.Load("shaders/OutlineShader"); MyraEnvironment.Game = this; ResetRenderTarget(); _mainWidget = new MainWidget(); _mainWidget.MenuConnectSync.Selected += OnMenuConnectSyncSelected; _mainWidget.MenuFileLoad.Selected += OnMenuFileLoadSelected; _mainWidget.MenuFileSave.Selected += OnMenuFileSaveSelected; _mainWidget.MenuFileSettings.Selected += OneMenuFileSettingsSelected; _mainWidget.MenuConnectLogin.Selected += OnMenuConnectLoginSelected; _mainWidget.MenuConnectNew.Selected += OnMenuConnectNewSelected; _mainWidget.MenuConnectJoin.Selected += OnMenuConnectJoinSelected; _mainWidget.MenuConnectUpload.Selected += OnMenuConnectUploadSelected; _mainWidget.MenuViewCenterOnSelection.Selected += OnMenuViewCenterOnSelectionSelected; _mainWidget.MenuViewShowCellNUmbers.Selected += OnMenuViewShowCellNUmbersSelected; _mainWidget.MenuViewShowNotes.Selected += OnMenuViewNotesSelected; _mainWidget.MenuCampaingNew.Selected += OnMenuCampaignNew; _mainWidget.MenuCampaignOpen.Selected += OnMenuCampaignOpen; _mainWidget.MenuConnectNew.Enabled = false; _mainWidget.MenuConnectJoin.Enabled = false; _mainWidget.MenuConnectSync.Enabled = false; _mainWidget.MenuConnectUpload.Enabled = false; _mainWidget.BtnToolbarLine.Click += OnBtnToolbarLinClicked; _mainWidget.BtnToolbarRoom.Click += OnBtnToolbarRoomClicked; _mainWidget.BtnToolbarDelete.Click += OnBtnToolbarDeleteClicked; _wallsContent = Content.LoadContentFolder("walls"); _spriteSheet = new SpriteSheet(); _spriteSheet.LoadContent(Content, "spriteIndex", "sprites"); _rippleSpriteSheet = new SpriteSheet(); _rippleSpriteSheet.LoadContent(Content, "handcursorsIndex", "handcursors"); _lblOverlayName = new Label(); _lblOverlayName.Background = new SolidBrush(Color.SlateGray); _lblOverlayName.Padding = new Myra.Graphics2D.Thickness(4); AddItemToToolGrid(_mainWidget.GridWalls, OnWallButtonClicked, _wallsContent); AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, _spriteSheet); _fonts = Content.LoadContentFolder("fonts"); _eye = Content.Load("eye"); _location = Content.Load("location"); _comment = Content.Load("comment"); _mainWidget.BtnToolbarLine.Image = new TextureRegion(Content.Load("icon_line")); _mainWidget.BtnToolbarRoom.Image = new TextureRegion(Content.Load("icon_room")); _mainWidget.BtnToolbarDelete.Image = new TextureRegion(Content.Load("icon_delete")); _mainWidget.TxtOverlaySearch.TextChangedByUser += OnTxtOverlaySearchChange; _desktop.Root = _mainWidget; _transparentRedRectangle = new Texture2D(GraphicsDevice, 1, 1); _transparentRedRectangle.SetData(new[] { new Color(Color.Red, 80) }); _whiteRectangle = new Texture2D(GraphicsDevice, 1, 1); _whiteRectangle.SetData(new[] { Color.White }); } private void OnTxtOverlaySearchChange(object sender, ValueChangedEventArgs e) { AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, _spriteSheet, e.NewValue); } private void OnBtnToolbarDeleteClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewDelete; _mainWidget.ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarWallClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewWall; _mainWidget.ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarTileClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewTile; _mainWidget.ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarRoomClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewRoom; _mainWidget.ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarLinClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewLine; _mainWidget.ClearSelection(); ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnMenuCampaignNew(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "New campaign" }; var content = new CampaignWindow(); //content.BtnNewCampaign.Text = "N"; content.BtnNewCampaign.Click += OnButtonNewCampaignClicked; window.Content = content; window.ShowModal(_desktop); content.TxtCampaign.SetKeyboardFocus(); } private async void OnMenuCampaignOpen(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Campaigns" }; var content = new CampaignList(); var campaigns = await _communicationManager.Api.GetCampaigns(); foreach(var campaign in campaigns) { var item = new CampaignListItem(); item.TxtCampaignName.Text=campaign.Name; item.Background= new NinePatchRegion( Content.Load("listBackground"), new Rectangle(0, 0, 418, 98), new Myra.Graphics2D.Thickness {Left = 11, Right = 11, Top = 11, Bottom = 11}); content.StackNotesList.AddChild(item); } //content.BtnNewCampaign.Text = "N"; // content.BtnNewCampaign.Click += OnButtonNewCampaignClicked; window.Content = content; window.ShowModal(_desktop); // content.TxtCampaign.SetKeyboardFocus(); } private void OneMenuFileSettingsSelected(object sender, EventArgs e) { var propertyGrid = new PropertyGrid { Object = _settings, Width = 350 }; var _windowEditor = new Window { Title = "Object Editor", Content = propertyGrid }; _windowEditor.ShowModal(_desktop); } private void OnMenuViewShowCellNUmbersSelected(object sender, EventArgs e) { _showCellNumbers = !_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(); for (var i = 0; i < _sessionData.Notes.Values.Count; i++) { var note = _sessionData.Notes.Values.ElementAt(i); var item = new NoteListItem(); item.LblNoteText.Text = $"{note.ToString()} - {note.Text}"; item.BtnNoteCenter.Image = new TextureRegion(_location); item.BtnNoteView.Image = new TextureRegion(_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 EditNote(Note note) { _state.SelectedNote = new Note { X = note.X, Y = note.Y, Text = note.Text }; var noteWindow = new NoteWindow(); Window window = new() { Title = $" Note on {note.X}:{note.Y}" }; noteWindow.NoteText.Text = note.Text; noteWindow.BtnOk.Click += OnButtonNoteOkClick; noteWindow.BtnCancel.Click += OnButtonNoteCancelClick; window.Content = noteWindow; window.ShowModal(_desktop); noteWindow.NoteText.SetKeyboardFocus(); } protected override void Update(GameTime gameTime) { KeyboardState newState = Keyboard.GetState(); if (IsActive && GraphicsDevice.Viewport.Bounds.Contains(Mouse.GetState().Position) && !_desktop.IsMouseOverGUI && !_desktop.HasModalWidget) { var mouseState = Mouse.GetState(); var screenPosition = new Point(mouseState.Position.X - (int)_viewportCenter.X, mouseState.Position.Y - (int)_viewportCenter.Y); _state.HoveredTile.X = screenPosition.X / _state.TileSize; _state.HoveredTile.Y = screenPosition.Y / _state.TileSize; if (screenPosition.X < 0) { _state.HoveredTile.X--; } if (screenPosition.Y < 0) { _state.HoveredTile.Y--; } if (_state.InsertMode == InsertMode.Wall) { _state.SelectClosestWall(screenPosition); } if (_state.InsertMode == InsertMode.Overlay) { _state.SelectOverlay(screenPosition); } if (_state.InsertMode == InsertMode.NewLine || _state.InsertMode == InsertMode.NewRoom || _state.InsertMode == InsertMode.NewDelete) { _state.SelectClosestSnapPoint(screenPosition); } if (!newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Pressed && mouseState.LeftButton == oldMouseState.LeftButton && !_desktop.IsMouseOverGUI ) { _viewportCenter = new Vector3(_viewportCenter.X + mouseState.Position.X - oldMouseState.Position.X, _viewportCenter.Y + mouseState.Position.Y - oldMouseState.Position.Y, 0); if (mouseState.Position != oldMouseState.Position) { _isDraggin = true; } } if (mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != oldMouseState.LeftButton) { if (_isDraggin) { _isDraggin = false; } else { _state.SelectedTile.X = _state.HoveredTile.X; _state.SelectedTile.Y = _state.HoveredTile.Y; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _sessionData.SessionId, _state.SelectedTile); } } if (mouseState.RightButton == ButtonState.Released && mouseState.RightButton != oldMouseState.RightButton) { _state.SelectedNote.X = _state.HoveredTile.X; _state.SelectedNote.Y = _state.HoveredTile.Y; var popup = new VerticalStackPanel { Padding = new Myra.Graphics2D.Thickness(1), Spacing = 2, Background = new SolidBrush(Color.DarkGray) }; if (!_sessionData.Notes.ContainsKey(_state.SelectedNote.ToString())) { var newNoteButton = new TextButton { Text = "New Note", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left }; newNoteButton.Click += OnContextMenuNewNoteClick; popup.AddChild(newNoteButton); } else { _sessionData.Notes.TryGetValue(_state.SelectedNote.ToString(), out var n); _state.SelectedNote = n; var viewNoteButton = new TextButton { Text = "View Note", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left }; var deleteNoteButton = new TextButton { Text = "Delete Note", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left }; viewNoteButton.Click += OnContextMenuViewNoteClick; deleteNoteButton.Click += OnContextMenuDeleteNoteClick; popup.AddChild(viewNoteButton); popup.AddChild(deleteNoteButton); } var pingButton = new TextButton { Text = "Ping", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left }; var location = new Tile { X = _state.HoveredTile.X, Y = _state.HoveredTile.Y }; pingButton.Click += (s, e) => OnContextMenuPingClick(s, e, location); popup.AddChild(pingButton); _desktop.ShowContextMenu(popup, mouseState.Position); } if (newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != oldMouseState.LeftButton) { switch (_state.InsertMode) { case InsertMode.Tile: _state.SelectedTile.X = _state.HoveredTile.X; _state.SelectedTile.Y = _state.HoveredTile.Y; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _sessionData.SessionId, _state.SelectedTile); _sessionData.NewTile(_state.SelectedTile, _state.CurrentTileId); break; case InsertMode.Wall: _sessionData.NewWall(_state.SelectedWall, _state.CurrentWallId); break; case InsertMode.Overlay: _sessionData.NewOverlay(_state.SelectedOverlay, _state.CurrentOverlayId); break; } } if (_state.InsertMode == InsertMode.NewLine && newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != oldMouseState.LeftButton) { if (_state.LineStart is null) { _state.LineStart = new SnapPoint { X = _state.SelectedSnapPoint.X, Y = _state.SelectedSnapPoint.Y, Index = _state.SelectedSnapPoint.Index }; } else { var line = new Line { Start = new SnapPoint { X = _state.LineStart.X, Y = _state.LineStart.Y, Index = _state.LineStart.Index }, End = new SnapPoint { X = _state.SelectedSnapPoint.X, Y = _state.SelectedSnapPoint.Y, Index = _state.SelectedSnapPoint.Index }, Width = _state.LineWidth }; _state.LineStart = null; _state.LineWidth = 1; _sessionData.NewLine(line); } } if (_state.InsertMode == InsertMode.NewRoom && newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != oldMouseState.LeftButton) { if (_state.LineStart is null) { _state.LineStart = new SnapPoint { X = _state.SelectedSnapPoint.X, Y = _state.SelectedSnapPoint.Y, Index = _state.SelectedSnapPoint.Index }; } else { var line = new Room { Start = new SnapPoint { X = _state.LineStart.X, Y = _state.LineStart.Y, Index = _state.LineStart.Index }, End = new SnapPoint { X = _state.SelectedSnapPoint.X, Y = _state.SelectedSnapPoint.Y, Index = _state.SelectedSnapPoint.Index } }; _state.LineStart = null; _state.LineWidth = 1; _sessionData.NewRoom(line); } } if (_state.InsertMode == InsertMode.NewDelete && newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != oldMouseState.LeftButton) { if (_state.LineStart is null) { _state.LineStart = new SnapPoint { X = _state.SelectedSnapPoint.X, Y = _state.SelectedSnapPoint.Y, Index = _state.SelectedSnapPoint.Index }; } else { var line = new Room { Start = new SnapPoint { X = _state.LineStart.X, Y = _state.LineStart.Y, Index = _state.LineStart.Index }, End = new SnapPoint { X = _state.SelectedSnapPoint.X, Y = _state.SelectedSnapPoint.Y, Index = _state.SelectedSnapPoint.Index }, Delete = true }; _state.LineStart = null; _state.LineWidth = 1; _sessionData.NewRoom(line); } } if (_state.InsertMode == InsertMode.NewLine && _state.LineStart != null) { if (mouseState.ScrollWheelValue > oldMouseState.ScrollWheelValue) { _state.LineWidth += .01f; } else if (mouseState.ScrollWheelValue < oldMouseState.ScrollWheelValue) { _state.LineWidth -= .01f; } } if (newState.IsKeyDown(Keys.LeftControl) && mouseState.ScrollWheelValue != oldMouseState.ScrollWheelValue) { var maxTileSize = 500; var center = new Point((Window.ClientBounds.Width + 200) / 2 - _state.TileSize / 2, Window.ClientBounds.Height / 2 - _state.TileSize / 2); var tx = (center.X - (int)_viewportCenter.X) / _state.TileSize; var ty = (center.Y - (int)_viewportCenter.Y) / _state.TileSize; if (mouseState.ScrollWheelValue > oldMouseState.ScrollWheelValue) { _state.TileSize = Math.Min(maxTileSize, _state.TileSize + 10); } else if (mouseState.ScrollWheelValue < oldMouseState.ScrollWheelValue) { _state.TileSize = Math.Max(10, _state.TileSize - 10); } CenterOnTile(tx, ty); } oldMouseState = mouseState; } if (newState.IsKeyDown(Keys.Delete)) { switch (_state.InsertMode) { case InsertMode.Tile: case InsertMode.NewRoom: case InsertMode.NewLine: case InsertMode.NewDelete: _state.SelectedTile.X = _state.HoveredTile.X; _state.SelectedTile.Y = _state.HoveredTile.Y; _sessionData.DeleteTile(_state.SelectedTile); break; case InsertMode.Wall: _sessionData.DeleteWall(_state.SelectedWall); break; case InsertMode.Overlay: _sessionData.DeleteOverlay(_state.SelectedOverlay); break; } } if (oldState.IsKeyDown(Keys.P) && newState.IsKeyUp(Keys.P)) { _communicationManager.Ping(_state.HoveredTile).SafeFireAndForget(); } foreach (var key in newState.GetPressedKeys()) { switch (key) { case Keys.Left: if (oldState.IsKeyUp(Keys.Left) && newState.IsKeyDown(Keys.Left)) { _state.SelectedTile.X--; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile); } break; case Keys.Right: if (oldState.IsKeyUp(Keys.Right) && newState.IsKeyDown(Keys.Right)) { _state.SelectedTile.X++; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile); } break; case Keys.Up: if (oldState.IsKeyUp(Keys.Up) && newState.IsKeyDown(Keys.Up)) { _state.SelectedTile.Y--; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile); } break; case Keys.Down: if (oldState.IsKeyUp(Keys.Down) && newState.IsKeyDown(Keys.Down)) { _state.SelectedTile.Y++; _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile); } break; } } oldState = newState; base.Update(gameTime); } private void CenterOnSelectedTile() { CenterOnTile(_state.SelectedTile.X, _state.SelectedTile.Y); } private void CenterOnTile(int x, int y) { var center = new Point((Window.ClientBounds.Width + 200) / 2 - _state.TileSize / 2, Window.ClientBounds.Height / 2 - _state.TileSize / 2); var dx = center.X - x * _state.TileSize - _viewportCenter.X; var dy = center.Y - y * _state.TileSize - _viewportCenter.Y; _viewportCenter.X += dx; _viewportCenter.Y += dy; } private void OnContextMenuPingClick(object sender, EventArgs e, Tile location) { _desktop.HideContextMenu(); _communicationManager.Ping(location).SafeFireAndForget(); } protected override void Draw(GameTime gameTime) { if (_spriteBatch is null) { return; } var visibleTilesX = GraphicsDevice.Viewport.Width / _state.TileSize + 1; var visibleTilesY = GraphicsDevice.Viewport.Height / _state.TileSize + 1; DrawMap(); //ApplyShaderToMap(); _spriteBatch.Begin( transformMatrix: Matrix.CreateTranslation(_viewportCenter), sortMode: SpriteSortMode.Texture, samplerState: SamplerState.PointClamp); DrawTiles(); //DrawWalls(); DrawOverlays(); DrawNotes(); DrawGrid(visibleTilesX, visibleTilesY); DrawPlayerOffline(); DrawPlayers(); DrawSelectedWall(); DrawSelectedIntersection(); DrawLinePreview(); DrawRoomPreview(); _spriteBatch.End(); DrawRipple(gameTime); try { _desktop?.Render(); } catch { System.Console.WriteLine("Desktop render error"); } base.Draw(gameTime); } private void DrawRipple(GameTime gameTime) { _spriteBatch.Begin( blendState: BlendState.NonPremultiplied, transformMatrix: Matrix.CreateTranslation(_viewportCenter)); var durationMs = 2000; var baseRadius = _state.TileSize / 4f; var baseOuterRadius = (float)_state.TileSize; var iterations = 3f; var guids = _sessionData.Pings.Keys.ToArray(); foreach (var guid in guids) { var pingFound = _sessionData.Pings.TryGetValue(guid, out var ping); if (!pingFound) { continue; } if (ping.StartTime == 0) { ping.StartTime = gameTime.TotalGameTime.TotalMilliseconds; } var x = ping.X * _state.TileSize + _state.TileSize / 2f; var y = ping.Y * _state.TileSize + _state.TileSize / 2f; if (IsOffscreen(new Tile { X = ping.X, Y = ping.Y })) { DrawPingPointer(ping, gameTime); } else { _spriteBatch.DrawCircle( center: new Vector2(x, y), radius: baseRadius, sides: 20, color: ping.Player.Color.ToColor(), thickness: baseRadius); for (var i = 0; i < iterations; i++) { var cycleTime = (((float)gameTime.TotalGameTime.TotalMilliseconds + (float)i * durationMs / iterations) % durationMs) / durationMs; var easing = Easings.Interpolate(cycleTime, Easings.Functions.SineEaseInOut); _spriteBatch.DrawCircle( center: new Vector2(x, y), radius: baseRadius + baseOuterRadius * easing, sides: 20, color: new Color(ping.Player.Color.ToColor(), (1 - easing)), thickness: 2 + 5 * (1 - easing)); } } } foreach (var guid in guids) { var pingFound = _sessionData.Pings.TryGetValue(guid, out var ping); if (!pingFound) { continue; } if ((gameTime.TotalGameTime.TotalMilliseconds - ping.StartTime) > _settings.PingDuration) { _sessionData.Pings.TryRemove(guid, out var _); } } _spriteBatch.End(); } private void DrawRoomPreview() { if ((_state.InsertMode == InsertMode.NewRoom || _state.InsertMode == InsertMode.NewDelete) && _state.SelectedSnapPoint != null) { var snapPoint = new Vector2(_state.SelectedSnapPoint.X * _state.TileSize, _state.SelectedSnapPoint.Y * _state.TileSize); switch (_state.SelectedSnapPoint.Index) { case 1: break; case 2: snapPoint.X += _state.TileSize / 2; break; case 3: snapPoint.Y += _state.TileSize / 2; break; case 4: snapPoint.Y += _state.TileSize / 2; snapPoint.X += _state.TileSize / 2; break; } _spriteBatch.DrawCircle(snapPoint, _state.TileSize / 6f, 50, Color.Red, 2); if (_state.LineStart != null) { var posX = _state.LineStart.X * _state.TileSize; var posY = _state.LineStart.Y * _state.TileSize; var endposX = _state.SelectedSnapPoint.X * _state.TileSize; var endposY = _state.SelectedSnapPoint.Y * _state.TileSize; if (_state.InsertMode == InsertMode.NewDelete) { var ww = _state.TileSize / _settings.TileDeleteDivider; if (posX == endposX) { endposX += ww; posX -= ww; } if (posY == endposY) { endposY += ww; posY -= ww; } } switch (_state.LineStart.Index) { case 1: break; case 2: posX += _state.TileSize / 2; break; case 3: posY += _state.TileSize / 2; break; case 4: posX += _state.TileSize / 2; posY += _state.TileSize / 2; break; } switch (_state.SelectedSnapPoint.Index) { case 1: break; case 2: endposX += _state.TileSize / 2; break; case 3: endposY += _state.TileSize / 2; break; case 4: endposX += _state.TileSize / 2; endposY += _state.TileSize / 2; break; } var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize / 3).Value ?? _fonts.Last().Value; var fscale = 1.2f; var width = Math.Abs((endposX - posX)); var height = Math.Abs((posY - endposY)); var tilesWidth = width / (double)_state.TileSize; var tilesHeight = height / (double)_state.TileSize; tilesWidth = Math.Round(tilesWidth * 2, MidpointRounding.AwayFromZero) / 2; tilesHeight = Math.Round(tilesHeight * 2, MidpointRounding.AwayFromZero) / 2; var xmeasure = ffont.MeasureString($"{tilesWidth}"); var ymeasure = ffont.MeasureString($"{tilesHeight}"); float widthX = 0, widthY = 0, heightX = 0, heightY = 0; Rectangle area = new Rectangle(); if (posX != endposX && posY != endposY) { if ((posX > endposX && posY > endposY) || (posX < endposX && posY < endposY)) { area = new Rectangle(posX, posY, endposX - posX, endposY - posY); if (posX > endposX && posY > endposY) { widthX = endposX + (width / 2) - xmeasure.X / 2; widthY = endposY - ymeasure.Y * 1.2f; heightX = posX + xmeasure.X / 2; heightY = endposY + (height / 2) - ymeasure.Y / 2; } else if (posX < endposX && posY < endposY) { widthX = posX + (width / 2) - xmeasure.X / 2; widthY = posY - ymeasure.Y * 1.2f; heightX = endposX + xmeasure.X / 2; heightY = posY + (height / 2) - ymeasure.Y / 2; } } else { if (endposY < posY) { area = new Rectangle(posX, endposY, endposX - posX, posY - endposY); widthX = posX + (width / 2) - xmeasure.X / 2; widthY = endposY - ymeasure.Y * 1.2f; heightX = endposX + xmeasure.X / 2; heightY = endposY + (height / 2) - ymeasure.Y / 2; } if (endposX < posX) { area = new Rectangle(endposX, posY, posX - endposX, endposY - posY); widthX = endposX + (width / 2) - xmeasure.X / 2; widthY = posY - ymeasure.Y * 1.2f; heightX = posX + xmeasure.X / 2; heightY = posY + (height / 2) - ymeasure.Y / 2; } } _spriteBatch.Draw(_transparentRedRectangle, area, null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); _spriteBatch.DrawString(ffont, $"{tilesWidth}", new Vector2( widthX, widthY ), Color.Red, 0, Vector2.Zero, fscale, SpriteEffects.None, 0); _spriteBatch.DrawString(ffont, $"{tilesHeight}", new Vector2( heightX, heightY ), Color.Red, 0, Vector2.Zero, fscale, SpriteEffects.None, 0); } } } } private void DrawLinePreview() { if (_state.InsertMode == InsertMode.NewLine && _state.SelectedSnapPoint != null) { var snapPoint = new Vector2(_state.SelectedSnapPoint.X * _state.TileSize, _state.SelectedSnapPoint.Y * _state.TileSize); switch (_state.SelectedSnapPoint.Index) { case 1: break; case 2: snapPoint.X += _state.TileSize / 2; break; case 3: snapPoint.Y += _state.TileSize / 2; break; case 4: snapPoint.Y += _state.TileSize / 2; snapPoint.X += _state.TileSize / 2; break; } _spriteBatch.DrawCircle(snapPoint, _state.TileSize / 5f, 100, Color.Red, 2); if (_state.LineStart != null) { var posX = _state.LineStart.X * _state.TileSize; var posY = _state.LineStart.Y * _state.TileSize; var endposX = _state.SelectedSnapPoint.X * _state.TileSize; var endposY = _state.SelectedSnapPoint.Y * _state.TileSize; switch (_state.LineStart.Index) { case 1: break; case 2: posX += _state.TileSize / 2; break; case 3: posY += _state.TileSize / 2; break; case 4: posX += _state.TileSize / 2; posY += _state.TileSize / 2; break; } switch (_state.SelectedSnapPoint.Index) { case 1: break; case 2: endposX += _state.TileSize / 2; break; case 3: endposY += _state.TileSize / 2; break; case 4: endposX += _state.TileSize / 2; endposY += _state.TileSize / 2; break; } var l = Math.Sqrt(Math.Pow(posX - endposX, 2) + Math.Pow(posY - endposY, 2)); var angle = Math.Atan2(posY - endposY, endposX - posX); var angleRad = -(float)angle; if (l > 0) { _spriteBatch.Draw(_transparentRedRectangle, new Rectangle(posX, posY, (int)l, (int)(_state.TileSize * _state.LineWidth)), null, Color.White, angleRad, new Vector2(0, 0), SpriteEffects.None, 1); } } } } private void DrawSelectedIntersection() { if (_state.InsertMode == InsertMode.Overlay && _state.SelectedOverlay.Intersection) { var overlay = new Vector2(_state.SelectedOverlay.X * _state.TileSize, _state.SelectedOverlay.Y * _state.TileSize); _spriteBatch.DrawCircle(overlay, _state.TileSize / 3f, 100, Color.Red, 2); } } private void DrawSelectedWall() { if (_state.InsertMode == InsertMode.Wall) { var startWall = new Vector2(_state.SelectedWall.X * _state.TileSize, _state.SelectedWall.Y * _state.TileSize); _spriteBatch.DrawLine(startWall, _state.TileSize, MathHelper.ToRadians(90 * _state.SelectedWall.Rotation), Color.Red, 2); } } private void DrawPlayerOffline() { if (string.IsNullOrWhiteSpace(_sessionData.SessionName)) { var isoffscreen = IsOffscreen(_state.SelectedTile); if (isoffscreen) { var validPointer = GetPointerVector(new Point(_state.SelectedTile.X, _state.SelectedTile.Y), out var points); if (validPointer) { _spriteBatch.DrawPolygon(Vector2.Zero, points, Color.Red, 2); } } else { _spriteBatch.DrawRectangle(new Rectangle((_state.SelectedTile.X * _state.TileSize) - 2, (_state.SelectedTile.Y * _state.TileSize) - 2, _state.TileSize + 3, _state.TileSize + 3), Color.Red, 2); } } } private void ApplyShaderToMap() { Vector2 texelSize = new Vector2((float)rendertarget.Width, (float)rendertarget.Height); outlineShader.Parameters["ImageSize"].SetValue(texelSize); outlineShader.Parameters["BorderSize"].SetValue((int)(_state.TileSize / 100f * 10f)); outlineShader.Parameters["R"].SetValue(_settings.OverlayTintColor.R / 255.0f); outlineShader.Parameters["G"].SetValue(_settings.OverlayTintColor.G / 255.0f); outlineShader.Parameters["B"].SetValue(_settings.OverlayTintColor.B / 255.0f); GraphicsDevice.Clear(_settings.BackgroundColor); _spriteBatch.Begin( effect: outlineShader, sortMode: SpriteSortMode.Immediate); _spriteBatch.Draw(rendertarget, Vector2.Zero, null, Color.White); _spriteBatch.End(); } private void DrawMap() { GraphicsDevice.SetRenderTarget(rendertarget); GraphicsDevice.Clear(Color.Transparent); _spriteBatch.Begin( transformMatrix: Matrix.CreateTranslation(_viewportCenter), samplerState: SamplerState.PointClamp ); var items = _sessionData.Lines.Values.Union(_sessionData.Rooms.Values).OrderBy(t => t.Timestamp).ToArray(); for (var i = 0; i < items.Count(); i++) { var item = items.ElementAt(i); if (IsMapElementOffscreen(item)) { continue; } switch (item) { case Line l: DrawLine(l); break; case Room room: if (room.Delete) { DrawDelete(room); } else { DrawRoom(room); } break; } } _spriteBatch.End(); GraphicsDevice.SetRenderTarget(null); } private bool IsMapElementOffscreen(BaseMapEntity item) { SnapPoint start, end; switch (item) { case Line l: start = l.Start; end = l.End; break; case Room room: start = room.Start; end = room.End; break; default: return true; } var visibleTilesX = GraphicsDevice.Viewport.Width / _state.TileSize + 1; var visibleTilesY = GraphicsDevice.Viewport.Height / _state.TileSize + 1; var screenPositionTopLeft = new Point(200 - _state.TileSize + 0 * _state.TileSize - (int)_viewportCenter.X, 0 * _state.TileSize + _state.TileSize - (int)_viewportCenter.Y); var screenPositionBottomRight = new Point(visibleTilesX * _state.TileSize - (int)_viewportCenter.X, visibleTilesY * _state.TileSize - 20 - (int)_viewportCenter.Y); var tileTopLeft = new Point(screenPositionTopLeft.X / _state.TileSize, screenPositionTopLeft.Y / _state.TileSize); var tileBottomRight = new Point(screenPositionBottomRight.X / _state.TileSize, screenPositionBottomRight.Y / _state.TileSize); if (start.X < tileTopLeft.X && start.Y < tileTopLeft.Y && start.X > tileBottomRight.X && start.Y > tileBottomRight.Y && end.X < tileTopLeft.X && end.Y < tileTopLeft.Y && end.X > tileBottomRight.X && end.Y > tileBottomRight.Y ) { return true; } return false; } private void DrawGrid(int visibleTilesX, int visibleTilesY) { for (var i = -1; i < visibleTilesX + 2; i++) { var posX1 = i * _state.TileSize - _viewportCenter.X; var posY1 = -_viewportCenter.Y; posX1 -= posX1 % _state.TileSize; posY1 -= posY1 % _state.TileSize; var posX2 = i * _state.TileSize - _viewportCenter.X; var posY2 = GraphicsDevice.Viewport.Height - _viewportCenter.Y; posX2 -= posX2 % _state.TileSize; posY2 -= posY2 % _state.TileSize; _spriteBatch.DrawLine( posX1, posY1, posX2, posY2, _settings.GridColor); } for (var i = -1; i < visibleTilesY + 2; i++) { var posX1 = -_viewportCenter.X; var posY1 = i * _state.TileSize - _viewportCenter.Y; posX1 -= posX1 % _state.TileSize; posY1 -= posY1 % _state.TileSize; var posX2 = GraphicsDevice.Viewport.Width - _viewportCenter.X; var posY2 = i * _state.TileSize - _viewportCenter.Y; posX2 -= posX2 % _state.TileSize; posY2 -= posY2 % _state.TileSize; _spriteBatch.DrawLine(posX1, posY1, posX2, posY2, _settings.GridColor); } if (_showCellNumbers && _state.TileSize >= 30) { var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize / 8).Value ?? _fonts.Last().Value; var fscale = 1f; for (var i = -1; i < visibleTilesX + 2; i++) { for (var j = -1; j < visibleTilesY + 2; j++) { var screenPosition = new Point(i * _state.TileSize - (int)_viewportCenter.X, j * _state.TileSize - (int)_viewportCenter.Y); var x = screenPosition.X / _state.TileSize; var y = screenPosition.Y / _state.TileSize; _spriteBatch.DrawString(ffont, $"{x}:{y}", new Vector2( x * _state.TileSize + 2, y * _state.TileSize + 2 ), Color.Black, 0, Vector2.Zero, fscale, SpriteEffects.None, 0); } } } } private void DrawNotes() { if (_state.TileSize < 30) { return; } for (var i = 0; i < _sessionData.Notes.Values.Count; i++) { var note = _sessionData.Notes.Values.ElementAt(i); _spriteBatch.Draw( _comment, new Rectangle( note.X * _state.TileSize + _state.TileSize - (int)(_state.TileSize / 2) + _state.TileSize / 25, note.Y * _state.TileSize + _state.TileSize / 8 + _state.TileSize / 25, (int)(_state.TileSize / 2.5), (int)(_state.TileSize / 2.5 / 1.136) ), Color.Black * .2f ); _spriteBatch.Draw( _comment, new Rectangle( note.X * _state.TileSize + _state.TileSize - (int)(_state.TileSize / 2), note.Y * _state.TileSize + _state.TileSize / 8, (int)(_state.TileSize / 2.5), (int)(_state.TileSize / 2.5 / 1.136) ), _settings.NoteColor ); } } private void DrawOverlays() { for (var i = 0; i < _sessionData.Overlays.Values.Count; i++) { var tile = _sessionData.Overlays.Values.ElementAt(i); var spriteRec = _spriteSheet.SourceRectangle(tile.ID); float posX; float posY; if (tile.Intersection) { posX = tile.X * _state.TileSize; posY = tile.Y * _state.TileSize; _spriteBatch.Draw(_spriteSheet.Texture, new Vector2(posX + _state.TileSize / 25, posY + _state.TileSize / 25), spriteRec, Color.Black * .2f, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(spriteRec.Value.Width / 2, spriteRec.Value.Height / 2), ((float)_state.TileSize - 10) / spriteRec.Value.Width, SpriteEffects.None, 0); _spriteBatch.Draw(_spriteSheet.Texture, new Vector2(posX, posY), spriteRec, _settings.OverlayTintColor, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(spriteRec.Value.Width / 2, spriteRec.Value.Height / 2), ((float)_state.TileSize - 10) / spriteRec.Value.Width, SpriteEffects.None, 0); } else { posX = tile.X * _state.TileSize + _state.TileSize / 2f; posY = tile.Y * _state.TileSize + _state.TileSize / 2f; } _spriteBatch.Draw(_spriteSheet.Texture, new Vector2(posX + _state.TileSize / 25, posY + _state.TileSize / 25), spriteRec, Color.Black * .2f, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(spriteRec.Value.Width / 2, spriteRec.Value.Height / 2), ((float)_state.TileSize - 10) / spriteRec.Value.Width, SpriteEffects.None, 0); _spriteBatch.Draw(_spriteSheet.Texture, new Vector2(posX, posY), spriteRec, _settings.OverlayTintColor, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(spriteRec.Value.Width / 2, spriteRec.Value.Height / 2), ((float)_state.TileSize - 10) / spriteRec.Value.Width, SpriteEffects.None, 0); } } private void DrawPlayers() { var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize).Value ?? _fonts.Last().Value; var fscale = _state.TileSize / ((float)ffont.LineSpacing * 3f); var playerCells = _sessionData.Players.Select(m => m.Position).Distinct().ToList(); foreach (var cell in playerCells.Where(c => !IsOffscreen(c))) { var playersInCell = _sessionData.Players.Where(m => m.Position == cell).ToList(); var i = 0; foreach (var player in playersInCell) { var color = player.Color.ToColor(); var rectangle = new Rectangle(); var stringPosition = new Vector2(); var measure = ffont.MeasureString(player.Initials); var maxSize = Math.Max(measure.X, measure.Y); if (playersInCell.Count == 1) { fscale = (_state.TileSize - 2) / maxSize; rectangle = new Rectangle( player.Position.X * _state.TileSize, player.Position.Y * _state.TileSize, _state.TileSize - 1, _state.TileSize - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + 1, player.Position.Y * _state.TileSize + _state.TileSize - measure.Y * fscale); } else if (playersInCell.Count == 2) { fscale = (_state.TileSize / 2 - 2) / maxSize; if (i == 0) { rectangle = new Rectangle( player.Position.X * _state.TileSize, player.Position.Y * _state.TileSize, _state.TileSize / 2 - 1, _state.TileSize - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + 1, player.Position.Y * _state.TileSize + _state.TileSize - measure.Y * fscale); } else { rectangle = new Rectangle( player.Position.X * _state.TileSize + _state.TileSize / 2, player.Position.Y * _state.TileSize, _state.TileSize / 2 - 1, _state.TileSize - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + _state.TileSize / 2 + 1, player.Position.Y * _state.TileSize + _state.TileSize - measure.Y * fscale); } i++; } else if (playersInCell.Count >= 3) { fscale = (_state.TileSize / 2 - 2) / maxSize; switch (i) { case 0: rectangle = new Rectangle( player.Position.X * _state.TileSize, player.Position.Y * _state.TileSize, _state.TileSize / 2 - 1, _state.TileSize / 2 - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + 1, player.Position.Y * _state.TileSize + _state.TileSize / 2 - measure.Y * fscale); break; case 1: rectangle = new Rectangle( player.Position.X * _state.TileSize + _state.TileSize / 2, player.Position.Y * _state.TileSize, _state.TileSize / 2 - 1, _state.TileSize / 2 - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + _state.TileSize / 2 + 1, player.Position.Y * _state.TileSize + _state.TileSize / 2 - measure.Y * fscale); break; case 2: rectangle = new Rectangle( player.Position.X * _state.TileSize, player.Position.Y * _state.TileSize + _state.TileSize / 2, _state.TileSize / 2 - 1, _state.TileSize / 2 - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + 1, player.Position.Y * _state.TileSize + _state.TileSize - measure.Y * fscale); break; case 3: default: rectangle = new Rectangle( player.Position.X * _state.TileSize + _state.TileSize / 2, player.Position.Y * _state.TileSize + _state.TileSize / 2, _state.TileSize / 2 - 1, _state.TileSize / 2 - 1); stringPosition = new Vector2( player.Position.X * _state.TileSize + _state.TileSize / 2 + 1, player.Position.Y * _state.TileSize + _state.TileSize - measure.Y * fscale); break; } i++; } _spriteBatch.DrawRectangle(rectangle, color, 2); _spriteBatch.DrawString(ffont, player.Initials, stringPosition, color, 0, Vector2.Zero, fscale, SpriteEffects.None, 0); } } foreach (var player in _sessionData.Players) { var isOffscreen = IsOffscreen(player.Position); if (isOffscreen) { DrawPlayerPointer(player); } } } private void DrawPingPointer(Ping ping, GameTime gameTime) { var durationMs = 2000f; var baseRadius = _state.TileSize / 4f; var baseOuterRadius = (float)_state.TileSize; var iterations = 3f; var validPointer = GetPointerVector(new Point(ping.X, ping.Y), out var points); if (validPointer) { _spriteBatch.DrawPolygon(Vector2.Zero, points, ping.Player.Color.ToColor(), 4); for (var j = 0; j < iterations; j++) { var cycleTime = (((float)gameTime.TotalGameTime.TotalMilliseconds + (float)j * durationMs / iterations) % durationMs) / durationMs; var easing = Easings.Interpolate(cycleTime, Easings.Functions.SineEaseInOut); var v2 = new Vector2[points.Length]; var tCenter = new Vector2((points[0].X + points[1].X + points[2].X) / 3f, (points[0].Y + points[1].Y + points[2].Y) / 3f); for (int i1 = 0; i1 < v2.Length; i1++) { var svx = ((points[i1].X - tCenter.X) * (1 + (2 * easing))) + tCenter.X; var svy = ((points[i1].Y - tCenter.Y) * (1 + (2 * easing))) + tCenter.Y; v2[i1] = new Vector2(svx, svy); } _spriteBatch.DrawPolygon( offset: Vector2.Zero, points: v2, color: new Color(ping.Player.Color.ToColor(), 1f - easing), thickness: 2 + 2 * (1 - easing)); } } } private bool GetPointerVector(Point target, out Vector2[] points) { var offset = _state.TileSize / 2; var leftBound = 200 + offset; var topBound = 75 + offset; var bottomBound = 25 + offset; var rightBound = offset; points = new Vector2[0]; var center = new Point((Window.ClientBounds.Width + leftBound) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y); // center var p1 = new Vector2(center.X, center.Y); // point var p2 = new Vector2( target.X * _state.TileSize + offset, target.Y * _state.TileSize + offset); // top right var p3 = new Vector2( Window.ClientBounds.Width - _viewportCenter.X - rightBound, topBound - _viewportCenter.Y); //bottom right var p4 = new Vector2( Window.ClientBounds.Width - _viewportCenter.X - rightBound, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); var ua1 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); p3 = new Vector2(leftBound - _viewportCenter.X, topBound - _viewportCenter.Y); p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, topBound - _viewportCenter.Y); var ua2 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); p3 = new Vector2(leftBound - _viewportCenter.X, topBound - _viewportCenter.Y); p4 = new Vector2(leftBound - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); var ua3 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); p3 = new Vector2(leftBound - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); var ua4 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); var uas = new List { ua1, ua2, ua3, ua4 }; if (uas.Any(u => u > 0 && u < 1)) { var ua = uas.Where(u => u > 0 && u < 1).Min(); var i = uas.IndexOf(ua); var x = (p1.X + ua * (p2.X - p1.X)); var y = (p1.Y + ua * (p2.Y - p1.Y)); switch (i) { case 0: x += offset; points = new Vector2[] { new Vector2(x, y), new Vector2(x - 20, y - 10), new Vector2(x - 20, y + 10), new Vector2(x, y) }; break; case 1: y -= offset; points = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y + 20), new Vector2(x + 10, y + 20), new Vector2(x, y) }; break; case 2: x -= offset; points = new Vector2[] { new Vector2(x, y), new Vector2(x + 20, y + 10), new Vector2(x + 20, y - 10), new Vector2(x, y) }; break; case 3: y += offset; points = new Vector2[] { new Vector2(x, y), new Vector2(x + 10, y - 20), new Vector2(x - 10, y - 20), new Vector2(x, y) }; break; } return true; } return false; } private void DrawPlayerPointer(Player player) { var validPointer = GetPointerVector(new Point(player.Position.X, player.Position.Y), out var points); if (validPointer) { _spriteBatch.DrawPolygon(Vector2.Zero, points, player.Color.ToColor(), 2); } } ArrayPool _vector2Pool; private bool IsOffscreen(Tile position) { var boxTL = new Point(200 - _state.TileSize / 2, 75 - _state.TileSize / 2); var boxBR = new Point(GraphicsDevice.Viewport.Width + _state.TileSize / 2, GraphicsDevice.Viewport.Height - 25 + _state.TileSize / 2); var tileTL = new Point(position.X * _state.TileSize + (int)_viewportCenter.X, position.Y * _state.TileSize + (int)_viewportCenter.Y); var tileBR = new Point(position.X * _state.TileSize + (int)_viewportCenter.X + _state.TileSize, position.Y * _state.TileSize + (int)_viewportCenter.Y + _state.TileSize); if (tileTL.X <= boxTL.X || tileTL.Y <= boxTL.Y || tileBR.X >= boxBR.X || tileBR.Y >= boxBR.Y) { return true; } return false; } private int _borderWidth => (_state.TileSize / 6) % 2 == 0 ? (_state.TileSize / 6) : (_state.TileSize / 6) + 1; private void DrawTiles() { for (var i = 0; i < _sessionData.Map.Values.Count; i++) { var tile = _sessionData.Map.Values.ElementAt(i); var content = Content.Load($"tiles/{tile.ID}"); var posX = tile.X * _state.TileSize + _state.TileSize / 2f; var posY = tile.Y * _state.TileSize + _state.TileSize / 2f; _spriteBatch.Draw(content, new Vector2(posX, posY), null, Color.White, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize) / content.Width, SpriteEffects.None, 0); } } private void DrawLine(Line tile) { var posX = tile.Start.X * _state.TileSize; var posY = tile.Start.Y * _state.TileSize; var endposX = tile.End.X * _state.TileSize; var endposY = tile.End.Y * _state.TileSize; switch (tile.Start.Index) { case 1: break; case 2: posX += _state.TileSize / 2; break; case 3: posY += _state.TileSize / 2; break; case 4: posX += _state.TileSize / 2; posY += _state.TileSize / 2; break; } switch (tile.End.Index) { case 1: break; case 2: endposX += _state.TileSize / 2; break; case 3: endposY += _state.TileSize / 2; break; case 4: endposX += _state.TileSize / 2; endposY += _state.TileSize / 2; break; } var length = (int)Math.Sqrt(Math.Pow(posX - endposX, 2) + Math.Pow(posY - endposY, 2)); var height = (int)(_state.TileSize * tile.Width); if (length > 0) { var angle = Math.Atan2(posY - endposY, endposX - posX); var angleRad = -(float)angle; _spriteBatch.Draw(_whiteRectangle, new Rectangle(posX, posY, length, height), null, Color.White, angleRad, new Vector2(0, 0), SpriteEffects.None, 1); } } private void DrawRoom(Room tile) { var posX = tile.Start.X * _state.TileSize; var posY = tile.Start.Y * _state.TileSize; var endposX = tile.End.X * _state.TileSize; var endposY = tile.End.Y * _state.TileSize; switch (tile.Start.Index) { case 1: break; case 2: posX += _state.TileSize / 2; break; case 3: posY += _state.TileSize / 2; break; case 4: posX += _state.TileSize / 2; posY += _state.TileSize / 2; break; } switch (tile.End.Index) { case 1: break; case 2: endposX += _state.TileSize / 2; break; case 3: endposY += _state.TileSize / 2; break; case 4: endposX += _state.TileSize / 2; endposY += _state.TileSize / 2; break; } if (posX != endposX && posY != endposY) { if ((posX < endposX && posY < endposY)) { _spriteBatch.Draw(_whiteRectangle, new Rectangle(posX, posY, endposX - posX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } else if ((posX > endposX && posY > endposY)) { _spriteBatch.Draw(_whiteRectangle, new Rectangle(posX, posY, endposX - posX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } else { if (endposY < posY) { _spriteBatch.Draw(_whiteRectangle, new Rectangle(posX, endposY, endposX - posX, posY - endposY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } if (endposX < posX) { _spriteBatch.Draw(_whiteRectangle, new Rectangle(endposX, posY, posX - endposX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } } } } Effect outlineShader; private Texture2D _transparentRedRectangle; private Texture2D _whiteRectangle; private Dictionary _wallsContent; private SpriteSheet _spriteSheet; private void DrawDelete(Room tile) { var posX = tile.Start.X * _state.TileSize; var posY = tile.Start.Y * _state.TileSize; var endposX = tile.End.X * _state.TileSize; var endposY = tile.End.Y * _state.TileSize; var ww = _state.TileSize / _settings.TileDeleteDivider; if (posX == endposX) { endposX += ww; posX -= ww; } if (posY == endposY) { endposY += ww; posY -= ww; } switch (tile.Start.Index) { case 1: break; case 2: posX += _state.TileSize / 2; break; case 3: posY += _state.TileSize / 2; break; case 4: posX += _state.TileSize / 2; posY += _state.TileSize / 2; break; } switch (tile.End.Index) { case 1: break; case 2: endposX += _state.TileSize / 2; break; case 3: endposY += _state.TileSize / 2; break; case 4: endposX += _state.TileSize / 2; endposY += _state.TileSize / 2; break; } if (posX != endposX && posY != endposY) { Rectangle r = new Rectangle(); if ((posX < endposX && posY < endposY)) { r = new Rectangle(posX, posY, endposX - posX, endposY - posY); } else if ((posX > endposX && posY > endposY)) { r = new Rectangle(posX, posY, endposX - posX, endposY - posY); } else { if (endposY < posY) { r = new Rectangle(posX, endposY, endposX - posX, posY - endposY); } if (endposX < posX) { r = new Rectangle(endposX, posY, posX - endposX, endposY - posY); } } _spriteBatch.Draw(_whiteRectangle, r, null, Color.Black, 0, Vector2.Zero, SpriteEffects.None, 1); } } private void DrawWalls() { for (var i = 0; i < _sessionData.Walls.Values.Count; i++) { var wall = _sessionData.Walls.Values.ElementAt(i); var content = _wallsContent[$"{wall.ID}"]; var scale = _state.TileSize / (float)content.Height; var offset = scale * content.Width / 2f; var posX = wall.X * _state.TileSize; var posY = wall.Y * _state.TileSize; if (wall.Rotation == 1) { posX -= (int)offset; } else if (wall.Rotation == 0) { posY += (int)offset; } _spriteBatch.Draw(content, new Vector2(posX, posY), null, Color.White, MathHelper.ToRadians(90 * (wall.Rotation - 1)), new Vector2(0, 0), scale, SpriteEffects.None, 0); } } private async void OnButtonJoinSessionClicked(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 SessionWindow; var isValid = ValidateTextbox(localContent.TxtSession); if (!isValid) { return; } if (_communicationManager.Connection.State != HubConnectionState.Connected) { _mainWidget.lblConnectionStatus.Text = "Connecting"; await _communicationManager.Connection.StartAsync(); UpdateConnectionState(_communicationManager.Connection); } var successful = false; try { var result = await _communicationManager.Connection?.InvokeAsync("JoinSession", localContent.TxtSession.Text); if (result != null) { _sessionData.Map = result.Map; _sessionData.Walls = result.Walls; _sessionData.Overlays = result.Overlays; _sessionData.MapEntityAdded -= OnMapEntityAdded; _sessionData.MapEntityDeleted -= OnMapEntityDeleted; _sessionData.MapEntityAdded += OnMapEntityAdded; _sessionData.MapEntityDeleted += OnMapEntityDeleted; _sessionData.SessionId = result.SessionId; _sessionData.SessionName = result.SessionName; } successful = result != null; } catch (Exception ex) { Console.WriteLine(ex.Message); } if (successful) { _sessionData.SessionName = localContent.TxtSession.Text; _mainWidget.lblSessionName.Text = _sessionData.SessionName; _mainWidget.MenuConnectSync.Enabled = true; _mainWidget.MenuConnectUpload.Enabled = true; localWindow.Close(); } } private async void OnButtonNewCampaignClicked(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 CampaignWindow; var isValid = ValidateTextbox(localContent.TxtCampaign); if (!isValid) { return; } if (_communicationManager.Connection.State != HubConnectionState.Connected) { _mainWidget.lblConnectionStatus.Text = "Connecting"; await _communicationManager.Connection.StartAsync(); UpdateConnectionState(_communicationManager.Connection); } var successful = false; try { await _communicationManager.Api.NewCampaign(localContent.TxtCampaign.Text); //if (result) //{ // _sessionData.SessionName = localContent.TxtSession.Text; // _sessionData.MapEntityAdded -= OnMapEntityAdded; // _sessionData.MapEntityDeleted -= OnMapEntityDeleted; // _sessionData.MapEntityAdded += OnMapEntityAdded; // _sessionData.MapEntityDeleted += OnMapEntityDeleted; //} //successful = result; //var result2 = await _communicationManager.Connection?.InvokeAsync("JoinSession", localContent.TxtSession.Text); //_sessionData.SessionId = result2.SessionId; //_sessionData.SessionName = localContent.TxtSession.Text; } catch (Exception ex) { ExceptionlessClient.Default.SubmitException(ex); } if (successful) { //_sessionData.SessionName = localContent.TxtSession.Text; //_communicationManager.SessionData.Map = _sessionData.Map; //_communicationManager.SessionData.Overlays = _sessionData.Overlays; //_communicationManager.SessionData.Walls = _sessionData.Walls; //_mainWidget.lblSessionName.Text = _sessionData.SessionName; //_mainWidget.MenuConnectSync.Enabled = true; //_mainWidget.MenuConnectUpload.Enabled = true; localWindow.Close(); } } private async void OnButtonNewSessionClicked(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 SessionWindow; var isValid = ValidateTextbox(localContent.TxtSession); if (!isValid) { return; } if (_communicationManager.Connection.State != HubConnectionState.Connected) { _mainWidget.lblConnectionStatus.Text = "Connecting"; await _communicationManager.Connection.StartAsync(); UpdateConnectionState(_communicationManager.Connection); } var successful = false; try { var result = await _communicationManager.Api.NewSession(localContent.TxtSession.Text); if (result) { _sessionData.SessionName = localContent.TxtSession.Text; _sessionData.MapEntityAdded -= OnMapEntityAdded; _sessionData.MapEntityDeleted -= OnMapEntityDeleted; _sessionData.MapEntityAdded += OnMapEntityAdded; _sessionData.MapEntityDeleted += OnMapEntityDeleted; } successful = result; var result2 = await _communicationManager.Connection?.InvokeAsync("JoinSession", localContent.TxtSession.Text); _sessionData.SessionId = result2.SessionId; _sessionData.SessionName = localContent.TxtSession.Text; } catch (Exception ex) { ExceptionlessClient.Default.SubmitException(ex); } if (successful) { _sessionData.SessionName = localContent.TxtSession.Text; _communicationManager.SessionData.Map = _sessionData.Map; _communicationManager.SessionData.Overlays = _sessionData.Overlays; _communicationManager.SessionData.Walls = _sessionData.Walls; _mainWidget.lblSessionName.Text = _sessionData.SessionName; _mainWidget.MenuConnectSync.Enabled = true; _mainWidget.MenuConnectUpload.Enabled = true; localWindow.Close(); } } private void UpdateConnectionState(HubConnection connection) { switch (connection.State) { case HubConnectionState.Connected: _mainWidget.lblConnectionStatus.Text = "Connected"; break; case HubConnectionState.Connecting: _mainWidget.lblConnectionStatus.Text = "Connecting"; break; case HubConnectionState.Disconnected: _mainWidget.lblConnectionStatus.Text = "Disconnected"; break; case HubConnectionState.Reconnecting: _mainWidget.lblConnectionStatus.Text = "Reconnecting"; break; } } private void OnButtonNoteOkClick(object sender, EventArgs e) { var button = ((TextButton)sender); Container container = button.Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as NoteWindow; var note = new Note { X = _state.SelectedNote.X, Y = _state.SelectedNote.Y, Text = localContent.NoteText.Text }; _sessionData.NewNote(note); localWindow.Close(); } private void OnButtonNoteCancelClick(object sender, EventArgs e) { var button = ((TextButton)sender); Container container = button.Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; localWindow.Close(); } private async void OnButtonLoginClick(object sender, EventArgs e) { var button = ((TextButton)sender); Container container = button.Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as LoginRegisterWindow; if (!button.Enabled) { return; } var isValid = true; isValid &= ValidateTextbox(localContent.TxtEmail); isValid &= ValidateTextbox(localContent.TxtPassword); if (!isValid) { localContent.LblLoginError.Text = "Username or password is not valid"; localContent.LblLoginError.Visible = true; return; } var successful = false; try { button.Text = "Wait..."; localContent.LblLoginError.Text = ""; localContent.LblLoginError.Visible = false; _authResponse = await _communicationManager.Login(new AuthenticateModel { Username = localContent.TxtEmail.Text, Email = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text }); successful = _authResponse != null; } catch (Refit.ApiException refitException) { ExceptionlessClient.Default.SubmitException(refitException); localContent.LblLoginError.Text = refitException.Content; localContent.LblLoginError.Visible = true; } catch (Exception ex) { ExceptionlessClient.Default.SubmitException(ex); localContent.LblLoginError.Text = "Can't connect to the server"; localContent.LblLoginError.Visible = true; Debug.Write(ex); } finally { button.Enabled = true; button.Text = "Login"; } if (successful) { _mainWidget.MenuConnectNew.Enabled = true; _mainWidget.MenuConnectJoin.Enabled = true; _mainWidget.lblUsername.Text = $"{_authResponse.Username} - {_authResponse.Initials}"; localWindow.Close(); } } private async void OnButtonRegisterClick(object sender, EventArgs e) { var button = ((TextButton)sender); Container container = button.Parent; while (!(container is Window)) { container = container.Parent; } var localWindow = (Window)container; var localContent = localWindow.Content as LoginRegisterWindow; if (!button.Enabled) { return; } var isValid = true; isValid &= ValidateTextbox(localContent.TxtEmail); isValid &= ValidateTextbox(localContent.TxtPassword); isValid &= ValidateTextbox(localContent.TxtFirstname); isValid &= ValidateTextbox(localContent.TxtLastname); isValid &= ValidateTextbox(localContent.TxtInitials); if (!isValid) { localContent.LblLoginError.Text = "Please complete all the fields"; localContent.LblLoginError.Visible = true; return; } var successful = false; try { button.Text = "Wait..."; localContent.LblLoginError.Text = ""; localContent.LblLoginError.Visible = false; var result = await _communicationManager.Register(new RegisterModel { Username = localContent.TxtEmail.Text, Email = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text, FirstName = localContent.TxtFirstname.Text, LastName = localContent.TxtLastname.Text, Initials = localContent.TxtInitials.Text }); if (result.Result) { _authResponse = await _communicationManager.Login(new AuthenticateModel { Username = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text }); successful = true; } else { localContent.LblLoginError.Text = result.Errors.FirstOrDefault(); localContent.LblLoginError.Visible = true; } } catch (Refit.ApiException refitException) { ExceptionlessClient.Default.SubmitException(refitException); localContent.LblLoginError.Text = refitException.Content; localContent.LblLoginError.Visible = true; } catch (Exception ex) { ExceptionlessClient.Default.SubmitException(ex); localContent.LblLoginError.Text = "Can't connect to the server"; localContent.LblLoginError.Visible = true; Debug.Write(ex); } finally { button.Enabled = true; button.Text = "Register"; } if (successful) { _mainWidget.MenuConnectNew.Enabled = true; _mainWidget.MenuConnectJoin.Enabled = true; _mainWidget.lblUsername.Text = $"{_authResponse.Username} - {_authResponse.Initials}"; localWindow.Close(); } } private void OnContextMenuNewNoteClick(object sender, EventArgs e) { _desktop.HideContextMenu(); var noteWindow = new NoteWindow(); Window window = new() { Title = $" Note on {_state.SelectedTile.X}:{_state.SelectedTile.Y}" }; noteWindow.BtnOk.Click += OnButtonNoteOkClick; noteWindow.BtnCancel.Click += OnButtonNoteCancelClick; window.Content = noteWindow; window.ShowModal(_desktop); noteWindow.NoteText.SetKeyboardFocus(); } private void OnContextMenuDeleteNoteClick(object sender, EventArgs e) { _desktop.HideContextMenu(); _sessionData.DeleteNote(_state.SelectedNote); } private void OnContextMenuViewNoteClick(object sender, EventArgs e) { _desktop.HideContextMenu(); EditNote(_state.SelectedNote); } private void OnOverlayButtonClicked(object sender, EventArgs e) { _state.CurrentOverlayId = ((ImageButton)sender).Id; _mainWidget.ClearSelection(); ((ImageButton)sender).Border = new SolidBrush(Color.Red); ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); _state.InsertMode = InsertMode.Overlay; } private void OnWallButtonClicked(object sender, EventArgs e) { _state.CurrentWallId = ((ImageButton)sender).Id; _mainWidget.ClearSelection(); ((ImageButton)sender).Border = new SolidBrush(Color.Red); ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); _state.InsertMode = InsertMode.Wall; } private void OnMenuConnectJoinSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "Join mapping session" }; var content = new SessionWindow(); content.BtnLogin.Text = "Join"; content.BtnLogin.Click += OnButtonJoinSessionClicked; window.Content = content; window.ShowModal(_desktop); content.TxtSession.SetKeyboardFocus(); } private void OnMenuConnectLoginSelected(object sender, EventArgs e) { Window window = new() { Title = "Login" }; var content = new LoginRegisterWindow(); #if DEBUG content.TxtEmail.Text="michele.scandura@outlook.com"; content.TxtPassword.Text="slePharland!79"; #endif content.RdoLogin.IsPressed = true; content.RdoLogin.Click += (s, e) => { content.TxtFirstname.Visible = false; content.TxtLastname.Visible = false; content.TxtInitials.Visible = false; content.LblFirstname.Visible = false; content.LblLastname.Visible = false; content.LblInitials.Visible = false; content.BtnLogin.Visible = true; content.BtnRegister.Visible = false; window.Title = "Login"; }; content.RdoRegister.Click += (s, e) => { content.TxtFirstname.Visible = true; content.TxtLastname.Visible = true; content.TxtInitials.Visible = true; content.LblFirstname.Visible = true; content.LblLastname.Visible = true; content.LblInitials.Visible = true; content.BtnLogin.Visible = false; content.BtnRegister.Visible = true; window.Title = "Register"; }; content.BtnRegister.Click += OnButtonRegisterClick; content.BtnLogin.Click += OnButtonLoginClick; window.Content = content; window.ShowModal(_desktop); content.TxtEmail.SetKeyboardFocus(); } private async void OnMenuConnectSyncSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } var serverMap = await _communicationManager.Api.Session(_sessionData.SessionName); _sessionData.Overlays = serverMap.Overlays; _sessionData.Map = serverMap.Map; _sessionData.Walls = serverMap.Walls; _sessionData.Notes = serverMap.Notes; _sessionData.Lines = serverMap.Lines; _sessionData.Rooms = serverMap.Rooms; } private async void OnMenuConnectUploadSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } await _communicationManager.Api.SaveSnapshot(_sessionData, _sessionData.SessionName); } private void OnMenuConnectNewSelected(object sender, EventArgs e) { if (!((MenuItem)sender).Enabled) { return; } Window window = new() { Title = "New mapping session" }; var content = new SessionWindow(); content.BtnLogin.Text = "Join"; content.BtnLogin.Click += OnButtonNewSessionClicked; window.Content = content; window.ShowModal(_desktop); content.TxtSession.SetKeyboardFocus(); } 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, _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)); _sessionData.Map = loadData.Map; _sessionData.Overlays = loadData.Overlays; _sessionData.Walls = loadData.Walls; _sessionData.Rooms = loadData.Rooms; _sessionData.Lines = loadData.Lines; }; dialog.ShowModal(_desktop); } 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 bool ValidateTextbox(TextBox textBox) { var valid = !string.IsNullOrWhiteSpace(textBox.Text); if (!valid) { textBox.Background = new SolidBrush(Color.Red); } else { textBox.Background = new SolidBrush(new Color(51, 51, 51)); ; } return valid; } } internal class SpriteSheet { internal Texture2D Texture; internal Dictionary index; public void LoadContent(ContentManager content, string spriteIndex, string texture) { index = content.Load>(spriteIndex); Texture = content.Load(texture); } internal Rectangle? SourceRectangle(string spriteName) { Rectangle r; bool hasValue = index.TryGetValue(spriteName, out r); if (hasValue) { return r; } else { throw new Exception("value doesn't exist"); } } } }