From 11b542dc20e13894ce02891aa065377840deb03f Mon Sep 17 00:00:00 2001 From: flightlex Date: Tue, 17 Oct 2023 02:42:07 +0300 Subject: [PATCH 1/5] level leeeeennnngggggttthhhhhhh System.TimeSpan && System.Int (Seconds) yey --- .../Levels/GameObjects/Specific/SpeedBlock.cs | 45 ++++++++++++++++--- ...ctTrigger.cs => DisableBgEffectTrigger.cs} | 0 ...ectTrigger.cs => EnableBgEffectTrigger.cs} | 0 GeometryDashAPI/Levels/Level.cs | 2 + GeometryDashAPI/Levels/LevelLength.cs | 41 +++++++++++++++++ GeometryDashAPI/Levels/LevelSpeed.cs | 25 +++++++++++ GeometryDashAPI/Levels/Portal.cs | 18 ++++++++ 7 files changed, 125 insertions(+), 6 deletions(-) rename GeometryDashAPI/Levels/GameObjects/Triggers/{DisableBGEffectTrigger.cs => DisableBgEffectTrigger.cs} (100%) rename GeometryDashAPI/Levels/GameObjects/Triggers/{EnableBGEffectTrigger.cs => EnableBgEffectTrigger.cs} (100%) create mode 100644 GeometryDashAPI/Levels/LevelLength.cs create mode 100644 GeometryDashAPI/Levels/LevelSpeed.cs create mode 100644 GeometryDashAPI/Levels/Portal.cs diff --git a/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs b/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs index 9fefb0a..4838f0f 100644 --- a/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs +++ b/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs @@ -1,6 +1,6 @@ -using GeometryDashAPI.Attributes; +using System; +using GeometryDashAPI.Attributes; using GeometryDashAPI.Levels.Enums; -using GeometryDashAPI.Levels.GameObjects.Default; namespace GeometryDashAPI.Levels.GameObjects.Specific { @@ -14,20 +14,23 @@ public enum SpeedBlockId } [GameBlock(200, 201, 202, 203, 1334)] - public class SpeedBlock : Block + public class SpeedBlock : Portal, IComparable { [GameProperty("24", (short)Layer.B2)] protected override short zLayer { get; set; } = (short)Layer.B2; [GameProperty("25", -6)] public override int ZOrder { get; set; } = -6; - [GameProperty("13", true, true)] - public bool Using { get; set; } = true; - public SpeedBlockId BlockType { get => (SpeedBlockId)Id; set => Id = (int)value; } + public SpeedType SpeedType + { + get => FromBlockIdToSpeedType(BlockType); + set => BlockType = FromSpeedTypeToBlockId(value); + } + public SpeedBlock() : base(201) { } @@ -35,5 +38,35 @@ public SpeedBlock() : base(201) public SpeedBlock(SpeedBlockId type) : base((int)type) { } + + public SpeedBlock(SpeedType type) : base((int)FromSpeedTypeToBlockId(type)) + { + } + + public static SpeedType FromBlockIdToSpeedType(SpeedBlockId id) + { + return id switch + { + SpeedBlockId.Orange => SpeedType.Half, + SpeedBlockId.Default => SpeedType.Default, + SpeedBlockId.Green => SpeedType.X2, + SpeedBlockId.Purple => SpeedType.X3, + SpeedBlockId.Red => SpeedType.X4 + }; + } + + public static SpeedBlockId FromSpeedTypeToBlockId(SpeedType speedType) + { + return speedType switch + { + SpeedType.Half => SpeedBlockId.Orange, + SpeedType.Default => SpeedBlockId.Default, + SpeedType.X2 => SpeedBlockId.Green, + SpeedType.X3 => SpeedBlockId.Purple, + SpeedType.X4 => SpeedBlockId.Red + }; + } + + public int CompareTo(SpeedBlock other) => PositionX.CompareTo(other.PositionX); } } diff --git a/GeometryDashAPI/Levels/GameObjects/Triggers/DisableBGEffectTrigger.cs b/GeometryDashAPI/Levels/GameObjects/Triggers/DisableBgEffectTrigger.cs similarity index 100% rename from GeometryDashAPI/Levels/GameObjects/Triggers/DisableBGEffectTrigger.cs rename to GeometryDashAPI/Levels/GameObjects/Triggers/DisableBgEffectTrigger.cs diff --git a/GeometryDashAPI/Levels/GameObjects/Triggers/EnableBGEffectTrigger.cs b/GeometryDashAPI/Levels/GameObjects/Triggers/EnableBgEffectTrigger.cs similarity index 100% rename from GeometryDashAPI/Levels/GameObjects/Triggers/EnableBGEffectTrigger.cs rename to GeometryDashAPI/Levels/GameObjects/Triggers/EnableBgEffectTrigger.cs diff --git a/GeometryDashAPI/Levels/Level.cs b/GeometryDashAPI/Levels/Level.cs index 64bca39..f832073 100644 --- a/GeometryDashAPI/Levels/Level.cs +++ b/GeometryDashAPI/Levels/Level.cs @@ -23,6 +23,8 @@ public Guidelines Guidelines set => Options.Guidelines = value; } + public LevelLength LevelLength => LevelLength.Measure(this); + public int CountBlock => Blocks.Count; public int CountColor => Options.Colors.Count; diff --git a/GeometryDashAPI/Levels/LevelLength.cs b/GeometryDashAPI/Levels/LevelLength.cs new file mode 100644 index 0000000..cca8831 --- /dev/null +++ b/GeometryDashAPI/Levels/LevelLength.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using GeometryDashAPI.Levels.GameObjects.Specific; + +namespace GeometryDashAPI.Levels +{ + public class LevelLength + { + public TimeSpan TimeLength { get; } + public int Seconds { get; } + + private LevelLength(double seconds) + { + TimeLength = TimeSpan.FromSeconds(seconds); + Seconds = (int)seconds; // basically (int)Math.Floor(seconds), geometry dash floors the doubles + } + + public static LevelLength Measure(Level level) + { + var x = level.Blocks.Max(x => x.PositionX); + var portals = GetSpeedBlocks(level); + + double seconds = 0; + int i = 1; + for (; i < portals.Count && portals.ElementAt(i).PositionX < x; i++) + seconds += (portals.ElementAt(i).PositionX - portals.ElementAt(i - 1).PositionX) / portals.ElementAt(i - 1).SpeedType.GetSpeed(); + + int final = Math.Min(i, portals.Count - 1); + var total = seconds + (x - portals.ElementAt(final).PositionX) / portals.ElementAt(final).SpeedType.GetSpeed(); + + return new LevelLength(total); + } + + private static SortedSet GetSpeedBlocks(Level level) + { + var speedPortals = new SpeedBlock[] { new SpeedBlock(level.Options.PlayerSpeed) }.Concat(level.Blocks.OfType().Where(x => x.Checked).OrderBy(x => x.PositionX)); + return new SortedSet(speedPortals); + } + } +} \ No newline at end of file diff --git a/GeometryDashAPI/Levels/LevelSpeed.cs b/GeometryDashAPI/Levels/LevelSpeed.cs new file mode 100644 index 0000000..e51213e --- /dev/null +++ b/GeometryDashAPI/Levels/LevelSpeed.cs @@ -0,0 +1,25 @@ +using GeometryDashAPI.Levels.Enums; + +namespace GeometryDashAPI.Levels +{ + public static class LevelSpeed + { + public const double Half = 251.16; + public const double Default = 311.58; + public const double X2 = 387.42; + public const double X3 = 468; + public const double X4 = 576; + + public static double GetSpeed(this SpeedType speedType) + { + return speedType switch + { + SpeedType.Half => Half, + SpeedType.Default => Default, + SpeedType.X2 => X2, + SpeedType.X3 => X3, + SpeedType.X4 => X4 + }; + } + } +} diff --git a/GeometryDashAPI/Levels/Portal.cs b/GeometryDashAPI/Levels/Portal.cs new file mode 100644 index 0000000..24cfcb5 --- /dev/null +++ b/GeometryDashAPI/Levels/Portal.cs @@ -0,0 +1,18 @@ +using GeometryDashAPI.Attributes; +using GeometryDashAPI.Levels.GameObjects.Default; + +namespace GeometryDashAPI.Levels +{ + public class Portal : Block + { + [GameProperty("13", true, true)] public bool Checked { get; set; } + + public Portal() + { + } + + public Portal(int id) : base(id) + { + } + } +} \ No newline at end of file From e110c975c7e05c077b436a7b4288a318955654fe Mon Sep 17 00:00:00 2001 From: Folleach Date: Sat, 21 Oct 2023 14:52:16 +0500 Subject: [PATCH 2/5] add tests for level length --- GeometryDashAPI.Tests/LevelLengthTests.cs | 24 ++++++++++++++++++++ GeometryDashAPI/Levels/Level.cs | 4 ++-- GeometryDashAPI/Levels/LevelLength.cs | 27 ++++++++--------------- 3 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 GeometryDashAPI.Tests/LevelLengthTests.cs diff --git a/GeometryDashAPI.Tests/LevelLengthTests.cs b/GeometryDashAPI.Tests/LevelLengthTests.cs new file mode 100644 index 0000000..ad0b5cb --- /dev/null +++ b/GeometryDashAPI.Tests/LevelLengthTests.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Net; +using FluentAssertions; +using GeometryDashAPI.Levels; +using GeometryDashAPI.Server; +using GeometryDashAPI.Server.Responses; +using NUnit.Framework; + +namespace GeometryDashAPI.Tests; + +[TestFixture] +public class LevelLengthTests +{ + [TestCase("12034598_Conclusion", 57)] + [TestCase("28755513_TheFinalLair", 154)] + [TestCase("116631_XmasParty", 87)] + public void Test(string fileName, int expectedSeconds) + { + var responseBody = File.ReadAllText(Path.Combine("data", "levels", fileName)); + var response = new ServerResponse(HttpStatusCode.OK, responseBody); + var level = new Level(response.GetResultOrDefault().Level.LevelString, compressed: true); + level.LevelLength.TotalSeconds.Should().BeApproximately(expectedSeconds, precision: 1); + } +} diff --git a/GeometryDashAPI/Levels/Level.cs b/GeometryDashAPI/Levels/Level.cs index f832073..851692c 100644 --- a/GeometryDashAPI/Levels/Level.cs +++ b/GeometryDashAPI/Levels/Level.cs @@ -23,8 +23,8 @@ public Guidelines Guidelines set => Options.Guidelines = value; } - public LevelLength LevelLength => LevelLength.Measure(this); - + public TimeSpan LevelLength => Levels.LevelLength.Measure(this); + public int CountBlock => Blocks.Count; public int CountColor => Options.Colors.Count; diff --git a/GeometryDashAPI/Levels/LevelLength.cs b/GeometryDashAPI/Levels/LevelLength.cs index cca8831..edb94c6 100644 --- a/GeometryDashAPI/Levels/LevelLength.cs +++ b/GeometryDashAPI/Levels/LevelLength.cs @@ -5,31 +5,22 @@ namespace GeometryDashAPI.Levels { - public class LevelLength + public static class LevelLength { - public TimeSpan TimeLength { get; } - public int Seconds { get; } - - private LevelLength(double seconds) - { - TimeLength = TimeSpan.FromSeconds(seconds); - Seconds = (int)seconds; // basically (int)Math.Floor(seconds), geometry dash floors the doubles - } - - public static LevelLength Measure(Level level) + public static TimeSpan Measure(Level level) { var x = level.Blocks.Max(x => x.PositionX); var portals = GetSpeedBlocks(level); - + double seconds = 0; - int i = 1; + var i = 1; for (; i < portals.Count && portals.ElementAt(i).PositionX < x; i++) seconds += (portals.ElementAt(i).PositionX - portals.ElementAt(i - 1).PositionX) / portals.ElementAt(i - 1).SpeedType.GetSpeed(); - - int final = Math.Min(i, portals.Count - 1); + + var final = Math.Min(i, portals.Count - 1); var total = seconds + (x - portals.ElementAt(final).PositionX) / portals.ElementAt(final).SpeedType.GetSpeed(); - return new LevelLength(total); + return TimeSpan.FromSeconds(total); } private static SortedSet GetSpeedBlocks(Level level) @@ -37,5 +28,5 @@ private static SortedSet GetSpeedBlocks(Level level) var speedPortals = new SpeedBlock[] { new SpeedBlock(level.Options.PlayerSpeed) }.Concat(level.Blocks.OfType().Where(x => x.Checked).OrderBy(x => x.PositionX)); return new SortedSet(speedPortals); } - } -} \ No newline at end of file + } +} From 2d65705fd71e9bdfc1e63867d8eceefb3ddf171c Mon Sep 17 00:00:00 2001 From: Folleach Date: Sat, 21 Oct 2023 15:07:06 +0500 Subject: [PATCH 3/5] remove IComparable and create IComparer for pos x --- .../Comparers/BlockXPositionComparer.cs | 20 +++++++++++++++++++ .../Levels/GameObjects/Specific/SpeedBlock.cs | 4 +--- GeometryDashAPI/Levels/LevelLength.cs | 3 ++- 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs diff --git a/GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs b/GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs new file mode 100644 index 0000000..ffbb251 --- /dev/null +++ b/GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using GeometryDashAPI.Levels.GameObjects; + +namespace GeometryDashAPI.Levels.Comparers; + +public class BlockPositionXComparer : IComparer +{ + public static readonly BlockPositionXComparer Instance = new(); + + public int Compare(IBlock x, IBlock y) + { + if (ReferenceEquals(x, y)) + return 0; + if (ReferenceEquals(null, y)) + return 1; + if (ReferenceEquals(null, x)) + return -1; + return x.PositionX.CompareTo(y.PositionY); + } +} diff --git a/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs b/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs index 4838f0f..3c4675d 100644 --- a/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs +++ b/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs @@ -14,7 +14,7 @@ public enum SpeedBlockId } [GameBlock(200, 201, 202, 203, 1334)] - public class SpeedBlock : Portal, IComparable + public class SpeedBlock : Portal { [GameProperty("24", (short)Layer.B2)] protected override short zLayer { get; set; } = (short)Layer.B2; [GameProperty("25", -6)] public override int ZOrder { get; set; } = -6; @@ -66,7 +66,5 @@ public static SpeedBlockId FromSpeedTypeToBlockId(SpeedType speedType) SpeedType.X4 => SpeedBlockId.Red }; } - - public int CompareTo(SpeedBlock other) => PositionX.CompareTo(other.PositionX); } } diff --git a/GeometryDashAPI/Levels/LevelLength.cs b/GeometryDashAPI/Levels/LevelLength.cs index edb94c6..3199ae5 100644 --- a/GeometryDashAPI/Levels/LevelLength.cs +++ b/GeometryDashAPI/Levels/LevelLength.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using GeometryDashAPI.Levels.Comparers; using GeometryDashAPI.Levels.GameObjects.Specific; namespace GeometryDashAPI.Levels @@ -26,7 +27,7 @@ public static TimeSpan Measure(Level level) private static SortedSet GetSpeedBlocks(Level level) { var speedPortals = new SpeedBlock[] { new SpeedBlock(level.Options.PlayerSpeed) }.Concat(level.Blocks.OfType().Where(x => x.Checked).OrderBy(x => x.PositionX)); - return new SortedSet(speedPortals); + return new SortedSet(speedPortals, BlockPositionXComparer.Instance); } } } From a1e80579f9bdedd4b98fe2b0da1a312328b28eae Mon Sep 17 00:00:00 2001 From: Folleach Date: Sat, 21 Oct 2023 15:12:59 +0500 Subject: [PATCH 4/5] small fixes in SpeedBlock --- .../Levels/GameObjects/Specific/SpeedBlock.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs b/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs index 3c4675d..b86ed87 100644 --- a/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs +++ b/GeometryDashAPI/Levels/GameObjects/Specific/SpeedBlock.cs @@ -4,6 +4,11 @@ namespace GeometryDashAPI.Levels.GameObjects.Specific { + /// + /// Represents the id of the speed block.

+ /// Not to be confused with .
+ /// Because it is responsible for a specific speed, instead of a specific block id + ///
public enum SpeedBlockId { Orange = 200, @@ -51,7 +56,8 @@ public static SpeedType FromBlockIdToSpeedType(SpeedBlockId id) SpeedBlockId.Default => SpeedType.Default, SpeedBlockId.Green => SpeedType.X2, SpeedBlockId.Purple => SpeedType.X3, - SpeedBlockId.Red => SpeedType.X4 + SpeedBlockId.Red => SpeedType.X4, + _ => throw new ArgumentOutOfRangeException(nameof(id), id, null) }; } @@ -63,7 +69,8 @@ public static SpeedBlockId FromSpeedTypeToBlockId(SpeedType speedType) SpeedType.Default => SpeedBlockId.Default, SpeedType.X2 => SpeedBlockId.Green, SpeedType.X3 => SpeedBlockId.Purple, - SpeedType.X4 => SpeedBlockId.Red + SpeedType.X4 => SpeedBlockId.Red, + _ => throw new ArgumentOutOfRangeException(nameof(speedType), speedType, null) }; } } From 90b16cfa8ee0526a2236c66d53103e9a2b703568 Mon Sep 17 00:00:00 2001 From: Folleach Date: Sat, 21 Oct 2023 15:24:10 +0500 Subject: [PATCH 5/5] LevelLength refactoring --- .../Comparers/BlockXPositionComparer.cs | 20 ---------------- GeometryDashAPI/Levels/LevelLength.cs | 23 ++++++++++--------- 2 files changed, 12 insertions(+), 31 deletions(-) delete mode 100644 GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs diff --git a/GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs b/GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs deleted file mode 100644 index ffbb251..0000000 --- a/GeometryDashAPI/Levels/Comparers/BlockXPositionComparer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using GeometryDashAPI.Levels.GameObjects; - -namespace GeometryDashAPI.Levels.Comparers; - -public class BlockPositionXComparer : IComparer -{ - public static readonly BlockPositionXComparer Instance = new(); - - public int Compare(IBlock x, IBlock y) - { - if (ReferenceEquals(x, y)) - return 0; - if (ReferenceEquals(null, y)) - return 1; - if (ReferenceEquals(null, x)) - return -1; - return x.PositionX.CompareTo(y.PositionY); - } -} diff --git a/GeometryDashAPI/Levels/LevelLength.cs b/GeometryDashAPI/Levels/LevelLength.cs index 3199ae5..555af5d 100644 --- a/GeometryDashAPI/Levels/LevelLength.cs +++ b/GeometryDashAPI/Levels/LevelLength.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using GeometryDashAPI.Levels.Comparers; using GeometryDashAPI.Levels.GameObjects.Specific; namespace GeometryDashAPI.Levels @@ -10,24 +8,27 @@ public static class LevelLength { public static TimeSpan Measure(Level level) { - var x = level.Blocks.Max(x => x.PositionX); + var maxX = level.Blocks.Max(x => x.PositionX); var portals = GetSpeedBlocks(level); - double seconds = 0; + var seconds = 0d; var i = 1; - for (; i < portals.Count && portals.ElementAt(i).PositionX < x; i++) - seconds += (portals.ElementAt(i).PositionX - portals.ElementAt(i - 1).PositionX) / portals.ElementAt(i - 1).SpeedType.GetSpeed(); + for (; i < portals.Length; i++) + seconds += (portals[i].PositionX - portals[i - 1].PositionX) / portals[i - 1].SpeedType.GetSpeed(); - var final = Math.Min(i, portals.Count - 1); - var total = seconds + (x - portals.ElementAt(final).PositionX) / portals.ElementAt(final).SpeedType.GetSpeed(); + var final = Math.Min(i, portals.Length - 1); + var total = seconds + (maxX - portals[final].PositionX) / portals[final].SpeedType.GetSpeed(); return TimeSpan.FromSeconds(total); } - private static SortedSet GetSpeedBlocks(Level level) + private static SpeedBlock[] GetSpeedBlocks(Level level) { - var speedPortals = new SpeedBlock[] { new SpeedBlock(level.Options.PlayerSpeed) }.Concat(level.Blocks.OfType().Where(x => x.Checked).OrderBy(x => x.PositionX)); - return new SortedSet(speedPortals, BlockPositionXComparer.Instance); + return new[] { new SpeedBlock(level.Options.PlayerSpeed) } + .Concat(level.Blocks.OfType() + .Where(x => x.Checked) + .OrderBy(x => x.PositionX) + ).ToArray(); } } }