diff --git a/Assets/walls.svg b/Assets/walls.svg new file mode 100644 index 0000000..99a44bd --- /dev/null +++ b/Assets/walls.svg @@ -0,0 +1,331 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + S + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sledgemapper.Api/Commands/PingCommand.cs b/Sledgemapper.Api/Commands/PingCommand.cs new file mode 100644 index 0000000..ef4db06 --- /dev/null +++ b/Sledgemapper.Api/Commands/PingCommand.cs @@ -0,0 +1,13 @@ +// using Sledgemapper.Shared.Entities; + +// namespace Sledgemapper.Api.Commands +// { +// public class PingCommand : BaseCommand +// { +// public Ping Location { get; private set; } +// public PingCommand(string sessionName, Ping location, int userId) : base(sessionName, userId) +// { +// Location = location; +// } +// } +// } diff --git a/Sledgemapper.Api/Controllers/SessionController.cs b/Sledgemapper.Api/Controllers/SessionController.cs index 2638ac5..4926ba2 100644 --- a/Sledgemapper.Api/Controllers/SessionController.cs +++ b/Sledgemapper.Api/Controllers/SessionController.cs @@ -2,6 +2,7 @@ using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Sledgemapper.Api.Commands; +using Sledgemapper.Api.Notifications; using Sledgemapper.Shared.Entities; using System.Threading.Tasks; @@ -30,6 +31,12 @@ namespace Sledgemapper.Api.Controllers return result; } + // [HttpPost("ping")] + // public async Task Post(string sessionName, [FromBody] Ping pingLocation) + // { + // await _mediator.Send(new PingCommand(sessionName, pingLocation, UserId)); + // } + [HttpPost("snapshot")] public async Task Post(string sessionName, [FromBody] Session session) { @@ -60,13 +67,13 @@ namespace Sledgemapper.Api.Controllers await _mediator.Send(new NewNoteCommand(sessionName, note, UserId)); } - [HttpPost("room")] + [HttpPost("room")] public async Task Post(string sessionName, [FromBody] Room room) { await _mediator.Send(new NewRoomCommand(sessionName, room, UserId)); } - [HttpPost("line")] + [HttpPost("line")] public async Task Post(string sessionName, [FromBody] Line line) { await _mediator.Send(new NewLineCommand(sessionName, line, UserId)); diff --git a/Sledgemapper.Api/Handlers/NewTileCommandHandler.cs b/Sledgemapper.Api/Handlers/NewTileCommandHandler.cs index 3169384..6b0a55c 100644 --- a/Sledgemapper.Api/Handlers/NewTileCommandHandler.cs +++ b/Sledgemapper.Api/Handlers/NewTileCommandHandler.cs @@ -35,7 +35,5 @@ namespace Sledgemapper.Api.Commands await _mediator.Publish(new NewTileNotification(session, notification.Tile, notification.UserId)); return true; } - - } } diff --git a/Sledgemapper.Api/Handlers/PingCommandHandler.cs b/Sledgemapper.Api/Handlers/PingCommandHandler.cs new file mode 100644 index 0000000..efc9325 --- /dev/null +++ b/Sledgemapper.Api/Handlers/PingCommandHandler.cs @@ -0,0 +1,28 @@ +// using MediatR; +// using Sledgemapper.Api.Data; +// using System.Threading; +// using System.Threading.Tasks; +// using Sledgemapper.Api.Notifications; +// using System.Linq; + +// namespace Sledgemapper.Api.Commands +// { +// public class PingCommandHandler : IRequestHandler +// { +// private readonly MyDbContext _dbcontext; +// private readonly IMediator _mediator; + +// public PingCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; } + +// public async Task Handle(PingCommand notification, CancellationToken cancellationToken) +// { +// var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName); + + +// await _mediator.Publish(new PingNotification(session, notification.Location, notification.UserId)); +// return true; +// } + + +// } +// } diff --git a/Sledgemapper.Api/Handlers/SendPingMessage.cs b/Sledgemapper.Api/Handlers/SendPingMessage.cs new file mode 100644 index 0000000..9b7f0d9 --- /dev/null +++ b/Sledgemapper.Api/Handlers/SendPingMessage.cs @@ -0,0 +1,23 @@ +// using MediatR; +// using Microsoft.AspNetCore.SignalR; +// using Sledgemapper.Api.Notifications; +// using Sledgemapper.Clients; +// using System.Threading; +// using System.Threading.Tasks; +// using Sledgemapper.Api.Hubs; + +// namespace Sledgemapper.Api.Handlers +// { +// public class SendPingMessage : INotificationHandler +// { +// private readonly IHubContext _hub; + +// public SendPingMessage(IHubContext hub) => _hub = hub; + +// public async Task Handle(PingNotification notification, CancellationToken cancellationToken) +// { + +// await _hub.Clients.Groups(notification.Session.SessionName).Ping(notification.Location, notification.UserId); +// } +// } +// } diff --git a/Sledgemapper.Api/Hubs/SledgemapperHub.cs b/Sledgemapper.Api/Hubs/SledgemapperHub.cs index c356461..235af86 100644 --- a/Sledgemapper.Api/Hubs/SledgemapperHub.cs +++ b/Sledgemapper.Api/Hubs/SledgemapperHub.cs @@ -51,7 +51,7 @@ namespace Sledgemapper.Api.Hubs await Clients.Group(sessionName).NewRoom(room); } - public async Task NewLine(string sessionName, Line line) + public async Task NewLine(string sessionName, Line line) { await Clients.Group(sessionName).NewLine(line); } @@ -86,6 +86,15 @@ namespace Sledgemapper.Api.Hubs await Clients.Group(sessionName).DeleteOverlay(tile); } + public async Task Ping(string sessionName, Tile location) + { + var userId = int.Parse(Context.User.Identity.Name); + var user = _datacontext.Users.First(u => u.Id == userId); + + var player = new Player { UserId = userId, Initials = user.Initials, Position = new Tile { X = 0, Y = 0 }, Color = UserColors[userId] }; + await Clients.Group(sessionName).Ping(new Ping{X=location.X, Y=location.Y, Player=player}); + } + public async Task JoinSession(string sessionName) { var session = _dbContext.Sessions.FirstOrDefault(s => s.SessionName == sessionName); diff --git a/Sledgemapper.Api/Notifications/PingNotification.cs b/Sledgemapper.Api/Notifications/PingNotification.cs new file mode 100644 index 0000000..e14c965 --- /dev/null +++ b/Sledgemapper.Api/Notifications/PingNotification.cs @@ -0,0 +1,14 @@ +// using Sledgemapper.Shared.Entities; + +// namespace Sledgemapper.Api.Notifications +// { +// public class PingNotification : BaseNotification +// { +// public Ping Location { get; private set; } + +// public PingNotification(Models.Session session, Ping location, int userId) : base(session, userId) +// { +// Location = location; +// } +// } +// } diff --git a/Sledgemapper.Api/db/LocalDatabase.db b/Sledgemapper.Api/db/LocalDatabase.db index 22b8623..29abb43 100644 Binary files a/Sledgemapper.Api/db/LocalDatabase.db and b/Sledgemapper.Api/db/LocalDatabase.db differ diff --git a/Sledgemapper.Api/db/sledgemapper.db b/Sledgemapper.Api/db/sledgemapper.db index 3991092..6e14010 100644 Binary files a/Sledgemapper.Api/db/sledgemapper.db and b/Sledgemapper.Api/db/sledgemapper.db differ diff --git a/Sledgemapper.Shared/Clients/ISledgemapperClient.cs b/Sledgemapper.Shared/Clients/ISledgemapperClient.cs index a1ec349..4e8bc07 100644 --- a/Sledgemapper.Shared/Clients/ISledgemapperClient.cs +++ b/Sledgemapper.Shared/Clients/ISledgemapperClient.cs @@ -20,5 +20,6 @@ namespace Sledgemapper.Clients Task UpdateMap(Session player); Task RefreshPlayers(); Task NewLine(Line line); + Task Ping(Ping ping); } } diff --git a/Sledgemapper.Shared/Easings.cs b/Sledgemapper.Shared/Easings.cs new file mode 100644 index 0000000..b82cd35 --- /dev/null +++ b/Sledgemapper.Shared/Easings.cs @@ -0,0 +1,450 @@ + +using System; +#if UNITY +using UnityEngine; +using Math = UnityEngine.Mathf; +#endif + +static public class Easings +{ + /// + /// Constant Pi. + /// + private const float PI = (float)(float)Math.PI; + + /// + /// Constant Pi / 2. + /// + private const float HALFPI =(float) (float)Math.PI / 2.0f; + + /// + /// Easing Functions enumeration + /// + public enum Functions + { + Linear, + QuadraticEaseIn, + QuadraticEaseOut, + QuadraticEaseInOut, + CubicEaseIn, + CubicEaseOut, + CubicEaseInOut, + QuarticEaseIn, + QuarticEaseOut, + QuarticEaseInOut, + QuinticEaseIn, + QuinticEaseOut, + QuinticEaseInOut, + SineEaseIn, + SineEaseOut, + SineEaseInOut, + CircularEaseIn, + CircularEaseOut, + CircularEaseInOut, + ExponentialEaseIn, + ExponentialEaseOut, + ExponentialEaseInOut, + ElasticEaseIn, + ElasticEaseOut, + ElasticEaseInOut, + BackEaseIn, + BackEaseOut, + BackEaseInOut, + BounceEaseIn, + BounceEaseOut, + BounceEaseInOut + } + + /// + /// Interpolate using the specified function. + /// + static public float Interpolate(float p, Functions function) + { + switch(function) + { + default: + case Functions.Linear: return Linear(p); + case Functions.QuadraticEaseOut: return QuadraticEaseOut(p); + case Functions.QuadraticEaseIn: return QuadraticEaseIn(p); + case Functions.QuadraticEaseInOut: return QuadraticEaseInOut(p); + case Functions.CubicEaseIn: return CubicEaseIn(p); + case Functions.CubicEaseOut: return CubicEaseOut(p); + case Functions.CubicEaseInOut: return CubicEaseInOut(p); + case Functions.QuarticEaseIn: return QuarticEaseIn(p); + case Functions.QuarticEaseOut: return QuarticEaseOut(p); + case Functions.QuarticEaseInOut: return QuarticEaseInOut(p); + case Functions.QuinticEaseIn: return QuinticEaseIn(p); + case Functions.QuinticEaseOut: return QuinticEaseOut(p); + case Functions.QuinticEaseInOut: return QuinticEaseInOut(p); + case Functions.SineEaseIn: return SineEaseIn(p); + case Functions.SineEaseOut: return SineEaseOut(p); + case Functions.SineEaseInOut: return SineEaseInOut(p); + case Functions.CircularEaseIn: return CircularEaseIn(p); + case Functions.CircularEaseOut: return CircularEaseOut(p); + case Functions.CircularEaseInOut: return CircularEaseInOut(p); + case Functions.ExponentialEaseIn: return ExponentialEaseIn(p); + case Functions.ExponentialEaseOut: return ExponentialEaseOut(p); + case Functions.ExponentialEaseInOut: return ExponentialEaseInOut(p); + case Functions.ElasticEaseIn: return ElasticEaseIn(p); + case Functions.ElasticEaseOut: return ElasticEaseOut(p); + case Functions.ElasticEaseInOut: return ElasticEaseInOut(p); + case Functions.BackEaseIn: return BackEaseIn(p); + case Functions.BackEaseOut: return BackEaseOut(p); + case Functions.BackEaseInOut: return BackEaseInOut(p); + case Functions.BounceEaseIn: return BounceEaseIn(p); + case Functions.BounceEaseOut: return BounceEaseOut(p); + case Functions.BounceEaseInOut: return BounceEaseInOut(p); + } + } + + /// + /// Modeled after the line y = x + /// + static public float Linear(float p) + { + return p; + } + + /// + /// Modeled after the parabola y = x^2 + /// + static public float QuadraticEaseIn(float p) + { + return p * p; + } + + /// + /// Modeled after the parabola y = -x^2 + 2x + /// + static public float QuadraticEaseOut(float p) + { + return -(p * (p - 2)); + } + + /// + /// Modeled after the piecewise quadratic + /// y = (1/2)((2x)^2) ; [0, 0.5) + /// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] + /// + static public float QuadraticEaseInOut(float p) + { + if(p < 0.5f) + { + return 2 * p * p; + } + else + { + return (-2 * p * p) + (4 * p) - 1; + } + } + + /// + /// Modeled after the cubic y = x^3 + /// + static public float CubicEaseIn(float p) + { + return p * p * p; + } + + /// + /// Modeled after the cubic y = (x - 1)^3 + 1 + /// + static public float CubicEaseOut(float p) + { + float f = (p - 1); + return f * f * f + 1; + } + + /// + /// Modeled after the piecewise cubic + /// y = (1/2)((2x)^3) ; [0, 0.5) + /// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] + /// + static public float CubicEaseInOut(float p) + { + if(p < 0.5f) + { + return 4 * p * p * p; + } + else + { + float f = ((2 * p) - 2); + return 0.5f * f * f * f + 1; + } + } + + /// + /// Modeled after the quartic x^4 + /// + static public float QuarticEaseIn(float p) + { + return p * p * p * p; + } + + /// + /// Modeled after the quartic y = 1 - (x - 1)^4 + /// + static public float QuarticEaseOut(float p) + { + float f = (p - 1); + return f * f * f * (1 - p) + 1; + } + + /// + // Modeled after the piecewise quartic + // y = (1/2)((2x)^4) ; [0, 0.5) + // y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] + /// + static public float QuarticEaseInOut(float p) + { + if(p < 0.5f) + { + return 8 * p * p * p * p; + } + else + { + float f = (p - 1); + return -8 * f * f * f * f + 1; + } + } + + /// + /// Modeled after the quintic y = x^5 + /// + static public float QuinticEaseIn(float p) + { + return p * p * p * p * p; + } + + /// + /// Modeled after the quintic y = (x - 1)^5 + 1 + /// + static public float QuinticEaseOut(float p) + { + float f = (p - 1); + return f * f * f * f * f + 1; + } + + /// + /// Modeled after the piecewise quintic + /// y = (1/2)((2x)^5) ; [0, 0.5) + /// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] + /// + static public float QuinticEaseInOut(float p) + { + if(p < 0.5f) + { + return 16 * p * p * p * p * p; + } + else + { + float f = ((2 * p) - 2); + return 0.5f * f * f * f * f * f + 1; + } + } + + /// + /// Modeled after quarter-cycle of sine wave + /// + static public float SineEaseIn(float p) + { + return (float)(float)Math.Sin((p - 1) * HALFPI) + 1; + } + + /// + /// Modeled after quarter-cycle of sine wave (different phase) + /// + static public float SineEaseOut(float p) + { + return (float)(float)Math.Sin(p * HALFPI); + } + + /// + /// Modeled after half sine wave + /// + static public float SineEaseInOut(float p) + { + return 0.5f * (1 - (float)(float)Math.Cos(p * PI)); + } + + /// + /// Modeled after shifted quadrant IV of unit circle + /// + static public float CircularEaseIn(float p) + { + return 1 - (float)Math.Sqrt(1 - (p * p)); + } + + /// + /// Modeled after shifted quadrant II of unit circle + /// + static public float CircularEaseOut(float p) + { + return (float)Math.Sqrt((2 - p) * p); + } + + /// + /// Modeled after the piecewise circular function + /// y = (1/2)(1 - (float)Math.Sqrt(1 - 4x^2)) ; [0, 0.5) + /// y = (1/2)((float)Math.Sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] + /// + static public float CircularEaseInOut(float p) + { + if(p < 0.5f) + { + return 0.5f * (1 - (float)Math.Sqrt(1 - 4 * (p * p))); + } + else + { + return 0.5f * ((float)Math.Sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1); + } + } + + /// + /// Modeled after the exponential function y = 2^(10(x - 1)) + /// + static public float ExponentialEaseIn(float p) + { + return (p == 0.0f) ? p : (float)Math.Pow(2, 10 * (p - 1)); + } + + /// + /// Modeled after the exponential function y = -2^(-10x) + 1 + /// + static public float ExponentialEaseOut(float p) + { + return (p == 1.0f) ? p : 1 - (float)Math.Pow(2, -10 * p); + } + + /// + /// Modeled after the piecewise exponential + /// y = (1/2)2^(10(2x - 1)) ; [0,0.5) + /// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] + /// + static public float ExponentialEaseInOut(float p) + { + if(p == 0.0 || p == 1.0) return p; + + if(p < 0.5f) + { + return 0.5f * (float)Math.Pow(2, (20 * p) - 10); + } + else + { + return -0.5f * (float)Math.Pow(2, (-20 * p) + 10) + 1; + } + } + + /// + /// Modeled after the damped sine wave y = sin(13pi/2*x)*(float)Math.Pow(2, 10 * (x - 1)) + /// + static public float ElasticEaseIn(float p) + { + return (float)Math.Sin(13 * HALFPI * p) * (float)Math.Pow(2, 10 * (p - 1)); + } + + /// + /// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*(float)Math.Pow(2, -10x) + 1 + /// + static public float ElasticEaseOut(float p) + { + return (float)Math.Sin(-13 * HALFPI * (p + 1)) * (float)Math.Pow(2, -10 * p) + 1; + } + + /// + /// Modeled after the piecewise exponentially-damped sine wave: + /// y = (1/2)*sin(13pi/2*(2*x))*(float)Math.Pow(2, 10 * ((2*x) - 1)) ; [0,0.5) + /// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*(float)Math.Pow(2,-10(2*x-1)) + 2) ; [0.5, 1] + /// + static public float ElasticEaseInOut(float p) + { + if(p < 0.5f) + { + return 0.5f * (float)Math.Sin(13 * HALFPI * (2 * p)) * (float)Math.Pow(2, 10 * ((2 * p) - 1)); + } + else + { + return 0.5f * ((float)Math.Sin(-13 * HALFPI * ((2 * p - 1) + 1)) * (float)Math.Pow(2, -10 * (2 * p - 1)) + 2); + } + } + + /// + /// Modeled after the overshooting cubic y = x^3-x*sin(x*pi) + /// + static public float BackEaseIn(float p) + { + return p * p * p - p * (float)Math.Sin(p * PI); + } + + /// + /// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi)) + /// + static public float BackEaseOut(float p) + { + float f = (1 - p); + return 1 - (f * f * f - f * (float)Math.Sin(f * PI)); + } + + /// + /// Modeled after the piecewise overshooting cubic function: + /// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5) + /// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1] + /// + static public float BackEaseInOut(float p) + { + if(p < 0.5f) + { + float f = 2 * p; + return 0.5f * (f * f * f - f * (float)Math.Sin(f * PI)); + } + else + { + float f = (1 - (2*p - 1)); + return 0.5f * (1 - (f * f * f - f * (float)Math.Sin(f * PI))) + 0.5f; + } + } + + /// + /// + static public float BounceEaseIn(float p) + { + return 1 - BounceEaseOut(1 - p); + } + + /// + /// + static public float BounceEaseOut(float p) + { + if(p < 4/11.0f) + { + return (121 * p * p)/16.0f; + } + else if(p < 8/11.0f) + { + return (363/40.0f * p * p) - (99/10.0f * p) + 17/5.0f; + } + else if(p < 9/10.0f) + { + return (4356/361.0f * p * p) - (35442/1805.0f * p) + 16061/1805.0f; + } + else + { + return (54/5.0f * p * p) - (513/25.0f * p) + 268/25.0f; + } + } + + /// + /// + static public float BounceEaseInOut(float p) + { + if(p < 0.5f) + { + return 0.5f * BounceEaseIn(p*2); + } + else + { + return 0.5f * BounceEaseOut(p * 2 - 1) + 0.5f; + } + } +} + \ No newline at end of file diff --git a/Sledgemapper.Shared/Entities/Session.cs b/Sledgemapper.Shared/Entities/Session.cs index 42c74fe..4ded5e6 100644 --- a/Sledgemapper.Shared/Entities/Session.cs +++ b/Sledgemapper.Shared/Entities/Session.cs @@ -27,10 +27,11 @@ namespace Sledgemapper.Shared.Entities Overlays = new ConcurrentDictionary(); Walls = new ConcurrentDictionary(); Notes = new ConcurrentDictionary(); - Lines=new ConcurrentDictionary(); - Rooms=new ConcurrentDictionary(); + Lines = new ConcurrentDictionary(); + Rooms = new ConcurrentDictionary(); Players = new List(); Colors = new List(); + Pings = new ConcurrentDictionary(); } public ConcurrentDictionary Map { get; set; } @@ -39,189 +40,190 @@ namespace Sledgemapper.Shared.Entities public ConcurrentDictionary Notes { get; set; } public bool IsValid { get; set; } public List Players { get; set; } - public List Colors { get; set; } - public string SessionName { get; set; } - public int SessionId { get; set; } - public ConcurrentDictionary Lines { get; set; } - public ConcurrentDictionary Rooms { get; set; } + public ConcurrentDictionary Pings { get; set; } + public List Colors { get; set; } + public string SessionName { get; set; } + public int SessionId { get; set; } + public ConcurrentDictionary Lines { get; set; } + public ConcurrentDictionary Rooms { get; set; } - public void NewTile(Tile selectedTile, string tileId) + public void NewTile(Tile selectedTile, string tileId) + { + if (selectedTile is null || string.IsNullOrWhiteSpace(tileId)) { - if (selectedTile is null || string.IsNullOrWhiteSpace(tileId)) - { - return; - } - - var tileExist = Map.TryGetValue(selectedTile.ToString(), out var tile); - var newTile = new Tile { X = selectedTile.X, Y = selectedTile.Y, ID = tileId }; - if (tileExist) - { - Map.TryRemove(tile.ToString(), out var _); - if (tile.ID == tileId) - { - newTile.Rotation = (tile.Rotation + 1) % 4; - } - } - - Map.TryAdd(newTile.ToString(), newTile); - OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newTile)); + return; } - public void NewOverlay(Overlay selectedOverlay, string overlayId) + var tileExist = Map.TryGetValue(selectedTile.ToString(), out var tile); + var newTile = new Tile { X = selectedTile.X, Y = selectedTile.Y, ID = tileId }; + if (tileExist) { - if (selectedOverlay is null || string.IsNullOrWhiteSpace(overlayId)) + Map.TryRemove(tile.ToString(), out var _); + if (tile.ID == tileId) { - return; - } - var overlayExist = Overlays.TryGetValue(selectedOverlay.ToString(), out var overlay); - var newOverlay = new Overlay { X = selectedOverlay.X, Y = selectedOverlay.Y, ID = overlayId, Intersection = selectedOverlay.Intersection }; - if (overlayExist) - { - Overlays.TryRemove(overlay.ToString(), out var _); - if (overlay.ID == overlayId) - { - newOverlay.Rotation = (overlay.Rotation + 1) % 4; - } - } - - Overlays.TryAdd(newOverlay.ToString(), newOverlay); - OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newOverlay)); - } - - public void NewWall(Wall selectedWall, string wallId) - { - if (selectedWall is null || string.IsNullOrWhiteSpace(wallId)) - { - return; - } - var tileExist = Walls.TryGetValue(selectedWall.ToString(), out var wall); - var newWall = new Wall { X = selectedWall.X, Y = selectedWall.Y, ID = wallId, Rotation = selectedWall.Rotation }; - if (tileExist) - { - Walls.TryRemove(wall.ToString(), out var _); - } - - Walls.TryAdd(newWall.ToString(), newWall); - OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newWall)); - } - - public void NewNote(Note selectedNote) - { - if (selectedNote is null) - { - return; - } - var noteExists = Notes.TryGetValue(selectedNote.ToString(), out var note); - var newNote = new Note { X = selectedNote.X, Y = selectedNote.Y, Text=selectedNote.Text }; - if (noteExists) - { - Walls.TryRemove(note.ToString(), out var _); - } - - Notes.TryAdd(newNote.ToString(), newNote); - OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newNote)); - } - - public void DeleteWall(Wall wall) - { - if (wall is null) - { - return; - } - var removed = Walls.TryRemove(wall.ToString(), out var _); - if (removed) - { - OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(wall)); + newTile.Rotation = (tile.Rotation + 1) % 4; } } - public void DeleteNote(Note note) + Map.TryAdd(newTile.ToString(), newTile); + OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newTile)); + } + + public void NewOverlay(Overlay selectedOverlay, string overlayId) + { + if (selectedOverlay is null || string.IsNullOrWhiteSpace(overlayId)) { - if (note is null) + return; + } + var overlayExist = Overlays.TryGetValue(selectedOverlay.ToString(), out var overlay); + var newOverlay = new Overlay { X = selectedOverlay.X, Y = selectedOverlay.Y, ID = overlayId, Intersection = selectedOverlay.Intersection }; + if (overlayExist) + { + Overlays.TryRemove(overlay.ToString(), out var _); + if (overlay.ID == overlayId) { - return; - } - var removed = Notes.TryRemove(note.ToString(), out var _); - if (removed) - { - OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(note)); + newOverlay.Rotation = (overlay.Rotation + 1) % 4; } } - public void DeleteOverlay(Overlay overlay) + Overlays.TryAdd(newOverlay.ToString(), newOverlay); + OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newOverlay)); + } + + public void NewWall(Wall selectedWall, string wallId) + { + if (selectedWall is null || string.IsNullOrWhiteSpace(wallId)) { - if (overlay is null) - { - return; - } - var removed = Overlays.TryRemove(overlay.ToString(), out var _); - if (removed) - { - OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(overlay)); - } + return; + } + var tileExist = Walls.TryGetValue(selectedWall.ToString(), out var wall); + var newWall = new Wall { X = selectedWall.X, Y = selectedWall.Y, ID = wallId, Rotation = selectedWall.Rotation }; + if (tileExist) + { + Walls.TryRemove(wall.ToString(), out var _); } - public void DeleteTile(Tile tile) + Walls.TryAdd(newWall.ToString(), newWall); + OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newWall)); + } + + public void NewNote(Note selectedNote) + { + if (selectedNote is null) { - if (tile is null) - { - return; - } - var removed = Map.TryRemove(tile.ToString(), out var _); - if (removed) - { - OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(tile)); - } + return; + } + var noteExists = Notes.TryGetValue(selectedNote.ToString(), out var note); + var newNote = new Note { X = selectedNote.X, Y = selectedNote.Y, Text = selectedNote.Text }; + if (noteExists) + { + Walls.TryRemove(note.ToString(), out var _); } - protected virtual void OnRaiseMapEntityAddedEvent(MapEntityAddedEventArgs e) + Notes.TryAdd(newNote.ToString(), newNote); + OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newNote)); + } + + public void DeleteWall(Wall wall) + { + if (wall is null) { - MapEntityAdded?.Invoke(this, e); + return; } - - protected virtual void OnRaiseMapEntityDeletedEvent(MapEntityDeletedEventArgs e) + var removed = Walls.TryRemove(wall.ToString(), out var _); + if (removed) { - MapEntityDeleted?.Invoke(this, e); - } - - public void NewLine(Line line) - { - if (line is null) - { - return; - } - - var lineExist = Lines.TryGetValue(line.ToString(), out var tile); - var newLine = new Line { Start=line.Start, End=line.End, Width=line.Width}; - if (lineExist) - { - Lines.TryRemove(line.ToString(), out var _); - } - - Lines.TryAdd(newLine.ToString(), newLine); - - //TODO fix this - OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newLine)); - } - - public void NewRoom(Room line) - { - if (line is null) - { - return; - } - - var lineExist = Rooms.TryGetValue(line.ToString(), out var tile); - var newLine = new Room { Start=line.Start, End=line.End, Delete=line.Delete}; - if (lineExist) - { - Rooms.TryRemove(line.ToString(), out var _); - } - - Rooms.TryAdd(newLine.ToString(), newLine); - - //TODO fix this - OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newLine)); + OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(wall)); } } + + public void DeleteNote(Note note) + { + if (note is null) + { + return; + } + var removed = Notes.TryRemove(note.ToString(), out var _); + if (removed) + { + OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(note)); + } + } + + public void DeleteOverlay(Overlay overlay) + { + if (overlay is null) + { + return; + } + var removed = Overlays.TryRemove(overlay.ToString(), out var _); + if (removed) + { + OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(overlay)); + } + } + + public void DeleteTile(Tile tile) + { + if (tile is null) + { + return; + } + var removed = Map.TryRemove(tile.ToString(), out var _); + if (removed) + { + OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(tile)); + } + } + + protected virtual void OnRaiseMapEntityAddedEvent(MapEntityAddedEventArgs e) + { + MapEntityAdded?.Invoke(this, e); + } + + protected virtual void OnRaiseMapEntityDeletedEvent(MapEntityDeletedEventArgs e) + { + MapEntityDeleted?.Invoke(this, e); + } + + public void NewLine(Line line) + { + if (line is null) + { + return; + } + + var lineExist = Lines.TryGetValue(line.ToString(), out var tile); + var newLine = new Line { Start = line.Start, End = line.End, Width = line.Width }; + if (lineExist) + { + Lines.TryRemove(line.ToString(), out var _); + } + + Lines.TryAdd(newLine.ToString(), newLine); + + //TODO fix this + OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newLine)); + } + + public void NewRoom(Room line) + { + if (line is null) + { + return; + } + + var lineExist = Rooms.TryGetValue(line.ToString(), out var tile); + var newLine = new Room { Start = line.Start, End = line.End, Delete = line.Delete }; + if (lineExist) + { + Rooms.TryRemove(line.ToString(), out var _); + } + + Rooms.TryAdd(newLine.ToString(), newLine); + + //TODO fix this + OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newLine)); + } +} } diff --git a/Sledgemapper.Shared/Entities/Tile.cs b/Sledgemapper.Shared/Entities/Tile.cs index aa0583f..f1e21e1 100644 --- a/Sledgemapper.Shared/Entities/Tile.cs +++ b/Sledgemapper.Shared/Entities/Tile.cs @@ -1,4 +1,5 @@ using System; +using System.Security.Cryptography; namespace Sledgemapper.Shared.Entities { @@ -20,8 +21,73 @@ namespace Sledgemapper.Shared.Entities public double Timestamp { get; set; } } + public class Ping : BaseMapEntity + { + public Player Player { get; set; } + public int Iterations {get;set;} + public double StartTime {get;set;} + } + public class Tile : BaseMapEntity { + + public bool Equals(Tile other) + { + if (other == null) return false; + return (X == other.X && Y == other.Y); + } + + public static bool operator ==(Tile a, Tile b) + { + // If both are null, or both are same instance, return true. + if (System.Object.ReferenceEquals(a, b)) + { + return true; + } + + // If one is null, but not both, return false. + if (((object)a == null) || ((object)b == null)) + { + return false; + } + + // Return true if the fields match: + return a.X == b.X && a.Y == b.Y; + } + + public static bool operator !=(Tile a, Tile b) + { + return !(a == b); + } + + + public override bool Equals(object obj) + { + // + // See the full list of guidelines at + // http://go.microsoft.com/fwlink/?LinkID=85237 + // and also the guidance for operator== at + // http://go.microsoft.com/fwlink/?LinkId=85238 + // + + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + return Equals(obj as Tile); + } + + // override object.GetHashCode + public override int GetHashCode() + { + unchecked + { + int hash = 13; + hash = (hash * 7) + X + Y; + return hash; + } + } } public class Line : BaseMapEntity diff --git a/Sledgemapper.Shared/Entities/Wall.cs b/Sledgemapper.Shared/Entities/Wall.cs index 9f671a3..106fdbb 100644 --- a/Sledgemapper.Shared/Entities/Wall.cs +++ b/Sledgemapper.Shared/Entities/Wall.cs @@ -1,11 +1,10 @@ namespace Sledgemapper.Shared.Entities { - - - public class Wall :BaseMapEntity + public class Wall : BaseMapEntity { - - - + public override string ToString() + { + return $"{X}_{Y}_{Rotation}"; + } } } diff --git a/Sledgemapper/CommunicationManager.cs b/Sledgemapper/CommunicationManager.cs index 0fc37b6..ccb35e9 100644 --- a/Sledgemapper/CommunicationManager.cs +++ b/Sledgemapper/CommunicationManager.cs @@ -155,6 +155,11 @@ namespace Sledgemapper p.Position = player.Position; } }); + + Connection.On("Ping", (ping) => + { + SessionData.Pings.TryAdd(Guid.NewGuid(), ping); + }); } private Task GetToken() @@ -233,6 +238,14 @@ namespace Sledgemapper } } + + internal async Task Ping(Tile location) + { + if (Connection!=null && Connection.State == HubConnectionState.Connected) + { + await Connection.InvokeAsync("Ping",SessionData.SessionName, location); + } + } } class AuthenticatedHttpClientHandler : HttpClientHandler diff --git a/Sledgemapper/Content/Content.mgcb b/Sledgemapper/Content/Content.mgcb index c2bc53e..7b85633 100644 --- a/Sledgemapper/Content/Content.mgcb +++ b/Sledgemapper/Content/Content.mgcb @@ -93,6 +93,23 @@ /processorParam:TextureFormat=Compressed /build:fonts/font99.spritefont +#begin handcursors +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:handcursors + +#begin handcursorsIndex +/importer:XmlImporter +/processor: +/build:handcursorsIndex + #begin icon_delete.png /importer:TextureImporter /processor:TextureProcessor @@ -141,6 +158,23 @@ /processorParam:TextureFormat=Color /build:location.png +#begin rippleSpriteIndex +/importer:XmlImporter +/processor:PassThroughProcessor +/build:rippleSpriteIndex + +#begin rippleSpriteMap +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:rippleSpriteMap + #begin shaders/OutlineShader.fx /importer:EffectImporter /processor:EffectProcessor @@ -428,3 +462,75 @@ /processorParam:TextureFormat=Color /build:walls/wall05.png +#begin walls/wall06.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:walls/wall06.png + +#begin walls/wall07.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:walls/wall07.png + +#begin walls/wall08.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:walls/wall08.png + +#begin walls/wall09.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:walls/wall09.png + +#begin walls/wall10.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:walls/wall10.png + +#begin walls/wall11.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:walls/wall11.png + diff --git a/Sledgemapper/Content/handcursors b/Sledgemapper/Content/handcursors new file mode 100644 index 0000000..ef1ac08 Binary files /dev/null and b/Sledgemapper/Content/handcursors differ diff --git a/Sledgemapper/Content/handcursors.png b/Sledgemapper/Content/handcursors.png new file mode 100644 index 0000000..c31f80a Binary files /dev/null and b/Sledgemapper/Content/handcursors.png differ diff --git a/Sledgemapper/Content/handcursorsIndex b/Sledgemapper/Content/handcursorsIndex new file mode 100644 index 0000000..9f60bbe --- /dev/null +++ b/Sledgemapper/Content/handcursorsIndex @@ -0,0 +1,7 @@ + + + +handcursor_000 0 60 60 +handcursor_0161 0 60 60 + + diff --git a/Sledgemapper/Content/rippleSpriteIndex b/Sledgemapper/Content/rippleSpriteIndex new file mode 100644 index 0000000..e345a97 --- /dev/null +++ b/Sledgemapper/Content/rippleSpriteIndex @@ -0,0 +1,24 @@ + + + +ripple_000 0 128 128 +ripple_01129 0 128 128 +ripple_02258 0 128 128 +ripple_030 129 128 128 +ripple_04129 129 128 128 +ripple_050 258 128 128 +ripple_06129 258 128 128 +ripple_07258 129 128 128 +ripple_08258 258 128 128 +ripple_09387 0 128 128 +ripple_10387 129 128 128 +ripple_11516 0 128 128 +ripple_12387 258 128 128 +ripple_13516 129 128 128 +ripple_14645 0 128 128 +ripple_15774 0 128 128 +ripple_16645 129 128 128 +ripple_17516 258 128 128 +ripple_18645 258 128 128 + + diff --git a/Sledgemapper/Content/rippleSpriteMap b/Sledgemapper/Content/rippleSpriteMap new file mode 100644 index 0000000..0e76603 Binary files /dev/null and b/Sledgemapper/Content/rippleSpriteMap differ diff --git a/Sledgemapper/Content/walls/wall01.png b/Sledgemapper/Content/walls/wall01.png index adac5d5..c392278 100644 Binary files a/Sledgemapper/Content/walls/wall01.png and b/Sledgemapper/Content/walls/wall01.png differ diff --git a/Sledgemapper/Content/walls/wall02.png b/Sledgemapper/Content/walls/wall02.png index ccb660d..5273266 100644 Binary files a/Sledgemapper/Content/walls/wall02.png and b/Sledgemapper/Content/walls/wall02.png differ diff --git a/Sledgemapper/Content/walls/wall03.png b/Sledgemapper/Content/walls/wall03.png index 8dcb491..0b1320e 100644 Binary files a/Sledgemapper/Content/walls/wall03.png and b/Sledgemapper/Content/walls/wall03.png differ diff --git a/Sledgemapper/Content/walls/wall04.png b/Sledgemapper/Content/walls/wall04.png index 5e52255..2a5d470 100644 Binary files a/Sledgemapper/Content/walls/wall04.png and b/Sledgemapper/Content/walls/wall04.png differ diff --git a/Sledgemapper/Content/walls/wall05.png b/Sledgemapper/Content/walls/wall05.png index bad772e..745185e 100644 Binary files a/Sledgemapper/Content/walls/wall05.png and b/Sledgemapper/Content/walls/wall05.png differ diff --git a/Sledgemapper/Content/walls/wall06.png b/Sledgemapper/Content/walls/wall06.png new file mode 100644 index 0000000..d60f496 Binary files /dev/null and b/Sledgemapper/Content/walls/wall06.png differ diff --git a/Sledgemapper/Content/walls/wall07.png b/Sledgemapper/Content/walls/wall07.png new file mode 100644 index 0000000..7f528fb Binary files /dev/null and b/Sledgemapper/Content/walls/wall07.png differ diff --git a/Sledgemapper/Content/walls/wall08.png b/Sledgemapper/Content/walls/wall08.png new file mode 100644 index 0000000..d1c1ff8 Binary files /dev/null and b/Sledgemapper/Content/walls/wall08.png differ diff --git a/Sledgemapper/Content/walls/wall09.png b/Sledgemapper/Content/walls/wall09.png new file mode 100644 index 0000000..d404c92 Binary files /dev/null and b/Sledgemapper/Content/walls/wall09.png differ diff --git a/Sledgemapper/Content/walls/wall10.png b/Sledgemapper/Content/walls/wall10.png new file mode 100644 index 0000000..ae52abd Binary files /dev/null and b/Sledgemapper/Content/walls/wall10.png differ diff --git a/Sledgemapper/Content/walls/wall11.png b/Sledgemapper/Content/walls/wall11.png new file mode 100644 index 0000000..0247761 Binary files /dev/null and b/Sledgemapper/Content/walls/wall11.png differ diff --git a/Sledgemapper/Settings.cs b/Sledgemapper/Settings.cs index 25b39f6..3188725 100644 --- a/Sledgemapper/Settings.cs +++ b/Sledgemapper/Settings.cs @@ -12,6 +12,7 @@ namespace Sledgemapper public Color NoteColor { get; set; } public string MachineName { get; set; } public int TileDeleteDivider { get; set; } + public int PingDuration {get;set;} public Settings() { @@ -19,7 +20,8 @@ namespace Sledgemapper GridColor = Color.Black; NoteColor = Color.DarkRed; OverlayTintColor = new Color(24, 118, 157); -TileDeleteDivider=14; + TileDeleteDivider = 14; + PingDuration = 4000; try { MachineName = Environment.MachineName; diff --git a/Sledgemapper/Sledgemapper.cs b/Sledgemapper/Sledgemapper.cs index 70b5772..07b9220 100644 --- a/Sledgemapper/Sledgemapper.cs +++ b/Sledgemapper/Sledgemapper.cs @@ -6,12 +6,14 @@ using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MonoGame.Extended; +using MonoGame.Extended.VectorDraw; using Myra; using Myra.Graphics2D.Brushes; using Myra.Graphics2D.TextureAtlases; using Myra.Graphics2D.UI; using Myra.Graphics2D.UI.File; using Myra.Graphics2D.UI.Properties; +using Myra.Utility; using Newtonsoft.Json; using Sledgemapper.Shared.Entities; using Sledgemapper.UI; @@ -23,6 +25,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using AsyncAwaitBestPractices; namespace Sledgemapper { @@ -147,14 +150,18 @@ namespace Sledgemapper } } - private void AddItemToToolGrid(Grid grid, EventHandler eventAction, SpriteSheet spriteSheet) + private void AddItemToToolGrid(Grid grid, EventHandler eventAction, SpriteSheet spriteSheet, string e = "") { var indexX = 0; var indexY = 0; - foreach (var item in spriteSheet.index) + 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) @@ -165,6 +172,25 @@ namespace Sledgemapper } } + private void OnTileButtonTouchLeft(object sender, EventArgs e) + { + _desktop.HideContextMenu(); + _lblOverlayName.Visible = false; + } + + private SpriteSheet _rippleSpriteSheet; + Label _lblOverlayName; + + private void OnTileButtonTouchEntered(object sender, EventArgs e) + { + var mouseState = Mouse.GetState().Position; + mouseState.X += 10; + mouseState.Y += 10; + _lblOverlayName.Visible = true; + _lblOverlayName.Text = ((ImageButton)sender).Id; + _desktop.ShowContextMenu(_lblOverlayName, mouseState); + } + protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); @@ -191,16 +217,18 @@ namespace Sledgemapper _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; _wallsContent = Content.LoadContentFolder("walls"); - + _spriteSheet = new SpriteSheet(); - _spriteSheet.LoadContent(Content); + _spriteSheet.LoadContent(Content, "spriteIndex", "sprites"); + _rippleSpriteSheet = new SpriteSheet(); + _rippleSpriteSheet.LoadContent(Content, "handcursorsIndex", "handcursors"); + + _lblOverlayName = new Label(); + _lblOverlayName.Background = new SolidBrush(Color.SlateGray); + _lblOverlayName.Padding = new Myra.Graphics2D.Thickness(4); AddItemToToolGrid(_mainWidget.GridWalls, OnWallButtonClicked, _wallsContent); AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, _spriteSheet); @@ -212,6 +240,8 @@ namespace Sledgemapper _mainWidget.BtnToolbarLine.Image = new TextureRegion(Content.Load("icon_line")); _mainWidget.BtnToolbarRoom.Image = new TextureRegion(Content.Load("icon_room")); _mainWidget.BtnToolbarDelete.Image = new TextureRegion(Content.Load("icon_delete")); + _mainWidget.TxtOverlaySearch.TextChangedByUser += OnTxtOverlaySearchChange; + _desktop.Root = _mainWidget; _transparentRedRectangle = new Texture2D(GraphicsDevice, 1, 1); @@ -221,30 +251,51 @@ namespace Sledgemapper _whiteRectangle.SetData(new[] { Color.White }); } + private void OnTxtOverlaySearchChange(object sender, ValueChangedEventArgs e) + { + AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, _spriteSheet, e.NewValue); + } + private void OnBtnToolbarDeleteClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewDelete; + + _mainWidget.ClearSelection(); + ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); + ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarWallClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewWall; + _mainWidget.ClearSelection(); + ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); + ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarTileClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewTile; + _mainWidget.ClearSelection(); + ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); + ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarRoomClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewRoom; + _mainWidget.ClearSelection(); + ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); + ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OnBtnToolbarLinClicked(object sender, EventArgs e) { _state.InsertMode = InsertMode.NewLine; + _mainWidget.ClearSelection(); + ((ImageTextButton)sender).Border = new SolidBrush(Color.Red); + ((ImageTextButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2); } private void OneMenuFileSettingsSelected(object sender, EventArgs e) @@ -389,6 +440,8 @@ namespace Sledgemapper 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 { @@ -403,6 +456,11 @@ namespace Sledgemapper popup.AddChild(deleteNoteButton); } + var pingButton = new TextButton { Text = "Ping", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left }; + var location = new Tile { X = _state.HoveredTile.X, Y = _state.HoveredTile.Y }; + pingButton.Click += (s, e) => OnContextMenuPingClick(s, e, location); + popup.AddChild(pingButton); + _desktop.ShowContextMenu(popup, mouseState.Position); } @@ -559,6 +617,11 @@ namespace Sledgemapper } } + if (oldState.IsKeyDown(Keys.P) && newState.IsKeyUp(Keys.P)) + { + _communicationManager.Ping(_state.HoveredTile).SafeFireAndForget(); + } + foreach (var key in newState.GetPressedKeys()) { switch (key) @@ -610,10 +673,17 @@ namespace Sledgemapper var center = new Point((Window.ClientBounds.Width + 200) / 2 - _state.TileSize / 2, Window.ClientBounds.Height / 2 - _state.TileSize / 2); var dx = center.X - x * _state.TileSize - _viewportCenter.X; var dy = center.Y - y * _state.TileSize - _viewportCenter.Y; + _viewportCenter.X += dx; _viewportCenter.Y += dy; } + private void OnContextMenuPingClick(object sender, EventArgs e, Tile location) + { + _desktop.HideContextMenu(); + _communicationManager.Ping(location).SafeFireAndForget(); + } + protected override void Draw(GameTime gameTime) { if (_spriteBatch is null) @@ -628,7 +698,7 @@ namespace Sledgemapper _spriteBatch.Begin( transformMatrix: Matrix.CreateTranslation(_viewportCenter), - sortMode: SpriteSortMode.Texture); + sortMode: SpriteSortMode.Texture, samplerState: SamplerState.PointClamp); DrawTiles(); @@ -654,6 +724,8 @@ namespace Sledgemapper _spriteBatch.End(); + DrawRipple(gameTime); + try { _desktop?.Render(); @@ -665,6 +737,81 @@ namespace Sledgemapper base.Draw(gameTime); } + private void DrawRipple(GameTime gameTime) + { + _spriteBatch.Begin( + blendState: BlendState.NonPremultiplied, + transformMatrix: Matrix.CreateTranslation(_viewportCenter)); + + var durationMs = 2000; + var baseRadius = _state.TileSize / 4f; + var baseOuterRadius = (float)_state.TileSize; + var iterations = 3f; + var guids = _sessionData.Pings.Keys.ToArray(); + + foreach (var guid in guids) + { + var pingFound = _sessionData.Pings.TryGetValue(guid, out var ping); + if (!pingFound) + { + continue; + } + + if (ping.StartTime == 0) + { + ping.StartTime = gameTime.TotalGameTime.TotalMilliseconds; + } + + var x = ping.X * _state.TileSize + _state.TileSize / 2f; + var y = ping.Y * _state.TileSize + _state.TileSize / 2f; + + if (IsOffscreen(new Tile { X = ping.X, Y = ping.Y })) + { + DrawPingPointer(ping, gameTime); + } + else + { + + _spriteBatch.DrawCircle( + center: new Vector2(x, y), + radius: baseRadius, + sides: 20, + color: ping.Player.Color.ToColor(), + thickness: baseRadius); + + for (var i = 0; i < iterations; i++) + { + var cycleTime = (((float)gameTime.TotalGameTime.TotalMilliseconds + (float)i * durationMs / iterations) % durationMs) / durationMs; + var easing = Easings.Interpolate(cycleTime, Easings.Functions.SineEaseInOut); + _spriteBatch.DrawCircle( + center: new Vector2(x, y), + radius: baseRadius + baseOuterRadius * easing, + sides: 20, + color: new Color(ping.Player.Color.ToColor(), (1 - easing)), + thickness: 2 + 5 * (1 - easing)); + + + } + } + } + foreach (var guid in guids) + { + var pingFound = _sessionData.Pings.TryGetValue(guid, out var ping); + if (!pingFound) + { + continue; + } + + if ((gameTime.TotalGameTime.TotalMilliseconds - ping.StartTime) > _settings.PingDuration) + { + _sessionData.Pings.TryRemove(guid, out var _); + } + } + + + _spriteBatch.End(); + } + private void DrawRoomPreview() { if ((_state.InsertMode == InsertMode.NewRoom || _state.InsertMode == InsertMode.NewDelete) && _state.SelectedSnapPoint != null) @@ -736,25 +883,94 @@ namespace Sledgemapper break; } + var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize / 3).Value ?? _fonts.Last().Value; + var fscale = 1.2f; + var width = Math.Abs((endposX - posX)); + var height = Math.Abs((posY - endposY)); + var tilesWidth = width / (double)_state.TileSize; + var tilesHeight = height / (double)_state.TileSize; + tilesWidth = Math.Round(tilesWidth * 2, MidpointRounding.AwayFromZero) / 2; + tilesHeight = Math.Round(tilesHeight * 2, MidpointRounding.AwayFromZero) / 2; + var xmeasure = ffont.MeasureString($"{tilesWidth}"); + var ymeasure = ffont.MeasureString($"{tilesHeight}"); + float widthX = 0, widthY = 0, heightX = 0, heightY = 0; + + Rectangle area = new Rectangle(); if (posX != endposX && posY != endposY) { - if ((posX > endposX && posY > endposY) || (posX < endposX && posY < endposY)) { - _spriteBatch.Draw(_transparentRedRectangle, new Rectangle(posX, posY, endposX - posX, endposY - posY), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); + 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) { - _spriteBatch.Draw(_transparentRedRectangle, new Rectangle(posX, endposY, endposX - posX, posY - endposY), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); + 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) { - _spriteBatch.Draw(_transparentRedRectangle, new Rectangle(endposX, posY, posX - endposX, endposY - posY), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 1); + 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); + } } } @@ -863,88 +1079,16 @@ namespace Sledgemapper 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 float[] { ua1, ua2, ua3, ua4 }; - if (uas.Any(u => u > 0 && u < 1)) + var validPointer = GetPointerVector(new Point(_state.SelectedTile.X, _state.SelectedTile.Y), out var points); + if (validPointer) { - var ua = uas.Where(u => u > 0 && u < 1).Min(); - - int i = 0; - for (var j = 0; j < 4; j++) - { - if (uas[j] == ua) - { - i = j; - } - } - - - var x = (p1.X + ua * (p2.X - p1.X)); - var y = (p1.Y + ua * (p2.Y - p1.Y)); - - - var v = _vector2Pool.Rent(4); - switch (i) - { - case 0: - v[0] = new Vector2(x, y); - v[1] = new Vector2(x - 20, y + 10); - v[2] = new Vector2(x - 20, y - 10); - v[3] = new Vector2(x, y); - break; - case 1: - v[0] = new Vector2(x, y); - v[1] = new Vector2(x - 10, y + 20); - v[2] = new Vector2(x + 10, y + 20); - v[3] = new Vector2(x, y); - y += 20; - break; - case 2: - x += 0; - v[0] = new Vector2(x, y); - v[1] = new Vector2(x + 20, y + 10); - v[2] = new Vector2(x + 20, y - 10); - v[3] = new Vector2(x, y); - break; - case 3: - y -= 20; - v[0] = new Vector2(x, y); - v[1] = new Vector2(x - 10, y - 20); - v[2] = new Vector2(x + 10, y - 20); - v[3] = new Vector2(x, y); - break; - } - _spriteBatch.DrawPolygon(Vector2.Zero, v, Color.Red, 2); - _vector2Pool.Return(v); - + _spriteBatch.DrawPolygon(Vector2.Zero, points, Color.Red, 2); } } - - - _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); + 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); + } } } @@ -1011,22 +1155,22 @@ namespace Sledgemapper private bool IsMapElementOffscreen(BaseMapEntity item) { - SnapPoint start,end; + SnapPoint start, end; - switch (item) - { - case Line l: - start=l.Start; - end=l.End; - break; + 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; - } + 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; @@ -1175,22 +1319,131 @@ namespace Sledgemapper private void DrawPlayers() { - for (int i = 0; i < _sessionData.Players.Count; i++) + var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize).Value ?? _fonts.Last().Value; + var fscale = _state.TileSize / ((float)ffont.LineSpacing * 3f); + + var playerCells = _sessionData.Players.Select(m => m.Position).Distinct().ToList(); + + foreach (var cell in playerCells.Where(c => !IsOffscreen(c))) { - Player player = _sessionData.Players[i]; - 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); + 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) @@ -1203,30 +1456,88 @@ namespace Sledgemapper } } - private void DrawPlayerPointer(Player player) + private void DrawPingPointer(Ping ping, GameTime gameTime) { - var center = new Point((Window.ClientBounds.Width + 200) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y); + 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); - var cx = center.X / _state.TileSize; - var cy = center.Y / _state.TileSize; + if (validPointer) + { + _spriteBatch.DrawPolygon(Vector2.Zero, points, ping.Player.Color.ToColor(), 4); - 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); + for (var j = 0; j < iterations; j++) + { + var cycleTime = (((float)gameTime.TotalGameTime.TotalMilliseconds + (float)j * durationMs / iterations) % durationMs) / durationMs; + var easing = Easings.Interpolate(cycleTime, Easings.Functions.SineEaseInOut); + var v2 = new Vector2[points.Length]; + + + var tCenter = new Vector2((points[0].X + points[1].X + points[2].X) / 3f, (points[0].Y + points[1].Y + points[2].Y) / 3f); + + + for (int i1 = 0; i1 < v2.Length; i1++) + { + + var svx = ((points[i1].X - tCenter.X) * (1 + (2 * easing))) + tCenter.X; + var svy = ((points[i1].Y - tCenter.Y) * (1 + (2 * easing))) + tCenter.Y; + v2[i1] = new Vector2(svx, svy); + } + + _spriteBatch.DrawPolygon( + offset: Vector2.Zero, + points: v2, + color: new Color(ping.Player.Color.ToColor(), 1f - easing), + thickness: 2 + 2 * (1 - easing)); + + } + + } + } + + private bool GetPointerVector(Point target, out Vector2[] points) + { + var offset = _state.TileSize / 2; + var leftBound = 200 + offset; + var topBound = 75 + offset; + var bottomBound = 25 + offset; + var rightBound = offset; + points = new Vector2[0]; + var center = new Point((Window.ClientBounds.Width + leftBound) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y); + + // center + var p1 = new Vector2(center.X, center.Y); + + // point + var p2 = new Vector2( + target.X * _state.TileSize + offset, + target.Y * _state.TileSize + offset); + + // top right + var p3 = new Vector2( + Window.ClientBounds.Width - _viewportCenter.X - rightBound, + topBound - _viewportCenter.Y); + + //bottom right + var p4 = new Vector2( + Window.ClientBounds.Width - _viewportCenter.X - rightBound, + Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); + var ua1 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); - p3 = new Vector2(200 - _viewportCenter.X, 0 - _viewportCenter.Y); - p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y); + p3 = new Vector2(leftBound - _viewportCenter.X, topBound - _viewportCenter.Y); + p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, topBound - _viewportCenter.Y); var ua2 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); - p3 = new Vector2(200 - _viewportCenter.X, 0 - _viewportCenter.Y); - p4 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); + p3 = new Vector2(leftBound - _viewportCenter.X, topBound - _viewportCenter.Y); + p4 = new Vector2(leftBound - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); var ua3 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); - p3 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); - p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y); + p3 = new Vector2(leftBound - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); + p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y - bottomBound); var ua4 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y)); var uas = new List { ua1, ua2, ua3, ua4 }; @@ -1236,26 +1547,37 @@ namespace Sledgemapper var i = uas.IndexOf(ua); var x = (p1.X + ua * (p2.X - p1.X)); var y = (p1.Y + ua * (p2.Y - p1.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) }; + x += offset; + points = new Vector2[] { new Vector2(x, y), new Vector2(x - 20, y - 10), new Vector2(x - 20, y + 10), new Vector2(x, y) }; break; case 1: - y += 20; - vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y + 20), new Vector2(x + 10, y + 20), new Vector2(x, y) }; + y -= offset; + points = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y + 20), new Vector2(x + 10, y + 20), new Vector2(x, y) }; break; case 2: - x += 0; - vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x + 20, y + 10), new Vector2(x + 20, y - 10), new Vector2(x, y) }; + x -= offset; + points = new Vector2[] { new Vector2(x, y), new Vector2(x + 20, y + 10), new Vector2(x + 20, y - 10), new Vector2(x, y) }; break; case 3: - y -= 20; - vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y - 20), new Vector2(x + 10, y - 20), new Vector2(x, y) }; + y += offset; + points = new Vector2[] { new Vector2(x, y), new Vector2(x + 10, y - 20), new Vector2(x - 10, y - 20), new Vector2(x, y) }; break; } - _spriteBatch.DrawPolygon(Vector2.Zero, vertexes, player.Color.ToColor(), 2); + return true; + } + return false; + } + + private void DrawPlayerPointer(Player player) + { + var validPointer = GetPointerVector(new Point(player.Position.X, player.Position.Y), out var points); + if (validPointer) + { + _spriteBatch.DrawPolygon(Vector2.Zero, points, player.Color.ToColor(), 2); } } @@ -1263,20 +1585,18 @@ namespace Sledgemapper private bool IsOffscreen(Tile position) { - var visibleTilesX = GraphicsDevice.Viewport.Width / _state.TileSize + 1; - var visibleTilesY = GraphicsDevice.Viewport.Height / _state.TileSize + 1; + 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 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); + var tileTL = new Point(position.X * _state.TileSize + (int)_viewportCenter.X, position.Y * _state.TileSize + (int)_viewportCenter.Y); + var tileBR = new Point(position.X * _state.TileSize + (int)_viewportCenter.X + _state.TileSize, position.Y * _state.TileSize + (int)_viewportCenter.Y + _state.TileSize); - if (position.X < tileTopLeft.X || position.Y < tileTopLeft.Y || position.X > tileBottomRight.X || position.Y > tileBottomRight.Y) + if (tileTL.X <= boxTL.X || tileTL.Y <= boxTL.Y || tileBR.X >= boxBR.X || tileBR.Y >= boxBR.Y) { return true; } - return false; + return false; } private int _borderWidth => (_state.TileSize / 6) % 2 == 0 ? (_state.TileSize / 6) : (_state.TileSize / 6) + 1; @@ -1419,11 +1739,11 @@ namespace Sledgemapper } } } - Effect outlineShader; + Effect outlineShader; private Texture2D _transparentRedRectangle; private Texture2D _whiteRectangle; private Dictionary _wallsContent; - + private SpriteSheet _spriteSheet; private void DrawDelete(Room tile) @@ -1870,6 +2190,8 @@ namespace Sledgemapper noteWindow.NoteText.SetKeyboardFocus(); } + + private void OnContextMenuDeleteNoteClick(object sender, EventArgs e) { _desktop.HideContextMenu(); @@ -2091,10 +2413,10 @@ namespace Sledgemapper internal Texture2D Texture; internal Dictionary index; - public void LoadContent(ContentManager content) + public void LoadContent(ContentManager content, string spriteIndex, string texture) { - index = content.Load>("spriteIndex"); - Texture = content.Load("sprites"); + index = content.Load>(spriteIndex); + Texture = content.Load(texture); } internal Rectangle? SourceRectangle(string spriteName) @@ -2112,4 +2434,6 @@ namespace Sledgemapper } } } + + } diff --git a/Sledgemapper/UI/MainWidget.Custom.cs b/Sledgemapper/UI/MainWidget.Custom.cs index 34cb9fb..1067a3a 100644 --- a/Sledgemapper/UI/MainWidget.Custom.cs +++ b/Sledgemapper/UI/MainWidget.Custom.cs @@ -8,9 +8,18 @@ namespace Sledgemapper.UI { ClearSelection(GridWalls); ClearSelection(GridOverlays); + ClearSelection(Toolbar); } private void ClearSelection(Grid grid) + { + foreach (var widget in grid.Widgets) + { + widget.Border = null; + } + } + + private void ClearSelection(HorizontalStackPanel grid) { foreach (var widget in grid.Widgets) { diff --git a/Sledgemapper/UI/MainWidget.Generated.cs b/Sledgemapper/UI/MainWidget.Generated.cs index 2f730cd..321f7d2 100644 --- a/Sledgemapper/UI/MainWidget.Generated.cs +++ b/Sledgemapper/UI/MainWidget.Generated.cs @@ -1,4 +1,4 @@ -/* Generated by MyraPad at 18/01/2021 09:38:43 */ +/* Generated by MyraPad at 04/02/2021 10:36:48 */ using Myra; using Myra.Graphics2D; using Myra.Graphics2D.TextureAtlases; @@ -127,31 +127,19 @@ namespace Sledgemapper.UI _mainMenu.Items.Add(menuItem2); _mainMenu.Items.Add(menuItem3); - BtnToolbarTile = new ImageTextButton(); - BtnToolbarTile.Text = " T"; - BtnToolbarTile.Width = 40; - BtnToolbarTile.Height = 40; - BtnToolbarTile.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; - BtnToolbarTile.Id = "BtnToolbarTile"; - BtnToolbarLine = new ImageTextButton(); BtnToolbarLine.Text = " C"; BtnToolbarLine.Width = 40; BtnToolbarLine.Height = 40; + BtnToolbarLine.BorderThickness = new Thickness(2); BtnToolbarLine.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; BtnToolbarLine.Id = "BtnToolbarLine"; - BtnToolbarWall = new ImageTextButton(); - BtnToolbarWall.Text = " W"; - BtnToolbarWall.Width = 40; - BtnToolbarWall.Height = 40; - BtnToolbarWall.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; - BtnToolbarWall.Id = "BtnToolbarWall"; - BtnToolbarRoom = new ImageTextButton(); BtnToolbarRoom.Text = " R"; BtnToolbarRoom.Width = 40; BtnToolbarRoom.Height = 40; + BtnToolbarRoom.BorderThickness = new Thickness(2); BtnToolbarRoom.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; BtnToolbarRoom.Id = "BtnToolbarRoom"; @@ -159,24 +147,24 @@ namespace Sledgemapper.UI BtnToolbarDelete.Text = " D"; BtnToolbarDelete.Width = 40; BtnToolbarDelete.Height = 40; + BtnToolbarDelete.BorderThickness = new Thickness(2); BtnToolbarDelete.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; BtnToolbarDelete.Id = "BtnToolbarDelete"; - var horizontalStackPanel1 = new HorizontalStackPanel(); - horizontalStackPanel1.Spacing = 5; - horizontalStackPanel1.Proportions.Add(new Proportion + Toolbar = new HorizontalStackPanel(); + Toolbar.Spacing = 12; + Toolbar.Proportions.Add(new Proportion { Type = Myra.Graphics2D.UI.ProportionType.Auto, }); - horizontalStackPanel1.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; - horizontalStackPanel1.Height = 51; - horizontalStackPanel1.Padding = new Thickness(4, 0, 0, 0); - horizontalStackPanel1.Background = new SolidBrush("#404040FF"); - horizontalStackPanel1.Widgets.Add(BtnToolbarTile); - horizontalStackPanel1.Widgets.Add(BtnToolbarLine); - horizontalStackPanel1.Widgets.Add(BtnToolbarWall); - horizontalStackPanel1.Widgets.Add(BtnToolbarRoom); - horizontalStackPanel1.Widgets.Add(BtnToolbarDelete); + Toolbar.VerticalAlignment = Myra.Graphics2D.UI.VerticalAlignment.Center; + Toolbar.Height = 51; + Toolbar.Padding = new Thickness(4, 0, 0, 0); + Toolbar.Background = new SolidBrush("#404040FF"); + Toolbar.Id = "Toolbar"; + Toolbar.Widgets.Add(BtnToolbarLine); + Toolbar.Widgets.Add(BtnToolbarRoom); + Toolbar.Widgets.Add(BtnToolbarDelete); GridWalls = new Grid(); GridWalls.ColumnSpacing = 8; @@ -218,15 +206,22 @@ namespace Sledgemapper.UI }; GridOverlays.Id = "GridOverlays"; - var scrollViewer2 = new ScrollViewer(); - scrollViewer2.Content = GridOverlays; + ScrOverlay = new ScrollViewer(); + ScrOverlay.Id = "ScrOverlay"; + ScrOverlay.Content = GridOverlays; + + TxtOverlaySearch = new TextBox(); + TxtOverlaySearch.Height = 40; + TxtOverlaySearch.Margin = new Thickness(0, 4); + TxtOverlaySearch.Id = "TxtOverlaySearch"; var verticalStackPanel2 = new VerticalStackPanel(); verticalStackPanel2.Proportions.Add(new Proportion { Type = Myra.Graphics2D.UI.ProportionType.Fill, }); - verticalStackPanel2.Widgets.Add(scrollViewer2); + verticalStackPanel2.Widgets.Add(ScrOverlay); + verticalStackPanel2.Widgets.Add(TxtOverlaySearch); var verticalSplitPane1 = new VerticalSplitPane(); verticalSplitPane1.Width = 200; @@ -262,22 +257,22 @@ namespace Sledgemapper.UI lblSessionName.MinWidth = 100; lblSessionName.Id = "lblSessionName"; - var horizontalStackPanel2 = new HorizontalStackPanel(); - horizontalStackPanel2.Spacing = 10; - horizontalStackPanel2.Proportions.Add(new Proportion + var horizontalStackPanel1 = new HorizontalStackPanel(); + horizontalStackPanel1.Spacing = 10; + horizontalStackPanel1.Proportions.Add(new Proportion { Type = Myra.Graphics2D.UI.ProportionType.Auto, }); - horizontalStackPanel2.Height = 25; - horizontalStackPanel2.Background = new SolidBrush("#333333FF"); - horizontalStackPanel2.Widgets.Add(label1); - horizontalStackPanel2.Widgets.Add(lblConnectionStatus); - horizontalStackPanel2.Widgets.Add(verticalSeparator1); - horizontalStackPanel2.Widgets.Add(label2); - horizontalStackPanel2.Widgets.Add(lblUsername); - horizontalStackPanel2.Widgets.Add(verticalSeparator2); - horizontalStackPanel2.Widgets.Add(label3); - horizontalStackPanel2.Widgets.Add(lblSessionName); + horizontalStackPanel1.Height = 25; + horizontalStackPanel1.Background = new SolidBrush("#333333FF"); + horizontalStackPanel1.Widgets.Add(label1); + horizontalStackPanel1.Widgets.Add(lblConnectionStatus); + horizontalStackPanel1.Widgets.Add(verticalSeparator1); + horizontalStackPanel1.Widgets.Add(label2); + horizontalStackPanel1.Widgets.Add(lblUsername); + horizontalStackPanel1.Widgets.Add(verticalSeparator2); + horizontalStackPanel1.Widgets.Add(label3); + horizontalStackPanel1.Widgets.Add(lblSessionName); Proportions.Add(new Proportion @@ -293,9 +288,9 @@ namespace Sledgemapper.UI Type = Myra.Graphics2D.UI.ProportionType.Fill, }); Widgets.Add(_mainMenu); - Widgets.Add(horizontalStackPanel1); + Widgets.Add(Toolbar); Widgets.Add(verticalSplitPane1); - Widgets.Add(horizontalStackPanel2); + Widgets.Add(horizontalStackPanel1); } @@ -314,13 +309,14 @@ namespace Sledgemapper.UI public MenuItem MenuViewCenterOnSelection; public MenuItem MenuHelpAbout; public HorizontalMenu _mainMenu; - public ImageTextButton BtnToolbarTile; public ImageTextButton BtnToolbarLine; - public ImageTextButton BtnToolbarWall; public ImageTextButton BtnToolbarRoom; public ImageTextButton BtnToolbarDelete; + public HorizontalStackPanel Toolbar; public Grid GridWalls; public Grid GridOverlays; + public ScrollViewer ScrOverlay; + public TextBox TxtOverlaySearch; public Label lblConnectionStatus; public Label lblUsername; public Label lblSessionName; diff --git a/Sledgemapper/UI/mainwidget.xml b/Sledgemapper/UI/mainwidget.xml index 0929de8..53e84c3 100644 --- a/Sledgemapper/UI/mainwidget.xml +++ b/Sledgemapper/UI/mainwidget.xml @@ -32,15 +32,13 @@ - + - - - - - + + + @@ -58,6 +56,9 @@ + + + diff --git a/Sledgemapper/UI/mainwidget.xml.xmmp b/Sledgemapper/UI/mainwidget.xml.xmmp new file mode 100644 index 0000000..aca05ef --- /dev/null +++ b/Sledgemapper/UI/mainwidget.xml.xmmp @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file