diff --git a/Entities/DownloadEntity.cs b/Entities/DownloadEntity.cs index 363ac84..489f958 100644 --- a/Entities/DownloadEntity.cs +++ b/Entities/DownloadEntity.cs @@ -1,5 +1,5 @@ using System.Drawing; -using SkinnerBox.Utilities.Gameplay; +using SkinnerBox.Utilities; using SlatedGameToolkit.Framework.Graphics.Render; using SlatedGameToolkit.Framework.Graphics.Textures; using SlatedGameToolkit.Framework.Utilities.Collections.Pooling; diff --git a/Entities/PacketEntity.cs b/Entities/PacketEntity.cs index 53de718..165f751 100644 --- a/Entities/PacketEntity.cs +++ b/Entities/PacketEntity.cs @@ -33,21 +33,21 @@ namespace SkinnerBox.Entities public struct PacketSpawnInfo { public float timeElapsed; - public int period; + public float interval; public int perSpawn; public float batchLocation; - public float distanceBetween; + public float range; public float jumpDistance; public float speed; public float lastSpawnLocation; - public PacketSpawnInfo(int period, int perSpawn, float location, float speed, float distance, float jump) { + public PacketSpawnInfo(float period, int perSpawn, float location, float speed, float distance, float jump) { timeElapsed = 0; lastSpawnLocation = 0; - this.period = period; + this.interval = period; this.perSpawn = perSpawn; this.batchLocation = location; - this.distanceBetween = distance; + this.range = distance; this.speed = speed; this.jumpDistance = jump; } diff --git a/Entities/WarningEntity.cs b/Entities/WarningEntity.cs index 6bf0b2f..b23429f 100644 --- a/Entities/WarningEntity.cs +++ b/Entities/WarningEntity.cs @@ -1,4 +1,4 @@ -using SkinnerBox.Utilities.Gameplay; +using SkinnerBox.Utilities; using SlatedGameToolkit.Framework.Graphics.Textures; using SlatedGameToolkit.Framework.Utilities.Collections.Pooling; using System; diff --git a/Game.cs b/Game.cs index 8ae357e..a165af5 100644 --- a/Game.cs +++ b/Game.cs @@ -1,4 +1,4 @@ -using SkinnerBox.States.Main; +using SkinnerBox.States; using SkinnerBox.Utilities; using SlatedGameToolkit.Framework; using SlatedGameToolkit.Framework.Logging; @@ -12,7 +12,7 @@ namespace SkinnerBox static void Main(string[] args) { GameEngine.targetFPS = 0; - GameEngine.UpdatesPerSecond = 20; + GameEngine.UpdatesPerSecond = 40; Logger.AddLogListener(new ConsoleLogger()); GameEngine.Ignite(new MenuState()); } diff --git a/States/GameOverState.cs b/States/GameOverState.cs new file mode 100644 index 0000000..9acc4d1 --- /dev/null +++ b/States/GameOverState.cs @@ -0,0 +1,86 @@ +using System; +using System.Drawing; +using System.Numerics; +using SlatedGameToolkit.Framework.AssetSystem; +using SlatedGameToolkit.Framework.Graphics.Render; +using SlatedGameToolkit.Framework.Graphics.Text; +using SlatedGameToolkit.Framework.StateSystem; +using SlatedGameToolkit.Framework.StateSystem.States; + +namespace SkinnerBox.States +{ + public class GameOverState : IState + { + private MeshBatchRenderer renderer; + private AssetManager assets; + BitmapFont font; + BitmapFont titleFont; + private StateManager manager; + + private int score; + private TimeSpan timeElapsed; + private int downloadsServed; + private int totalDownloads; + private int packetsReceived; + private int totalPackets; + public GameOverState(BitmapFont font, BitmapFont titleFont, MeshBatchRenderer renderer, AssetManager assets) { + this.font = font; + this.assets = assets; + this.titleFont = titleFont; + this.renderer = renderer; + } + public bool Activate() + { + titleFont.PixelHeight = 120; + titleFont.PrepareCharacterGroup("GameOvr!".ToCharArray()); + this.font.PixelHeight = 48; + font.PrepareCharacterGroup("01234567890.Your Stats: Score,ServerUp-timedownloadservedpacketssentWebsitePDRLN%que".ToCharArray()); + return true; + } + + public bool Deactivate() + { + return true; + } + + public void Deinitialize() + { + } + + public string getName() + { + return "GameOver"; + } + + public void Initialize(StateManager manager) + { + this.manager = manager; + } + + public void Render(double delta) + { + renderer.Begin(Matrix4x4.Identity, delta); + titleFont.WriteLine(renderer, 1.95f, Game.HEIGHT_UNITS * 0.75f, "Game Over!", Color.Purple); + font.WriteLine(renderer, 1.95f, Game.HEIGHT_UNITS * 0.6f, "Score: " + score, Color.Black); + font.WriteLine(renderer, 1.95f, Game.HEIGHT_UNITS * 0.53f, "Website Uptime: " + timeElapsed.ToString("h\\:mm\\:ss"), Color.Black); + font.WriteLine(renderer, 1.95f, Game.HEIGHT_UNITS * 0.46f, "Packets Received: " + totalPackets + " Packet Loss: " + Math.Round((100f * (1f - ((float) packetsReceived / totalPackets))), 1) + "%", Color.Black); + font.WriteLine(renderer, 1.95f, Game.HEIGHT_UNITS * 0.39f, "Downloads Served: " + downloadsServed, Color.Black); + font.WriteLine(renderer, 1.95f, Game.HEIGHT_UNITS * 0.32f, "Downloads Requested: " + totalDownloads, Color.Black); + + renderer.End(); + } + + public void SetStats(int score, float secondsElapsed, int downloads, int totalDownloads, int packetsReceived, int totalPackets) { + this.score = score; + this.timeElapsed = TimeSpan.FromSeconds(secondsElapsed); + this.downloadsServed = downloads; + this.totalPackets = totalPackets; + this.packetsReceived = packetsReceived; + this.totalDownloads = totalDownloads; + } + + public void Update(double timeStep) + { + } + } +} \ No newline at end of file diff --git a/States/Gameplay/GamePlayState.cs b/States/GamePlayState.cs similarity index 65% rename from States/Gameplay/GamePlayState.cs rename to States/GamePlayState.cs index 729bf2d..46064bd 100644 --- a/States/Gameplay/GamePlayState.cs +++ b/States/GamePlayState.cs @@ -14,8 +14,9 @@ using SlatedGameToolkit.Framework.StateSystem.States; using SlatedGameToolkit.Framework.Utilities.Collections.Pooling; using SlatedGameToolkit.Framework.Utilities; using SlatedGameToolkit.Framework.Graphics.Text; +using SkinnerBox.Utilities; -namespace SkinnerBox.States.Gameplay +namespace SkinnerBox.States { public class GamePlayState : IState { @@ -23,6 +24,7 @@ namespace SkinnerBox.States.Gameplay private AssetManager assets; private StateManager stateManager; private BitmapFont font; + private GameOverState gameOverState; private Random random; #region CursorVars @@ -52,14 +54,22 @@ namespace SkinnerBox.States.Gameplay #endregion #region PlayerStats - private readonly int totalUsage = 3; - private int usedUsage = 0; - private RectangleMesh usageMesh; - private int score; - private float timeElapsed; + private int speedBoost = 0; + private int bandwithBoost = 0; + private RectangleMesh bandwithMesh; + private RectangleMesh speedMesh; + private float score; + private TransitionValue timeElapsed; + private int downloadsServed; + private int totalDownloads; + private int packetsReceived; + private int totalPackets; + private readonly float totalStability = 5; + private float stability; + private RectangleMesh stabilityMesh; #endregion - public GamePlayState(MeshBatchRenderer renderer, AssetManager asset, BitmapFont font) + public GamePlayState(MeshBatchRenderer renderer, AssetManager asset, BitmapFont font, GameOverState gameOverState) { this.assets = asset; this.renderer = renderer; @@ -67,7 +77,7 @@ namespace SkinnerBox.States.Gameplay warningPool = new ObjectPool(createWarning); downloadPool = new ObjectPool(createDownload); this.font = font; - font.PrepareCharacterGroup("score:0123456789timlapsd".ToCharArray()); + this.gameOverState = gameOverState; } public bool Activate() @@ -76,11 +86,26 @@ namespace SkinnerBox.States.Gameplay Mouse.mouseUpdateEvent += MouseInput; serverTargetPos = 0.5f * Game.WIDTH_UNITS; server = new ServerEntity((Texture)assets["serverunit.png"], serverTargetPos, 0.1f); - usageMesh = new RectangleMesh(new RectangleF(0, Game.HEIGHT_UNITS - 0.75f, 0.5f, 0.5f), (ITexture)assets["usage.png"], Color.White); + bandwithMesh = new RectangleMesh(new RectangleF(0, Game.HEIGHT_UNITS - 0.75f, 0.5f, 0.5f), (ITexture)assets["serverunit.png"], Color.White); + stabilityMesh = new RectangleMesh(new RectangleF(0.05f, Game.HEIGHT_UNITS - 3.2f, 0.5f, 0.5f), (ITexture)assets["health.png"], Color.White); + speedMesh = new RectangleMesh(bandwithMesh.Bounds, (ITexture)assets["ram.png"], Color.White); random = new Random(); - packetSpawnInfo = new PacketSpawnInfo(2, 1, (float)(random.NextDouble() * Game.WIDTH_UNITS), 1f, 0.2f, 3f); + packetSpawnInfo = new PacketSpawnInfo(2, 1, (float)(random.NextDouble() * Game.WIDTH_UNITS), 1f, 0.2f, 2f); downloadSpawnInfo = new DownloadSpawnInfo(4, 6, 3, 1, 4, 2); + score = 0; + timeElapsed.HardSet(0); + server.Size = 4; + bandwithBoost = server.Size; + speedBoost = 0; + stability = totalStability; + packetsReceived = 0; + totalPackets = 0; + downloadsServed = 0; + totalDownloads = 0; + + this.font.PixelHeight = 32; + font.PrepareCharacterGroup("Score:0123456789Uptim.%".ToCharArray()); return true; } @@ -145,24 +170,33 @@ namespace SkinnerBox.States.Gameplay renderer.Draw(server); #region StatusRender - for (int i = 0; i < totalUsage; i++) + for (int i = 0; i < bandwithBoost + speedBoost; i++) { - usageMesh.X = 0.25f + (i * (usageMesh.Width + 0.2f)); - if (i >= usedUsage) { - usageMesh.Color = Color.Yellow; + if (i < bandwithBoost) { + bandwithMesh.X = 0.25f + (i * (bandwithMesh.Width + 0.2f)); + renderer.Draw(bandwithMesh); } else { - usageMesh.Color = Color.White; + speedMesh.X = 0.25f + (i * (speedMesh.Width + 0.2f)); + renderer.Draw(speedMesh); } - renderer.Draw(usageMesh); } + float normalizedHealth = stability / totalStability; + int red = (int) (byte.MaxValue * normalizedHealth); + stabilityMesh.Color = Color.FromArgb(red, 0, 0); + renderer.Draw(stabilityMesh); + font.WriteLine(renderer, stabilityMesh.Width + 0.1f, stabilityMesh.Y, Math.Round(100 * normalizedHealth, 1) + "%", Color.Red); - font.WriteLine(renderer, 0.05f, Game.HEIGHT_UNITS - 2f, "score: " + score, Color.Black); + timeElapsed.InterpolatePosition((float)delta); + font.WriteLine(renderer, 0.05f, Game.HEIGHT_UNITS - 1.75f, "Score: " + Math.Round(score, 0), Color.Black); + font.WriteLine(renderer, 0.05f, Game.HEIGHT_UNITS - 2.5f, "Uptime: " + Math.Round(timeElapsed.Value), Color.Black); #endregion renderer.End(); } public void Update(double timeStep) { + timeElapsed.Value = (float)timeStep + timeElapsed.DesignatedValue; + score += (float) timeStep * 0.5f; #region ServerUpdate if (Mouse.LeftButtonPressed) { serverTargetPos = cursorWidthScale * Mouse.X; @@ -180,30 +214,33 @@ namespace SkinnerBox.States.Gameplay #endregion #region PacketUpdate packetSpawnInfo.timeElapsed += (float) timeStep; - if (packetSpawnInfo.timeElapsed >= packetSpawnInfo.period) { + if (packetSpawnInfo.timeElapsed >= packetSpawnInfo.interval) { packetSpawnInfo.timeElapsed = 0; //do spawning for(int i = 0; i < packetSpawnInfo.perSpawn; i++) { PacketEntity packet = packetPool.Retrieve(); packet.CenterX = packetSpawnInfo.batchLocation; - packet.Y = i * packet.Height + packetSpawnInfo.distanceBetween + Game.HEIGHT_UNITS + packetSpawnInfo.speed * (2/3f); + packet.Y = i * packet.Height + packetSpawnInfo.range + Game.HEIGHT_UNITS + packetSpawnInfo.speed * (2/3f); packet.velocity = packetSpawnInfo.speed; packet.Color = Color.Blue; + totalPackets++; activePackets.Add(packet); } //Spawn Warning WarningEntity warning = warningPool.Retrieve(); warning.CenterX = packetSpawnInfo.batchLocation; - warning.LifeTime = packetSpawnInfo.period * (2/3f); + warning.LifeTime = packetSpawnInfo.interval * (2/3f); warning.Y = Game.HEIGHT_UNITS - warning.Height; activeWarnings.Add(warning); //Prepare next batch - packetSpawnInfo.batchLocation = (float)(packetSpawnInfo.batchLocation + (random.NextDouble() - 1/2f) * packetSpawnInfo.jumpDistance * 2); - if (packetSpawnInfo.batchLocation > Game.WIDTH_UNITS - packetSafeMargin) { - packetSpawnInfo.batchLocation = Game.WIDTH_UNITS - packetSafeMargin; - } else if (packetSpawnInfo.batchLocation < packetSafeMargin) packetSpawnInfo.batchLocation = packetSafeMargin; + float change = (float)((float)(random.NextDouble() - 1/2f) * packetSpawnInfo.jumpDistance * 2); + if (packetSpawnInfo.batchLocation + change > Game.WIDTH_UNITS - packetSafeMargin || packetSpawnInfo.batchLocation + change < packetSafeMargin) { + packetSpawnInfo.batchLocation -= change; + } else { + packetSpawnInfo.batchLocation += change; + } } for (int i = 0; i < activePackets.Count; i++) @@ -215,15 +252,20 @@ namespace SkinnerBox.States.Gameplay packet.Color = Color.Cyan; } if (packet.Y <= 0 - packet.Height) { + stability -= 0.5f; packetPool.Release(packet); activePackets.RemoveAt(i); i--; continue; } if (packet.Y >= Game.HEIGHT_UNITS && packet.velocity < 0) { + score += -2 * packet.velocity; + packetsReceived++; + stability += 0.05f; packetPool.Release(packet); activePackets.RemoveAt(i); i--; + continue; } } #endregion @@ -238,6 +280,7 @@ namespace SkinnerBox.States.Gameplay download.Y = (float)(downloadSafeMargin + random.NextDouble() * (Game.HEIGHT_UNITS - 2 * downloadSafeMargin)); download.stepSize = downloadSpawnInfo.stepSize; download.upTime = downloadSpawnInfo.upTime; + totalDownloads++; activeDownloads.Add(download); WarningEntity warning = warningPool.Retrieve(); @@ -266,6 +309,8 @@ namespace SkinnerBox.States.Gameplay if (download.progressValue.Value >= download.Width) { + score += downloadSpawnInfo.maximumAmount * 2 + downloadSpawnInfo.sizeRange + 1f / downloadSpawnInfo.period + 2 * downloadSpawnInfo.stepSize; + downloadsServed++; downloadPool.Release(download); activeDownloads.RemoveAt(i); i--; @@ -273,6 +318,7 @@ namespace SkinnerBox.States.Gameplay } if (download.timeElapsed.Value >= download.upTime) { + stability -= 1.5f; downloadPool.Release(download); activeDownloads.RemoveAt(i); i--; @@ -292,34 +338,65 @@ namespace SkinnerBox.States.Gameplay } } #endregion + #region DifficultyUpdate + //packet curve + packetSpawnInfo.perSpawn = (int)(0.5f * (Math.Pow(timeElapsed.Value, 0.5f) + 1)); + packetSpawnInfo.speed = (float)((0.025f * Math.Pow(timeElapsed.Value, 1.1f)) + 1f); + if (packetSpawnInfo.range < 4) { + packetSpawnInfo.range = (float)(0.1f * (Math.Pow(timeElapsed.Value, 1.15f)) + 2f); + if (packetSpawnInfo.range > 4) packetSpawnInfo.range = 4; + } + if (packetSpawnInfo.interval > 0.3f) { + packetSpawnInfo.interval = (float) (-0.0055 * timeElapsed.Value) + 2f; + if (packetSpawnInfo.interval < 0.3f) packetSpawnInfo.interval = 0.3f; + } + + //download curve + if (downloadSpawnInfo.maximumAmount < 4) { + downloadSpawnInfo.maximumAmount = (int)(0.02f * timeElapsed.Value + 1); + } + if (downloadSpawnInfo.upTime > 3) { + downloadSpawnInfo.upTime = (float)(8 + (-0.1f * Math.Pow(timeElapsed.Value, 0.8f))); + if (downloadSpawnInfo.upTime < 3) downloadSpawnInfo.upTime = 3; + } + if (downloadSpawnInfo.period > 1.5f) { + downloadSpawnInfo.period = (float) (-0.006 * timeElapsed.Value) + 4; + if (packetSpawnInfo.interval < 1.5f) packetSpawnInfo.interval = 1.5f; + } + #endregion + #region BoundaryChecking + if (stability > totalStability) { + stability = totalStability; + } else if (stability <= 0) + { + stability = 0; + gameOverState.SetStats((int)Math.Round(this.score), this.timeElapsed.Value, downloadsServed, totalDownloads, packetsReceived, totalPackets); + stateManager.ChangeState("GameOver"); + } + #endregion } public void KeyInputListener(SDL.SDL_Keycode keycode, bool down) { if (!down) return; - if (keycode == SDL.SDL_Keycode.SDLK_a) { - if (usedUsage > 0 && server.Size > 1) { - usedUsage--; + int currentUsage = bandwithBoost + speedBoost; + if (keycode == SDL.SDL_Keycode.SDLK_SPACE) { + if (server.Size > 1) { server.Size--; - } - } - if (keycode == SDL.SDL_Keycode.SDLK_d) { - if (usedUsage < totalUsage) { - usedUsage++; - server.Size++; - } - } - if (keycode == SDL.SDL_Keycode.SDLK_s) { - if (usedUsage > 0 && server.Speed > ServerEntity.MIN_SPEED) { - usedUsage--; - server.Speed -= ServerEntity.SPEED_STEP; - } - } - if (keycode == SDL.SDL_Keycode.SDLK_w) { - if (usedUsage < totalUsage) { - usedUsage++; + bandwithBoost--; + + speedBoost++; server.Speed += ServerEntity.SPEED_STEP; } } + if (keycode == SDL.SDL_Keycode.SDLK_LSHIFT) { + if (server.Speed > ServerEntity.MIN_SPEED) { + server.Speed -= ServerEntity.SPEED_STEP; + speedBoost--; + + server.Size++; + bandwithBoost++; + } + } } public void MouseInput(bool leftDown, bool rightDown, bool middle, int x, int y, int scrollX, int scrollY) { diff --git a/States/Main/MenuState.cs b/States/MenuState.cs similarity index 94% rename from States/Main/MenuState.cs rename to States/MenuState.cs index 1d5bd4f..fe8c44b 100644 --- a/States/Main/MenuState.cs +++ b/States/MenuState.cs @@ -2,7 +2,6 @@ using System; using System.Drawing; using System.Numerics; using SDL2; -using SkinnerBox.States.Gameplay; using SlatedGameToolkit.Framework.AssetSystem; using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Render; @@ -14,7 +13,7 @@ using SlatedGameToolkit.Framework.Loaders; using SlatedGameToolkit.Framework.StateSystem; using SlatedGameToolkit.Framework.StateSystem.States; -namespace SkinnerBox.States.Main +namespace SkinnerBox.States { public class MenuState : IState { @@ -29,6 +28,12 @@ namespace SkinnerBox.States.Main public bool Activate() { Keyboard.keyboardUpdateEvent += KeyInput; + this.titleFont.PixelHeight = 120; + this.titleFont.PrepareCharacterGroup("You Are the Website.".ToCharArray()); + this.titleFont.PixelHeight = 40; + this.titleFont.PrepareCharacterGroup("By: Reslate".ToCharArray()); + boldFont.PixelHeight = 60; + boldFont.PrepareCharacterGroup("Press any key to start...".ToCharArray()); return true; } @@ -62,12 +67,17 @@ namespace SkinnerBox.States.Main this.camera.MoveTo = this.camera.Position; this.renderer = new MeshBatchRenderer(camera); - //Add additional states + //Set up title TTF + this.titleFont = new BitmapFont("resources/BigShouldersDisplay-Regular.ttf", textureSizes: 512); + this.titleFont.PixelsPerUnitHeight = 80; + this.titleFont.PixelsPerUnitWidth = 80; BitmapFont genericFont = new BitmapFont("resources/BigShouldersDisplay-Light.ttf"); genericFont.PixelsPerUnitHeight = 80; genericFont.PixelsPerUnitWidth = 80; - genericFont.PixelHeight = 32; - manager.AddState(new GamePlayState(renderer, this.assets, genericFont)); + //Add additional states + GameOverState gameOverState = new GameOverState(genericFont, titleFont, renderer, assets); + manager.AddState(gameOverState); + manager.AddState(new GamePlayState(renderer, this.assets, genericFont, gameOverState)); //Load assets assets.Load("serverunit.png"); @@ -76,22 +86,14 @@ namespace SkinnerBox.States.Main assets.Load("downloadbar.png"); assets.Load("drag.png"); assets.Load("usage.png"); + assets.Load("health.png"); + assets.Load("ram.png"); - //Set up title TTF - this.titleFont = new BitmapFont("resources/BigShouldersDisplay-Regular.ttf", textureSizes: 512); - this.titleFont.PixelHeight = 120; - this.titleFont.PixelsPerUnitHeight = 80; - this.titleFont.PixelsPerUnitWidth = 80; - this.titleFont.PrepareCharacterGroup("You Are the Website.".ToCharArray()); - this.titleFont.PixelHeight = 40; - this.titleFont.PrepareCharacterGroup("By: Reslate".ToCharArray()); //Set up bold TTF boldFont = new BitmapFont("resources/BigShouldersDisplay-Black.ttf", textureSizes: 512); - boldFont.PixelHeight = 60; boldFont.PixelsPerUnitWidth = 80; boldFont.PixelsPerUnitHeight = 80; - boldFont.PrepareCharacterGroup("Press any key to start...".ToCharArray()); //Set up icon Texture serverUnitTex = (Texture)assets["serverunit.png"]; diff --git a/Utilities/TransitionValue.cs b/Utilities/TransitionValue.cs index 6c900ea..8558a5a 100644 --- a/Utilities/TransitionValue.cs +++ b/Utilities/TransitionValue.cs @@ -1,6 +1,6 @@ using SlatedGameToolkit.Framework.Graphics.Render; -namespace SkinnerBox.Utilities.Gameplay +namespace SkinnerBox.Utilities { public struct TransitionValue : IPositionInterpolable { diff --git a/resources/health.png b/resources/health.png new file mode 100644 index 0000000..2eb464b Binary files /dev/null and b/resources/health.png differ diff --git a/resources/ram.png b/resources/ram.png new file mode 100644 index 0000000..fc6bf06 Binary files /dev/null and b/resources/ram.png differ