using Exceptionless; using Exceptionless.Models; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MonoGame.Extended; 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 Newtonsoft.Json; using Sledgemapper.Shared.Entities; using Sledgemapper.UI; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; 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 = false; _communicationManager = new CommunicationManager(_sessionData); _communicationManager.Connection.Reconnected += OnHubReconnected; _communicationManager.Connection.Reconnecting += OnHubReconnecting; _communicationManager.Connection.Closed += OnHubDisconnected; _state = new State(); _settings = new Settings(); } 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(); // rendertarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, true, SurfaceFormat.Alpha8, DepthFormat.Depth16, 0, RenderTargetUsage.DiscardContents); //rendertarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height); } catch { System.Console.WriteLine("rendertarget dispose exception"); } } private void AddItemToToolGrid(Grid grid, EventHandler eventAction, string folder) { var tilesFolderContent = Content.LoadContentFolder(folder); 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++; } } } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); outlineShader = Content.Load("shaders/OutlineShader"); outlineShader2 = Content.Load("shaders/OutlineShader2"); MyraEnvironment.Game = this; ResetRenderTarget(); // rendertarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, true, SurfaceFormat.Alpha8, DepthFormat.Depth16, 0, RenderTargetUsage.DiscardContents); _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.MenuConnectNew.Enabled = false; _mainWidget.MenuConnectJoin.Enabled = false; _mainWidget.MenuConnectSync.Enabled = false; _mainWidget.MenuConnectUpload.Enabled = false; _mainWidget.BtnToolbarLine.Click += OnBtnToolbarLinClicked; _mainWidget.BtnToolbarRoom.Click += OnBtnToolbarRoomClicked; _mainWidget.BtnToolbarTile.Click += OnBtnToolbarTileClicked; _mainWidget.BtnToolbarWall.Click += OnBtnToolbarWallClicked; _mainWidget.BtnToolbarDelete.Click += OnBtnToolbarDeleteClicked; _mainWidget.BtnToolbarTile.Visible = false; _mainWidget.BtnToolbarWall.Visible = false; AddItemToToolGrid(_mainWidget.GridWalls, OnWallButtonClicked, "walls"); AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, "overlays"); _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")); _desktop.Root = _mainWidget; } private void OnBtnToolbarDeleteClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewDelete; } private void OnBtnToolbarWallClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewWall; } private void OnBtnToolbarTileClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewTile; } private void OnBtnToolbarRoomClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewRoom; } private void OnBtnToolbarLinClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewLine; } 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(); foreach (var note in _sessionData.Notes.Values) { 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) { _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); } _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 center = new Point(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2); 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; } } 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; } 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; GraphicsDevice.SetRenderTarget(rendertarget); _spriteBatch.Begin(depthStencilState: DepthStencilState.None, transformMatrix: Matrix.CreateTranslation(_viewportCenter), blendState: BlendState.Opaque, //rasterizerState:RasterizerState.CullClockwise, samplerState: SamplerState.PointClamp, sortMode: SpriteSortMode.Deferred); GraphicsDevice.Clear(Color.Transparent); foreach (var item in _sessionData.Lines.Values.Union(_sessionData.Rooms.Values).OrderBy(t => t.Timestamp)) { 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); 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); // outlineShader2.Parameters["ScreenSize"].SetValue(texelSize); _spriteBatch.Begin( effect: outlineShader, blendState: BlendState.NonPremultiplied, sortMode: SpriteSortMode.Deferred); GraphicsDevice.Clear(_settings.BackgroundColor); _spriteBatch.Draw(rendertarget, Vector2.Zero, null, Color.White); _spriteBatch.End(); _spriteBatch.Begin( transformMatrix: Matrix.CreateTranslation(_viewportCenter), sortMode: SpriteSortMode.Deferred); DrawTiles(); DrawWalls(); DrawOverlays(); DrawNotes(); DrawGrid(visibleTilesX, visibleTilesY); if (string.IsNullOrWhiteSpace(_sessionData.SessionName)) { var isoffscreen = IsOffscreen(_state.SelectedTile); if (isoffscreen) { var center = new Point((Window.ClientBounds.Width + 200) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y); var cx = center.X / _state.TileSize; var cy = center.Y / _state.TileSize; var p1 = new Vector2(cx * _state.TileSize, cy * _state.TileSize); var p2 = new Vector2(_state.SelectedTile.X * _state.TileSize, _state.SelectedTile.Y * _state.TileSize); var p3 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y); var p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); 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(200 - _viewportCenter.X, 0 - _viewportCenter.Y); p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _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(200 - _viewportCenter.X, 0 - _viewportCenter.Y); p4 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); 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(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); 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));// + _viewportCenter.X; var y = (p1.Y + ua * (p2.Y - p1.Y));// + _viewportCenter.Y; switch (i) { case 0: _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x - 20, y + 10), new Vector2(x - 20, y - 10), new Vector2(x, y) }, Color.Red, 2); break; case 1: y += 20; _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y + 20), new Vector2(x + 10, y + 20), new Vector2(x, y) }, Color.Red, 2); break; case 2: x += 0; _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x + 20, y + 10), new Vector2(x + 20, y - 10), new Vector2(x, y) }, Color.Red, 2); break; case 3: y -= 20; _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y - 20), new Vector2(x + 10, y - 20), new Vector2(x, y) }, Color.Red, 2); break; } } } _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); } DrawPlayers(); 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); } 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); } 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); //line preview if (_state.LineStart != null) { // border pass // var content = Content.Load($"tiles/{tile.ID}"); 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;//MathHelper.ToRadians((float)angle); if (l > 0) { var whiteRectangle = new Texture2D(GraphicsDevice, (int)l, (int)(_state.TileSize * _state.LineWidth)); whiteRectangle.SetData(Enumerable.Range(0, (int)l * ((int)(_state.TileSize * _state.LineWidth))).Select(i => new Color(Color.Red, 80)).ToArray()); _spriteBatch.Draw(whiteRectangle, new Rectangle(posX, posY, (int)l, (int)(_state.TileSize * _state.LineWidth)), null, Color.White, angleRad, new Vector2(0, 0), SpriteEffects.None, 1); } } } 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); //line preview 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; } if (posX != endposX && posY != endposY) { var whiteRectangle = new Texture2D(GraphicsDevice, 1, 1); // whiteRectangle.SetData(new[] { new Color(Color.Red, 80) }); whiteRectangle.SetData(new[] { new Color(Color.Red, 80) }); if ((posX > endposX && posY > endposY) || (posX < endposX && posY < endposY)) { _spriteBatch.Draw(whiteRectangle, new Rectangle(posX, posY, endposX - posX, endposY - posY), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); } else { if (endposY < posY) { _spriteBatch.Draw(whiteRectangle, new Rectangle(posX, endposY, endposX - posX, posY - endposY), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); } if (endposX < posX) { _spriteBatch.Draw(whiteRectangle, new Rectangle(endposX, posY, posX - endposX, endposY - posY), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); } } } } } _spriteBatch.End(); try { _desktop?.Render(); } catch { System.Console.WriteLine("Desktop render error"); } base.Draw(gameTime); } 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; } foreach (var note in _sessionData.Notes.Values) { _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() { foreach (var tile in _sessionData.Overlays.Values) { var content = Content.Load($"overlays/{tile.ID}"); float posX; float posY; if (tile.Intersection) { posX = tile.X * _state.TileSize; posY = tile.Y * _state.TileSize; _spriteBatch.Draw(content, new Vector2(posX + _state.TileSize / 25, posY + _state.TileSize / 25), null, Color.Black * .2f, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0); _spriteBatch.Draw(content, new Vector2(posX, posY), null, _settings.OverlayTintColor, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0); } else { posX = tile.X * _state.TileSize + _state.TileSize / 2f; posY = tile.Y * _state.TileSize + _state.TileSize / 2f; } _spriteBatch.Draw(content, new Vector2(posX + _state.TileSize / 25, posY + _state.TileSize / 25), null, Color.Black * .2f, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0); _spriteBatch.Draw(content, new Vector2(posX, posY), null, _settings.OverlayTintColor, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0); } } private void DrawPlayers() { foreach (var player in _sessionData.Players.Copy()) { var color = player.Color.ToColor(); _spriteBatch.DrawRectangle(new Rectangle(player.Position.X * _state.TileSize - 4, player.Position.Y * _state.TileSize - 4, _state.TileSize + 7, _state.TileSize + 7), color, 2); var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize).Value ?? _fonts.Last().Value; var fscale = _state.TileSize / ((float)ffont.LineSpacing * 2); _spriteBatch.DrawString(ffont, player.Initials, new Vector2(player.Position.X * _state.TileSize + 2, player.Position.Y * _state.TileSize + _state.TileSize - 2 - ffont.LineSpacing * fscale), color, 0, Vector2.Zero, fscale, SpriteEffects.None, 0); } foreach (var player in _sessionData.Players.Copy()) { var isOffscreen = IsOffscreen(player.Position); if (isOffscreen) { DrawPlayerPointer(player); } } } private void DrawPlayerPointer(Player player) { var center = new Point((Window.ClientBounds.Width + 200) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y); var cx = center.X / _state.TileSize; var cy = center.Y / _state.TileSize; var p1 = new Vector2(cx * _state.TileSize, cy * _state.TileSize); var p2 = new Vector2(player.Position.X * _state.TileSize, player.Position.Y * _state.TileSize); var p3 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y); var p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); 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(200 - _viewportCenter.X, 0 - _viewportCenter.Y); p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _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(200 - _viewportCenter.X, 0 - _viewportCenter.Y); p4 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); 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(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); 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));// + _viewportCenter.X; var y = (p1.Y + ua * (p2.Y - p1.Y));// + _viewportCenter.Y; Vector2[] vertexes = new Vector2[0]; switch (i) { case 0: vertexes = 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 += 20; vertexes = 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 += 0; vertexes = 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 -= 20; vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y - 20), new Vector2(x + 10, y - 20), new Vector2(x, y) }; break; } _spriteBatch.DrawPolygon(Vector2.Zero, vertexes, player.Color.ToColor(), 2); } } private bool IsOffscreen(Tile position) { 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 (position.X < tileTopLeft.X || position.Y < tileTopLeft.Y || position.X > tileBottomRight.X || position.Y > tileBottomRight.Y) { return true; } return false; } private int _borderWidth => (_state.TileSize / 6) % 2 == 0 ? (_state.TileSize / 6) : (_state.TileSize / 6) + 1; private void DrawTiles() { // foreach (var tile in _sessionData.Map.Values) // { // 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); // } // border pass // foreach (var tile in _sessionData.Map.Values) // { // var content = Content.Load($"tiles/{tile.ID}"); // var posX = tile.X * _state.TileSize; // var posY = tile.Y * _state.TileSize; // var whiteRectangle = new Texture2D(GraphicsDevice, 1, 1); // whiteRectangle.SetData(new[] { _settings.OverlayTintColor }); // _spriteBatch.Draw(whiteRectangle, new Rectangle((int)posX - _borderWidth / 2, (int)posY - _borderWidth / 2, _state.TileSize + _borderWidth, _state.TileSize + _borderWidth), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); // } //inner pass // foreach (var tile in _sessionData.Map.Values) // { // var content = Content.Load($"tiles/{tile.ID}"); // var posX = tile.X * _state.TileSize; // var posY = tile.Y * _state.TileSize; // var whiteRectangle = new Texture2D(GraphicsDevice, 1, 1); // whiteRectangle.SetData(new[] { Color.White }); // _spriteBatch.Draw(whiteRectangle, new Rectangle((int)posX, (int)posY, _state.TileSize, _state.TileSize), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); // } foreach (var tile in _sessionData.Map.Values) { 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 innerRectangle = new Texture2D(GraphicsDevice, 1, 1); innerRectangle.SetData(new[] { Color.White }); var angle = Math.Atan2(posY - endposY, endposX - posX); var angleRad = -(float)angle; _spriteBatch.Draw(innerRectangle, 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) { var borderRectangle = new Texture2D(GraphicsDevice, 1, 1); borderRectangle.SetData(new[] { Color.White }); if ((posX < endposX && posY < endposY)) { _spriteBatch.Draw(borderRectangle, 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(borderRectangle, new Rectangle(posX, posY, endposX - posX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } else { if (endposY < posY) { _spriteBatch.Draw(borderRectangle, new Rectangle(posX, endposY, endposX - posX, posY - endposY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } if (endposX < posX) { _spriteBatch.Draw(borderRectangle, new Rectangle(endposX, posY, posX - endposX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } } } } Effect outlineShader; // Outline shader effect private Effect outlineShader2; 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) { var borderRectangle = new Texture2D(GraphicsDevice, 1, 1); borderRectangle.SetData(new[] { Color.Transparent }); if ((posX < endposX && posY < endposY)) { _spriteBatch.Draw(borderRectangle, 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(borderRectangle, new Rectangle(posX, posY, endposX - posX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } else { if (endposY < posY) { _spriteBatch.Draw(borderRectangle, new Rectangle(posX, endposY, endposX - posX, posY - endposY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } if (endposX < posX) { _spriteBatch.Draw(borderRectangle, new Rectangle(endposX, posY, posX - endposX, endposY - posY), null, Color.White, 0, Vector2.Zero, SpriteEffects.None, 1); } } } } private void DrawWalls() { foreach (var wall in _sessionData.Walls.Values) { var content = Content.Load($"walls/{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 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, 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, Password = localContent.TxtPassword.Text, FirstName = localContent.TxtFirstname.Text, LastName = localContent.TxtLastname.Text, Initials = localContent.TxtInitials.Text }); if (result.IsSuccessStatusCode) { _authResponse = await _communicationManager.Login(new AuthenticateModel { Username = localContent.TxtEmail.Text, Password = localContent.TxtPassword.Text }); successful = true; } else { localContent.LblLoginError.Text = result.ReasonPhrase; 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(); 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; } 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; }; 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; } } }