diff --git a/PDGServer_WPF/MainWindow.xaml b/PDGServer_WPF/MainWindow.xaml
index 31cbd2a..2f568ef 100644
--- a/PDGServer_WPF/MainWindow.xaml
+++ b/PDGServer_WPF/MainWindow.xaml
@@ -42,13 +42,28 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PDGServer_WPF/MainWindow.xaml.cs b/PDGServer_WPF/MainWindow.xaml.cs
index 32e2fa0..0300c5e 100644
--- a/PDGServer_WPF/MainWindow.xaml.cs
+++ b/PDGServer_WPF/MainWindow.xaml.cs
@@ -1,4 +1,5 @@
using Microsoft.Win32;
+using RBG.Helpers;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -72,9 +73,7 @@ namespace RBG_Server.WPF
catch (Exception e)
{
Console.WriteLine(e);
-
}
-
}
///
/// Creates and starts the game server
@@ -154,22 +153,17 @@ namespace RBG_Server.WPF
}
///
- /// Generates elements to go inside the game grid from the provided list of players
+ /// Generates the game board overlay grid
/// The grid should be appropriately sized before using this
///
private void GenerateGameBoard(List players)
{
- SolidColorBrush buttonBrush = new(Color.FromArgb(1, 255, 255, 255));
- Grid[] cells = new Grid[PreviewImageOverlay.RowDefinitions.Count * PreviewImageOverlay.ColumnDefinitions.Count];
+ // Create an array of BoardCells Rows*Columns in size
+ BoardCell[] cells = new BoardCell[PreviewImageOverlay.RowDefinitions.Count * PreviewImageOverlay.ColumnDefinitions.Count];
List unaddedPlayers = players;
for (int i = 0; i < cells.Length; i++)
{
- cells[i] = new Grid();
- // Each cell has several components:
- // Stackpanel, which has each sprite in the cell
- UniformGrid cellStack = new();
- cellStack.Columns = 4;
- //cellStack.ItemWidth = cellStack.Width / 4;
+ cells[i] = new BoardCell();
Queue removedPlayers = new();
foreach (Player item in unaddedPlayers)
{
@@ -182,50 +176,66 @@ namespace RBG_Server.WPF
{
Player removed = removedPlayers.Dequeue();
_ = unaddedPlayers.Remove(removed);
- _ = cellStack.Children.Add(removed);
+ _ = cells[i].CellStack.Children.Add(removed);
}
- _ = cells[i].Children.Add(cellStack);
- // Button, whose content is empty
- Button cellButton = new()
- {
- Content = ""
- };
- cellButton.Background = buttonBrush;
- cellButton.Tag = new Tuple(i / PreviewImageOverlay.ColumnDefinitions.Count, i % PreviewImageOverlay.ColumnDefinitions.Count); // Tag is a Tuple
- cellButton.Click += CellButton_Click;
- _ = cells[i].Children.Add(cellButton);
_ = PreviewImageOverlay.Children.Add(cells[i]);
+ cells[i].CellButton.Tag = new Tuple(i % PreviewImageOverlay.ColumnDefinitions.Count, i / PreviewImageOverlay.ColumnDefinitions.Count);
Grid.SetColumn(cells[i], i % PreviewImageOverlay.ColumnDefinitions.Count);
Grid.SetRow(cells[i], i / PreviewImageOverlay.ColumnDefinitions.Count);
}
}
///
- /// Represents a cell on the board
+ /// Represents a cell on the board; as a grid
///
- class BoardCell
+ class BoardCell : Grid
{
// brush the button should have
+
static SolidColorBrush buttonBrush = new(Color.FromArgb(1, 255, 255, 255));
+
// Each cell has a uniform grid that
- UniformGrid cellStack = new()
+ public UniformGrid CellStack { get; } = new()
{
Columns = 4,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
};
- Button cellButton = new()
+ public Button CellButton { get; } = new()
{
- Content = ""
+ Content = "",
};
Queue removedPlayers = new();
- Grid parentGrid;
+ // Grid parentGrid; // Parent grid is now this itself
- public BoardCell(Grid parentGrid)
+ public BoardCell() : base()
{
- this.parentGrid = parentGrid;
+ CellButton.MouseEnter += CellButton_MouseEnter;
+ CellButton.MouseLeave += CellButton_MouseLeave;
+ CellButton.Click += CellButton_Click;
+ CellButton.Background = buttonBrush;
+ CellStack.Columns = 2;
+ CellStack.VerticalAlignment = VerticalAlignment.Top;
+ // Add our grid and button
+ _ = Children.Add(CellStack);
+ _ = Children.Add(CellButton);
}
+ private void CellButton_Click(object sender, RoutedEventArgs e)
+ {
+ Button cellButton = sender as Button;
+ System.Diagnostics.Debug.WriteLine("Button at {0}, {1} was clicked", ((Tuple)cellButton.Tag).Item2, ((Tuple)cellButton.Tag).Item1);
+ }
+
+ private void CellButton_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
+ {
+ //throw new NotImplementedException();
+ }
+
+ private void CellButton_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
+ {
+ //throw new NotImplementedException();
+ }
}
private void GenerateNewGameBoard(List players)
@@ -239,11 +249,7 @@ namespace RBG_Server.WPF
}
}
- private void CellButton_Click(object sender, RoutedEventArgs e)
- {
- Button cellButton = sender as Button;
- Console.WriteLine("Button at {0}, {1} was clicked", ((Tuple)cellButton.Tag).Item2, ((Tuple)cellButton.Tag).Item1);
- }
+
private void Sliders_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
@@ -275,26 +281,68 @@ namespace RBG_Server.WPF
// Create a new player at a random location
Player p = new("Player " + i, rand.Next(0, PreviewImageOverlay.RowDefinitions.Count), rand.Next(PreviewImageOverlay.ColumnDefinitions.Count));
// Load the sprite
- BitmapSource pSprite = new BitmapImage(new Uri("pack://application:,,,/Sprites/" + sprites[rand.Next(0, sprites.Length)]));
- // Recolour:
- if (rand.Next(1, 4) == 1)
+ string[] keys = new string[loadedSprites.Count];
+ loadedSprites.Keys.CopyTo(keys, 0);
+ string key = keys[rand.Next(keys.Length)];
+ if (key.ToLower().EndsWith(".gif"))
{
- pSprite = RBG.Helpers.ImageProcessing.UpdatePixelColours(pSprite, 0.0, 0.0, 1.0);
+ try
+ {
+ AnimatedBitmapImage animatedBitmap = new(new MemoryStream(loadedSprites[key]), p.PlayerSprite, Application.Current.Dispatcher);
+ }
+ catch
+ {
+ BitmapSource pSprite;
+ {
+ // Scope & create the base data for pSprite
+ BitmapImage src = new();
+ src.BeginInit();
+
+ src.StreamSource = new MemoryStream(loadedSprites[key]);
+ src.EndInit();
+ pSprite = src;
+ }
+ // Recolour:
+ if (rand.Next(1, 4) == 1)
+ {
+ pSprite = ImageProcessing.UpdatePixelColours(pSprite, 0.0, 0.0, 1.0);
+ }
+ p.PlayerSprite.Source = pSprite;
+
+ }
}
- p.Source = pSprite;
+ else
+ {
+ BitmapSource pSprite;
+ {
+ // Scope & create the base data for pSprite
+ BitmapImage src = new();
+ src.BeginInit();
+ src.StreamSource = new MemoryStream(loadedSprites[key]);
+ src.EndInit();
+ pSprite = src;
+ }
+ // Recolour:
+ if (rand.Next(1, 4) == 1)
+ {
+ pSprite = ImageProcessing.UpdatePixelColours(pSprite, 0.0, 0.0, 1.0);
+ }
+ p.PlayerSprite.Source = pSprite;
+ }
+
randPlayers.Add(p);
}
GenerateGameBoard(randPlayers);
}
- ConcurrentDictionary loadedSprites = new();
+ ConcurrentDictionary loadedSprites = new();
Task loadingFiles;
private void GameSpritesBrowser_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new();
- ofd.Filter = "Image Files|*.png;*.jpg";
+ ofd.Filter = "Image Files|*.png;*.jpg;*.gif";
ofd.Multiselect = true;
if (ofd.ShowDialog() == true)
{
@@ -327,23 +375,57 @@ namespace RBG_Server.WPF
{
fs.CopyTo(ms);
}
- _ = loadedSprites.TryAdd(file, ms);
+ _ = loadedSprites.TryAdd(file, ms.ToArray());
});
// Invoke the dispatcher to add all of the sprites to the loaded sprites list
_ = Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() =>
{
LoadedSprites.Children.Clear();
- foreach (KeyValuePair item in loadedSprites)
+ foreach (KeyValuePair item in loadedSprites)
{
- BitmapImage image = new();
- image.BeginInit();
- image.StreamSource = item.Value;
- image.EndInit();
- Image spriteImage = new()
+ if (item.Key.ToLower().EndsWith(".gif"))
{
- Source = image
- };
- _ = LoadedSprites.Children.Add(spriteImage);
+ try
+ {
+ /*
+ // Must write-out the stream to
+ MediaElement gifMedia = new();
+ gifMedia.LoadedBehavior = MediaState.Play;
+ gifMedia.Source = new Uri(item.Key);
+ _ = LoadedSprites.Children.Add(gifMedia);
+ */
+ Image spriteImage = new();
+ RBG.Helpers.AnimatedBitmapImage animatedBitmap = new(new MemoryStream(item.Value), spriteImage, Application.Current.Dispatcher);
+ _ = LoadedSprites.Children.Add(spriteImage);
+ }
+ catch
+ {
+ // On failed, use the cached data
+ BitmapImage image = new();
+ image.BeginInit();
+ image.StreamSource = new MemoryStream(item.Value);
+ image.EndInit();
+ Image spriteImage = new()
+ {
+ Source = image
+ };
+ _ = LoadedSprites.Children.Add(spriteImage);
+
+ }
+ }
+ else
+ {
+
+ BitmapImage image = new();
+ image.BeginInit();
+ image.StreamSource = new MemoryStream(item.Value);
+ image.EndInit();
+ Image spriteImage = new()
+ {
+ Source = image
+ };
+ _ = LoadedSprites.Children.Add(spriteImage);
+ }
}
}));
});
diff --git a/RBG.Helpers/ImageProcessing.cs b/RBG.Helpers/ImageProcessing.cs
index 3f88666..09a2444 100644
--- a/RBG.Helpers/ImageProcessing.cs
+++ b/RBG.Helpers/ImageProcessing.cs
@@ -1,6 +1,12 @@
using System;
using System.Windows;
using System.Windows.Media.Imaging;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Threading;
+using System.Windows.Controls;
+using System.Windows.Threading;
+using System.Collections.Generic;
namespace RBG.Helpers
{
@@ -83,4 +89,47 @@ namespace RBG.Helpers
}
+
+ public class AnimatedBitmapImage
+ {
+ public BitmapSource BaseImage { get; set; }
+ BitmapFrame[] frames;
+ int framerate;
+ int currentFrame;
+ Timer frameChange;
+ public AnimatedBitmapImage(Stream source, Image target, Dispatcher dispatcher)
+ {
+ GifBitmapDecoder decoder = new(source, BitmapCreateOptions.None, BitmapCacheOption.Default);
+ frames = new BitmapFrame[decoder.Frames.Count];
+ decoder.Frames.CopyTo(frames, 0);
+
+ byte[] frameratebytes = new byte[2];
+ source.Position = 0x324;
+ frameratebytes[0] = (byte)source.ReadByte();
+ frameratebytes[1] = (byte)source.ReadByte();
+ framerate = 33;
+
+ BaseImage = frames[0];
+ // cycle through frames
+ frameChange = new Timer((t) =>
+ {
+ currentFrame++;
+ currentFrame %= frames.Length;
+ BaseImage = frames[currentFrame];
+ _ = dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() =>
+ {
+ try
+ {
+ target.Source = BaseImage;
+ }
+ catch
+ {
+
+ }
+
+ }));
+ },null, framerate, framerate);
+ }
+ }
+
}
diff --git a/RBG.Helpers/RBG.Helpers.csproj b/RBG.Helpers/RBG.Helpers.csproj
index 29f3391..72f7ba3 100644
--- a/RBG.Helpers/RBG.Helpers.csproj
+++ b/RBG.Helpers/RBG.Helpers.csproj
@@ -1,7 +1,7 @@
- net5.0
+ net5.0-windows10.0.19041.0
diff --git a/RBG_Server.Core/Player.cs b/RBG_Server.Core/Player.cs
index 56052c9..73c42fa 100644
--- a/RBG_Server.Core/Player.cs
+++ b/RBG_Server.Core/Player.cs
@@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
namespace RBG_Server
{
@@ -11,8 +13,18 @@ namespace RBG_Server
/// Data class containing information about a player. Drawn directly to the screen, hence the inheritance
/// of Image, which allows this entire object to be
///
- public class Player : Image
+ public class Player : Grid
{
+ static GradientStopCollection gradientStops = new(5)
+ {
+ new GradientStop(Color.FromArgb(255, 0, 255, 255), 0),
+ new GradientStop(Color.FromArgb(192, 0, 255, 255), 0.75),
+ new GradientStop(Color.FromArgb(128, 0, 255, 255), 0.85),
+ new GradientStop(Color.FromArgb(32, 0, 255, 255), 0.95),
+ new GradientStop(Color.FromArgb(0, 0, 255, 255), 1)
+ };
+ static RadialGradientBrush shadowBrush = new(gradientStops);
+
// C# uses implicit field definitions; i.e. declaring a property creates an implicit private field
// Note that it is also possible to assign values, and specify access modifiers for the get & set independantly
public string PlayerName { get; set; }
@@ -21,6 +33,10 @@ namespace RBG_Server
public long LastTime { get; set; } = DateTime.Now.Ticks;
public bool Connected { get; private set; }
public byte[] UnhandledBuffer { get; set; }
+
+ public Image PlayerSprite { get; set; } = new();
+ private Rectangle spriteShadow = new();
+
private int processing;
public int ObtainLock()
@@ -43,6 +59,10 @@ namespace RBG_Server
LastTime = DateTime.Now.Ticks;
Connected = true;
UnhandledBuffer = Array.Empty();
+
+ spriteShadow.Fill = shadowBrush;
+ Children.Add(spriteShadow);
+ Children.Add(PlayerSprite);
}
public new bool Equals(object obj)