From 735f83c60bc7d1eae46684056ac95b96ba2c0826 Mon Sep 17 00:00:00 2001 From: andykras Date: Sat, 16 Jan 2016 13:30:22 +0300 Subject: [PATCH] Added Game Objects --- Game/Game.csproj | 23 +++- Game/Game/Draw.cs | 200 ++++++++++++++++++++++++++++++++++ Game/Game/Game.cs | 122 +++++++++++++++++++++ Game/Game/GameEvent.cs | 41 +++++++ Game/Game/KeyToEvent.cs | 121 ++++++++++++++++++++ Game/Game/Render.cs | 90 +++++++++++++++ Game/Painter/EmptyPainter.cs | 14 +++ Game/Painter/IPainter.cs | 8 ++ Game/Painter/PixelPainter.cs | 22 ++++ Game/Painter/SpritePainter.cs | 22 ++++ Game/Pixel/CharPixel.cs | 51 +++++++++ Game/Pixel/CharSprite.cs | 43 ++++++++ Game/Pixel/IPixel.cs | 18 +++ Game/Pixel/Sprite.cs | 91 ++++++++++++++++ Game/Program.cs | 39 ++----- Game/TestClass.cs | 40 +++++++ Game/Visitor/IPixelVisitor.cs | 7 ++ Util/ConsoleScreen.cs | 67 ++++++++++++ Util/Rnd.cs | 104 ++++++++++++++++++ Util/SimpleLock.cs | 1 - Util/Util.csproj | 2 + X11/FromDLL.cs | 13 ++- X11/MessageLoop.cs | 20 ++++ 23 files changed, 1124 insertions(+), 35 deletions(-) create mode 100644 Game/Game/Draw.cs create mode 100644 Game/Game/Game.cs create mode 100644 Game/Game/GameEvent.cs create mode 100644 Game/Game/KeyToEvent.cs create mode 100644 Game/Game/Render.cs create mode 100644 Game/Painter/EmptyPainter.cs create mode 100644 Game/Painter/IPainter.cs create mode 100644 Game/Painter/PixelPainter.cs create mode 100644 Game/Painter/SpritePainter.cs create mode 100644 Game/Pixel/CharPixel.cs create mode 100644 Game/Pixel/CharSprite.cs create mode 100644 Game/Pixel/IPixel.cs create mode 100644 Game/Pixel/Sprite.cs create mode 100644 Game/TestClass.cs create mode 100644 Game/Visitor/IPixelVisitor.cs create mode 100644 Util/ConsoleScreen.cs create mode 100644 Util/Rnd.cs diff --git a/Game/Game.csproj b/Game/Game.csproj index 1049709..2cb8fe4 100644 --- a/Game/Game.csproj +++ b/Game/Game.csproj @@ -35,8 +35,23 @@ - + + + + + + + + + + + + + + + + @@ -49,4 +64,10 @@ Util + + + + + + \ No newline at end of file diff --git a/Game/Game/Draw.cs b/Game/Game/Draw.cs new file mode 100644 index 0000000..daa263b --- /dev/null +++ b/Game/Game/Draw.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using Util; + +namespace Game +{ + public partial class Game + { + void Draw() + { + ConsoleScreen.Clear(); + ConsoleScreen.Zoom = Math.Max(ConsoleScreen.Zoom, 0.1); + ConsoleScreen.Zoom = Math.Min(ConsoleScreen.Zoom, 100); + asteriks_count = Math.Min(asteriks_count, asteriks_count_max / 2); + count = Math.Max(count, 1); + count = Math.Min(count, 100); + d1 = Math.Max(d1, 0); + d1 = Math.Min(d1, 10); + d2 = Math.Max(d2, 0); + d2 = Math.Min(d2, d1 * ratioD); + + // STARS + var asteriks = new Sprite(null, angle, ConsoleColor.Yellow){ ShowLabel = show_label }; + for (var i = 0; i < asteriks_count; i++) asteriks.Add(new CharPixel('*', xc + random_numbers[2 * i], yc + random_numbers[2 * i + 1], ConsoleColor.DarkMagenta)); + if (asteriks_count != 0) asteriks.Draw(painter); + + if (!show_intel) { + + // CIRCLE + if (show_circle) { + var alpha = Math.PI * 2.0 / count; + for (var i = 0; i < count; i++) { + var x = xc + R * Math.Cos(i * alpha); + var y = yc + R * Math.Sin(i * alpha); + new CharPixel('█', x, y, (ConsoleColor) rnd.Next(1, 15)).Draw(pixel_painter); + } + + var pix = new CharPixel('@', xc, yc, ConsoleColor.Yellow); + pix.Draw(pixel_painter); + + new CharSprite(new [] { + " █ ", + "███", + " " + }, xc - 3, yc + 3, angle){ ShowLabel = show_label }.Draw(painter); + } + + if (show_dollar) { + var dollars = new CharSprite(new[] { + " $$$$", + " $$$$$$$$$$$$$$$$$", + " $$$$$$$$$$$$$$$$$$$$$$", + " $$$$$$$ $$$$ $$$$$$$$", + " $$$$$$ $$$$ $$$$$$$", + " $$$$$$ $$$$ $$$$$$", + " $$$$$$ $$$$", + " $$$$$$ $$$$", + " $$$$$$ $$$$", + " $$$$$$$$ $$$$", + " $$$$$$$$$$$$$$$", + " $$$$$$$$$$$$$$$$$$", + " $$$$$$$$$$$$$$$$", + " $$$$ $$$$$$$$$$", + " $$$$ $$$$$$$", + " $$$$ $$$$$$", + " $$$$ $$$$$$", + "$$$$$$$ $$$$ $$$$$$$", + " $$$$$$ $$$$ $$$$$$$", + " $$$$$$$ $$$$ $$$$$$$$", + " $$$$$$$$ $$$$ $$$$$$$$", + " $$$$$$$$$$$$$$$$$$$$$$$$", + " $$$$$$$$$$$$$$$$$$$", + " $$$$$$$$" + }, xc - 65, yc + 15, angle, ConsoleColor.DarkBlue){ ShowLabel = show_label }; + dollars.Draw(painter); + } + + // ship + var ship = new Sprite(new List { + new CharPixel('█', xc + offsetX + 4, yc + offsetY - 0, ConsoleColor.Red), + new CharPixel('█', xc + offsetX + 3, yc + offsetY - 1, ConsoleColor.Red), + new CharPixel('█', xc + offsetX + 2, yc + offsetY - 2, ConsoleColor.Red), + new CharPixel('█', xc + offsetX + 1, yc + offsetY - 3, ConsoleColor.Red), + new CharPixel('█', xc + offsetX + 0, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 1, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 2, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 3, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 4, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 5, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 6, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 7, yc + offsetY - 4, ConsoleColor.Green), + new CharPixel('█', xc + offsetX + 5, yc + offsetY - 1, ConsoleColor.Blue), + new CharPixel('█', xc + offsetX + 6, yc + offsetY - 2, ConsoleColor.Blue), + new CharPixel('█', xc + offsetX + 7, yc + offsetY - 3, ConsoleColor.Blue), + new CharPixel('█', xc + offsetX + 8, yc + offsetY - 4, ConsoleColor.Blue), + }, angle_ship, ConsoleColor.Yellow){ ShowLabel = show_label }; + //ship.Draw(painter); + + // ArchUser + var up = new CharSprite(new [] { + " + ", + " # ", + " ### ", + " ##### ", + " ###### ", + " ; #####; ", + " +##.##### ", + " +########## ", + " ###### ##; ", + " ### + ", + " # ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + }, 4, 4, angle_ship, ConsoleColor.Magenta){ ShowLabel = show_label }; + var down = new CharSprite(new [] { + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ##### ", + " ############ ", + " ###### ####### ", + " .######; ;###;`\". ", + " .#######; ;#####. ", + " #########. .########` ", + " ######' '###### ", + " ;#### ####; ", + " ##' '## ", + "#' `#" + }, 4, 4, angle_ship, ConsoleColor.DarkMagenta){ ShowLabel = show_label }; + //up.Draw(painter); + //down.Draw(painter); + var sp = new Sprite(null, angle_ship){ ShowLabel = false }; + sp.Add(up); + sp.Add(down); + sp.Add(ship); +// sp.Add(new CharPixel('╬', 0, 0, ConsoleColor.Red)); +// sp.Add(new CharPixel('╬', 5, 0, ConsoleColor.Green)); +// sp.Add(new CharPixel('╬', -5, 0, ConsoleColor.DarkBlue)); +// sp.Add(new CharSprite(new []{ "╔╗", "╚╝" }, -10, 0, angle, ConsoleColor.Yellow){ ShowLabel = show_label }); +// sp.Add(new CharPixel('╔', 10, 0, ConsoleColor.Yellow)); +// sp.Add(new CharPixel('╗', 11, 0, ConsoleColor.Yellow)); +// sp.Add(new CharPixel('╚', 10, -1, ConsoleColor.Yellow)); +// sp.Add(new CharPixel('╝', 11, -1, ConsoleColor.Yellow)); + sp.Draw(painter); + + } else { + // INTEL LOGO + var intel = new CharSprite(new[] { + " ,_88888888888888888888888888888888888888_", + " _88888888888~~~~~~ ~~~~~~~88888888888", + " ,g8888888~` g8. ~888!", + " _88888~ i8! 88! __ '`", + " _888~ _d8bg88. 88! 888888_.", + " ,888` -888888888` 88! 8888888888.", + " g88` i88 88888! ,88888 88! 888888888888.", + " d8P '88 i88g8. 88` ,88~_88 88` '8888888.", + " 888 ,8 888888 88 888888` 88 '88888i", + " d8P 88! d8! 88 88 88P~ 88 '8888.", + " i88 88 88 88 88 88b_88i 88 8888", + " d8! 88 88 88 88 '88888 88 88i 888i", + " 88 88 88 88 88 , 88! '88!", + " 8! 88 '~ 88 88! 88!", + " 8b __d88888i ,88 88! ,__ 88!", + " 88 888888888` 888 ,g888! g88888 88!", + " 88i 88~88! ,g8i g88888!i88L888 88`", + " '88 ,_ 88! 88888 88i d888888`888888 d88", + " 88b g8888i 88 88 88!888| 88` 88! 888 88P g88`", + " 888 ,88~~88 88 88 d8 8888 88 888.,888 888g88! g88!", + " 888. 88! 88!88 88 88 88! 88 '8888888 8888` ,d88!", + " 8888 !88 88`88b888 88 88888` 88 '~~` 88 g888`", + " '8888. 888888 !88888 88 8888 ,g8888", + " ~8888_ '88~ '~ ,g88888`", + " ~888888_ _8888888`", + " '88888888__. __g88888888~", + " '~888888888888888____8888888888888888~", + " '~888888888888888888888888888~", + " ~~~8888888888~~~`" + }, xc - 35, yc + 15, angle, ConsoleColor.DarkBlue){ ShowLabel = false }; + intel.Draw(painter); + } + + // TODO: calc Total Pixels using visitor + var total = 0; + + Console.SetCursorPosition(0, Console.WindowHeight - 2); + Console.ForegroundColor = ConsoleColor.Red; + Console.Write("Zoom: {0:F2} angle: {1} stars: {4} C=[{2:F2}:{3:F2}]\nd1={6:F2} d2={7:F2} t1={8} t1={9}\nTotal pixels: {5}", ConsoleScreen.Zoom, (int) (angle / Math.PI * 180), xc, yc, asteriks_count, total, d1, d2, timer1, timer2); + } + } +} diff --git a/Game/Game/Game.cs b/Game/Game/Game.cs new file mode 100644 index 0000000..48378f7 --- /dev/null +++ b/Game/Game/Game.cs @@ -0,0 +1,122 @@ +using System; +using System.Threading; +using Util; + +namespace Game +{ + public partial class Game + { + PixelPainter pixel_painter; + SpritePainter sprite_painter; + IPainter painter; + Random rnd; + const int asteriks_count_max = 5000; + double[] random_numbers; + int count, asteriks_count, timer1 = 16, timer2 = 16; + double xc, yc, angle, angle_ship, offsetX, offsetY, deg = Math.PI / 24, R = 13, d1, d2, ratioD; + bool show_intel, show_label, show_circle, show_dollar; + + public Game() + { + ResetToDefault(); + ratioD = d2 / d1; + + pixel_painter = new PixelPainter(); + sprite_painter = new SpritePainter(); + painter = sprite_painter; + + rnd = new Random((int) DateTime.Now.ToBinary()); + random_numbers = new double[asteriks_count_max]; + for (var i = 0; i < asteriks_count_max; i++) random_numbers[i] = (rnd.NextGaussian(rnd.Next(-100, 100), 100 / 2)); + + X11.MessageLoop.Get.OnKeyPress += KeyPress; + X11.MessageLoop.Get.OnKeyRelease += KeyRelease; + X11.MessageLoop.Get.OnConfigure += Invalidate; + X11.MessageLoop.Get.OnEnterLeave += Invalidate; + X11.MessageLoop.Get.OnExpose += Invalidate; + X11.MessageLoop.Get.OnFocus += Invalidate; + X11.MessageLoop.Get.OnProperty += Invalidate; + X11.MessageLoop.Get.OnVisibility += Invalidate; + + new Thread(Render){ IsBackground = true }.Start(); + } + + + void ResetToDefault() + { + count = 30; + xc = 0.0; + yc = 0.0; + angle = 0.0; + angle_ship = 0.0; + offsetX = 14.0; + offsetY = 20.0; + show_intel = false; + show_label = true; + show_circle = true; + show_dollar = true; + asteriks_count = 0; + ConsoleScreen.Zoom = 1.0; + d1 = 0.5; + d2 = 2; + } + + public void Run() + { + renderScene.Set(); + isStopped.WaitOne(); + stopped = true; + X11.MessageLoop.Get.OnKeyPress -= KeyPress; + X11.MessageLoop.Get.OnKeyRelease -= KeyRelease; + X11.MessageLoop.Get.OnConfigure -= Invalidate; + X11.MessageLoop.Get.OnEnterLeave -= Invalidate; + X11.MessageLoop.Get.OnExpose -= Invalidate; + X11.MessageLoop.Get.OnFocus -= Invalidate; + X11.MessageLoop.Get.OnProperty -= Invalidate; + X11.MessageLoop.Get.OnVisibility -= Invalidate; + } + + void KeyPress(X11.Key key) + { + if (KeyToEvent(key, true)) processEvent.Set(); + } + + void KeyRelease(X11.Key key) + { + if (key == X11.Key.Escape) Close(); + KeyToEvent(key, false); + } + + bool isSetEvent(GameEvent eventToTest) + { + return (gameEvent & eventToTest) == eventToTest; + } + + void SetEvent(GameEvent eventToSet, bool enable) + { + if (enable) gameEvent = gameEvent | eventToSet; + else gameEvent = gameEvent ^ eventToSet; + } + + void Invalidate() + { + renderScene.Set(); + } + + void Invalidate(bool enable) + { + if (enable) renderScene.Set(); + } + + void Invalidate(int state) + { + renderScene.Set(); + } + + void Close() + { + isStopped.Set(); + } + } +} + diff --git a/Game/Game/GameEvent.cs b/Game/Game/GameEvent.cs new file mode 100644 index 0000000..4db5259 --- /dev/null +++ b/Game/Game/GameEvent.cs @@ -0,0 +1,41 @@ +using System; + +namespace Game +{ + [Flags] + enum GameEvent + { + None = 0x0, + + Update = 1<<0, + Stop = 1<<1, + + ShipForward = 1<<2, + ShipBackward = 1<<3, + ShipTurnLeft = 1<<4, + ShipTurnRight = 1<<5, + + ZoomIn = 1<<6, + ZoomOut = 1<<7, + SpeedUp = 1<<8, + + ShipStrafeLeft = 1<<9, + ShipStrafeRight = 1<<10, + + Reset = 1<<11, + + MoveSceneUp = 1<<12, + MoveSceneDown = 1<<13, + MoveSceneLeft = 1<<14, + MoveSceneRight = 1<<15, + RotateSceneLeft = 1<<16, + RotateSceneRight = 1<<17, + + ShiftPressed = 1<<18, + + AddStar = 1<<19, + + DeltaDec = 1<<20, + DeltaInc = 1<<21, + } +} \ No newline at end of file diff --git a/Game/Game/KeyToEvent.cs b/Game/Game/KeyToEvent.cs new file mode 100644 index 0000000..fe5e822 --- /dev/null +++ b/Game/Game/KeyToEvent.cs @@ -0,0 +1,121 @@ +using Util; + +namespace Game +{ + public partial class Game + { + bool KeyToEvent(X11.Key key, bool enable) + { + switch (key) { + case X11.Key.KP_Up: + SetEvent(GameEvent.MoveSceneUp, enable); + break; + case X11.Key.KP_Begin: + SetEvent(GameEvent.MoveSceneDown, enable); + break; + case X11.Key.KP_Left: + SetEvent(GameEvent.MoveSceneLeft, enable); + break; + case X11.Key.KP_Right: + SetEvent(GameEvent.MoveSceneRight, enable); + break; + case X11.Key.e: + SetEvent(GameEvent.ShipForward, enable); + break; + case X11.Key.d: + SetEvent(GameEvent.ShipBackward, enable); + break; + case X11.Key.s: + SetEvent(GameEvent.ShipTurnLeft, enable); + break; + case X11.Key.f: + SetEvent(GameEvent.ShipTurnRight, enable); + break; + case X11.Key.w: + SetEvent(GameEvent.ShipStrafeLeft, enable); + break; + case X11.Key.r: + SetEvent(GameEvent.ShipStrafeRight, enable); + break; + case X11.Key.Alt_L: + SetEvent(GameEvent.SpeedUp, enable); + break; + case X11.Key.KP_Add: + SetEvent(GameEvent.ZoomIn, enable); + break; + case X11.Key.KP_Subtract: + SetEvent(GameEvent.ZoomOut, enable); + break; + case X11.Key.KP_Divide: + SetEvent(GameEvent.Reset, enable); + break; + case X11.Key.KP_Home: + SetEvent(GameEvent.RotateSceneLeft, enable); + break; + case X11.Key.KP_Page_Up: + SetEvent(GameEvent.RotateSceneRight, enable); + break; + case X11.Key.i: + if (enable) show_intel = !show_intel; + break; + case X11.Key.l: + if (enable) show_label = !show_label; + break; + case X11.Key.c: + if (enable) show_circle = !show_circle; + break; + case X11.Key.Key_4: + if (enable && isSetEvent(GameEvent.ShiftPressed)) show_dollar = !show_dollar; + break; + case X11.Key.Insert: + if (enable) count++; + break; + case X11.Key.Delete: + if (enable) count--; + break; + case X11.Key.o: + SetEvent(GameEvent.DeltaDec, enable); + // if (enable) { + // d1 -= 0.05; + // d2 = d1 * ratioD; + // } + break; + case X11.Key.p: + SetEvent(GameEvent.DeltaInc, enable); + // if (enable) { + // d1 += 0.05; + // d2 = d1 * ratioD; + // } + break; + case X11.Key.bracketleft: + if (enable) { + timer1 -= 1; + timer2 -= 1; + } + break; + case X11.Key.bracketright: + if (enable) { + timer1 += 1; + timer2 += 1; + } + break; + case X11.Key.KP_Enter: + ConsoleScreen.Zoom = 1.0; + break; + case X11.Key.Return: + if (enable) painter = painter == pixel_painter ? sprite_painter : pixel_painter; + break; + + case X11.Key.KP_Multiply: + SetEvent(GameEvent.AddStar, enable); + break; + case X11.Key.Shift_L: + SetEvent(GameEvent.ShiftPressed, enable); + break; + default: + return false; + } + return true; + } + } +} diff --git a/Game/Game/Render.cs b/Game/Game/Render.cs new file mode 100644 index 0000000..dc7855d --- /dev/null +++ b/Game/Game/Render.cs @@ -0,0 +1,90 @@ +using System; +using System.Threading; +using Util; + +namespace Game +{ + public partial class Game + { + private readonly ManualResetEvent isStopped = new ManualResetEvent(false); + private readonly ManualResetEvent renderScene = new ManualResetEvent(false); + private readonly ManualResetEvent processEvent = new ManualResetEvent(false); + GameEvent gameEvent; + bool stopped; + + void Render() + { + ConsoleScreen.SetDotAsSeparator(); + new Thread(delegate() + { + while (!stopped) { + processEvent.WaitOne(); + var speedUp = isSetEvent(GameEvent.SpeedUp); + if (isSetEvent(GameEvent.ShipForward)) { + var rx = (speedUp ? d2 : d1) * Math.Sin(angle_ship); + var ry = (speedUp ? d2 : d1) * Math.Cos(angle_ship); + offsetX += rx; + offsetY += ry; + xc -= rx; + yc -= ry; + // xc = ConsoleScreen.Fit(xc, 0.5); + // yc = ConsoleScreen.Fit(yc, 0.5); + } + if (isSetEvent(GameEvent.ShipBackward)) { + var rx = (speedUp ? d2 : d1) * Math.Sin(angle_ship); + var ry = (speedUp ? d2 : d1) * Math.Cos(angle_ship); + offsetX -= (speedUp ? d2 : d1) * Math.Sin(angle_ship); + offsetY -= (speedUp ? d2 : d1) * Math.Cos(angle_ship); + xc += rx; + yc += ry; + } + if (isSetEvent(GameEvent.ShipStrafeLeft)) { + offsetX -= (speedUp ? d2 : d1) * Math.Cos(-angle_ship); + offsetY -= (speedUp ? d2 : d1) * Math.Sin(-angle_ship); + } + if (isSetEvent(GameEvent.ShipStrafeRight)) { + offsetX += (speedUp ? d2 : d1) * Math.Cos(-angle_ship); + offsetY += (speedUp ? d2 : d1) * Math.Sin(-angle_ship); + } + + if (isSetEvent(GameEvent.Reset)) ResetToDefault(); + + if (isSetEvent(GameEvent.MoveSceneUp)) yc -= speedUp ? d2 : d1; + if (isSetEvent(GameEvent.MoveSceneDown)) yc += speedUp ? d2 : d1; + if (isSetEvent(GameEvent.MoveSceneLeft)) xc += speedUp ? d2 : d1; + if (isSetEvent(GameEvent.MoveSceneRight)) xc -= speedUp ? d2 : d1; + + if (isSetEvent(GameEvent.AddStar)) asteriks_count += speedUp ? 10 : 1; + + if (isSetEvent(GameEvent.RotateSceneLeft)) angle -= speedUp ? deg : deg * 0.1; + if (isSetEvent(GameEvent.RotateSceneRight)) angle += speedUp ? deg : deg * 0.1; + + if (isSetEvent(GameEvent.ShipTurnLeft)) angle_ship -= speedUp ? deg : deg * 0.5; + if (isSetEvent(GameEvent.ShipTurnRight)) angle_ship += speedUp ? deg : deg * 0.5; + if (isSetEvent(GameEvent.ZoomIn)) ConsoleScreen.Zoom += speedUp ? 0.1 : 0.03; + if (isSetEvent(GameEvent.ZoomOut)) ConsoleScreen.Zoom -= speedUp ? 0.1 : 0.03; + + if (isSetEvent(GameEvent.DeltaDec)) { + d1 -= 0.05; + d2 = d1 * ratioD; + } + if (isSetEvent(GameEvent.DeltaInc)) { + d1 += 0.05; + d2 = d1 * ratioD; + } + + renderScene.Set(); + Thread.Sleep(timer1); + if (gameEvent == GameEvent.None) processEvent.Reset(); + } + }){ IsBackground = true }.Start(); + + while (!stopped) { + renderScene.WaitOne(); + Thread.Sleep(timer2); + Draw(); + renderScene.Reset(); + } + } + } +} \ No newline at end of file diff --git a/Game/Painter/EmptyPainter.cs b/Game/Painter/EmptyPainter.cs new file mode 100644 index 0000000..874e8a6 --- /dev/null +++ b/Game/Painter/EmptyPainter.cs @@ -0,0 +1,14 @@ +namespace Game +{ + class EmptyPainter:IPainter + { + public virtual void Draw(CharPixel pixel) + { + } + + public virtual void Draw(Sprite sprite) + { + } + } + +} diff --git a/Game/Painter/IPainter.cs b/Game/Painter/IPainter.cs new file mode 100644 index 0000000..a190d03 --- /dev/null +++ b/Game/Painter/IPainter.cs @@ -0,0 +1,8 @@ +namespace Game +{ + interface IPainter + { + void Draw(CharPixel pixel); + void Draw(Sprite sprite); + } +} diff --git a/Game/Painter/PixelPainter.cs b/Game/Painter/PixelPainter.cs new file mode 100644 index 0000000..014b643 --- /dev/null +++ b/Game/Painter/PixelPainter.cs @@ -0,0 +1,22 @@ +using System; +using Util; + +namespace Game +{ + class PixelPainter:EmptyPainter + { + public override void Draw(CharPixel pixel) + { + ConsoleScreen.Draw(pixel.X, pixel.Y, pixel.Color, () => Console.Write(pixel.Value)); + } + + public override void Draw(Sprite sprite) + { + foreach (var pixel in sprite) { + pixel.Draw(this); + } + ConsoleScreen.Draw(sprite.X + sprite.Width, sprite.Y, sprite.Color, () => Console.Write(sprite)); + } + } + +} diff --git a/Game/Painter/SpritePainter.cs b/Game/Painter/SpritePainter.cs new file mode 100644 index 0000000..f9bf1fd --- /dev/null +++ b/Game/Painter/SpritePainter.cs @@ -0,0 +1,22 @@ +using System; +using Util; + +namespace Game +{ + class SpritePainter:PixelPainter + { + public override void Draw(Sprite sprite) + { + var xc = sprite.X + sprite.Width / 2; + var yc = sprite.Y - sprite.Height / 2; + foreach (var pixel in sprite) { + var xn = xc + (pixel.X - xc) * Math.Cos(sprite.Angle) + (pixel.Y - yc) * Math.Sin(sprite.Angle); + var yn = yc - (pixel.X - xc) * Math.Sin(sprite.Angle) + (pixel.Y - yc) * Math.Cos(sprite.Angle); + var rep = pixel.Clone(); + rep.Set(xn, yn); + rep.Draw(this); + } + ConsoleScreen.Draw(sprite.X + sprite.Width, sprite.Y, sprite.Color, () => Console.Write(sprite)); + } + } +} diff --git a/Game/Pixel/CharPixel.cs b/Game/Pixel/CharPixel.cs new file mode 100644 index 0000000..325154c --- /dev/null +++ b/Game/Pixel/CharPixel.cs @@ -0,0 +1,51 @@ +using System; + +namespace Game +{ + class CharPixel:IPixel + { + public virtual double X { get; protected set; } + public virtual double Y { get; protected set; } + public ConsoleColor Color { get; } + public virtual int Width { get { return 1; } } + public virtual int Height { get { return 1; } } + + readonly char pixel; + public char Value { get { return pixel; } } + + public CharPixel(char pixel = (char) 0, double x = 0, double y = 0, ConsoleColor color = ConsoleColor.White) + { + this.pixel = pixel; + X = x; + Y = y; + Color = color; + } + + public virtual void Move(double dx, double dy) + { + X += dx; + Y += dy; + } + + public virtual void Accept(IPixelVisitor visitor) + { + visitor.Visit(this); + } + + public virtual void Draw(IPainter painter) + { + painter.Draw(this); + } + + public void Set(double x, double y) + { + X = x; + Y = y; + } + + public IPixel Clone() + { + return new CharPixel(pixel, X, Y, Color); + } + } +} diff --git a/Game/Pixel/CharSprite.cs b/Game/Pixel/CharSprite.cs new file mode 100644 index 0000000..35e49c3 --- /dev/null +++ b/Game/Pixel/CharSprite.cs @@ -0,0 +1,43 @@ +using System; + +namespace Game +{ + class CharSprite:Sprite + { + private readonly string[] sprite; + public CharSprite(string[] sprite, double x = 0, double y = 0, double angle = 0, ConsoleColor color = ConsoleColor.White) : base(null, angle, color) + { + this.sprite = sprite; + var first = x; + foreach (var line in sprite) { + x = first; + foreach (var symbol in line) { + Add(new CharPixel(symbol, x, y, Color), symbol != ' '); + x++; + } + y--; + } + } + + // can be virtual? + public override void Accept(IPixelVisitor visitor) + { + foreach (var pixel in pixels) { + pixel.Accept(visitor); + } + visitor.Visit(this); + } + + public new bool ShowLabel = true; + public override string ToString() + { + return ShowLabel ? string.Format("[CharSprite: {0}° {1}:{2} ({3:F2},{4:F2})]", (int) Math.Round(Angle / Math.PI * 180), Width, Height, X, Y) : null; + } + + public override IPixel Clone() + { + return new CharSprite(sprite, X, Y, Angle, Color){ ShowLabel = ShowLabel }; + } + } + +} diff --git a/Game/Pixel/IPixel.cs b/Game/Pixel/IPixel.cs new file mode 100644 index 0000000..0ed3f4f --- /dev/null +++ b/Game/Pixel/IPixel.cs @@ -0,0 +1,18 @@ +using System; + +namespace Game +{ + interface IPixel + { + double X { get; } + double Y { get; } + ConsoleColor Color { get; } + int Width { get; } + int Height { get; } + void Set(double x, double y); + void Move(double dx, double dy); + void Accept(IPixelVisitor visitor); + void Draw(IPainter painter); + IPixel Clone(); + } +} diff --git a/Game/Pixel/Sprite.cs b/Game/Pixel/Sprite.cs new file mode 100644 index 0000000..7733ef0 --- /dev/null +++ b/Game/Pixel/Sprite.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Game +{ + class Sprite : IPixel, IEnumerable + { + protected readonly List pixels = new List(); + private double x, y; + + public virtual double X { get { return width == 0 ? x : xmin; } protected set { x = value; } } + public virtual double Y { get { return height == 0 ? y : ymax; } protected set { y = value; } } + public ConsoleColor Color { get; } + public double Angle { get; protected set; } + + protected int width; + protected int height; + public virtual int Width { get { return width; } } + public virtual int Height { get { return height; } } + + private double xmin = 1e6, xmax = -1e6, ymin = 1e6, ymax = -1e6; + public void Add(IPixel pixel, bool show = true) + { + xmin = Math.Min(xmin, pixel.X); + xmax = Math.Max(xmax, pixel.X + pixel.Width); + ymin = Math.Min(ymin, pixel.Y - pixel.Height); + ymax = Math.Max(ymax, pixel.Y); + width = (int) (xmax - xmin); + height = (int) (ymax - ymin); + if (show) pixels.Add(pixel); + } + + public Sprite(List pixels = null, double angle = 0, ConsoleColor color = ConsoleColor.White) + { + X = 0; + Y = 0; + Angle = angle; + Color = color; + height = 0; + width = 0; + if (pixels != null) foreach (var pixel in pixels) Add(pixel); + } + + public void Move(double dx, double dy) + { + foreach (var pixel in pixels) { + pixel.Move(dx, dy); + } + X += dx; + Y += dy; + } + public virtual void Accept(IPixelVisitor visitor) + { + foreach (var pixel in pixels) { + pixel.Accept(visitor); + } + visitor.Visit(this); + } + public void Draw(IPainter painter) + { + painter.Draw(this); + } + + public bool ShowLabel = true; + public override string ToString() + { + return ShowLabel ? string.Format("[Sprite: {0}° {1}:{2} ({3:F2},{4:F2})]", (int) Math.Round(Angle / Math.PI * 180), Width, Height, X, Y) : null; + } + + public void Set(double x, double y) + { + } + + public virtual IPixel Clone() + { + return new Sprite(pixels, Angle, Color){ ShowLabel = ShowLabel }; + } + + public IEnumerator GetEnumerator() + { + foreach (var pixel in pixels) yield return pixel; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + } +} diff --git a/Game/Program.cs b/Game/Program.cs index c98b3c6..6551412 100644 --- a/Game/Program.cs +++ b/Game/Program.cs @@ -1,40 +1,15 @@ -using System; -using System.Threading; -using System.Net; +using Util; namespace Game { - class MainClass : Util.IKeyboardListener + public class Program { - public void Update(ConsoleKeyInfo key) + public static void Main() { - Console.WriteLine("From ConsoleKeyboard: {0}, {1}, {2}", key.Key, key.KeyChar, key.Modifiers); - } - - private MainClass() - { - Util.ConsoleKeyboard.Get.Add(this); - } - - public static void Main(string[] args) - { - new MainClass(); - var stop = new ManualResetEvent(false); - X11.MessageLoop.Get.OnKeyRelease += key => - { - Console.WriteLine("\nKey {0} released", key); - if (key == X11.Key.Escape) stop.Set(); - }; - - X11.MessageLoop.Get.OnKeyPress += key => Console.WriteLine("\nKey {0} pressed", key); - X11.MessageLoop.Get.OnConfigure += () => Console.WriteLine("OnConfigure"); - X11.MessageLoop.Get.OnEnterLeave += result => Console.WriteLine("OnEnterLeave: {0}", result); - X11.MessageLoop.Get.OnExpose += () => Console.WriteLine("OnExpose"); - X11.MessageLoop.Get.OnFocus += result => Console.WriteLine("OnFocus: {0}", result); - X11.MessageLoop.Get.OnProperty += state => Console.WriteLine("OnProperty: {0}", state); - X11.MessageLoop.Get.OnVisibility += () => Console.WriteLine("OnVisibility"); - - stop.WaitOne(); + new Game().Run(); + ConsoleScreen.Clear(); + X11.MessageLoop.Get.Stop(); } } } + diff --git a/Game/TestClass.cs b/Game/TestClass.cs new file mode 100644 index 0000000..16215e3 --- /dev/null +++ b/Game/TestClass.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading; + +namespace Game +{ + class TestClass : Util.IKeyboardListener + { + public void Update(ConsoleKeyInfo key) + { + Console.WriteLine("From ConsoleKeyboard: {0}, {1}, {2}", key.Key, key.KeyChar, key.Modifiers); + } + + private TestClass() + { + Util.ConsoleKeyboard.Get.Add(this); + } + + public static void Test(string[] args) + { +// Console.ReadKey(); + new TestClass(); + var stop = new ManualResetEvent(false); + X11.MessageLoop.Get.OnKeyRelease += key => + { + Console.WriteLine("\nKey {0} released", key); + if (key == X11.Key.Escape) stop.Set(); + }; + + X11.MessageLoop.Get.OnKeyPress += key => Console.WriteLine("\nKey {0} pressed", key); + X11.MessageLoop.Get.OnConfigure += () => Console.WriteLine("OnConfigure"); + X11.MessageLoop.Get.OnEnterLeave += result => Console.WriteLine("OnEnterLeave: {0}", result); + X11.MessageLoop.Get.OnExpose += () => Console.WriteLine("OnExpose"); + X11.MessageLoop.Get.OnFocus += result => Console.WriteLine("OnFocus: {0}", result); + X11.MessageLoop.Get.OnProperty += state => Console.WriteLine("OnProperty: {0}", state); + X11.MessageLoop.Get.OnVisibility += () => Console.WriteLine("OnVisibility"); + + stop.WaitOne(); + } + } +} diff --git a/Game/Visitor/IPixelVisitor.cs b/Game/Visitor/IPixelVisitor.cs new file mode 100644 index 0000000..da957f3 --- /dev/null +++ b/Game/Visitor/IPixelVisitor.cs @@ -0,0 +1,7 @@ +namespace Game +{ + interface IPixelVisitor + { + void Visit(IPixel pixel); + } +} diff --git a/Util/ConsoleScreen.cs b/Util/ConsoleScreen.cs new file mode 100644 index 0000000..ff2bcf7 --- /dev/null +++ b/Util/ConsoleScreen.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading; +using System.Globalization; + +namespace Util +{ + public static class ConsoleScreen + { + public static double Zoom = 1.0; + + public static double Fit(double value, double limit, double epsilon = 0.000001) + { + var sign = Math.Sign(value); + value = Math.Abs(value); + + int count = (int) (value / limit); + double rest = value - count * limit; + if (Math.Abs(limit - rest) < epsilon) count++; + return count * limit * sign; + } + + private static int ToScreenX(double x) + { + return (int) Fit(Zoom * x + Console.WindowWidth / 2, 0.5); + } + private static int ToScreenY(double y) + { + return (int) Fit(Console.WindowHeight / 2 - Zoom * y, 0.5); + } + + public static void Draw(double cartX, double cartY, ConsoleColor color, Action draw) + { + var x = ToScreenX(cartX); + var y = ToScreenY(cartY); + if (x < 0 || x >= Console.WindowWidth || y < 0 || y >= Console.WindowHeight) return; + Console.SetCursorPosition(x, y); + Console.ForegroundColor = color; + draw(); + } + + public static void Clear() + { + Console.BackgroundColor = ConsoleColor.Black; + Console.Clear(); + } + + public static void ManualClear() + { + Console.BackgroundColor = ConsoleColor.Black; + var line = new string(' ', Console.WindowWidth); + Console.ForegroundColor = ConsoleColor.Black; + for (var i = 0; i < Console.WindowHeight; i++) { + Console.SetCursorPosition(0, i); + Console.Write(line); + } + } + + public static void SetDotAsSeparator() + { + Console.CursorVisible = false; + var info = (CultureInfo) Thread.CurrentThread.CurrentCulture.Clone(); + info.NumberFormat.NumberDecimalSeparator = "."; + Thread.CurrentThread.CurrentCulture = info; + } + } + +} diff --git a/Util/Rnd.cs b/Util/Rnd.cs new file mode 100644 index 0000000..6f7649b --- /dev/null +++ b/Util/Rnd.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Util +{ + /// + /// Some extension methods for for creating a few more kinds of random stuff. + /// + public static class Rnd + { + /// + /// Generates normally distributed numbers. Each operation makes two Gaussians for the price of one, and apparently they can be cached or something for better performance, but who cares. + /// + /// + /// Mean of the distribution + /// Standard deviation + /// + public static double NextGaussian(this Random r, double mu = 0, double sigma = 1) + { + var u1 = r.NextDouble(); + var u2 = r.NextDouble(); + + var rand_std_normal = Math.Sqrt(-2.0 * Math.Log(u1)) * + Math.Sin(2.0 * Math.PI * u2); + + var rand_normal = mu + sigma * rand_std_normal; + + return rand_normal; + } + + /// + /// Generates values from a triangular distribution. + /// + /// + /// See http://en.wikipedia.org/wiki/Triangular_distribution for a description of the triangular probability distribution and the algorithm for generating one. + /// + /// + /// Minimum + /// Maximum + /// Mode (most frequent value) + /// + public static double NextTriangular(this Random r, double a, double b, double c) + { + var u = r.NextDouble(); + + return u < (c - a) / (b - a) + ? a + Math.Sqrt(u * (b - a) * (c - a)) + : b - Math.Sqrt((1 - u) * (b - a) * (b - c)); + } + + /// + /// Equally likely to return true or false. Uses . + /// + /// + public static bool NextBoolean(this Random r) + { + return r.Next(2) > 0; + } + + /// + /// Shuffles a list in O(n) time by using the Fisher-Yates/Knuth algorithm. + /// + /// + /// + public static void Shuffle(this Random r, IList list) + { + for (var i = 0; i < list.Count; i++) { + var j = r.Next(0, i + 1); + + var temp = list[j]; + list[j] = list[i]; + list[i] = temp; + } + } + + /// + /// Returns n unique random numbers in the range [1, n], inclusive. + /// This is equivalent to getting the first n numbers of some random permutation of the sequential numbers from 1 to max. + /// Runs in O(k^2) time. + /// + /// + /// Maximum number possible. + /// How many numbers to return. + /// + public static int[] Permutation(this Random rand, int n, int k) + { + var result = new List(); + var sorted = new SortedSet(); + + for (var i = 0; i < k; i++) { + var r = rand.Next(1, n + 1 - i); + + foreach (var q in sorted) if (r >= q) r++; + + result.Add(r); + sorted.Add(r); + } + + return result.ToArray(); + } + } +} + diff --git a/Util/SimpleLock.cs b/Util/SimpleLock.cs index 3b3dab0..1a87fc3 100644 --- a/Util/SimpleLock.cs +++ b/Util/SimpleLock.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; namespace Util diff --git a/Util/Util.csproj b/Util/Util.csproj index 9248d87..e4cf220 100644 --- a/Util/Util.csproj +++ b/Util/Util.csproj @@ -37,6 +37,8 @@ + + \ No newline at end of file diff --git a/X11/FromDLL.cs b/X11/FromDLL.cs index 3dc16e8..3825767 100644 --- a/X11/FromDLL.cs +++ b/X11/FromDLL.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using System.Runtime.InteropServices; namespace X11 @@ -51,9 +50,18 @@ static class FromDLL [DllImport("libX11")] extern public static IntPtr XCreateSimpleWindow(IntPtr x11display, IntPtr x11window, int x, int y, uint width, uint height, uint outsideBorderWidth, ulong border, ulong background); + [DllImport("libX11")] + extern public static void XLowerWindow(IntPtr x11display, IntPtr x11window); + + [DllImport("libX11")] + extern public static int XGrabKeyboard(IntPtr display, IntPtr window, bool owner, int pointer_mode, int keyboard_mode, long time); + [DllImport("libX11")] extern public static IntPtr XRootWindow(IntPtr x11display, int screenNumber); + [DllImport("libX11")] + extern public static IntPtr XDefaultRootWindow(IntPtr x11display); + [DllImport("libX11")] extern public static ulong XBlackPixel(IntPtr x11display, int screenNumber); @@ -83,5 +91,8 @@ static class FromDLL [DllImport("libX11")] public static extern int XLookupKeysym(ref XKeyEvent keyEvent, int index); + + [DllImport("libX11")] + public static extern int XSendEvent(IntPtr display, IntPtr w, bool propagate, EventMask mask, ref XEvent e); } } diff --git a/X11/MessageLoop.cs b/X11/MessageLoop.cs index 430347b..286485a 100644 --- a/X11/MessageLoop.cs +++ b/X11/MessageLoop.cs @@ -36,6 +36,15 @@ private void Loop() int res = 0; FromDLL.XGetInputFocus(display, ref window, ref res); if (window == IntPtr.Zero) return; + + //XSetInputFocus + bool grabKeyboard = false; + if (grabKeyboard) { + var rootWindow = FromDLL.XDefaultRootWindow(display); + window = FromDLL.XCreateSimpleWindow(display, rootWindow, -1, -1, 1, 1, 0, FromDLL.XBlackPixel(display, 0), FromDLL.XWhitePixel(display, 0)); + FromDLL.XLowerWindow(display, window); + } + FromDLL.XSelectInput(display, window, EventMask.StructureNotifyMask | EventMask.ExposureMask @@ -48,6 +57,17 @@ private void Loop() | EventMask.VisibilityChangeMask ); FromDLL.XMapWindow(display, window); + + if (grabKeyboard) { + var e2 = new XEvent { type = XEventName.None }; + do { + FromDLL.XNextEvent(display, ref e2); + } while (e2.type != XEventName.MapNotify); + + FromDLL.XGrabKeyboard(display, window, false, 1, 1, 0); + FromDLL.XLowerWindow(display, window); + } + var e = new XEvent { type = XEventName.None }; do { switch (e.type) {