// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Spludlow.Tetris; namespace SpludlowTetris { /// /// Spludlow Tetris Main Playing Window /// public partial class MainWindow : Window { public TetrisSettings _TetrisSettings = null; public TetrisServer _TetrisServer = null; public TetrisClient _TetrisClient = null; private TetrisAudio _TetrisAudio = null; private TetrisInput _TetrisInput = null; private bool _BoardSetup = false; private SolidColorBrush[] _SolidColorBrushs; private static Color _BackGroundColour = Colors.Black; private static double _DimPlaced = 0.8; private TetrisBot _TetrisBot = new TetrisBot(); private TetrisBotFarm _TetrisBotFarm = new TetrisBotFarm(); private List _Keys; public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; this.Closed += MainWindow_Closed; this.SizeChanged += MainWindow_SizeChanged; this.StateChanged += MainWindow_StateChanged; this.KeyDown += MainWindow_KeyDown; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { this._TetrisSettings = new TetrisSettings(); this.Width = this._TetrisSettings.State.WindowWidth; this.Height = this._TetrisSettings.State.WindowHeight; if (this._TetrisSettings.State.WindowX != -1) { this.WindowStartupLocation = WindowStartupLocation.Manual; this.Left = this._TetrisSettings.State.WindowX; this.Top = this._TetrisSettings.State.WindowY; } this.ReadKeysSettings(); // Colours 0 1-7(-1) 8:gery 9-15(-9) _SolidColorBrushs = new SolidColorBrush[16]; _SolidColorBrushs[0] = new SolidColorBrush(_BackGroundColour); _SolidColorBrushs[8] = new SolidColorBrush(Colors.Gray); for (int index = 0; index < 7; ++index) { double angle = index * (360.0 / 7); _SolidColorBrushs[index + 1] = new SolidColorBrush(Spludlow.Media.Colour.FromHSV(angle, 1, _DimPlaced)); _SolidColorBrushs[index + 1 + 8] = new SolidColorBrush(Spludlow.Media.Colour.FromHSV(angle, 1, 1)); } // Audio try { _TetrisAudio = new TetrisAudio(Environment.CurrentDirectory + @"\SOUNDS"); _TetrisAudio.Volume(this._TetrisSettings.State.Volume / 10.0F); } catch (Exception ee) { Spludlow.Log.Error("Tetris Form Audio Error", ee); } // Input try { this._TetrisInput = new TetrisInput(); this._TetrisInput.MoveEvent += _TetrisInput_MoveEvent; } catch (Exception ee) { Spludlow.Log.Error("Tetris Form Input Error", ee); } // Help this.AppendText("F1 for Settings" + Environment.NewLine + "More info:" + Environment.NewLine + "http://tetris.spludlow.co.uk/" + Environment.NewLine + "Client V " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + Environment.NewLine); try { if (TetrisClient.OnlyProcess() == true) _StartStopServer(); _StartStopClient(); } catch (Exception ee) { Spludlow.Log.Error("Tetris Autostart", ee); MessageBox.Show("Problem automatically starting client and/or server please check configuration (F1)" + Environment.NewLine + ee.Message); } } private void MainWindow_Closed(object sender, EventArgs e) { try { this._TetrisSettings.State.WindowWidth = (int)this.Width; this._TetrisSettings.State.WindowHeight = (int)this.Height; this._TetrisSettings.State.WindowX = (int)this.Left; this._TetrisSettings.State.WindowY = (int)this.Top; this.WriteKeysSettings(); if (this._TetrisSettings != null) this._TetrisSettings.Dispose(); if (this._TetrisBot != null) this._TetrisBot.Stop(); if (this._TetrisBotFarm != null) this._TetrisBotFarm.Stop(); if (this._TetrisInput != null) this._TetrisInput.Dispose(); if (this._TetrisClient != null) this._TetrisClient.Stop(); if (this._TetrisAudio != null) this._TetrisAudio.Dispose(); if (this._TetrisServer != null) this._TetrisServer.Stop(); Spludlow.Log.Finish("Spludlow Tetris, Clean Application Exit"); } catch (Exception ee) { Spludlow.Log.Error("Spludlow Tetris, Application Exit", ee); } } private void ReadKeysSettings() { string[] parts = this._TetrisSettings.State.Keys.Split(new char[] { ',' }); if (parts.Length != 7) throw new ApplicationException("Spludlow Tetris: Not 7 parts to keys settings: '" + this._TetrisSettings.State.Keys + "'"); this._Keys = new List(); foreach (string part in parts) this._Keys.Add((int)Enum.Parse(typeof(Key), part.Trim())); } private void WriteKeysSettings() { this._TetrisSettings.State.Keys = ""; foreach (int keyInt in this._Keys) { if (this._TetrisSettings.State.Keys.Length > 0) this._TetrisSettings.State.Keys += ", "; this._TetrisSettings.State.Keys += ((Key)keyInt).ToString(); } } private void MainWindow_StateChanged(object sender, EventArgs e) { _SetSize(); } private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e) { _SetSize(); } private void _TetrisInput_MoveEvent(TetrisInput.InputCommand command) { if (this._TetrisClient == null) return; this._TetrisClient.Move((byte)command); } private void _StartStopServer() { if (this._TetrisServer == null) { Task.Run(() => _RunServer()); } else { _TetrisServer.Stop(); this._TetrisServer = null; } } private void _RunServer() { try { _TetrisServer = new TetrisServer(); _TetrisServer.Run(this._TetrisSettings.State.ServerAddress, this._TetrisSettings.State.ServerWidth, this._TetrisSettings.State.ServerHeight, this._TetrisSettings.State.ServerTick); } catch (Exception ee) { MessageBox.Show("Server Error" + Environment.NewLine + ee.ToString()); } _TetrisServer = null; } private void _StartStopClient() { if (this._TetrisClient == null) { this.Title = this._TetrisSettings.State.ClientDisplayName; Task.Run(() => _RunClient()); } else { this.Title = "No Connection"; if (this._TetrisInput != null) this._TetrisInput.Stop(); this._TetrisClient.Stop(); this._TetrisClient = null; } } private void _RunClient() { try { _TetrisClient = new TetrisClient(); // Wire up the events _TetrisClient.DrawBoardEvent += _TetrisClient_DrawBoardEvent; _TetrisClient.ReceiveInfoEvent += _TetrisClient_ReceiveInfoEvent; _TetrisClient.ReceiveTextEvent += _TetrisClient_ReceiveTextEvent; _TetrisClient.ReceiveSoundEvent += _TetrisClient_ReceiveSoundEvent; // Set up joystock if (this._TetrisSettings.State.InputIndex > 0) this._TetrisInput.Start(this._TetrisSettings.State.InputIndex - 1); // Run the client _TetrisClient.Run(this._TetrisSettings.State.ClientAddress, this._TetrisSettings.State.ClientDisplayName); } catch (Exception ee) { MessageBox.Show("Client Error" + Environment.NewLine + ee.ToString()); } _TetrisClient = null; this._BoardSetup = false; } private void _TetrisClient_ReceiveTextEvent(string text) { this.AppendText(text + Environment.NewLine); } private void AppendText(string text) { this.Dispatcher.Invoke(() => { StringBuilder builder = new StringBuilder(this.TextBoxText.Text); builder.Append(text); if (builder.Length > 1024) builder.Remove(0, 512); this.TextBoxText.Text = builder.ToString(); this.TextBoxText.ScrollToEnd(); }); } private void _TetrisClient_ReceiveSoundEvent(string sound) { if (this._TetrisAudio == null) return; if (sound.StartsWith("!") == true) { if (this._TetrisSettings.State.NoServerSounds == true) return; sound = sound.Substring(1); } bool queue = false; if (sound.EndsWith("*") == true) { sound = sound.Substring(0, sound.Length - 1); queue = true; } this._TetrisAudio.Play(sound, queue); } private void _TetrisClient_ReceiveInfoEvent(TetrisClientInfo info) { if (this._BoardSetup == false) _SetSize(); string target = "no target"; if (info.TargetClientId != 0) { target = info.TargetDisplayName; if (info.ClientId == info.TargetClientId) target += " (SELF)"; } this.Dispatcher.Invoke(() => { this.TextBlockTarget.Text = target; // More like score board }); } private const int MinPixelSize = 3; private const int MaxTextSize = 24; public void _SetSize() { if (this._TetrisClient == null || this._TetrisClient.Info == null) return; double pixelSize = (this.CanvasWindow.ActualHeight - 1) / this._TetrisClient.Info.Height; if (pixelSize < MinPixelSize) pixelSize = MinPixelSize; double pixelSizeTarget = pixelSize / 3; double textSize = pixelSize * 0.5; if (textSize > MaxTextSize) textSize = MaxTextSize; this.Dispatcher.Invoke(() => { this.ResizeBoard(this.CanvasBoard, pixelSize, this._TetrisClient.Info.Width, this._TetrisClient.Info.Height); double x = this._TetrisClient.Info.Width * pixelSize + 1; // Next Peice double y = 0; Canvas.SetLeft(this.CanvasNext, x); Canvas.SetTop(this.CanvasNext, y); this.ResizeBoard(this.CanvasNext, pixelSize, 4, 4); // Target Text y += 4 * pixelSize + 1; Canvas.SetLeft(this.TextBlockTarget, x); Canvas.SetTop(this.TextBlockTarget, y); this.TextBlockTarget.Height = textSize * 1.2; this.TextBlockTarget.FontSize = textSize; // Target Board y += textSize * 1.3; Canvas.SetLeft(this.CanvasTarget, x); Canvas.SetTop(this.CanvasTarget, y); this.ResizeBoard(this.CanvasTarget, pixelSizeTarget, this._TetrisClient.Info.Width, this._TetrisClient.Info.Height); // Hyperlink y += this._TetrisClient.Info.Height * pixelSizeTarget; Canvas.SetLeft(this.GridLink, x); Canvas.SetTop(this.GridLink, y); this.GridLink.Height = pixelSize; this.TextBlockLink.FontSize = textSize; // Server Text y += pixelSize; if (textSize > MaxTextSize) textSize = MaxTextSize; Canvas.SetLeft(this.TextBoxText, x); Canvas.SetTop(this.TextBoxText, y); this.TextBoxText.FontSize = textSize; this.TextBoxText.Width = MinSize(this.CanvasWindow.ActualWidth - x); this.TextBoxText.Height = MinSize(this.CanvasWindow.ActualHeight - y); }); this._BoardSetup = true; } private static double MinSize(double value) { if (value < 1) return 1; return value; } private void ResizeBoard(Canvas canvas, double pixelSizeD, int width, int height) { int pixelSize = (int)pixelSizeD; canvas.Width = pixelSize * width + 1; canvas.Height = pixelSize * height + 1; int rectSize = pixelSize - 1; if (this._BoardSetup == false) canvas.Children.Clear(); for (int yIndex = 0; yIndex < height; ++yIndex) { int yPos = 0 + (yIndex * pixelSize) + 1; for (int xIndex = 0; xIndex < width; ++xIndex) { Rectangle rectangle; if (this._BoardSetup == false) { rectangle = new Rectangle(); canvas.Children.Add(rectangle); } else { rectangle = (Rectangle)canvas.Children[xIndex + (yIndex * width)]; } rectangle.Width = rectSize; rectangle.Height = rectSize; Canvas.SetLeft(rectangle, 0 + (xIndex * pixelSize) + 1); Canvas.SetTop(rectangle, yPos); } } } private void _TetrisClient_DrawBoardEvent(int boardKey, TetrisBoard board, TetrisBoard previousBoard, bool reDraw) { if (this._BoardSetup == false) return; if (boardKey == -1) // Next this._DrawBoard(this.CanvasNext, board, previousBoard, reDraw); if (boardKey == 0) // Players board this._DrawBoard(this.CanvasBoard, board, previousBoard, reDraw); if (boardKey > 0) // Target board this._DrawBoard(this.CanvasTarget, board, previousBoard, reDraw); } public void _DrawBoard(Canvas canvas, TetrisBoard board, TetrisBoard lastBoard, bool reDraw) { this.Dispatcher.Invoke(() => { for (int yIndex = 0; yIndex < board.Height; ++yIndex) { int down = yIndex * board.Width; for (int xIndex = 0; xIndex < board.Width; ++xIndex) { byte data = board.Peek(xIndex, yIndex); Rectangle rectangle = (Rectangle)canvas.Children[xIndex + down]; if (rectangle.Fill != _SolidColorBrushs[data]) rectangle.Fill = _SolidColorBrushs[data]; } } }); } private void MainWindow_KeyDown(object sender, KeyEventArgs e) { int index = _Keys.IndexOf((int)e.Key); if (index != -1) { if (this._TetrisClient != null) this._TetrisClient.Move((byte)index); } else { switch (e.Key) { case Key.F1: this._Dialogue(); this._SetJoystick(false); break; case Key.F2: this._SetJoystick(true); break; case Key.F3: if (this._TetrisBotFarm.Running == false) this._TetrisBotFarm.Start(this._TetrisSettings.State.ClientAddress, this._TetrisSettings.State.BotFarmCount, this._TetrisSettings.State.BotSpeed); else this._TetrisBotFarm.Stop(); break; case Key.F4: if (this._TetrisBot.Running == false) this._TetrisBot.Start(this._TetrisClient, this._TetrisSettings.State.BotSpeed); else this._TetrisBot.Stop(); break; case Key.F5: this._TetrisSettings.State.Volume = 0; break; case Key.F6: if (--this._TetrisSettings.State.Volume < 0) this._TetrisSettings.State.Volume = 0; break; case Key.F7: if (++this._TetrisSettings.State.Volume > 10) this._TetrisSettings.State.Volume = 10; break; case Key.F8: this._TetrisSettings.State.Volume = 10; break; case Key.F9: this._TetrisSettings.State.NoServerSounds = !this._TetrisSettings.State.NoServerSounds; break; case Key.System: if (e.SystemKey == Key.F10) MessageBox.Show(this.ActualWidth + ", " + this.ActualHeight); e.Handled = true; break; case Key.F11: this._StartStopServer(); break; case Key.F12: this._StartStopClient(); break; case Key.Pause: this._TetrisClient.SendText("@BR"); break; } if (this._TetrisAudio != null) this._TetrisAudio.Volume((float)this._TetrisSettings.State.Volume / 10.0F); } } private void _SetJoystick(bool cycle) { if (this._TetrisInput == null) return; this._TetrisInput.Stop(); int joyStickCount = TetrisInput.DeviceInstancesText().Length; if (cycle == true) ++this._TetrisSettings.State.InputIndex; if (this._TetrisSettings.State.InputIndex > joyStickCount) this._TetrisSettings.State.InputIndex = 0; if (this._TetrisSettings.State.InputIndex > 0) this._TetrisInput.Start(this._TetrisSettings.State.InputIndex - 1); } private void _Dialogue() { bool clientRunning = (this._TetrisClient != null); bool serverRuning = (this._TetrisServer != null); this._TetrisSettings.State.Command = ""; SettingsWindow settingsWindow = new SettingsWindow(this._TetrisSettings.State, this._Keys, clientRunning, serverRuning); settingsWindow.ShowDialog(); if (this._TetrisSettings.State.Command == "SERVER") _StartStopServer(); if (this._TetrisSettings.State.Command == "CLIENT") _StartStopClient(); this._TetrisSettings.State.Command = null; } private void Hyperlink_Click(object sender, RoutedEventArgs e) { System.Diagnostics.Process.Start(((Hyperlink)e.Source).NavigateUri.ToString()); } } }