From 173dbd4a620b6f3ff90ccbfd849a5a5be0f4ad7c Mon Sep 17 00:00:00 2001 From: Difegue Date: Tue, 28 Jun 2022 01:31:59 +0200 Subject: [PATCH] (#4) sgrinder logic --- .../GameAssets/sgrinder/sgrinder.svg | 14 +- LCDonald.Core/Games/ShadowGrinder.cs | 214 ++++++++++++++++++ LCDonald/ViewModels/MainWindowViewModel.cs | 1 + 3 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 LCDonald.Core/Games/ShadowGrinder.cs diff --git a/LCDonald.Core/GameAssets/sgrinder/sgrinder.svg b/LCDonald.Core/GameAssets/sgrinder/sgrinder.svg index 710a663..f129fde 100644 --- a/LCDonald.Core/GameAssets/sgrinder/sgrinder.svg +++ b/LCDonald.Core/GameAssets/sgrinder/sgrinder.svg @@ -28,11 +28,11 @@ fit-margin-right="0" fit-margin-bottom="0" inkscape:zoom="2.1997092" - inkscape:cx="273.21793" + inkscape:cx="251.85147" inkscape:cy="188.6613" - inkscape:window-width="3840" - inkscape:window-height="2126" - inkscape:window-x="-11" + inkscape:window-width="1800" + inkscape:window-height="2366" + inkscape:window-x="5749" inkscape:window-y="-11" inkscape:window-maximized="1" inkscape:current-layer="layer1" /> @@ -46,7 +46,7 @@ + id="shadow-0" /> + id="shadow-1" /> diff --git a/LCDonald.Core/Games/ShadowGrinder.cs b/LCDonald.Core/Games/ShadowGrinder.cs new file mode 100644 index 0000000..8fa0a78 --- /dev/null +++ b/LCDonald.Core/Games/ShadowGrinder.cs @@ -0,0 +1,214 @@ +using LCDonald.Core.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LCDonald.Core.Games +{ + public class ShadowGrinder : LCDGameBase + { + public override string ShortName => "sgrinder"; + public override string Name => "Shadow Grinder (2003)"; + + #region SVG Group Names + public const string LEVEL_1 = "level-1"; + public const string LEVEL_2 = "level-2"; + public const string LEVEL_3 = "level-3"; + public const string RAIL_0 = "rail-0"; + public const string RAIL_1 = "rail-1"; + public const string RAIL_2 = "rail-2"; + public const string RAIL_3 = "rail-3"; + public const string RAIL_4 = "rail-4"; + public const string RAIL_5 = "rail-5"; + public const string RAIL_6 = "rail-6"; + public const string RAIL_7 = "rail-7"; + public const string MISS = "miss"; + public const string SHADOW_JUMP = "shadow-2"; + public const string SHADOW = "shadow-1"; + public const string SHADOW_FALL = "shadow-0"; + #endregion SVG Group Names + + public override List GetAllGameElements() + { + return new List() + { + SHADOW_JUMP, LEVEL_1, LEVEL_2, LEVEL_3, + + SHADOW, + RAIL_0, RAIL_1, + RAIL_2, RAIL_3, + SHADOW_FALL, RAIL_4, RAIL_5, + MISS, RAIL_6, RAIL_7 + }; + } + + public override List GetAvailableInputs() + { + return new List() + { + new LCDGameInput + { + Name = "Jump", + Description = "Jump over gaps", + KeyCode = 18, // space + } + }; + } + + private int _shadowPosition; + private int _gapsDodged; + private int _misses; + private int _level; + private bool _spawnFirstGap; + + private List _railPositions = new List(); + + protected override List GetVisibleElements() + { + var elements = new List(); + + if (_level >= 1) + elements.Add(LEVEL_1); + if (_level >= 2) + elements.Add(LEVEL_2); + if (_level >= 3) + elements.Add(LEVEL_3); + + elements.Add(GetShadowPosition()); + + if (_shadowPosition == 0) + elements.Add(MISS); + + foreach (var rail in ThreadSafeRailList()) + elements.Add("rail-" + rail); + + return elements; + } + + private string GetShadowPosition() => "shadow-" + _shadowPosition; + + public override void InitializeGameState() + { + _shadowPosition = 1; + _gapsDodged = 0; + _misses = 0; + _level = 0; + _spawnFirstGap = true; + + _railPositions = new List() { 0, 1, 2, 3, 4, 5, 6, 7}; + _customUpdateSpeed = 800; + + StartupMusic(); + } + + public override void HandleInputs(List pressedInputs) + { + foreach (var input in pressedInputs) + { + if (input == null) continue; + + if (input.Name == "Jump" && _shadowPosition == 1) + { + _shadowPosition = 2; + } + } + } + + protected override void UpdateCore() + { + // Unused in this game + } + + public override void CustomUpdate() + { + if (_gapsDodged >= 15) + { + QueueSound(new LCDGameSound("../common/level_up.ogg")); + _misses = 0; + _gapsDodged = 0; + _level++; + + // Speed up + _customUpdateSpeed -= 125; + } + + if (_level == 4) + { + Victory(); + return; + } + + // Move rails forward + _railPositions = ThreadSafeRailList().Select(x => x-= 1).ToList(); + + if (!_railPositions.Contains(0)) + { + switch (_shadowPosition) + { + case 1: + _shadowPosition = 0; + QueueSound(new LCDGameSound("../common/miss.ogg")); + _misses++; + break; + case 2: + QueueSound(new LCDGameSound("../common/hit.ogg")); + _gapsDodged++; + break; + } + } + + // Apply gravity to shadow - unless he's currently jumping over a gap + if (_shadowPosition != 1 && _railPositions.Contains(0)) + { + _shadowPosition = 1; + } + + if (_misses == 10) + { + GameOver(); + return; + } + + // Always spawn a gap on first cycle + if (_spawnFirstGap) + { + _spawnFirstGap = false; + } + else + { + // 30% chance of not spawning a new rail_7, creating a 1 rail-wide gap + // Note: The original game seems to loop through predefined sets of gaps instead, but it's easier to just rng it.. + if (_rng.Next(1, 3) != 1) + { + _railPositions.Add(7); + } else if (!_railPositions.Contains(6)) + { + _railPositions.Add(7); // Prevent 2rail-wide gaps + } + } + } + + private void GameOver() + { + _shadowPosition = -1; + _level = 0; + _railPositions.Clear(); + + GenericGameOverAnimation(new List { MISS, SHADOW_FALL, RAIL_1, RAIL_2, RAIL_3, RAIL_4, RAIL_5, RAIL_6, RAIL_7 }); + Stop(); + } + + private void Victory() + { + _shadowPosition = -1; + _level = 0; + _railPositions.Clear(); + + GenericVictoryAnimation(new List { LEVEL_1, LEVEL_2, LEVEL_3 }); + Stop(); + } + + private List ThreadSafeRailList() => new List(_railPositions); + } +} diff --git a/LCDonald/ViewModels/MainWindowViewModel.cs b/LCDonald/ViewModels/MainWindowViewModel.cs index 75058b3..453333f 100644 --- a/LCDonald/ViewModels/MainWindowViewModel.cs +++ b/LCDonald/ViewModels/MainWindowViewModel.cs @@ -21,6 +21,7 @@ public MainWindowViewModel() new SonicActionGame(), new TailsSkyPatrol(), new KnucklesSoccer(), + new ShadowGrinder(), new SonicSpeedway(), new AiAiBananaCatch(), new SonicExtremeBoarding(),