sledgemapper/Sledgemapper/Sledgemapper.cs
2021-09-24 16:54:44 +01:00

1806 lines
74 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AsyncAwaitBestPractices;
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.Utility;
using Sledgemapper.Shared.Entities;
using Sledgemapper.UI;
using TinyMessenger;
using Thickness = Myra.Graphics2D.Thickness;
namespace Sledgemapper
{
public class Sledgemapper : Game
{
private readonly CommunicationManager _communicationManager;
private readonly Desktop _desktop;
private readonly GraphicsDeviceManager _graphics;
private readonly TinyMessengerHub _messenger;
private readonly Session _sessionData;
private readonly State _state;
private CachedContent _cachedContent;
private Texture2D _comment;
private Dictionary<string, SpriteFont> _fonts;
private bool _isDragging;
private Label _lblOverlayName;
private MainWidget _mainWidget;
private MouseState _oldMouseState;
private KeyboardState _oldState;
private Effect _outlineShader;
private RenderTarget2D _renderTarget;
private SpriteSheet _rippleSpriteSheet;
private SpriteBatch _spriteBatch;
private SpriteSheet _spriteSheet;
private Texture2D _transparentRedRectangle;
private Dictionary<string, Texture2D> _wallsContent;
private Texture2D _whiteRectangle;
public Sledgemapper()
{
_graphics = new GraphicsDeviceManager(this);
_graphics.GraphicsProfile = GraphicsProfile.Reach;
_graphics.PreferMultiSampling = true;
Content.RootDirectory = "Content";
_desktop = new Desktop();
MyraEnvironment.Game = this;
_sessionData = new Session();
IsFixedTimeStep = true;
TargetElapsedTime = TimeSpan.FromSeconds(1d / 30d);
_messenger = new TinyMessengerHub();
_communicationManager = new CommunicationManager(_sessionData,_messenger);
_communicationManager.Connection.Reconnected += OnHubReconnected;
_communicationManager.Connection.Reconnecting += OnHubReconnecting;
_communicationManager.Connection.Closed += OnHubDisconnected;
_state = State.Instance;
}
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(_state.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
{
Console.WriteLine("Desktop render error");
}
base.Draw(gameTime);
}
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();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
_cachedContent = CachedContent.Instance;
_outlineShader = Content.Load<Effect>("shaders/OutlineShader");
MyraEnvironment.Game = this;
ResetRenderTarget();
// Inform Myra that external text input is available
// So it stops translating Keys to chars
_desktop.HasExternalTextInput = true;
// Provide that text input
Window.TextInput += (s, a) =>
{
_desktop.OnChar(a.Character);
};
_mainWidget = new MainWidget(_communicationManager, _messenger, Window);
_communicationManager.CheckLogin();
_wallsContent = Content.LoadContentFolder<Texture2D>("walls");
_spriteSheet = new SpriteSheet();
_spriteSheet.LoadContent(Content, "spriteIndex", "sprites");
_rippleSpriteSheet = new SpriteSheet();
_rippleSpriteSheet.LoadContent(Content, "handcursorsIndex", "handcursors");
_lblOverlayName = new Label
{
Background = new SolidBrush(Color.SlateGray),
Padding = new Thickness(4)
};
AddItemToToolGrid(_mainWidget.GridWalls, OnWallButtonClicked, _wallsContent);
AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, _spriteSheet);
_fonts = Content.LoadContentFolder<SpriteFont>("fonts");
_cachedContent.Eye = Content.Load<Texture2D>("eye");
_cachedContent.Location = Content.Load<Texture2D>("location");
_comment = Content.Load<Texture2D>("comment");
_mainWidget.BtnToolbarLine.Image = new TextureRegion(Content.Load<Texture2D>("icon_line"));
_mainWidget.BtnToolbarRoom.Image = new TextureRegion(Content.Load<Texture2D>("icon_room"));
_mainWidget.BtnToolbarDelete.Image = new TextureRegion(Content.Load<Texture2D>("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 });
CenterOnTile(0, 0);
}
protected override void Update(GameTime gameTime)
{
var 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)_state.ViewportCenter.X,
mouseState.Position.Y - (int)_state.ViewportCenter.Y);
if (!_desktop.ContextMenu?.Visible ?? true)
{
_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
)
{
_state.ViewportCenter =
new Vector3(_state.ViewportCenter.X + mouseState.Position.X - _oldMouseState.Position.X,
_state.ViewportCenter.Y + mouseState.Position.Y - _oldMouseState.Position.Y, 0);
if (mouseState.Position != _oldMouseState.Position) _isDragging = true;
}
if (mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != _oldMouseState.LeftButton)
{
if (_isDragging)
{
_isDragging = false;
}
else
{
_state.SelectedTile.X = _state.HoveredTile.X;
_state.SelectedTile.Y = _state.HoveredTile.Y;
_communicationManager.Connection?.SendAsync("UpdatePosition", State.Instance.MapName,
State.Instance.MapId, _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 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 Thickness(2),
HorizontalAlignment = HorizontalAlignment.Left
};
newNoteButton.Click += OnContextMenuNewNoteClick;
popup.AddChild(newNoteButton);
}
else
{
_sessionData.Notes.TryGetValue(_state.SelectedNote.ToString(), out var n);
_state.SelectedNote = new Note(n);
var viewNoteButton = new TextButton
{
Text = "View Note", Width = 80, Height = 20, Padding = new Thickness(2),
HorizontalAlignment = HorizontalAlignment.Left
};
var deleteNoteButton = new TextButton
{
Text = "Delete Note", Width = 80, Height = 20, Padding = new 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 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", State.Instance.MapName,
State.Instance.MapId, _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)_state.ViewportCenter.X) / _state.TileSize;
var ty = (center.Y - (int)_state.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", State.Instance.MapId,
_state.SelectedTile);
}
break;
case Keys.Right:
if (_oldState.IsKeyUp(Keys.Right) && newState.IsKeyDown(Keys.Right))
{
_state.SelectedTile.X++;
_communicationManager.Connection?.SendAsync("UpdatePosition", State.Instance.MapId,
_state.SelectedTile);
}
break;
case Keys.Up:
if (_oldState.IsKeyUp(Keys.Up) && newState.IsKeyDown(Keys.Up))
{
_state.SelectedTile.Y--;
_communicationManager.Connection?.SendAsync("UpdatePosition", State.Instance.MapId,
_state.SelectedTile);
}
break;
case Keys.Down:
if (_oldState.IsKeyUp(Keys.Down) && newState.IsKeyDown(Keys.Down))
{
_state.SelectedTile.Y++;
_communicationManager.Connection?.SendAsync("UpdatePosition", State.Instance.MapId,
_state.SelectedTile);
}
break;
}
_oldState = newState;
base.Update(gameTime);
}
private void AddItemToToolGrid(Grid grid, EventHandler eventAction, Dictionary<string, Texture2D> 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 ApplyShaderToMap()
{
var texelSize = new Vector2(_renderTarget.Width, _renderTarget.Height);
_outlineShader.Parameters["ImageSize"].SetValue(texelSize);
_outlineShader.Parameters["BorderSize"].SetValue((int)(_state.TileSize / 100f * 10f));
_outlineShader.Parameters["R"].SetValue(Settings.Instance.OverlayTintColor.R / 255.0f);
_outlineShader.Parameters["G"].SetValue(Settings.Instance.OverlayTintColor.G / 255.0f);
_outlineShader.Parameters["B"].SetValue(Settings.Instance.OverlayTintColor.B / 255.0f);
GraphicsDevice.Clear(Settings.Instance.BackgroundColor);
_spriteBatch.Begin(
effect: _outlineShader,
sortMode: SpriteSortMode.Immediate);
_spriteBatch.Draw(_renderTarget, Vector2.Zero, null, Color.White);
_spriteBatch.End();
}
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 - _state.ViewportCenter.X;
var dy = center.Y - y * _state.TileSize - _state.ViewportCenter.Y;
_state.ViewportCenter = new Vector3(_state.ViewportCenter.X + dx, _state.ViewportCenter.Y + dy,
_state.ViewportCenter.Z);
}
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.Instance.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 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 DrawGrid(int visibleTilesX, int visibleTilesY)
{
for (var i = -1; i < visibleTilesX + 3; i++)
{
var posX1 = i * _state.TileSize - _state.ViewportCenter.X;
var posY1 = -_state.ViewportCenter.Y;
posX1 -= posX1 % _state.TileSize;
posY1 -= posY1 % _state.TileSize;
var posX2 = i * _state.TileSize - _state.ViewportCenter.X;
var posY2 = GraphicsDevice.Viewport.Height - _state.ViewportCenter.Y;
posX2 -= posX2 % _state.TileSize;
posY2 -= posY2 % _state.TileSize;
_spriteBatch.DrawLine(
posX1, posY1,
posX2,
posY2+ _state.TileSize,
Settings.Instance.GridColor);
}
for (var i = -1; i < visibleTilesY + 2; i++)
{
var posX1 = -_state.ViewportCenter.X;
var posY1 = i * _state.TileSize - _state.ViewportCenter.Y;
posX1 -= posX1 % _state.TileSize;
posY1 -= posY1 % _state.TileSize;
var posX2 = GraphicsDevice.Viewport.Width - _state.ViewportCenter.X;
var posY2 = i * _state.TileSize - _state.ViewportCenter.Y;
posX2 -= posX2 % _state.TileSize;
posY2 -= posY2 % _state.TileSize;
_spriteBatch.DrawLine(posX1, posY1,
posX2 + _state.TileSize,
posY2,
Settings.Instance.GridColor);
}
if (_state.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)_state.ViewportCenter.X,
j * _state.TileSize - (int)_state.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 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 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 DrawMap()
{
GraphicsDevice.SetRenderTarget(_renderTarget);
GraphicsDevice.Clear(Color.Transparent);
_spriteBatch.Begin(
transformMatrix: Matrix.CreateTranslation(_state.ViewportCenter),
samplerState: SamplerState.PointClamp
);
var items = _sessionData.Lines.Values.Union<BaseMapEntity>(_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 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 - _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 - _state.TileSize / 2,
note.Y * _state.TileSize + _state.TileSize / 8,
(int)(_state.TileSize / 2.5), (int)(_state.TileSize / 2.5 / 1.136)
), Settings.Instance.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.Instance.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.Instance.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 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 + 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 (var 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(
Vector2.Zero,
v2,
new Color(ping.Player.Color.ToColor(), 1f - easing),
2 + 2 * (1 - easing));
}
}
}
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 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);
}
private void DrawPlayers()
{
var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize).Value ??
_fonts.Last().Value;
var fscale = _state.TileSize / (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 DrawRipple(GameTime gameTime)
{
_spriteBatch.Begin(
blendState: BlendState.NonPremultiplied,
transformMatrix: Matrix.CreateTranslation(_state.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(
new Vector2(x, y),
baseRadius,
20,
ping.Player.Color.ToColor(),
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(
new Vector2(x, y),
baseRadius + baseOuterRadius * easing,
20,
new Color(ping.Player.Color.ToColor(), 1 - easing),
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.Instance.PingDuration)
_sessionData.Pings.TryRemove(guid, out var _);
}
_spriteBatch.End();
}
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);
}
}
}
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.Instance.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;
var 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 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 DrawTiles()
{
for (var i = 0; i < _sessionData.Map.Values.Count; i++)
{
var tile = _sessionData.Map.Values.ElementAt(i);
var content = Content.Load<Texture2D>($"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 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 void EditNote(Note note)
{
_state.SelectedNote = new Note { X = note.X, Y = note.Y, Text = note.Text };
new NoteWindow(_communicationManager, note).ShowInModalWindow(_desktop, $" Note on {note.X}:{note.Y}");
}
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 = Array.Empty<Vector2>();
var center = new Point((Window.ClientBounds.Width + leftBound) / 2 - (int)_state.ViewportCenter.X,
Window.ClientBounds.Height / 2 - (int)_state.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 - _state.ViewportCenter.X - rightBound,
topBound - _state.ViewportCenter.Y);
//bottom right
var p4 = new Vector2(
Window.ClientBounds.Width - _state.ViewportCenter.X - rightBound,
Window.ClientBounds.Height - _state.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 - _state.ViewportCenter.X, topBound - _state.ViewportCenter.Y);
p4 = new Vector2(Window.ClientBounds.Width - _state.ViewportCenter.X, topBound - _state.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 - _state.ViewportCenter.X, topBound - _state.ViewportCenter.Y);
p4 = new Vector2(leftBound - _state.ViewportCenter.X,
Window.ClientBounds.Height - _state.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 - _state.ViewportCenter.X,
Window.ClientBounds.Height - _state.ViewportCenter.Y - bottomBound);
p4 = new Vector2(Window.ClientBounds.Width - _state.ViewportCenter.X,
Window.ClientBounds.Height - _state.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<float> { 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[]
{ new(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[]
{ new(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[]
{ new(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[]
{ new(x, y), new Vector2(x + 10, y - 20), new Vector2(x - 10, y - 20), new Vector2(x, y) };
break;
}
return true;
}
return false;
}
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)_state.ViewportCenter.X,
0 * _state.TileSize + _state.TileSize - (int)_state.ViewportCenter.Y);
var screenPositionBottomRight = new Point(visibleTilesX * _state.TileSize - (int)_state.ViewportCenter.X,
visibleTilesY * _state.TileSize - 20 - (int)_state.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 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)_state.ViewportCenter.X,
position.Y * _state.TileSize + (int)_state.ViewportCenter.Y);
var tileBR = new Point(position.X * _state.TileSize + (int)_state.ViewportCenter.X + _state.TileSize,
position.Y * _state.TileSize + (int)_state.ViewportCenter.Y + _state.TileSize);
return tileTL.X <= boxTL.X || tileTL.Y <= boxTL.Y || tileBR.X >= boxBR.X || tileBR.Y >= boxBR.Y;
}
private void OnClientSizeChanged(object sender, EventArgs e)
{
_renderTarget?.Dispose();
try
{
ResetRenderTarget();
}
catch
{
Console.WriteLine("rendertarget dispose exception");
}
}
private void OnContextMenuDeleteNoteClick(object sender, EventArgs e)
{
_desktop.HideContextMenu();
_sessionData.DeleteNote(_state.SelectedNote);
}
private void OnContextMenuNewNoteClick(object sender, EventArgs e)
{
_desktop.HideContextMenu();
new NoteWindow(_communicationManager, new Note()).ShowInModalWindow(_desktop,
$" Note on {_state.HoveredTile.X}:{_state.HoveredTile.Y}");
}
private void OnContextMenuPingClick(object sender, EventArgs e, Tile location)
{
_desktop.HideContextMenu();
_communicationManager.Ping(location).SafeFireAndForget();
}
private void OnContextMenuViewNoteClick(object sender, EventArgs e)
{
_desktop.HideContextMenu();
EditNote(_state.SelectedNote);
}
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 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();
}
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 void OnOverlayButtonClicked(object sender, EventArgs e)
{
_state.CurrentOverlayId = ((ImageButton)sender).Id;
_mainWidget.ClearSelection();
((ImageButton)sender).Border = new SolidBrush(Color.Red);
((ImageButton)sender).BorderThickness = new Thickness(2);
_state.InsertMode = InsertMode.Overlay;
}
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);
}
private void OnTileButtonTouchLeft(object sender, EventArgs e)
{
_desktop.HideContextMenu();
_lblOverlayName.Visible = false;
}
private void OnTxtOverlaySearchChange(object sender, ValueChangedEventArgs<string> e)
{
AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, _spriteSheet, e.NewValue);
}
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 Thickness(2);
_state.InsertMode = InsertMode.Wall;
}
private void ResetRenderTarget()
{
_renderTarget = new RenderTarget2D(GraphicsDevice,
GraphicsDevice.Viewport.Width,
GraphicsDevice.Viewport.Height,
false,
SurfaceFormat.Color,
DepthFormat.None,
0,
RenderTargetUsage.DiscardContents);
}
}
}