From acdd08c96649b9162f27be19d9a85f3f0bb8b3db Mon Sep 17 00:00:00 2001 From: MBmasher Date: Sun, 8 Aug 2021 23:56:03 +1000 Subject: [PATCH 01/92] Add Flashlight skill --- .../Difficulty/OsuDifficultyAttributes.cs | 1 + .../Difficulty/OsuDifficultyCalculator.cs | 5 +- .../Difficulty/OsuPerformanceCalculator.cs | 51 ++++++++++---- .../Difficulty/Skills/Flashlight.cs | 67 +++++++++++++++++++ .../Difficulty/Skills/OsuStrainSkill.cs | 2 + 5 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 141138c125..1e870dac68 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public double AimStrain { get; set; } public double SpeedStrain { get; set; } + public double FlashlightStrain { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } public int HitCircleCount { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index e47f82fb39..b0dd4dc9b0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double flashlightRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; HitWindows hitWindows = new OsuHitWindows(); @@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, + FlashlightStrain = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo, @@ -82,7 +84,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[] { new Aim(mods), - new Speed(mods) + new Speed(mods), + new Flashlight(mods) }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index e6ab978dfb..d409eae9af 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -52,11 +52,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimValue = computeAimValue(); double speedValue = computeSpeedValue(); double accuracyValue = computeAccuracyValue(); + double flashlightValue = computeFlashlightValue(); double totalValue = Math.Pow( Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1) + - Math.Pow(accuracyValue, 1.1), 1.0 / 1.1 + Math.Pow(accuracyValue, 1.1) + + Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; if (categoryRatings != null) @@ -64,6 +66,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty categoryRatings.Add("Aim", aimValue); categoryRatings.Add("Speed", speedValue); categoryRatings.Add("Accuracy", accuracyValue); + categoryRatings.Add("Flashlight", flashlightValue); categoryRatings.Add("OD", Attributes.OverallDifficulty); categoryRatings.Add("AR", Attributes.ApproachRate); categoryRatings.Add("Max Combo", Attributes.MaxCombo); @@ -109,19 +112,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModHidden)) aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - double flashlightBonus = 1.0; - - if (mods.Any(h => h is OsuModFlashlight)) - { - // Apply object-based bonus for flashlight. - flashlightBonus = 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 - ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) + - (totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0) - : 0.0); - } - - aimValue *= Math.Max(flashlightBonus, approachRateBonus); + aimValue *= approachRateBonus; // Scale the aim value with accuracy _slightly_ aimValue *= 0.5 + accuracy / 2.0; @@ -197,6 +188,38 @@ namespace osu.Game.Rulesets.Osu.Difficulty return accuracyValue; } + private double computeFlashlightValue() + { + double flashlightValue = 0.0; + + if (mods.Any(h => h is OsuModFlashlight)) { + flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; + + // Add an additional bonus for HDFL. + if (mods.Any(h => h is OsuModHidden)) + flashlightValue *= 1.2; + + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. + if (countMiss > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + + // Combo scaling + if (Attributes.MaxCombo > 0) + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + + // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. + flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); + + // Scale the aim value with accuracy _slightly_ + flashlightValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that + flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; + } + + return flashlightValue; + } + private int totalHits => countGreat + countOk + countMeh + countMiss; private int totalSuccessfulHits => countGreat + countOk + countMeh; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs new file mode 100644 index 0000000000..b48e6e30c0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -0,0 +1,67 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Difficulty.Skills +{ + /// + /// Represents the skill required to memorise and hit every object in a map with the Flashlight mod enabled. + /// + public class Flashlight : OsuStrainSkill + { + public Flashlight(Mod[] mods) + : base(mods) + { + } + + protected override double SkillMultiplier => 0.065; + protected override double StrainDecayBase => 0.15; + protected override double DecayWeight => 1.0; + + protected override double StrainValueOf(DifficultyHitObject current) + { + if (current.BaseObject is Spinner) + return 0; + + var osuCurrent = (OsuDifficultyHitObject)current; + var osuHitObject = (OsuHitObject)(osuCurrent.BaseObject); + + double scalingFactor = 52.0 / osuHitObject.Radius; + double smallDistNerf = 1.0; + + double result = 0.0; + + if (Previous.Count > 0) + { + double cumulativeStrainTime = 0.0; + + for (int i = 0; i < Previous.Count; i++) { + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + + if (!(osuPrevious.BaseObject is Spinner)) { + double JumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + + cumulativeStrainTime += osuPrevious.StrainTime; + + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0 && JumpDistance < 50.0) { + smallDistNerf = JumpDistance / 50.0; + } + + result += Math.Pow(0.8, i) * scalingFactor * JumpDistance / cumulativeStrainTime; + } + } + } + + return Math.Pow(smallDistNerf * result, 2.5); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..f74298cdca 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. + protected OsuStrainSkill(Mod[] mods) : base(mods) { From 6b1a4a53d44d980c28b510996269fe9f3742f270 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 9 Aug 2021 08:31:28 +1000 Subject: [PATCH 02/92] Cleanup of code --- .../Difficulty/OsuPerformanceCalculator.cs | 6 +++--- .../Difficulty/Skills/Flashlight.cs | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d409eae9af..3634374f50 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Math.Pow( Math.Pow(aimValue, 1.1) + Math.Pow(speedValue, 1.1) + - Math.Pow(accuracyValue, 1.1) + + Math.Pow(accuracyValue, 1.1) + Math.Pow(flashlightValue, 1.1), 1.0 / 1.1 ) * multiplier; @@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModFlashlight)) { flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; - + // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) flashlightValue *= 1.2; @@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Combo scaling if (Attributes.MaxCombo > 0) - flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b48e6e30c0..fd771ab768 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -2,12 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; -using osuTK; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -45,18 +43,18 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = 0; i < Previous.Count; i++) { var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - + if (!(osuPrevious.BaseObject is Spinner)) { - double JumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && JumpDistance < 50.0) { - smallDistNerf = JumpDistance / 50.0; + if (i == 0 && jumpDistance < 50.0) { + smallDistNerf = jumpDistance / 50.0; } - result += Math.Pow(0.8, i) * scalingFactor * JumpDistance / cumulativeStrainTime; + result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; } } } From f4ceb170642f802a2420b7e39d9b6887292ad270 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 10 Aug 2021 16:06:20 +1000 Subject: [PATCH 03/92] Cleanup of code --- .../Difficulty/OsuPerformanceCalculator.cs | 3 ++- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 3634374f50..267e332372 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -192,7 +192,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double flashlightValue = 0.0; - if (mods.Any(h => h is OsuModFlashlight)) { + if (mods.Any(h => h is OsuModFlashlight)) + { flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; // Add an additional bonus for HDFL. diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index fd771ab768..b8a96b3310 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -40,19 +40,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { double cumulativeStrainTime = 0.0; - for (int i = 0; i < Previous.Count; i++) { + for (int i = 0; i < Previous.Count; i++) + { var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - if (!(osuPrevious.BaseObject is Spinner)) { + if (!(osuPrevious.BaseObject is Spinner)) + { double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && jumpDistance < 50.0) { + if (i == 0 && jumpDistance < 50.0) smallDistNerf = jumpDistance / 50.0; - } result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; } From cee69eaad0c50c35031e74c59b99db52793e926c Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 06:14:38 +1000 Subject: [PATCH 04/92] Add a nerf to FL for TD plays --- .../Difficulty/OsuPerformanceCalculator.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 267e332372..2d4e4cf551 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -194,7 +194,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(h => h is OsuModFlashlight)) { - flashlightValue = Math.Pow(Attributes.FlashlightStrain, 2.0) * 25.0; + double rawFlashlight = Attributes.FlashlightStrain; + + if (mods.Any(m => m is OsuModTouchDevice)) + rawFlashlight = Math.Pow(rawFlashlight, 0.8); + + flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) From b1d25346a2e646200c759f1763f6b1da7367bfa9 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 13:30:40 +1000 Subject: [PATCH 05/92] Move HistoryLength override from OsuStrainSkill to Flashlight --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 1 + osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index b8a96b3310..dc60ab4041 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 0.065; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; + protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. protected override double StrainValueOf(DifficultyHitObject current) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index f74298cdca..e47edc37cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// protected virtual double DifficultyMultiplier => 1.06; - protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. - protected OsuStrainSkill(Mod[] mods) : base(mods) { From 1cadcb43d958d3e00e4941eff674b321b392db43 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 15:54:30 +1000 Subject: [PATCH 06/92] Apply nerf to Flashlight skill on high star maps --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index dc60ab4041..d225486cc8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 0.065; + protected override double SkillMultiplier => 0.13; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } } - return Math.Pow(smallDistNerf * result, 2.5); + return Math.Pow(smallDistNerf * result, 2.0); } } } From 27918583e1143a2b8e717d9824998c0a28848ec4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 11 Aug 2021 15:55:13 +1000 Subject: [PATCH 07/92] Increase the multiplier when hidden is applied on the Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2d4e4cf551..2ca5145c6a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -203,7 +203,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // Add an additional bonus for HDFL. if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.2; + flashlightValue *= 1.3; // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. if (countMiss > 0) From b06226e7385cbae56dce1729594003f5a5502cd4 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 12 Aug 2021 09:54:25 +1000 Subject: [PATCH 08/92] Change comments --- .../Difficulty/OsuPerformanceCalculator.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2ca5145c6a..2c8ee93819 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); // Custom multipliers for NoFail and SpunOut. - double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things + double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things. if (mods.Any(m => m is OsuModNoFail)) multiplier *= Math.Max(0.90, 1.0 - 0.02 * countMiss); @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0; - // Longer maps are worth more + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -114,9 +114,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty aimValue *= approachRateBonus; - // Scale the aim value with accuracy _slightly_ + // Scale the aim value with accuracy _slightly_. aimValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that + // It is important to also consider accuracy difficulty when doing that. aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; return aimValue; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; - // Longer maps are worth more + // Longer maps are worth more. double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); speedValue *= lengthBonus; @@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (mods.Any(m => m is OsuModHidden)) speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); - // Scale the speed value with accuracy and OD + // Scale the speed value with accuracy and OD. speedValue *= (0.95 + Math.Pow(Attributes.OverallDifficulty, 2) / 750) * Math.Pow(accuracy, (14.5 - Math.Max(Attributes.OverallDifficulty, 8)) / 2); // Scale the speed value with # of 50s to punish doubletapping. speedValue *= Math.Pow(0.98, countMeh < totalHits / 500.0 ? 0 : countMeh - totalHits / 500.0); @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeAccuracyValue() { - // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window. double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount; @@ -169,15 +169,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty else betterAccuracyPercentage = 0; - // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points. if (betterAccuracyPercentage < 0) betterAccuracyPercentage = 0; // Lots of arbitrary values from testing. - // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution + // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution. double accuracyValue = Math.Pow(1.52163, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83; - // Bonus for many hitcircles - it's harder to keep good accuracy up for longer + // Bonus for many hitcircles - it's harder to keep good accuracy up for longer. accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3)); if (mods.Any(m => m is OsuModHidden)) @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (countMiss > 0) flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling + // Combo scaling. if (Attributes.MaxCombo > 0) flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); @@ -217,9 +217,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); - // Scale the aim value with accuracy _slightly_ + // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that + // It is important to also consider accuracy difficulty when doing that. flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; } From eaa0d383158facbf8b779b17010ba9c8b0fcd00b Mon Sep 17 00:00:00 2001 From: MBmasher Date: Thu, 12 Aug 2021 10:00:24 +1000 Subject: [PATCH 09/92] Add a buff to short maps for Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 2c8ee93819..ad7376a044 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -214,8 +214,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. - flashlightValue *= 0.5 + 0.15 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 ? 0.35 * Math.Min(1.0, (totalHits - 200) / 600.0) : 0.0); + flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); // Scale the flashlight value with accuracy _slightly_. flashlightValue *= 0.5 + accuracy / 2.0; From 7188a3268fbdfc24fb66b8aa6783ca151833c72d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 24 Aug 2021 14:01:54 +1000 Subject: [PATCH 10/92] Apply a nerf to stacks for Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index d225486cc8..f048142b56 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - protected override double SkillMultiplier => 0.13; + protected override double SkillMultiplier => 0.15; protected override double StrainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -53,10 +53,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills cumulativeStrainTime += osuPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0 && jumpDistance < 50.0) - smallDistNerf = jumpDistance / 50.0; + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 50.0); - result += Math.Pow(0.8, i) * scalingFactor * jumpDistance / cumulativeStrainTime; + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, osuPrevious.JumpDistance * scalingFactor / 50.0); + + result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } } From c91feb29684c3a80b9af50be9ba63a53f3754b91 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 25 Aug 2021 11:18:21 +1000 Subject: [PATCH 11/92] Fix multiplying instead of dividing by scalingFactor --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index f048142b56..f6760235b4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We want to nerf objects that can be easily seen within the Flashlight circle radius. if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 50.0); + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, osuPrevious.JumpDistance * scalingFactor / 50.0); + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } From 738ce0f6894953445cd9287ff00fbdde8a0454c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Sep 2021 19:34:57 +0900 Subject: [PATCH 12/92] Fix repeat arrows being hidden beneath head circles in legacy skins Aims to make minimal changes to `DrawableSlider` itself. I'm not super happy about the slider ball being moved above the head circle, but it *is* what people are used to so no one except for me is going to complain. Supersedes and closes https://github.com/ppy/osu/pull/14561. --- .../Objects/Drawables/DrawableSlider.cs | 11 ++++- .../Objects/Drawables/DrawableSliderHead.cs | 2 +- .../Objects/Drawables/DrawableSliderRepeat.cs | 2 +- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 40 +++++++---------- .../Skinning/Legacy/LegacyReverseArrow.cs | 44 +++++++++++++++++++ .../Legacy/LegacySliderHeadHitCircle.cs | 30 +++++++++++++ .../Legacy/OsuLegacySkinTransformer.cs | 8 +++- 7 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 0bec33bf77..0e1d1043e3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -32,6 +32,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public SliderBall Ball { get; private set; } public SkinnableDrawable Body { get; private set; } + /// + /// A target container which can be used to add top level elements to the slider's display. + /// Intended to be used for proxy purposes only. + /// + public Container OverlayElementContainer { get; private set; } + public override bool DisplayResult => !HitObject.OnlyJudgeNestedObjects; [CanBeNull] @@ -65,6 +71,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tailContainer = new Container { RelativeSizeAxes = Axes.Both }, tickContainer = new Container { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, + headContainer = new Container { RelativeSizeAxes = Axes.Both }, + OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, }, Ball = new SliderBall(this) { GetInitialHitAction = () => HeadCircle.HitAction, @@ -72,7 +80,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - headContainer = new Container { RelativeSizeAxes = Axes.Both }, slidingSample = new PausableSkinnableSound { Looping = true } }; @@ -179,6 +186,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tailContainer.Clear(false); repeatContainer.Clear(false); tickContainer.Clear(false); + + OverlayElementContainer.Clear(); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 01c0d988ee..2b026e6840 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [CanBeNull] public Slider Slider => DrawableSlider?.HitObject; - protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; + public DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; public override bool DisplayResult => HitObject?.JudgeAsNormalHitCircle ?? base.DisplayResult; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 4a2a18ffd6..673211ac6c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [CanBeNull] public Slider Slider => DrawableSlider?.HitObject; - protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; + public DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; private double animDuration; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index 7a210324d7..3afd814174 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Container circleSprites; private Drawable hitCircleSprite; - private Drawable hitCircleOverlay; + + protected Drawable HitCircleOverlay { get; private set; } private SkinnableSpriteText hitCircleText; @@ -70,28 +70,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin). Texture overlayTexture = getTextureWithFallback("overlay"); - InternalChildren = new Drawable[] + InternalChildren = new[] { - circleSprites = new Container + hitCircleSprite = new KiaiFlashingSprite { + Texture = baseTexture, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + HitCircleOverlay = new KiaiFlashingSprite + { + Texture = overlayTexture, Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - hitCircleSprite = new KiaiFlashingSprite - { - Texture = baseTexture, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - hitCircleOverlay = new KiaiFlashingSprite - { - Texture = overlayTexture, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - } }, }; @@ -111,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; if (overlayAboveNumber) - AddInternal(hitCircleOverlay.CreateProxy()); + ChangeInternalChildDepth(HitCircleOverlay, float.MinValue); accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable); @@ -153,8 +144,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (state) { case ArmedState.Hit: - circleSprites.FadeOut(legacy_fade_duration, Easing.Out); - circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out); + hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + + HitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out); + HitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); if (hasNumber) { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs new file mode 100644 index 0000000000..b6956693b6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacyReverseArrow : CompositeDrawable + { + private ISkin skin { get; set; } + + [Resolved(canBeNull: true)] + private DrawableHitObject drawableHitObject { get; set; } + + public LegacyReverseArrow(ISkin skin) + { + this.skin = skin; + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName; + + InternalChild = skin.GetAnimation(lookupName, true, true) ?? Drawable.Empty(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // see logic in LegacySliderHeadHitCircle. + (drawableHitObject as DrawableSliderRepeat)?.DrawableSlider + .OverlayElementContainer.Add(CreateProxy()); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs new file mode 100644 index 0000000000..83ebdafa50 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacySliderHeadHitCircle : LegacyMainCirclePiece + { + [Resolved(canBeNull: true)] + private DrawableHitObject drawableHitObject { get; set; } + + public LegacySliderHeadHitCircle() + : base("sliderstartcircle") + { + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + // see logic in LegacyReverseArrow. + (drawableHitObject as DrawableSliderHead)?.DrawableSlider + .OverlayElementContainer.Add(HitCircleOverlay.CreateProxy().With(d => d.Depth = float.MinValue)); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 41b0a88f11..8df8001d42 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -67,7 +67,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case OsuSkinComponents.SliderHeadHitCircle: if (hasHitCircle.Value) - return new LegacyMainCirclePiece("sliderstartcircle"); + return new LegacySliderHeadHitCircle(); + + return null; + + case OsuSkinComponents.ReverseArrow: + if (hasHitCircle.Value) + return new LegacyReverseArrow(this); return null; From bf0150bab4670d05e3112e24db50d29ac1c814ce Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Thu, 9 Sep 2021 16:21:51 -0400 Subject: [PATCH 13/92] Clear UR bar display on keyboard input --- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 89f61785e8..5998c1b494 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -10,7 +10,9 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; using osu.Game.Graphics; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osuTK; @@ -18,7 +20,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public class BarHitErrorMeter : HitErrorMeter + public class BarHitErrorMeter : HitErrorMeter, IKeyBindingHandler { private const int arrow_move_duration = 400; @@ -279,5 +281,22 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters this.FadeTo(0.8f, 150).Then().FadeOut(judgement_fade_duration).Expire(); } } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.SeekReplayBackward: + case GlobalAction.SeekReplayForward: + judgementsContainer.Clear(true); + return false; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } } From bde092f816c3e9570d54e7484baca3877ef0a7d1 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Thu, 9 Sep 2021 20:08:16 -0400 Subject: [PATCH 14/92] Clear UR bar display on seek with mouse --- .../Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 12 +++++++++++- osu.Game/Screens/Play/SongProgress.cs | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 5998c1b494..2854fabbae 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; +using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Judgements; @@ -143,6 +144,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters arrow.Alpha = 0; arrow.Delay(200).FadeInFromZero(600); + + var progressBar = Parent.ChildrenOfType().FirstOrDefault(); + if (progressBar != null) + progressBar.Bar.OnSeek += _ => handleSeek(); } private void createColourBars(OsuColour colours) @@ -282,13 +287,18 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } + private void handleSeek() + { + judgementsContainer.Clear(true); + } + public bool OnPressed(GlobalAction action) { switch (action) { case GlobalAction.SeekReplayBackward: case GlobalAction.SeekReplayForward: - judgementsContainer.Clear(true); + handleSeek(); return false; } diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index b27a9c5f5d..dff2dcc86b 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -35,6 +35,8 @@ namespace osu.Game.Screens.Play private readonly SongProgressGraph graph; private readonly SongProgressInfo info; + public SongProgressBar Bar => bar; + public Action RequestSeek; /// From 0dc31a476f9ccf0eafdf7c1f16fe96da287977bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 13 Sep 2021 16:39:05 +0900 Subject: [PATCH 15/92] Invert condition to reduce nesting --- .../Difficulty/OsuPerformanceCalculator.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index ad7376a044..f9a3423eab 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -190,38 +190,36 @@ namespace osu.Game.Rulesets.Osu.Difficulty private double computeFlashlightValue() { - double flashlightValue = 0.0; + if (!mods.Any(h => h is OsuModFlashlight)) + return 0.0; - if (mods.Any(h => h is OsuModFlashlight)) - { - double rawFlashlight = Attributes.FlashlightStrain; + double rawFlashlight = Attributes.FlashlightStrain; - if (mods.Any(m => m is OsuModTouchDevice)) - rawFlashlight = Math.Pow(rawFlashlight, 0.8); + if (mods.Any(m => m is OsuModTouchDevice)) + rawFlashlight = Math.Pow(rawFlashlight, 0.8); - flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; + double flashlightValue = Math.Pow(rawFlashlight, 2.0) * 25.0; - // Add an additional bonus for HDFL. - if (mods.Any(h => h is OsuModHidden)) - flashlightValue *= 1.3; + // Add an additional bonus for HDFL. + if (mods.Any(h => h is OsuModHidden)) + flashlightValue *= 1.3; - // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. - if (countMiss > 0) - flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); + // Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses. + if (countMiss > 0) + flashlightValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875)); - // Combo scaling. - if (Attributes.MaxCombo > 0) - flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); + // Combo scaling. + if (Attributes.MaxCombo > 0) + flashlightValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); - // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. - flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + - (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); + // Account for shorter maps having a higher ratio of 0 combo/100 combo flashlight radius. + flashlightValue *= 0.7 + 0.1 * Math.Min(1.0, totalHits / 200.0) + + (totalHits > 200 ? 0.2 * Math.Min(1.0, (totalHits - 200) / 200.0) : 0.0); - // Scale the flashlight value with accuracy _slightly_. - flashlightValue *= 0.5 + accuracy / 2.0; - // It is important to also consider accuracy difficulty when doing that. - flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; - } + // Scale the flashlight value with accuracy _slightly_. + flashlightValue *= 0.5 + accuracy / 2.0; + // It is important to also consider accuracy difficulty when doing that. + flashlightValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; return flashlightValue; } From 1a60ce164e31c5cf3706e25d4b7e42aa70f359da Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 24 Aug 2021 00:15:16 +0200 Subject: [PATCH 16/92] Add `ParticleJet` --- .../Visual/Gameplay/TestSceneParticleJet.cs | 61 +++++++ osu.Game/Graphics/Particles/ParticleJet.cs | 49 +++++ osu.Game/Graphics/Particles/ParticleSpewer.cs | 172 ++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs create mode 100644 osu.Game/Graphics/Particles/ParticleJet.cs create mode 100644 osu.Game/Graphics/Particles/ParticleSpewer.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs new file mode 100644 index 0000000000..6438ba0b22 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs @@ -0,0 +1,61 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.Particles; +using osu.Game.Skinning; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [TestFixture] + public class TestSceneParticleJet : OsuTestScene + { + private ParticleJet jet; + + [Resolved] + private SkinManager skinManager { get; set; } + + public TestSceneParticleJet() + { + AddStep("create", () => + { + Child = jet = createJet(); + }); + + AddToggleStep("toggle spawning", value => jet.Active = value); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create jet", () => Child = jet = createJet()); + } + + [Test] + public void TestPresence() + { + AddStep("start jet", () => jet.Active = true); + AddAssert("is present", () => jet.IsPresent); + + AddWaitStep("wait for some particles", 3); + AddStep("stop jet", () => jet.Active = false); + + AddWaitStep("wait for clean screen", 5); + AddAssert("is not present", () => !jet.IsPresent); + } + + private ParticleJet createJet() + { + return new ParticleJet(skinManager.DefaultLegacySkin.GetTexture("star2"), 180) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativePositionAxes = Axes.Y, + Y = -0.1f, + }; + } + } +} diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs new file mode 100644 index 0000000000..039dd36ddc --- /dev/null +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; +using osuTK; + +namespace osu.Game.Graphics.Particles +{ + public class ParticleJet : ParticleSpewer + { + private const int particles_per_second = 80; + private const double particle_lifetime = 500; + private const float angular_velocity = 3f; + private const int angle_spread = 10; + private const float velocity_min = 1.3f; + private const float velocity_max = 1.5f; + + private readonly int angle; + + protected override float ParticleGravity => 0.25f; + + public ParticleJet(Texture texture, int angle) + : base(texture, particles_per_second, particle_lifetime) + { + this.angle = angle; + } + + protected override FallingParticle SpawnParticle() + { + var directionRads = MathUtils.DegreesToRadians( + RNG.NextSingle(angle - angle_spread / 2, angle + angle_spread / 2) + ); + var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); + + return new FallingParticle + { + StartTime = (float)Time.Current, + Position = OriginPosition, + Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime), + Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)), + AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity), + StartScale = 1f, + EndScale = 2f, + }; + } + } +} diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs new file mode 100644 index 0000000000..7196727ca1 --- /dev/null +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -0,0 +1,172 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osuTK; + +namespace osu.Game.Graphics.Particles +{ + public abstract class ParticleSpewer : Sprite + { + private readonly FallingParticle[] particles; + private int currentIndex; + private double lastParticleAdded; + + private readonly double cooldown; + private readonly double maxLifetime; + + /// + /// Determines whether particles are being spawned. + /// + public bool Active { get; set; } + + public bool HasActiveParticles => Active || (lastParticleAdded + maxLifetime) > Time.Current; + public override bool IsPresent => base.IsPresent && HasActiveParticles; + + protected virtual float ParticleGravity => 0.5f; + + protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) + { + Texture = texture; + Blending = BlendingParameters.Additive; + + particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; + + cooldown = 1000f / perSecond; + this.maxLifetime = maxLifetime; + } + + protected override void Update() + { + base.Update(); + + if (Active && Time.Current > lastParticleAdded + cooldown) + { + addParticle(SpawnParticle()); + } + + Invalidate(Invalidation.DrawNode); + } + + /// + /// Called each time a new particle should be spawned. + /// + protected abstract FallingParticle SpawnParticle(); + + private void addParticle(FallingParticle fallingParticle) + { + particles[currentIndex] = fallingParticle; + + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; + } + + protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); + + private class ParticleSpewerDrawNode : SpriteDrawNode + { + private readonly FallingParticle[] particles; + + protected new ParticleSpewer Source => (ParticleSpewer)base.Source; + + private float currentTime; + private float gravity; + + public ParticleSpewerDrawNode(Sprite source) + : base(source) + { + particles = new FallingParticle[Source.particles.Length]; + } + + public override void ApplyState() + { + base.ApplyState(); + + Source.particles.CopyTo(particles, 0); + + currentTime = (float)Source.Time.Current; + gravity = Source.ParticleGravity; + } + + protected override void Blit(Action vertexAction) + { + foreach (var p in particles) + { + // ignore particles that weren't initialized. + if (p.StartTime <= 0) continue; + + var timeSinceStart = currentTime - p.StartTime; + + var alpha = p.AlphaAtTime(timeSinceStart); + if (alpha <= 0) continue; + + var scale = p.ScaleAtTime(timeSinceStart); + var pos = p.PositionAtTime(timeSinceStart, gravity); + var angle = p.AngleAtTime(timeSinceStart); + + var rect = new RectangleF( + pos.X - Texture.DisplayWidth * scale / 2, + pos.Y - Texture.DisplayHeight * scale / 2, + Texture.DisplayWidth * scale, + Texture.DisplayHeight * scale); + + var quad = new Quad( + transformPosition(rect.TopLeft, rect.Centre, angle), + transformPosition(rect.TopRight, rect.Centre, angle), + transformPosition(rect.BottomLeft, rect.Centre, angle), + transformPosition(rect.BottomRight, rect.Centre, angle) + ); + + DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction, + new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height), + null, TextureCoords); + } + } + + private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) + { + // rotate point around centre. + float cos = MathF.Cos(angle); + float sin = MathF.Sin(angle); + + float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; + float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; + + // convert to screen space. + return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); + } + } + + protected struct FallingParticle + { + public float StartTime; + public Vector2 Position; + public Vector2 Velocity; + public float Duration; + public float AngularVelocity; + public float StartScale; + public float EndScale; + + public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); + + public float ScaleAtTime(float timeSinceStart) => StartScale + (EndScale - StartScale) * progressAtTime(timeSinceStart); + + public float AngleAtTime(float timeSinceStart) => AngularVelocity / 1000 * timeSinceStart; + + public Vector2 PositionAtTime(float timeSinceStart, float gravity) + { + var progress = progressAtTime(timeSinceStart); + var grav = new Vector2(0, -gravity) * progress; + + return Position + (Velocity - grav) * timeSinceStart; + } + + private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); + } + } +} From 714cf33aac1b94ce27d6411b4f0efdc3cab92c8b Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 29 Aug 2021 21:41:47 +0200 Subject: [PATCH 17/92] Change `ParticleSpewer` to use screen space --- osu.Game/Graphics/Particles/ParticleJet.cs | 19 ++++--- osu.Game/Graphics/Particles/ParticleSpewer.cs | 51 +++++++++++++------ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 039dd36ddc..eb7a49abc3 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -29,21 +29,20 @@ namespace osu.Game.Graphics.Particles protected override FallingParticle SpawnParticle() { + var p = base.SpawnParticle(); + var directionRads = MathUtils.DegreesToRadians( RNG.NextSingle(angle - angle_spread / 2, angle + angle_spread / 2) ); var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); - return new FallingParticle - { - StartTime = (float)Time.Current, - Position = OriginPosition, - Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime), - Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)), - AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity), - StartScale = 1f, - EndScale = 2f, - }; + p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); + p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); + p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); + p.StartScale = 1f; + p.EndScale = 2f; + + return p; } } } diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 7196727ca1..f2c358bd96 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -45,6 +45,10 @@ namespace osu.Game.Graphics.Particles { base.Update(); + // reset cooldown if the clock was rewound. + // this can happen when seeking in replays. + if (lastParticleAdded > Time.Current) lastParticleAdded = 0; + if (Active && Time.Current > lastParticleAdded + cooldown) { addParticle(SpawnParticle()); @@ -56,7 +60,14 @@ namespace osu.Game.Graphics.Particles /// /// Called each time a new particle should be spawned. /// - protected abstract FallingParticle SpawnParticle(); + protected virtual FallingParticle SpawnParticle() + { + return new FallingParticle + { + StartTime = (float)Time.Current, + StartPosition = ToScreenSpace(OriginPosition), + }; + } private void addParticle(FallingParticle fallingParticle) { @@ -68,6 +79,8 @@ namespace osu.Game.Graphics.Particles protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); + # region DrawNode + private class ParticleSpewerDrawNode : SpriteDrawNode { private readonly FallingParticle[] particles; @@ -102,6 +115,10 @@ namespace osu.Game.Graphics.Particles var timeSinceStart = currentTime - p.StartTime; + // ignore particles from the future. + // these can appear when seeking in replays. + if (timeSinceStart < 0) continue; + var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; @@ -109,17 +126,21 @@ namespace osu.Game.Graphics.Particles var pos = p.PositionAtTime(timeSinceStart, gravity); var angle = p.AngleAtTime(timeSinceStart); + var matrixScale = DrawInfo.Matrix.ExtractScale(); + var width = Texture.DisplayWidth * scale * matrixScale.X; + var height = Texture.DisplayHeight * scale * matrixScale.Y; + var rect = new RectangleF( - pos.X - Texture.DisplayWidth * scale / 2, - pos.Y - Texture.DisplayHeight * scale / 2, - Texture.DisplayWidth * scale, - Texture.DisplayHeight * scale); + pos.X - width / 2, + pos.Y - height / 2, + width, + height); var quad = new Quad( - transformPosition(rect.TopLeft, rect.Centre, angle), - transformPosition(rect.TopRight, rect.Centre, angle), - transformPosition(rect.BottomLeft, rect.Centre, angle), - transformPosition(rect.BottomRight, rect.Centre, angle) + rotatePosition(rect.TopLeft, rect.Centre, angle), + rotatePosition(rect.TopRight, rect.Centre, angle), + rotatePosition(rect.BottomLeft, rect.Centre, angle), + rotatePosition(rect.BottomRight, rect.Centre, angle) ); DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction, @@ -128,24 +149,24 @@ namespace osu.Game.Graphics.Particles } } - private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) + private Vector2 rotatePosition(Vector2 pos, Vector2 centre, float angle) { - // rotate point around centre. float cos = MathF.Cos(angle); float sin = MathF.Sin(angle); float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; - // convert to screen space. - return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); + return new Vector2(x, y); } } + #endregion + protected struct FallingParticle { public float StartTime; - public Vector2 Position; + public Vector2 StartPosition; public Vector2 Velocity; public float Duration; public float AngularVelocity; @@ -163,7 +184,7 @@ namespace osu.Game.Graphics.Particles var progress = progressAtTime(timeSinceStart); var grav = new Vector2(0, -gravity) * progress; - return Position + (Velocity - grav) * timeSinceStart; + return StartPosition + (Velocity - grav) * timeSinceStart; } private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); From 4c753420d3859df95a3eed7751df08b0f83a6747 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 4 Sep 2021 16:47:12 +0200 Subject: [PATCH 18/92] Fix `ParticleSpewer` gravity calculation --- osu.Game/Graphics/Particles/ParticleJet.cs | 6 +++--- osu.Game/Graphics/Particles/ParticleSpewer.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index eb7a49abc3..6bdde44a2c 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -14,12 +14,12 @@ namespace osu.Game.Graphics.Particles private const double particle_lifetime = 500; private const float angular_velocity = 3f; private const int angle_spread = 10; - private const float velocity_min = 1.3f; - private const float velocity_max = 1.5f; + private const float velocity_min = 1300f; + private const float velocity_max = 1500f; private readonly int angle; - protected override float ParticleGravity => 0.25f; + protected override float ParticleGravity => 750f; public ParticleJet(Texture texture, int angle) : base(texture, particles_per_second, particle_lifetime) diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index f2c358bd96..52e089fcca 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Graphics.Particles public bool HasActiveParticles => Active || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; - protected virtual float ParticleGravity => 0.5f; + protected virtual float ParticleGravity => 0; protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) { @@ -182,9 +182,9 @@ namespace osu.Game.Graphics.Particles public Vector2 PositionAtTime(float timeSinceStart, float gravity) { var progress = progressAtTime(timeSinceStart); - var grav = new Vector2(0, -gravity) * progress; + var currentGravity = new Vector2(0, gravity * Duration / 1000 * progress); - return StartPosition + (Velocity - grav) * timeSinceStart; + return StartPosition + (Velocity + currentGravity) * timeSinceStart / 1000; } private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); From 328c9a5dd038d9882ebd11d8f0ba068b16826252 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 4 Sep 2021 20:50:30 +0200 Subject: [PATCH 19/92] Change `ParticleSpewer.Active` to a Bindable --- osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs | 6 +++--- osu.Game/Graphics/Particles/ParticleSpewer.cs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs index 6438ba0b22..e570abcf88 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Gameplay Child = jet = createJet(); }); - AddToggleStep("toggle spawning", value => jet.Active = value); + AddToggleStep("toggle spawning", value => jet.Active.Value = value); } [SetUpSteps] @@ -37,11 +37,11 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestPresence() { - AddStep("start jet", () => jet.Active = true); + AddStep("start jet", () => jet.Active.Value = true); AddAssert("is present", () => jet.IsPresent); AddWaitStep("wait for some particles", 3); - AddStep("stop jet", () => jet.Active = false); + AddStep("stop jet", () => jet.Active.Value = false); AddWaitStep("wait for clean screen", 5); AddAssert("is not present", () => !jet.IsPresent); diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 52e089fcca..bc25206311 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -23,9 +24,9 @@ namespace osu.Game.Graphics.Particles /// /// Determines whether particles are being spawned. /// - public bool Active { get; set; } + public readonly BindableBool Active = new BindableBool(); - public bool HasActiveParticles => Active || (lastParticleAdded + maxLifetime) > Time.Current; + public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; protected virtual float ParticleGravity => 0; @@ -49,7 +50,7 @@ namespace osu.Game.Graphics.Particles // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && Time.Current > lastParticleAdded + cooldown) { addParticle(SpawnParticle()); } From ee4006f3d73389eddfcc382de288bb041d4f1989 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 4 Sep 2021 21:49:05 +0200 Subject: [PATCH 20/92] Add legacy cursor star particles --- .../Skinning/Legacy/LegacyCursor.cs | 5 + .../Legacy/LegacyCursorStarParticles.cs | 185 ++++++++++++++++++ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 1 + 3 files changed, 191 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index b2ffc171be..cd7954a8d6 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { + new LegacyCursorStarParticles + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, ExpandTarget = new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs new file mode 100644 index 0000000000..f10d9a0fa9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -0,0 +1,185 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Particles; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Play; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Skinning.Legacy +{ + public class LegacyCursorStarParticles : BeatSyncedContainer, IKeyBindingHandler + { + private StarParticleSpewer breakSpewer; + private StarParticleSpewer kiaiSpewer; + + [Resolved(canBeNull: true)] + private Player player { get; set; } + + [Resolved(canBeNull: true)] + private OsuPlayfield osuPlayfield { get; set; } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin, OsuColour colour) + { + var texture = skin.GetTexture("star2"); + + InternalChildren = new[] + { + breakSpewer = new StarParticleSpewer(texture, 20) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colour.PinkLighter, + Direction = SpewDirection.None, + Active = + { + Value = true, + } + }, + kiaiSpewer = new StarParticleSpewer(texture, 60) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colour.PinkLighter, + Direction = SpewDirection.None, + Active = + { + Value = false, + } + }, + }; + + if (player != null) + { + breakSpewer.Active.BindTarget = player.IsBreakTime; + } + } + + protected override void Update() + { + if (osuPlayfield == null) return; + + // find active kiai slider or spinner. + var kiaiHitObject = osuPlayfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => + h.HitObject.Kiai && + ( + (h is DrawableSlider slider && slider.Tracking.Value) || + (h is DrawableSpinner spinner && spinner.RotationTracker.Tracking) + ) + ); + + kiaiSpewer.Active.Value = kiaiHitObject != null; + } + + public bool OnPressed(OsuAction action) + { + handleInput(action, true); + return false; + } + + public void OnReleased(OsuAction action) + { + handleInput(action, false); + } + + private bool leftPressed; + private bool rightPressed; + + private void handleInput(OsuAction action, bool pressed) + { + switch (action) + { + case OsuAction.LeftButton: + leftPressed = pressed; + break; + + case OsuAction.RightButton: + rightPressed = pressed; + break; + } + + if (leftPressed && rightPressed) + breakSpewer.Direction = SpewDirection.Both; + else if (leftPressed) + breakSpewer.Direction = SpewDirection.Left; + else if (rightPressed) + breakSpewer.Direction = SpewDirection.Right; + else + breakSpewer.Direction = SpewDirection.None; + } + + private class StarParticleSpewer : ParticleSpewer + { + private const int particle_lifetime_min = 300; + private const int particle_lifetime_max = 1000; + + public SpewDirection Direction { get; set; } + + protected override float ParticleGravity => 460; + + public StarParticleSpewer(Texture texture, int perSecond) + : base(texture, perSecond, particle_lifetime_max) + { + } + + protected override FallingParticle SpawnParticle() + { + var p = base.SpawnParticle(); + + p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); + p.AngularVelocity = RNG.NextSingle(-3f, 3f); + p.StartScale = RNG.NextSingle(0.5f, 1f); + p.EndScale = RNG.NextSingle(2f); + + switch (Direction) + { + case SpewDirection.None: + p.Velocity = Vector2.Zero; + break; + + case SpewDirection.Left: + p.Velocity = new Vector2( + RNG.NextSingle(-460f, 0) * 2, + RNG.NextSingle(-40f, 40f) * 2 + ); + break; + + case SpewDirection.Right: + p.Velocity = new Vector2( + RNG.NextSingle(0, 460f) * 2, + RNG.NextSingle(-40f, 40f) * 2 + ); + break; + + case SpewDirection.Both: + p.Velocity = new Vector2( + RNG.NextSingle(-460f, 460f) * 2, + RNG.NextSingle(-160f, 160f) * 2 + ); + break; + } + + return p; + } + } + + private enum SpewDirection + { + None, + Left, + Right, + Both, + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index c1c2ea2299..2233a547b9 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -24,6 +24,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.UI { + [Cached] public class OsuPlayfield : Playfield { private readonly PlayfieldBorder playfieldBorder; From 5b1b36436f0ec4ccf95f9dbdf45c28f5a87c3fbc Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 01:01:49 +0200 Subject: [PATCH 21/92] Add cursor velocity to star particles --- .../Legacy/LegacyCursorStarParticles.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index f10d9a0fa9..52d4eedf42 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -131,6 +131,44 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public StarParticleSpewer(Texture texture, int perSecond) : base(texture, perSecond, particle_lifetime_max) { + Active.BindValueChanged(_ => resetVelocityCalculation()); + } + + private Vector2 screenPosition => ToScreenSpace(OriginPosition); + + private Vector2 screenVelocity; + + private const double velocity_calculation_delay = 15; + private double lastVelocityCalculation; + private Vector2 positionDifference; + private Vector2? lastPosition; + + protected override void Update() + { + base.Update(); + + if (lastPosition != null) + { + positionDifference += (screenPosition - lastPosition.Value); + lastVelocityCalculation += Clock.ElapsedFrameTime; + } + + lastPosition = screenPosition; + + if (lastVelocityCalculation > velocity_calculation_delay) + { + screenVelocity = positionDifference / (float)lastVelocityCalculation; + + positionDifference = Vector2.Zero; + lastVelocityCalculation = 0; + } + } + + private void resetVelocityCalculation() + { + positionDifference = Vector2.Zero; + lastVelocityCalculation = 0; + lastPosition = null; } protected override FallingParticle SpawnParticle() @@ -170,6 +208,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy break; } + p.Velocity += screenVelocity * 50; + return p; } } From db662f8c5c9ae2b7b35a0da4317e1e9ee94ab9dd Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 16:08:22 +0200 Subject: [PATCH 22/92] Add `ParticleParent` option to `ParticleSpewer` --- .../Legacy/LegacyCursorStarParticles.cs | 31 ++++++++++++------- osu.Game/Graphics/Particles/ParticleJet.cs | 1 + osu.Game/Graphics/Particles/ParticleSpewer.cs | 17 +++++++--- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index 52d4eedf42..58fe4f9b7c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Direction = SpewDirection.None, Active = { - Value = true, + Value = false, } }, kiaiSpewer = new StarParticleSpewer(texture, 60) @@ -64,6 +64,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { breakSpewer.Active.BindTarget = player.IsBreakTime; } + + if (osuPlayfield != null) + { + breakSpewer.ParticleParent = osuPlayfield; + kiaiSpewer.ParticleParent = osuPlayfield; + } } protected override void Update() @@ -126,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy public SpewDirection Direction { get; set; } - protected override float ParticleGravity => 460; + protected override float ParticleGravity => 240; public StarParticleSpewer(Texture texture, int perSecond) : base(texture, perSecond, particle_lifetime_max) @@ -134,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Active.BindValueChanged(_ => resetVelocityCalculation()); } - private Vector2 screenPosition => ToScreenSpace(OriginPosition); + private Vector2 positionInParent => ToSpaceOfOtherDrawable(OriginPosition, ParticleParent); private Vector2 screenVelocity; @@ -149,11 +155,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (lastPosition != null) { - positionDifference += (screenPosition - lastPosition.Value); + positionDifference += (positionInParent - lastPosition.Value); lastVelocityCalculation += Clock.ElapsedFrameTime; } - lastPosition = screenPosition; + lastPosition = positionInParent; if (lastVelocityCalculation > velocity_calculation_delay) { @@ -175,6 +181,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { var p = base.SpawnParticle(); + p.StartPosition = positionInParent; p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); p.AngularVelocity = RNG.NextSingle(-3f, 3f); p.StartScale = RNG.NextSingle(0.5f, 1f); @@ -188,27 +195,27 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy case SpewDirection.Left: p.Velocity = new Vector2( - RNG.NextSingle(-460f, 0) * 2, - RNG.NextSingle(-40f, 40f) * 2 + RNG.NextSingle(-460f, 0), + RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Right: p.Velocity = new Vector2( - RNG.NextSingle(0, 460f) * 2, - RNG.NextSingle(-40f, 40f) * 2 + RNG.NextSingle(0, 460f), + RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Both: p.Velocity = new Vector2( - RNG.NextSingle(-460f, 460f) * 2, - RNG.NextSingle(-160f, 160f) * 2 + RNG.NextSingle(-460f, 460f), + RNG.NextSingle(-160f, 160f) ); break; } - p.Velocity += screenVelocity * 50; + p.Velocity += screenVelocity * 40; return p; } diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 6bdde44a2c..763f8d0a9e 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -36,6 +36,7 @@ namespace osu.Game.Graphics.Particles ); var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); + p.StartPosition = OriginPosition; p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index bc25206311..2251d9590d 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -26,6 +26,12 @@ namespace osu.Game.Graphics.Particles /// public readonly BindableBool Active = new BindableBool(); + /// + /// whose DrawInfo will be used to draw each particle. + /// Defaults to the itself. + /// + public IDrawable ParticleParent; + public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; @@ -35,6 +41,7 @@ namespace osu.Game.Graphics.Particles { Texture = texture; Blending = BlendingParameters.Additive; + ParticleParent = this; particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; @@ -66,7 +73,6 @@ namespace osu.Game.Graphics.Particles return new FallingParticle { StartTime = (float)Time.Current, - StartPosition = ToScreenSpace(OriginPosition), }; } @@ -90,6 +96,7 @@ namespace osu.Game.Graphics.Particles private float currentTime; private float gravity; + private Matrix3 particleDrawMatrix; public ParticleSpewerDrawNode(Sprite source) : base(source) @@ -105,6 +112,7 @@ namespace osu.Game.Graphics.Particles currentTime = (float)Source.Time.Current; gravity = Source.ParticleGravity; + particleDrawMatrix = Source.ParticleParent.DrawInfo.Matrix; } protected override void Blit(Action vertexAction) @@ -127,9 +135,8 @@ namespace osu.Game.Graphics.Particles var pos = p.PositionAtTime(timeSinceStart, gravity); var angle = p.AngleAtTime(timeSinceStart); - var matrixScale = DrawInfo.Matrix.ExtractScale(); - var width = Texture.DisplayWidth * scale * matrixScale.X; - var height = Texture.DisplayHeight * scale * matrixScale.Y; + var width = Texture.DisplayWidth * scale; + var height = Texture.DisplayHeight * scale; var rect = new RectangleF( pos.X - width / 2, @@ -158,7 +165,7 @@ namespace osu.Game.Graphics.Particles float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; - return new Vector2(x, y); + return Vector2Extensions.Transform(new Vector2(x, y), particleDrawMatrix); } } From 6d68da8ff00229bfcbccd68f7b6d73c80c4da70d Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 16:25:24 +0200 Subject: [PATCH 23/92] Remove `StartScale` from `ParticleSpewer` particles --- .../Skinning/Legacy/LegacyCursorStarParticles.cs | 1 - osu.Game/Graphics/Particles/ParticleJet.cs | 1 - osu.Game/Graphics/Particles/ParticleSpewer.cs | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index 58fe4f9b7c..d6c4d9f92a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -184,7 +184,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy p.StartPosition = positionInParent; p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); p.AngularVelocity = RNG.NextSingle(-3f, 3f); - p.StartScale = RNG.NextSingle(0.5f, 1f); p.EndScale = RNG.NextSingle(2f); switch (Direction) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 763f8d0a9e..2d70a7f697 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -40,7 +40,6 @@ namespace osu.Game.Graphics.Particles p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); - p.StartScale = 1f; p.EndScale = 2f; return p; diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 2251d9590d..f9fb30abdc 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -178,12 +178,11 @@ namespace osu.Game.Graphics.Particles public Vector2 Velocity; public float Duration; public float AngularVelocity; - public float StartScale; public float EndScale; public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); - public float ScaleAtTime(float timeSinceStart) => StartScale + (EndScale - StartScale) * progressAtTime(timeSinceStart); + public float ScaleAtTime(float timeSinceStart) => 1 + (EndScale - 1) * progressAtTime(timeSinceStart); public float AngleAtTime(float timeSinceStart) => AngularVelocity / 1000 * timeSinceStart; From c2f7b01ca400b8286365877c3f363f247897c857 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 5 Sep 2021 17:08:00 +0200 Subject: [PATCH 24/92] Change particle `AngularVelocity` into `StartAngle` and `EndAngle` --- .../Skinning/Legacy/LegacyCursorStarParticles.cs | 3 ++- osu.Game/Graphics/Particles/ParticleJet.cs | 4 ++-- osu.Game/Graphics/Particles/ParticleSpewer.cs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index d6c4d9f92a..9c901a7de5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -183,7 +183,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy p.StartPosition = positionInParent; p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); - p.AngularVelocity = RNG.NextSingle(-3f, 3f); + p.StartAngle = (float)(RNG.NextDouble() * 4 - 2); + p.EndAngle = RNG.NextSingle(-2f, 2f); p.EndScale = RNG.NextSingle(2f); switch (Direction) diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs index 2d70a7f697..a76aa6b75f 100644 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ b/osu.Game/Graphics/Particles/ParticleJet.cs @@ -12,7 +12,6 @@ namespace osu.Game.Graphics.Particles { private const int particles_per_second = 80; private const double particle_lifetime = 500; - private const float angular_velocity = 3f; private const int angle_spread = 10; private const float velocity_min = 1300f; private const float velocity_max = 1500f; @@ -39,7 +38,8 @@ namespace osu.Game.Graphics.Particles p.StartPosition = OriginPosition; p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); - p.AngularVelocity = RNG.NextSingle(-angular_velocity, angular_velocity); + p.StartAngle = RNG.NextSingle(-2f, 2f); + p.EndAngle = RNG.NextSingle(-2f, 2f); p.EndScale = 2f; return p; diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index f9fb30abdc..3d2225d382 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -177,14 +177,15 @@ namespace osu.Game.Graphics.Particles public Vector2 StartPosition; public Vector2 Velocity; public float Duration; - public float AngularVelocity; + public float StartAngle; + public float EndAngle; public float EndScale; public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); public float ScaleAtTime(float timeSinceStart) => 1 + (EndScale - 1) * progressAtTime(timeSinceStart); - public float AngleAtTime(float timeSinceStart) => AngularVelocity / 1000 * timeSinceStart; + public float AngleAtTime(float timeSinceStart) => StartAngle + (EndAngle - StartAngle) * progressAtTime(timeSinceStart); public Vector2 PositionAtTime(float timeSinceStart, float gravity) { From 99eff4f41f5e5d126fc165fed86309e98ed05cfd Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Thu, 9 Sep 2021 23:18:19 +0200 Subject: [PATCH 25/92] Move cursor particles under `OsuCursorContainer` --- .../Skinning/Legacy/LegacyCursor.cs | 5 -- .../Legacy/LegacyCursorStarParticles.cs | 71 ++++++++++--------- .../UI/Cursor/OsuCursorContainer.cs | 11 ++- osu.Game/Graphics/Particles/ParticleSpewer.cs | 27 ++----- 4 files changed, 53 insertions(+), 61 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index cd7954a8d6..b2ffc171be 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -31,11 +31,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - new LegacyCursorStarParticles - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, ExpandTarget = new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs index 9c901a7de5..89ffa4ca15 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs @@ -4,11 +4,13 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; +using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Particles; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; @@ -18,7 +20,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorStarParticles : BeatSyncedContainer, IKeyBindingHandler + public class LegacyCursorStarParticles : CompositeDrawable, IKeyBindingHandler { private StarParticleSpewer breakSpewer; private StarParticleSpewer kiaiSpewer; @@ -64,12 +66,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { breakSpewer.Active.BindTarget = player.IsBreakTime; } - - if (osuPlayfield != null) - { - breakSpewer.ParticleParent = osuPlayfield; - kiaiSpewer.ParticleParent = osuPlayfield; - } } protected override void Update() @@ -116,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy } if (leftPressed && rightPressed) - breakSpewer.Direction = SpewDirection.Both; + breakSpewer.Direction = SpewDirection.Omni; else if (leftPressed) breakSpewer.Direction = SpewDirection.Left; else if (rightPressed) @@ -125,13 +121,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy breakSpewer.Direction = SpewDirection.None; } - private class StarParticleSpewer : ParticleSpewer + private class StarParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition { private const int particle_lifetime_min = 300; private const int particle_lifetime_max = 1000; public SpewDirection Direction { get; set; } + protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; protected override float ParticleGravity => 240; public StarParticleSpewer(Texture texture, int perSecond) @@ -140,48 +137,52 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Active.BindValueChanged(_ => resetVelocityCalculation()); } - private Vector2 positionInParent => ToSpaceOfOtherDrawable(OriginPosition, ParticleParent); + private Vector2? cursorScreenPosition; + private Vector2 cursorVelocity; - private Vector2 screenVelocity; + private const double max_velocity_frame_length = 15; + private double velocityFrameLength; + private Vector2 totalPosDifference; - private const double velocity_calculation_delay = 15; - private double lastVelocityCalculation; - private Vector2 positionDifference; - private Vector2? lastPosition; - - protected override void Update() + protected override bool OnMouseMove(MouseMoveEvent e) { - base.Update(); - - if (lastPosition != null) + if (cursorScreenPosition == null) { - positionDifference += (positionInParent - lastPosition.Value); - lastVelocityCalculation += Clock.ElapsedFrameTime; + cursorScreenPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); } - lastPosition = positionInParent; + // calculate cursor velocity. + totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; + cursorScreenPosition = e.ScreenSpaceMousePosition; - if (lastVelocityCalculation > velocity_calculation_delay) + velocityFrameLength += Clock.ElapsedFrameTime; + + if (velocityFrameLength > max_velocity_frame_length) { - screenVelocity = positionDifference / (float)lastVelocityCalculation; + cursorVelocity = totalPosDifference / (float)velocityFrameLength; - positionDifference = Vector2.Zero; - lastVelocityCalculation = 0; + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; } + + return base.OnMouseMove(e); } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + private void resetVelocityCalculation() { - positionDifference = Vector2.Zero; - lastVelocityCalculation = 0; - lastPosition = null; + cursorScreenPosition = null; + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; } protected override FallingParticle SpawnParticle() { var p = base.SpawnParticle(); - p.StartPosition = positionInParent; + p.StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero); p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); p.StartAngle = (float)(RNG.NextDouble() * 4 - 2); p.EndAngle = RNG.NextSingle(-2f, 2f); @@ -207,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy ); break; - case SpewDirection.Both: + case SpewDirection.Omni: p.Velocity = new Vector2( RNG.NextSingle(-460f, 460f), RNG.NextSingle(-160f, 160f) @@ -215,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy break; } - p.Velocity += screenVelocity * 40; + p.Velocity += cursorVelocity * 40; return p; } @@ -226,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy None, Left, Right, - Both, + Omni, } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 5812e8cf75..fbb7bfd7b1 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; @@ -42,7 +43,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor InternalChild = fadeContainer = new Container { RelativeSizeAxes = Axes.Both, - Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling) + Children = new[] + { + cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), + new LegacyCursorStarParticles() + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } }; } diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/Particles/ParticleSpewer.cs index 3d2225d382..f0c9dff239 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/Particles/ParticleSpewer.cs @@ -26,22 +26,16 @@ namespace osu.Game.Graphics.Particles /// public readonly BindableBool Active = new BindableBool(); - /// - /// whose DrawInfo will be used to draw each particle. - /// Defaults to the itself. - /// - public IDrawable ParticleParent; - public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; public override bool IsPresent => base.IsPresent && HasActiveParticles; + protected virtual bool CanSpawnParticles => true; protected virtual float ParticleGravity => 0; protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) { Texture = texture; Blending = BlendingParameters.Additive; - ParticleParent = this; particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; @@ -57,9 +51,12 @@ namespace osu.Game.Graphics.Particles // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active.Value && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) { - addParticle(SpawnParticle()); + particles[currentIndex] = SpawnParticle(); + + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; } Invalidate(Invalidation.DrawNode); @@ -76,14 +73,6 @@ namespace osu.Game.Graphics.Particles }; } - private void addParticle(FallingParticle fallingParticle) - { - particles[currentIndex] = fallingParticle; - - currentIndex = (currentIndex + 1) % particles.Length; - lastParticleAdded = Time.Current; - } - protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); # region DrawNode @@ -96,7 +85,6 @@ namespace osu.Game.Graphics.Particles private float currentTime; private float gravity; - private Matrix3 particleDrawMatrix; public ParticleSpewerDrawNode(Sprite source) : base(source) @@ -112,7 +100,6 @@ namespace osu.Game.Graphics.Particles currentTime = (float)Source.Time.Current; gravity = Source.ParticleGravity; - particleDrawMatrix = Source.ParticleParent.DrawInfo.Matrix; } protected override void Blit(Action vertexAction) @@ -165,7 +152,7 @@ namespace osu.Game.Graphics.Particles float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin; float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin; - return Vector2Extensions.Transform(new Vector2(x, y), particleDrawMatrix); + return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix); } } From cfcb46034c51bc4a95186f4e304e8b84294706fa Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:02:37 +0200 Subject: [PATCH 26/92] Remove `ParticleJet` --- .../Visual/Gameplay/TestSceneParticleJet.cs | 61 ------------ .../Gameplay/TestSceneParticleSpewer.cs | 94 +++++++++++++++++++ osu.Game/Graphics/Particles/ParticleJet.cs | 48 ---------- 3 files changed, 94 insertions(+), 109 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs delete mode 100644 osu.Game/Graphics/Particles/ParticleJet.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs deleted file mode 100644 index e570abcf88..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleJet.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Testing; -using osu.Game.Graphics.Particles; -using osu.Game.Skinning; - -namespace osu.Game.Tests.Visual.Gameplay -{ - [TestFixture] - public class TestSceneParticleJet : OsuTestScene - { - private ParticleJet jet; - - [Resolved] - private SkinManager skinManager { get; set; } - - public TestSceneParticleJet() - { - AddStep("create", () => - { - Child = jet = createJet(); - }); - - AddToggleStep("toggle spawning", value => jet.Active.Value = value); - } - - [SetUpSteps] - public void SetUpSteps() - { - AddStep("create jet", () => Child = jet = createJet()); - } - - [Test] - public void TestPresence() - { - AddStep("start jet", () => jet.Active.Value = true); - AddAssert("is present", () => jet.IsPresent); - - AddWaitStep("wait for some particles", 3); - AddStep("stop jet", () => jet.Active.Value = false); - - AddWaitStep("wait for clean screen", 5); - AddAssert("is not present", () => !jet.IsPresent); - } - - private ParticleJet createJet() - { - return new ParticleJet(skinManager.DefaultLegacySkin.GetTexture("star2"), 180) - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativePositionAxes = Axes.Y, - Y = -0.1f, - }; - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs new file mode 100644 index 0000000000..3a59374c98 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Graphics.Particles; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [TestFixture] + public class TestSceneParticleSpewer : OsuTestScene + { + private TestParticleSpewer spewer; + + [Resolved] + private SkinManager skinManager { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Child = spewer = createSpewer(); + + AddToggleStep("toggle spawning", value => spewer.Active.Value = value); + AddSliderStep("particle gravity", 0f, 250f, 0f, value => spewer.Gravity = value); + AddSliderStep("particle velocity", 0f, 500f, 250f, value => spewer.MaxVelocity = value); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create jet", () => Child = spewer = createSpewer()); + } + + [Test] + public void TestPresence() + { + AddStep("start jet", () => spewer.Active.Value = true); + AddAssert("is present", () => spewer.IsPresent); + + AddWaitStep("wait for some particles", 3); + AddStep("stop jet", () => spewer.Active.Value = false); + + AddWaitStep("wait for clean screen", 8); + AddAssert("is not present", () => !spewer.IsPresent); + } + + private TestParticleSpewer createSpewer() + { + return new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + } + + private class TestParticleSpewer : ParticleSpewer + { + private const int lifetime = 1500; + private const int rate = 250; + + public float Gravity = 0; + public float MaxVelocity = 250; + + protected override float ParticleGravity => Gravity; + + public TestParticleSpewer(Texture texture) + : base(texture, rate, lifetime) + { + } + + protected override FallingParticle SpawnParticle() + { + var p = base.SpawnParticle(); + p.Velocity = new Vector2( + RNG.NextSingle(-MaxVelocity, MaxVelocity), + RNG.NextSingle(-MaxVelocity, MaxVelocity) + ); + p.Duration = RNG.NextSingle(lifetime); + p.StartAngle = RNG.NextSingle(MathF.PI * 2); + p.EndAngle = RNG.NextSingle(MathF.PI * 2); + p.EndScale = RNG.NextSingle(0.5f, 1.5f); + + return p; + } + } + } +} diff --git a/osu.Game/Graphics/Particles/ParticleJet.cs b/osu.Game/Graphics/Particles/ParticleJet.cs deleted file mode 100644 index a76aa6b75f..0000000000 --- a/osu.Game/Graphics/Particles/ParticleJet.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Graphics.Textures; -using osu.Framework.Utils; -using osuTK; - -namespace osu.Game.Graphics.Particles -{ - public class ParticleJet : ParticleSpewer - { - private const int particles_per_second = 80; - private const double particle_lifetime = 500; - private const int angle_spread = 10; - private const float velocity_min = 1300f; - private const float velocity_max = 1500f; - - private readonly int angle; - - protected override float ParticleGravity => 750f; - - public ParticleJet(Texture texture, int angle) - : base(texture, particles_per_second, particle_lifetime) - { - this.angle = angle; - } - - protected override FallingParticle SpawnParticle() - { - var p = base.SpawnParticle(); - - var directionRads = MathUtils.DegreesToRadians( - RNG.NextSingle(angle - angle_spread / 2, angle + angle_spread / 2) - ); - var direction = new Vector2(MathF.Sin(directionRads), MathF.Cos(directionRads)); - - p.StartPosition = OriginPosition; - p.Duration = RNG.NextSingle((float)particle_lifetime * 0.8f, (float)particle_lifetime); - p.Velocity = direction * new Vector2(RNG.NextSingle(velocity_min, velocity_max)); - p.StartAngle = RNG.NextSingle(-2f, 2f); - p.EndAngle = RNG.NextSingle(-2f, 2f); - p.EndScale = 2f; - - return p; - } - } -} From 8862d3fa1ea67d42c54dda8710206f036aed6436 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:29:05 +0200 Subject: [PATCH 27/92] Add `OsuSkinComponents.CursorParticles` --- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 3 +++ osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 7 +------ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index 46e501758b..484d1c6753 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu FollowPoint, Cursor, CursorTrail, + CursorParticles, SliderScorePoint, ReverseArrow, HitCircleText, diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 41b0a88f11..0887e4d1d3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -89,6 +89,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; + case OsuSkinComponents.CursorParticles: + return new LegacyCursorStarParticles(); + case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) return null; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index fbb7bfd7b1..463b1f4eaf 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Osu.Configuration; -using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; @@ -46,11 +45,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Children = new[] { cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling), - new LegacyCursorStarParticles() - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, + new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling), } }; } From 911282234e9a0944caec1af51c6c544e28927edf Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:30:58 +0200 Subject: [PATCH 28/92] Rename legacy cursor particle classes --- ...orStarParticles.cs => LegacyCursorParticles.cs} | 14 +++++++------- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/Legacy/{LegacyCursorStarParticles.cs => LegacyCursorParticles.cs} (93%) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs similarity index 93% rename from osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs rename to osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 89ffa4ca15..876af29e88 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorStarParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -20,10 +20,10 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorStarParticles : CompositeDrawable, IKeyBindingHandler + public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler { - private StarParticleSpewer breakSpewer; - private StarParticleSpewer kiaiSpewer; + private LegacyCursorParticleSpewer breakSpewer; + private LegacyCursorParticleSpewer kiaiSpewer; [Resolved(canBeNull: true)] private Player player { get; set; } @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy InternalChildren = new[] { - breakSpewer = new StarParticleSpewer(texture, 20) + breakSpewer = new LegacyCursorParticleSpewer(texture, 20) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Value = false, } }, - kiaiSpewer = new StarParticleSpewer(texture, 60) + kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy breakSpewer.Direction = SpewDirection.None; } - private class StarParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition + private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition { private const int particle_lifetime_min = 300; private const int particle_lifetime_max = 1000; @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; protected override float ParticleGravity => 240; - public StarParticleSpewer(Texture texture, int perSecond) + public LegacyCursorParticleSpewer(Texture texture, int perSecond) : base(texture, perSecond, particle_lifetime_max) { Active.BindValueChanged(_ => resetVelocityCalculation()); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 0887e4d1d3..20c432b298 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.CursorParticles: - return new LegacyCursorStarParticles(); + return new LegacyCursorParticles(); case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) From a688e69859262529d668ad6db5758d58d29aa8f6 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 11 Sep 2021 01:55:16 +0200 Subject: [PATCH 29/92] Scale down cursor particles --- .../Skinning/Legacy/LegacyCursorParticles.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 876af29e88..f8b5ab97df 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -35,6 +35,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void load(ISkinSource skin, OsuColour colour) { var texture = skin.GetTexture("star2"); + if (texture == null) + return; + + texture.ScaleAdjust = 3.2f; InternalChildren = new[] { From 82d16ab394e747a333b47a9ac725d8152b94ce07 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:45:50 +0200 Subject: [PATCH 30/92] Fix `LegacyCursorParticles` texture null reference --- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 20c432b298..02a67ea44f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -90,7 +90,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.CursorParticles: - return new LegacyCursorParticles(); + if (GetTexture("star2") != null) + return new LegacyCursorParticles(); + + return null; case OsuSkinComponents.HitCircleText: if (!this.HasFont(LegacyFont.HitCircle)) From 16f98357e621260b5a7e9107055e391e2c453a62 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:53:41 +0200 Subject: [PATCH 31/92] Add cursor particles tests --- .../TestSceneCursorParticles.cs | 174 ++++++++++++++++++ .../Skinning/Legacy/LegacyCursorParticles.cs | 8 +- 2 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs new file mode 100644 index 0000000000..11b1f5b2af --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.Skinning.Legacy; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Skinning; +using osuTK; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestSceneCursorParticles : TestSceneOsuPlayer + { + protected override bool Autoplay => autoplay; + protected override bool HasCustomSteps => true; + + private bool autoplay; + private IBeatmap currentBeatmap; + + [Resolved] + private SkinManager skinManager { get; set; } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentBeatmap ?? base.CreateBeatmap(ruleset); + + [Test] + public void TestLegacyBreakParticles() + { + LegacyCursorParticles cursorParticles = null; + + createLegacyTest(false, () => new Beatmap + { + Breaks = + { + new BreakPeriod(8500, 10000), + }, + HitObjects = + { + new HitCircle + { + StartTime = 8000, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + new HitCircle + { + StartTime = 11000, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + } + }); + + AddUntilStep("fetch cursor particles", () => + { + cursorParticles = this.ChildrenOfType().SingleOrDefault(); + return cursorParticles != null; + }); + + AddStep("move mouse to centre", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left)); + AddWaitStep("wait a bit", 5); + AddStep("press right mouse button", () => InputManager.PressButton(MouseButton.Right)); + AddWaitStep("wait a bit", 5); + AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left)); + AddWaitStep("wait a bit", 5); + AddStep("release right mouse button", () => InputManager.ReleaseButton(MouseButton.Right)); + + AddUntilStep("wait for beatmap start", () => !Player.IsBreakTime.Value); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + + AddUntilStep("wait for break", () => Player.IsBreakTime.Value); + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddUntilStep("wait for break end", () => !Player.IsBreakTime.Value); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + } + + [Test] + public void TestLegacyKiaiParticles() + { + LegacyCursorParticles cursorParticles = null; + DrawableSpinner spinner = null; + DrawableSlider slider = null; + + createLegacyTest(true, () => + { + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true }); + + return new Beatmap + { + ControlPointInfo = controlPointInfo, + HitObjects = + { + new Spinner + { + StartTime = 0, + Duration = 1000, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + new Slider + { + StartTime = 2500, + RepeatCount = 0, + Position = OsuPlayfield.BASE_SIZE / 2, + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(new Vector2(100, 0)), + }) + }, + new HitCircle + { + StartTime = 4500, + Position = OsuPlayfield.BASE_SIZE / 2, + }, + }, + }; + } + ); + + AddUntilStep("fetch cursor particles", () => + { + cursorParticles = this.ChildrenOfType().SingleOrDefault(); + return cursorParticles != null; + }); + + AddUntilStep("wait for spinner tracking", () => + { + spinner = this.ChildrenOfType().SingleOrDefault(); + return spinner?.RotationTracker.Tracking == true; + }); + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddUntilStep("spinner tracking stopped", () => !spinner.RotationTracker.Tracking); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + + AddUntilStep("wait for slider tracking", () => + { + slider = this.ChildrenOfType().SingleOrDefault(); + return slider?.Tracking.Value == true; + }); + AddAssert("particles are being spawned", () => cursorParticles.Active); + + AddUntilStep("slider tracking stopped", () => !slider.Tracking.Value); + AddAssert("particle spawning stopped", () => !cursorParticles.Active); + } + + private void createLegacyTest(bool autoplay, Func beatmap) => CreateTest(() => + { + AddStep("set beatmap", () => + { + this.autoplay = autoplay; + currentBeatmap = beatmap(); + }); + AddStep("setup default legacy skin", () => + { + skinManager.CurrentSkinInfo.Value = skinManager.DefaultLegacySkin.SkinInfo; + }); + }); + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index f8b5ab97df..2080b1a3ce 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler { + public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true; + private LegacyCursorParticleSpewer breakSpewer; private LegacyCursorParticleSpewer kiaiSpewer; @@ -32,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private OsuPlayfield osuPlayfield { get; set; } [BackgroundDependencyLoader] - private void load(ISkinSource skin, OsuColour colour) + private void load(ISkinSource skin, OsuColour colours) { var texture = skin.GetTexture("star2"); if (texture == null) @@ -46,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colour.PinkLighter, + Colour = colours.PinkLighter, Direction = SpewDirection.None, Active = { @@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colour.PinkLighter, + Colour = colours.PinkLighter, Direction = SpewDirection.None, Active = { From 7327603fc834de3cbb32b29e5a511d1df8061107 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Mon, 13 Sep 2021 00:05:49 +0200 Subject: [PATCH 32/92] Fix outdated test step description --- osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 3a59374c98..e58bbb737e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -35,17 +35,17 @@ namespace osu.Game.Tests.Visual.Gameplay [SetUpSteps] public void SetUpSteps() { - AddStep("create jet", () => Child = spewer = createSpewer()); + AddStep("create spewer", () => Child = spewer = createSpewer()); } [Test] public void TestPresence() { - AddStep("start jet", () => spewer.Active.Value = true); + AddStep("start spewer", () => spewer.Active.Value = true); AddAssert("is present", () => spewer.IsPresent); AddWaitStep("wait for some particles", 3); - AddStep("stop jet", () => spewer.Active.Value = false); + AddStep("stop spewer", () => spewer.Active.Value = false); AddWaitStep("wait for clean screen", 8); AddAssert("is not present", () => !spewer.IsPresent); From 224244801f35ed2b031c9be907cd891181b5da97 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Mon, 13 Sep 2021 00:35:13 +0200 Subject: [PATCH 33/92] Remove Particles namespace --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs | 1 - osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs | 2 +- osu.Game/Graphics/{Particles => }/ParticleSpewer.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game/Graphics/{Particles => }/ParticleSpewer.cs (99%) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 2080b1a3ce..95c2a6b930 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -11,7 +11,6 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Graphics.Particles; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Play; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index e58bbb737e..fe14869e9a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Graphics.Particles; +using osu.Game.Graphics; using osu.Game.Skinning; using osuTK; diff --git a/osu.Game/Graphics/Particles/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs similarity index 99% rename from osu.Game/Graphics/Particles/ParticleSpewer.cs rename to osu.Game/Graphics/ParticleSpewer.cs index f0c9dff239..544b2bd2ed 100644 --- a/osu.Game/Graphics/Particles/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osuTK; -namespace osu.Game.Graphics.Particles +namespace osu.Game.Graphics { public abstract class ParticleSpewer : Sprite { From b009772206a4ffeebb2b7d36dad890ec655c2ff6 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Mon, 13 Sep 2021 00:45:36 +0200 Subject: [PATCH 34/92] Fix code inspect failure --- osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index fe14869e9a..e6b5763f2c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual.Gameplay private const int lifetime = 1500; private const int rate = 250; - public float Gravity = 0; + public float Gravity; public float MaxVelocity = 250; protected override float ParticleGravity => Gravity; From 9fd616c578107ab375fa102ab313a6d5f40a34cf Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 14 Sep 2021 00:16:42 +0200 Subject: [PATCH 35/92] Tiny refactor --- .../Skinning/Legacy/LegacyCursorParticles.cs | 4 ++-- osu.Game/Graphics/ParticleSpewer.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 95c2a6b930..8dc486285b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -149,6 +149,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private double velocityFrameLength; private Vector2 totalPosDifference; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + protected override bool OnMouseMove(MouseMoveEvent e) { if (cursorScreenPosition == null) @@ -174,8 +176,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return base.OnMouseMove(e); } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - private void resetVelocityCalculation() { cursorScreenPosition = null; diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 544b2bd2ed..8b82dfb7c6 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -132,10 +132,10 @@ namespace osu.Game.Graphics height); var quad = new Quad( - rotatePosition(rect.TopLeft, rect.Centre, angle), - rotatePosition(rect.TopRight, rect.Centre, angle), - rotatePosition(rect.BottomLeft, rect.Centre, angle), - rotatePosition(rect.BottomRight, rect.Centre, angle) + transformPosition(rect.TopLeft, rect.Centre, angle), + transformPosition(rect.TopRight, rect.Centre, angle), + transformPosition(rect.BottomLeft, rect.Centre, angle), + transformPosition(rect.BottomRight, rect.Centre, angle) ); DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction, @@ -144,7 +144,7 @@ namespace osu.Game.Graphics } } - private Vector2 rotatePosition(Vector2 pos, Vector2 centre, float angle) + private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) { float cos = MathF.Cos(angle); float sin = MathF.Sin(angle); From c4886be7e1132ed189dbc400db3930cb3c218b11 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 14 Sep 2021 00:36:01 +0200 Subject: [PATCH 36/92] Add `StarBreakAdditive` config support --- .../Skinning/Legacy/LegacyCursorParticles.cs | 7 +++++-- osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 8dc486285b..a2ee9afff1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -36,6 +37,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private void load(ISkinSource skin, OsuColour colours) { var texture = skin.GetTexture("star2"); + var starBreakAdditive = skin.GetConfig(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255); + if (texture == null) return; @@ -47,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colours.PinkLighter, + Colour = starBreakAdditive, Direction = SpewDirection.None, Active = { @@ -58,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colours.PinkLighter, + Colour = starBreakAdditive, Direction = SpewDirection.None, Active = { diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs index f7ba8b9fc4..24f9217a5f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs @@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning SliderBorder, SliderBall, SpinnerBackground, + StarBreakAdditive, } } From d13ff12a3e4cb0218f1aa1560b9439354aea0353 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Tue, 14 Sep 2021 00:36:52 +0200 Subject: [PATCH 37/92] Remove hardcoded particle scale --- .../Skinning/Legacy/LegacyCursorParticles.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index a2ee9afff1..ccccd1810c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -39,11 +39,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy var texture = skin.GetTexture("star2"); var starBreakAdditive = skin.GetConfig(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255); - if (texture == null) - return; - - texture.ScaleAdjust = 3.2f; - InternalChildren = new[] { breakSpewer = new LegacyCursorParticleSpewer(texture, 20) From 178a3d1132801dbe0d0e6725def89622cc48932d Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 14 Sep 2021 10:23:11 +1000 Subject: [PATCH 38/92] Remove redundant check --- .../Difficulty/Skills/Flashlight.cs | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index f6760235b4..abd900a80d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -34,33 +34,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double scalingFactor = 52.0 / osuHitObject.Radius; double smallDistNerf = 1.0; + double cumulativeStrainTime = 0.0; double result = 0.0; - if (Previous.Count > 0) + for (int i = 0; i < Previous.Count; i++) { - double cumulativeStrainTime = 0.0; + var osuPrevious = (OsuDifficultyHitObject)Previous[i]; + var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); - for (int i = 0; i < Previous.Count; i++) + if (!(osuPrevious.BaseObject is Spinner)) { - var osuPrevious = (OsuDifficultyHitObject)Previous[i]; - var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; - if (!(osuPrevious.BaseObject is Spinner)) - { - double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + cumulativeStrainTime += osuPrevious.StrainTime; - cumulativeStrainTime += osuPrevious.StrainTime; + // We want to nerf objects that can be easily seen within the Flashlight circle radius. + if (i == 0) + smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); - // We want to nerf objects that can be easily seen within the Flashlight circle radius. - if (i == 0) - smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); + // We also want to nerf stacks so that only the first object of the stack is accounted for. + double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - - result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; - } + result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } From c4fbae136a8c4e53785227a30cd9d92323d06376 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 14 Sep 2021 10:34:21 +1000 Subject: [PATCH 39/92] Rename FlashlightStrain to FlashlightRating --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 1e870dac68..ac77a93239 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { public double AimStrain { get; set; } public double SpeedStrain { get; set; } - public double FlashlightStrain { get; set; } + public double FlashlightRating { get; set; } public double ApproachRate { get; set; } public double OverallDifficulty { get; set; } public int HitCircleCount { get; set; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b0dd4dc9b0..28b2174071 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, - FlashlightStrain = flashlightRating, + FlashlightRating = flashlightRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo, diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index f9a3423eab..bf4d92652c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (!mods.Any(h => h is OsuModFlashlight)) return 0.0; - double rawFlashlight = Attributes.FlashlightStrain; + double rawFlashlight = Attributes.FlashlightRating; if (mods.Any(m => m is OsuModTouchDevice)) rawFlashlight = Math.Pow(rawFlashlight, 0.8); From 33e1273df8095a1afb0a64950d82d13e79a55475 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 15 Sep 2021 19:03:42 +1000 Subject: [PATCH 40/92] Include Flashlight in total SR calculation --- .../Difficulty/OsuDifficultyCalculator.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index da879cb02e..5087da6153 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -37,7 +37,15 @@ namespace osu.Game.Rulesets.Osu.Difficulty double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; - double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1); + double baseFlashlightPerformance = 0.0; + if (mods.Any(h => h is OsuModFlashlight)) + baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0; + double basePerformance = + Math.Pow( + Math.Pow(baseAimPerformance, 1.1) + + Math.Pow(baseSpeedPerformance, 1.1) + + Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1 + ); double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; HitWindows hitWindows = new OsuHitWindows(); @@ -95,6 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModHalfTime(), new OsuModEasy(), new OsuModHardRock(), + new OsuModFlashlight(), }; } -} +} \ No newline at end of file From a8539bc75b6e88589fa16619b729a13c73611fcf Mon Sep 17 00:00:00 2001 From: MBmasher Date: Wed, 15 Sep 2021 19:04:36 +1000 Subject: [PATCH 41/92] Add newline to end of file --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 5087da6153..411140c756 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -106,4 +106,4 @@ namespace osu.Game.Rulesets.Osu.Difficulty new OsuModFlashlight(), }; } -} \ No newline at end of file +} From 32d65adb35c225dac77a43604012f0d426293508 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:22:37 +0200 Subject: [PATCH 42/92] Fix cursor particle scale --- .../Skinning/Legacy/LegacyCursorParticles.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index ccccd1810c..73820b8df9 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -39,6 +39,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy var texture = skin.GetTexture("star2"); var starBreakAdditive = skin.GetConfig(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255); + if (texture != null) + { + // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. + texture.ScaleAdjust *= 1.6f; + } + InternalChildren = new[] { breakSpewer = new LegacyCursorParticleSpewer(texture, 20) From 29ce2f05bdfeaeb317220383f608b081f39195f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:44:46 +0900 Subject: [PATCH 43/92] Remove implied defaults --- .../Skinning/Legacy/LegacyCursorParticles.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 73820b8df9..3357508d24 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -53,10 +53,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Origin = Anchor.Centre, Colour = starBreakAdditive, Direction = SpewDirection.None, - Active = - { - Value = false, - } }, kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) { @@ -64,10 +60,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Origin = Anchor.Centre, Colour = starBreakAdditive, Direction = SpewDirection.None, - Active = - { - Value = false, - } }, }; From 2df4073946bed60dc96fe5f3e44b925af63a0a13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:50:03 +0900 Subject: [PATCH 44/92] `SpawnParticle` -> `CreateParticle` (and set time outside of `virtual` call) Allows easier overriding (no need to call the `base.CreateParticle` call and worry about overwriting the time value. --- .../Skinning/Legacy/LegacyCursorParticles.cs | 35 ++++++++++--------- .../Gameplay/TestSceneParticleSpewer.cs | 26 +++++++------- osu.Game/Graphics/ParticleSpewer.cs | 13 +++---- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 3357508d24..858ba98b86 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -179,47 +179,48 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy velocityFrameLength = 0; } - protected override FallingParticle SpawnParticle() - { - var p = base.SpawnParticle(); + protected override FallingParticle CreateParticle() => + new FallingParticle + { + StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero), + Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), + StartAngle = (float)(RNG.NextDouble() * 4 - 2), + EndAngle = RNG.NextSingle(-2f, 2f), + EndScale = RNG.NextSingle(2f), + Velocity = getVelocity(), + }; - p.StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero); - p.Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max); - p.StartAngle = (float)(RNG.NextDouble() * 4 - 2); - p.EndAngle = RNG.NextSingle(-2f, 2f); - p.EndScale = RNG.NextSingle(2f); + private Vector2 getVelocity() + { + Vector2 velocity = Vector2.Zero; switch (Direction) { - case SpewDirection.None: - p.Velocity = Vector2.Zero; - break; - case SpewDirection.Left: - p.Velocity = new Vector2( + velocity = new Vector2( RNG.NextSingle(-460f, 0), RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Right: - p.Velocity = new Vector2( + velocity = new Vector2( RNG.NextSingle(0, 460f), RNG.NextSingle(-40f, 40f) ); break; case SpewDirection.Omni: - p.Velocity = new Vector2( + velocity = new Vector2( RNG.NextSingle(-460f, 460f), RNG.NextSingle(-160f, 160f) ); break; } - p.Velocity += cursorVelocity * 40; + velocity += cursorVelocity * 40; - return p; + return velocity; } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index e6b5763f2c..c259718a7a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -75,20 +75,18 @@ namespace osu.Game.Tests.Visual.Gameplay { } - protected override FallingParticle SpawnParticle() - { - var p = base.SpawnParticle(); - p.Velocity = new Vector2( - RNG.NextSingle(-MaxVelocity, MaxVelocity), - RNG.NextSingle(-MaxVelocity, MaxVelocity) - ); - p.Duration = RNG.NextSingle(lifetime); - p.StartAngle = RNG.NextSingle(MathF.PI * 2); - p.EndAngle = RNG.NextSingle(MathF.PI * 2); - p.EndScale = RNG.NextSingle(0.5f, 1.5f); - - return p; - } + protected override FallingParticle CreateParticle() => + new FallingParticle + { + Velocity = new Vector2( + RNG.NextSingle(-MaxVelocity, MaxVelocity), + RNG.NextSingle(-MaxVelocity, MaxVelocity) + ), + Duration = RNG.NextSingle(lifetime), + StartAngle = RNG.NextSingle(MathF.PI * 2), + EndAngle = RNG.NextSingle(MathF.PI * 2), + EndScale = RNG.NextSingle(0.5f, 1.5f) + }; } } } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 8b82dfb7c6..fc119b3ea2 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -53,7 +53,10 @@ namespace osu.Game.Graphics if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) { - particles[currentIndex] = SpawnParticle(); + var newParticle = CreateParticle(); + newParticle.StartTime = (float)Time.Current; + + particles[currentIndex] = newParticle; currentIndex = (currentIndex + 1) % particles.Length; lastParticleAdded = Time.Current; @@ -65,13 +68,7 @@ namespace osu.Game.Graphics /// /// Called each time a new particle should be spawned. /// - protected virtual FallingParticle SpawnParticle() - { - return new FallingParticle - { - StartTime = (float)Time.Current, - }; - } + protected virtual FallingParticle CreateParticle() => new FallingParticle(); protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); From 9127a706ac2444daf504ae60fffb0dc38754fd0f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Sep 2021 16:54:22 +0900 Subject: [PATCH 45/92] Use `private` for internally used property --- osu.Game/Graphics/ParticleSpewer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index fc119b3ea2..c022fd4598 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -26,12 +26,13 @@ namespace osu.Game.Graphics /// public readonly BindableBool Active = new BindableBool(); - public bool HasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; - public override bool IsPresent => base.IsPresent && HasActiveParticles; + public override bool IsPresent => base.IsPresent && hasActiveParticles; protected virtual bool CanSpawnParticles => true; protected virtual float ParticleGravity => 0; + private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; + protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) { Texture = texture; From b2b3108afa84f7c156c00876ee75390f8933a2b2 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Fri, 17 Sep 2021 16:19:41 -0400 Subject: [PATCH 46/92] Resolve addressed issues + Stopped using testing methods in non-testing classes + Resolve Player and add OnSeek event + Take bindings away from BarHitErrorMeter + Add support for ColourHitErrorMeter --- .idea/.idea.osu.Desktop/.idea/discord.xml | 7 +++++ .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 31 ++----------------- .../HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 ++ .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 8 +++++ osu.Game/Screens/Play/Player.cs | 8 ++++- 5 files changed, 26 insertions(+), 30 deletions(-) create mode 100644 .idea/.idea.osu.Desktop/.idea/discord.xml diff --git a/.idea/.idea.osu.Desktop/.idea/discord.xml b/.idea/.idea.osu.Desktop/.idea/discord.xml new file mode 100644 index 0000000000..30bab2abb1 --- /dev/null +++ b/.idea/.idea.osu.Desktop/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 2854fabbae..2b5228cab0 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -10,10 +10,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Testing; using osu.Game.Graphics; -using osu.Game.Input.Bindings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osuTK; @@ -21,7 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { - public class BarHitErrorMeter : HitErrorMeter, IKeyBindingHandler + public class BarHitErrorMeter : HitErrorMeter { private const int arrow_move_duration = 400; @@ -144,10 +141,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters arrow.Alpha = 0; arrow.Delay(200).FadeInFromZero(600); - - var progressBar = Parent.ChildrenOfType().FirstOrDefault(); - if (progressBar != null) - progressBar.Bar.OnSeek += _ => handleSeek(); } private void createColourBars(OsuColour colours) @@ -287,26 +280,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - private void handleSeek() - { - judgementsContainer.Clear(true); - } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.SeekReplayBackward: - case GlobalAction.SeekReplayForward: - handleSeek(); - return false; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { - } + public override void Clear() => judgementsContainer.Clear(true); } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index dda2a6da95..ea64d1f4d9 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -33,6 +33,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } + public override void Clear() => judgementsFlow.Clear(true); + private class JudgementFlow : FillFlowContainer { private const int max_available_judgements = 20; diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 788ba5be8c..22ae3900d6 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -22,6 +22,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters [Resolved] private OsuColour colours { get; set; } + [Resolved(canBeNull: true)] + private Player player { get; set; } + public bool UsesFixedAnchor { get; set; } [BackgroundDependencyLoader(true)] @@ -34,6 +37,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { base.LoadComplete(); + if (player != null) + player.OnSeek += Clear; + processor.NewJudgement += OnNewJudgement; } @@ -67,6 +73,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } + public abstract void Clear(); + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e8a2790c94..f0544d5fcd 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,6 +69,8 @@ namespace osu.Game.Screens.Play public Action RestartRequested; + public Action OnSeek; + public bool HasFailed { get; private set; } private Bindable mouseWheelDisabled; @@ -584,7 +586,11 @@ namespace osu.Game.Screens.Play /// Seek to a specific time in gameplay. /// /// The destination time to seek to. - public void Seek(double time) => GameplayClockContainer.Seek(time); + public void Seek(double time) + { + GameplayClockContainer.Seek(time); + OnSeek.Invoke(); + } private ScheduledDelegate frameStablePlaybackResetDelegate; From 79438c19a45eecb5c0983e6a14efb98539e3686c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 16:27:30 +0200 Subject: [PATCH 47/92] Fix slider parts not reproxying after first hitobject freed --- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Skinning/Legacy/LegacyReverseArrow.cs | 29 +++++++++++++++++-- .../Legacy/LegacySliderHeadHitCircle.cs | 27 +++++++++++++++-- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 0e1d1043e3..3acec4498d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables repeatContainer.Clear(false); tickContainer.Clear(false); - OverlayElementContainer.Clear(); + OverlayElementContainer.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index b6956693b6..9e4bd258bd 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -17,12 +18,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } + private Drawable proxy; + public LegacyReverseArrow(ISkin skin) { this.skin = skin; } - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load() { AutoSizeAxes = Axes.Both; @@ -36,9 +39,29 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { base.LoadComplete(); + proxy = CreateProxy(); + + if (drawableHitObject != null) + { + drawableHitObject.HitObjectApplied += onHitObjectApplied; + onHitObjectApplied(drawableHitObject); + } + } + + private void onHitObjectApplied(DrawableHitObject drawableObject) + { + Debug.Assert(proxy.Parent == null); + // see logic in LegacySliderHeadHitCircle. - (drawableHitObject as DrawableSliderRepeat)?.DrawableSlider - .OverlayElementContainer.Add(CreateProxy()); + (drawableObject as DrawableSliderRepeat)?.DrawableSlider + .OverlayElementContainer.Add(proxy); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + if (drawableHitObject != null) + drawableHitObject.HitObjectApplied -= onHitObjectApplied; } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs index 83ebdafa50..13ba42ba50 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySliderHeadHitCircle.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -13,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } + private Drawable proxiedHitCircleOverlay; + public LegacySliderHeadHitCircle() : base("sliderstartcircle") { @@ -21,10 +24,30 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override void LoadComplete() { base.LoadComplete(); + proxiedHitCircleOverlay = HitCircleOverlay.CreateProxy(); + + if (drawableHitObject != null) + { + drawableHitObject.HitObjectApplied += onHitObjectApplied; + onHitObjectApplied(drawableHitObject); + } + } + + private void onHitObjectApplied(DrawableHitObject drawableObject) + { + Debug.Assert(proxiedHitCircleOverlay.Parent == null); // see logic in LegacyReverseArrow. - (drawableHitObject as DrawableSliderHead)?.DrawableSlider - .OverlayElementContainer.Add(HitCircleOverlay.CreateProxy().With(d => d.Depth = float.MinValue)); + (drawableObject as DrawableSliderHead)?.DrawableSlider + .OverlayElementContainer.Add(proxiedHitCircleOverlay.With(d => d.Depth = float.MinValue)); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (drawableHitObject != null) + drawableHitObject.HitObjectApplied -= onHitObjectApplied; } } } From 59657aca9a5e42bef22d93ceb8053557f8bbf8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 16:28:25 +0200 Subject: [PATCH 48/92] Remove redundant qualifier --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index 9e4bd258bd..298079fa6c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName; - InternalChild = skin.GetAnimation(lookupName, true, true) ?? Drawable.Empty(); + InternalChild = skin.GetAnimation(lookupName, true, true) ?? Empty(); } protected override void LoadComplete() From c23354bb67fc63d54e490d195436d5adfa1441a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 16:28:44 +0200 Subject: [PATCH 49/92] Remove unused setter --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index 298079fa6c..ab774a2b67 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { public class LegacyReverseArrow : CompositeDrawable { - private ISkin skin { get; set; } + private ISkin skin { get; } [Resolved(canBeNull: true)] private DrawableHitObject drawableHitObject { get; set; } From 680484bb165361ef1b6ea81562266d190937268d Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:04:25 -0400 Subject: [PATCH 50/92] Remove discord.xml Not sure how that slipped in there, but it's gone now! --- .idea/.idea.osu.Desktop/.idea/discord.xml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .idea/.idea.osu.Desktop/.idea/discord.xml diff --git a/.idea/.idea.osu.Desktop/.idea/discord.xml b/.idea/.idea.osu.Desktop/.idea/discord.xml deleted file mode 100644 index 30bab2abb1..0000000000 --- a/.idea/.idea.osu.Desktop/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file From 12cc16c598f4bab59c0ab0e805ada327c4ca4c18 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:05:06 -0400 Subject: [PATCH 51/92] Remove unused property in `SongProgress` --- osu.Game/Screens/Play/SongProgress.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index dff2dcc86b..b27a9c5f5d 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -35,8 +35,6 @@ namespace osu.Game.Screens.Play private readonly SongProgressGraph graph; private readonly SongProgressInfo info; - public SongProgressBar Bar => bar; - public Action RequestSeek; /// From f6e279baa1764a876884341a66f3af6ab05afd5d Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:18:11 -0400 Subject: [PATCH 52/92] Add xmldoc to HitErrorMeter.Clear Explains how the method is called and what inheritors should do when implementing it. --- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 22ae3900d6..1871519ab5 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -73,6 +73,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } + /// + /// Invoked by when the active seeks through the current beatmap. + /// Any inheritors of should have this method clear their container that displays the hit error results. + /// public abstract void Clear(); protected override void Dispose(bool isDisposing) From 04715a54719766a519287f1bf58538f3d0414d0c Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sat, 18 Sep 2021 12:20:36 -0400 Subject: [PATCH 53/92] Add null-check when invoking OnSeek --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f0544d5fcd..e982d02baf 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -589,7 +589,7 @@ namespace osu.Game.Screens.Play public void Seek(double time) { GameplayClockContainer.Seek(time); - OnSeek.Invoke(); + OnSeek?.Invoke(); } private ScheduledDelegate frameStablePlaybackResetDelegate; From 36237398fa307f6939306df065e897b4683cf740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 18 Sep 2021 18:24:36 +0200 Subject: [PATCH 54/92] Remove accidental leftover nullable BDL spec --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index ab774a2b67..fd7bfe7e60 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy this.skin = skin; } - [BackgroundDependencyLoader(true)] + [BackgroundDependencyLoader] private void load() { AutoSizeAxes = Axes.Both; From 846cde53b3dd7c2dd79034cb0ef2e214228c976a Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 18 Sep 2021 22:54:12 +0200 Subject: [PATCH 55/92] Add `RelativePositionAxes` support --- .../Gameplay/TestSceneParticleSpewer.cs | 25 +++++++++----- osu.Game/Graphics/ParticleSpewer.cs | 33 ++++++++++++++----- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index c259718a7a..31cc505a0d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -28,8 +28,12 @@ namespace osu.Game.Tests.Visual.Gameplay Child = spewer = createSpewer(); AddToggleStep("toggle spawning", value => spewer.Active.Value = value); - AddSliderStep("particle gravity", 0f, 250f, 0f, value => spewer.Gravity = value); - AddSliderStep("particle velocity", 0f, 500f, 250f, value => spewer.MaxVelocity = value); + AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.25f, value => spewer.MaxVelocity = value); + AddStep("move to new location", () => + { + spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); + }); } [SetUpSteps] @@ -51,14 +55,15 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } - private TestParticleSpewer createSpewer() - { - return new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) + private TestParticleSpewer createSpewer() => + new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) { - Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativePositionAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, + Position = new Vector2(0.5f), + Size = new Vector2(0.5f), }; - } private class TestParticleSpewer : ParticleSpewer { @@ -66,7 +71,10 @@ namespace osu.Game.Tests.Visual.Gameplay private const int rate = 250; public float Gravity; - public float MaxVelocity = 250; + + public float MaxVelocity = 0.25f; + + public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f); protected override float ParticleGravity => Gravity; @@ -82,6 +90,7 @@ namespace osu.Game.Tests.Visual.Gameplay RNG.NextSingle(-MaxVelocity, MaxVelocity), RNG.NextSingle(-MaxVelocity, MaxVelocity) ), + StartPosition = SpawnPosition, Duration = RNG.NextSingle(lifetime), StartAngle = RNG.NextSingle(MathF.PI * 2), EndAngle = RNG.NextSingle(MathF.PI * 2), diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index c022fd4598..6bf9bff05a 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -83,6 +84,8 @@ namespace osu.Game.Graphics private float currentTime; private float gravity; + private Axes relativePositionAxes; + private Vector2 sourceSize; public ParticleSpewerDrawNode(Sprite source) : base(source) @@ -98,6 +101,8 @@ namespace osu.Game.Graphics currentTime = (float)Source.Time.Current; gravity = Source.ParticleGravity; + relativePositionAxes = Source.RelativePositionAxes; + sourceSize = Source.DrawSize; } protected override void Blit(Action vertexAction) @@ -116,18 +121,11 @@ namespace osu.Game.Graphics var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; - var scale = p.ScaleAtTime(timeSinceStart); var pos = p.PositionAtTime(timeSinceStart, gravity); + var scale = p.ScaleAtTime(timeSinceStart); var angle = p.AngleAtTime(timeSinceStart); - var width = Texture.DisplayWidth * scale; - var height = Texture.DisplayHeight * scale; - - var rect = new RectangleF( - pos.X - width / 2, - pos.Y - height / 2, - width, - height); + var rect = createDrawRect(pos, scale); var quad = new Quad( transformPosition(rect.TopLeft, rect.Centre, angle), @@ -142,6 +140,23 @@ namespace osu.Game.Graphics } } + private RectangleF createDrawRect(Vector2 position, float scale) + { + var width = Texture.DisplayWidth * scale; + var height = Texture.DisplayHeight * scale; + + if (relativePositionAxes.HasFlagFast(Axes.X)) + position.X *= sourceSize.X; + if (relativePositionAxes.HasFlagFast(Axes.Y)) + position.Y *= sourceSize.Y; + + return new RectangleF( + position.X - width / 2, + position.Y - height / 2, + width, + height); + } + private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle) { float cos = MathF.Cos(angle); From ef530ed87cc984d2d0b23e8f78ae3c464d04c984 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 18 Sep 2021 23:45:58 +0200 Subject: [PATCH 56/92] Normalize particle velocity based on max duration --- .../Visual/Gameplay/TestSceneParticleSpewer.cs | 2 +- osu.Game/Graphics/ParticleSpewer.cs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 31cc505a0d..086b1c2ac2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddToggleStep("toggle spawning", value => spewer.Active.Value = value); AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); - AddSliderStep("particle velocity", 0f, 1f, 0.25f, value => spewer.MaxVelocity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value); AddStep("move to new location", () => { spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 6bf9bff05a..1ad4672238 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -82,6 +82,8 @@ namespace osu.Game.Graphics protected new ParticleSpewer Source => (ParticleSpewer)base.Source; + private readonly float maxLifetime; + private float currentTime; private float gravity; private Axes relativePositionAxes; @@ -91,6 +93,7 @@ namespace osu.Game.Graphics : base(source) { particles = new FallingParticle[Source.particles.Length]; + maxLifetime = (float)Source.maxLifetime; } public override void ApplyState() @@ -121,7 +124,7 @@ namespace osu.Game.Graphics var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; - var pos = p.PositionAtTime(timeSinceStart, gravity); + var pos = p.PositionAtTime(timeSinceStart, gravity, maxLifetime); var scale = p.ScaleAtTime(timeSinceStart); var angle = p.AngleAtTime(timeSinceStart); @@ -187,12 +190,12 @@ namespace osu.Game.Graphics public float AngleAtTime(float timeSinceStart) => StartAngle + (EndAngle - StartAngle) * progressAtTime(timeSinceStart); - public Vector2 PositionAtTime(float timeSinceStart, float gravity) + public Vector2 PositionAtTime(float timeSinceStart, float gravity, float maxDuration) { var progress = progressAtTime(timeSinceStart); - var currentGravity = new Vector2(0, gravity * Duration / 1000 * progress); + var currentGravity = new Vector2(0, gravity * Duration / maxDuration * progress); - return StartPosition + (Velocity + currentGravity) * timeSinceStart / 1000; + return StartPosition + (Velocity + currentGravity) * timeSinceStart / maxDuration; } private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1); From 3f8454cb76cc5f2d0c52f8270a27a843bab366eb Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 03:19:16 +0200 Subject: [PATCH 57/92] Remove abstract from `ParticleSpewer` --- .../Skinning/Legacy/LegacyCursorParticles.cs | 218 ++++++++---------- .../Gameplay/TestSceneParticleSpewer.cs | 62 ++--- osu.Game/Graphics/ParticleSpewer.cs | 48 ++-- 3 files changed, 144 insertions(+), 184 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index 858ba98b86..d4e5bdd46f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -1,11 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -20,12 +20,17 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler + public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition { + private const int particle_lifetime_min = 300; + private const int particle_lifetime_max = 1000; + private const float particle_gravity = 240; + public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true; - private LegacyCursorParticleSpewer breakSpewer; - private LegacyCursorParticleSpewer kiaiSpewer; + private Vector2 cursorVelocity; + private ParticleSpewer breakSpewer; + private ParticleSpewer kiaiSpewer; [Resolved(canBeNull: true)] private Player player { get; set; } @@ -45,21 +50,25 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy texture.ScaleAdjust *= 1.6f; } + RelativeSizeAxes = Axes.Both; + Anchor = Anchor.Centre; InternalChildren = new[] { - breakSpewer = new LegacyCursorParticleSpewer(texture, 20) + breakSpewer = new ParticleSpewer(texture, 20, particle_lifetime_max) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, Colour = starBreakAdditive, - Direction = SpewDirection.None, + ParticleGravity = particle_gravity, + CreateParticle = createBreakParticle, }, - kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) + kiaiSpewer = new ParticleSpewer(texture, 60, particle_lifetime_max) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One, Colour = starBreakAdditive, - Direction = SpewDirection.None, + ParticleGravity = particle_gravity, + CreateParticle = createParticle, }, }; @@ -85,6 +94,39 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy kiaiSpewer.Active.Value = kiaiHitObject != null; } + private Vector2? cursorScreenPosition; + + private const double max_velocity_frame_length = 15; + private double velocityFrameLength; + private Vector2 totalPosDifference; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (cursorScreenPosition == null) + { + cursorScreenPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); + } + + // calculate cursor velocity. + totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; + cursorScreenPosition = e.ScreenSpaceMousePosition; + + velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime); + + if (velocityFrameLength > max_velocity_frame_length) + { + cursorVelocity = totalPosDifference / (float)velocityFrameLength; + + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; + } + + return base.OnMouseMove(e); + } + public bool OnPressed(OsuAction action) { handleInput(action, true); @@ -111,125 +153,53 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy rightPressed = pressed; break; } + } + + private ParticleSpewer.FallingParticle? createParticle() + { + if (!cursorScreenPosition.HasValue) return null; + + return new ParticleSpewer.FallingParticle + { + StartPosition = ToLocalSpace(cursorScreenPosition.Value), + Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), + StartAngle = (float)(RNG.NextDouble() * 4 - 2), + EndAngle = RNG.NextSingle(-2f, 2f), + EndScale = RNG.NextSingle(2f), + Velocity = cursorVelocity * 40, + }; + } + + private ParticleSpewer.FallingParticle? createBreakParticle() + { + var baseParticle = createParticle(); + if (!baseParticle.HasValue) return null; + + var p = baseParticle.Value; if (leftPressed && rightPressed) - breakSpewer.Direction = SpewDirection.Omni; + { + p.Velocity += new Vector2( + RNG.NextSingle(-460f, 460f), + RNG.NextSingle(-160f, 160f) + ); + } else if (leftPressed) - breakSpewer.Direction = SpewDirection.Left; + { + p.Velocity += new Vector2( + RNG.NextSingle(-460f, 0), + RNG.NextSingle(-40f, 40f) + ); + } else if (rightPressed) - breakSpewer.Direction = SpewDirection.Right; - else - breakSpewer.Direction = SpewDirection.None; - } - - private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition - { - private const int particle_lifetime_min = 300; - private const int particle_lifetime_max = 1000; - - public SpewDirection Direction { get; set; } - - protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; - protected override float ParticleGravity => 240; - - public LegacyCursorParticleSpewer(Texture texture, int perSecond) - : base(texture, perSecond, particle_lifetime_max) { - Active.BindValueChanged(_ => resetVelocityCalculation()); + p.Velocity += new Vector2( + RNG.NextSingle(0, 460f), + RNG.NextSingle(-40f, 40f) + ); } - private Vector2? cursorScreenPosition; - private Vector2 cursorVelocity; - - private const double max_velocity_frame_length = 15; - private double velocityFrameLength; - private Vector2 totalPosDifference; - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnMouseMove(MouseMoveEvent e) - { - if (cursorScreenPosition == null) - { - cursorScreenPosition = e.ScreenSpaceMousePosition; - return base.OnMouseMove(e); - } - - // calculate cursor velocity. - totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; - cursorScreenPosition = e.ScreenSpaceMousePosition; - - velocityFrameLength += Clock.ElapsedFrameTime; - - if (velocityFrameLength > max_velocity_frame_length) - { - cursorVelocity = totalPosDifference / (float)velocityFrameLength; - - totalPosDifference = Vector2.Zero; - velocityFrameLength = 0; - } - - return base.OnMouseMove(e); - } - - private void resetVelocityCalculation() - { - cursorScreenPosition = null; - totalPosDifference = Vector2.Zero; - velocityFrameLength = 0; - } - - protected override FallingParticle CreateParticle() => - new FallingParticle - { - StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero), - Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), - StartAngle = (float)(RNG.NextDouble() * 4 - 2), - EndAngle = RNG.NextSingle(-2f, 2f), - EndScale = RNG.NextSingle(2f), - Velocity = getVelocity(), - }; - - private Vector2 getVelocity() - { - Vector2 velocity = Vector2.Zero; - - switch (Direction) - { - case SpewDirection.Left: - velocity = new Vector2( - RNG.NextSingle(-460f, 0), - RNG.NextSingle(-40f, 40f) - ); - break; - - case SpewDirection.Right: - velocity = new Vector2( - RNG.NextSingle(0, 460f), - RNG.NextSingle(-40f, 40f) - ); - break; - - case SpewDirection.Omni: - velocity = new Vector2( - RNG.NextSingle(-460f, 460f), - RNG.NextSingle(-160f, 160f) - ); - break; - } - - velocity += cursorVelocity * 40; - - return velocity; - } - } - - private enum SpewDirection - { - None, - Left, - Right, - Omni, + return p; } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 086b1c2ac2..390534745d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -5,7 +5,6 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics; @@ -17,7 +16,12 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneParticleSpewer : OsuTestScene { - private TestParticleSpewer spewer; + private const int max_particle_duration = 1500; + + private float particleMaxVelocity = 0.5f; + private Vector2 particleSpawnPosition = new Vector2(0.5f); + + private ParticleSpewer spewer; [Resolved] private SkinManager skinManager { get; set; } @@ -28,11 +32,11 @@ namespace osu.Game.Tests.Visual.Gameplay Child = spewer = createSpewer(); AddToggleStep("toggle spawning", value => spewer.Active.Value = value); - AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); - AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => particleMaxVelocity = value); + AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.ParticleGravity = value); AddStep("move to new location", () => { - spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); + this.TransformTo(nameof(particleSpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); }); } @@ -55,47 +59,29 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } - private TestParticleSpewer createSpewer() => - new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) + private ParticleSpewer createSpewer() => + new ParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2"), 1500, max_particle_duration) { Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, Position = new Vector2(0.5f), Size = new Vector2(0.5f), + CreateParticle = createParticle, }; - private class TestParticleSpewer : ParticleSpewer - { - private const int lifetime = 1500; - private const int rate = 250; - - public float Gravity; - - public float MaxVelocity = 0.25f; - - public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f); - - protected override float ParticleGravity => Gravity; - - public TestParticleSpewer(Texture texture) - : base(texture, rate, lifetime) + private ParticleSpewer.FallingParticle? createParticle() => + new ParticleSpewer.FallingParticle { - } - - protected override FallingParticle CreateParticle() => - new FallingParticle - { - Velocity = new Vector2( - RNG.NextSingle(-MaxVelocity, MaxVelocity), - RNG.NextSingle(-MaxVelocity, MaxVelocity) - ), - StartPosition = SpawnPosition, - Duration = RNG.NextSingle(lifetime), - StartAngle = RNG.NextSingle(MathF.PI * 2), - EndAngle = RNG.NextSingle(MathF.PI * 2), - EndScale = RNG.NextSingle(0.5f, 1.5f) - }; - } + Velocity = new Vector2( + RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity), + RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity) + ), + StartPosition = particleSpawnPosition, + Duration = RNG.NextSingle(max_particle_duration), + StartAngle = RNG.NextSingle(MathF.PI * 2), + EndAngle = RNG.NextSingle(MathF.PI * 2), + EndScale = RNG.NextSingle(0.5f, 1.5f) + }; } } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 1ad4672238..911f5894e7 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -13,14 +13,14 @@ using osuTK; namespace osu.Game.Graphics { - public abstract class ParticleSpewer : Sprite + public class ParticleSpewer : Sprite { private readonly FallingParticle[] particles; private int currentIndex; private double lastParticleAdded; private readonly double cooldown; - private readonly double maxLifetime; + private readonly double maxDuration; /// /// Determines whether particles are being spawned. @@ -29,20 +29,24 @@ namespace osu.Game.Graphics public override bool IsPresent => base.IsPresent && hasActiveParticles; - protected virtual bool CanSpawnParticles => true; - protected virtual float ParticleGravity => 0; + /// + /// Called each time a new particle should be spawned. + /// + public Func CreateParticle = () => new FallingParticle(); - private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxLifetime) > Time.Current; + public float ParticleGravity; - protected ParticleSpewer(Texture texture, int perSecond, double maxLifetime) + private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxDuration) > Time.Current; + + public ParticleSpewer(Texture texture, int perSecond, double maxDuration) { Texture = texture; Blending = BlendingParameters.Additive; - particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxLifetime / 1000)]; + particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxDuration / 1000)]; cooldown = 1000f / perSecond; - this.maxLifetime = maxLifetime; + this.maxDuration = maxDuration; } protected override void Update() @@ -53,25 +57,25 @@ namespace osu.Game.Graphics // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && Time.Current > lastParticleAdded + cooldown) { var newParticle = CreateParticle(); - newParticle.StartTime = (float)Time.Current; - particles[currentIndex] = newParticle; + if (newParticle.HasValue) + { + var particle = newParticle.Value; + particle.StartTime = (float)Time.Current; - currentIndex = (currentIndex + 1) % particles.Length; - lastParticleAdded = Time.Current; + particles[currentIndex] = particle; + + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; + } } Invalidate(Invalidation.DrawNode); } - /// - /// Called each time a new particle should be spawned. - /// - protected virtual FallingParticle CreateParticle() => new FallingParticle(); - protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); # region DrawNode @@ -82,7 +86,7 @@ namespace osu.Game.Graphics protected new ParticleSpewer Source => (ParticleSpewer)base.Source; - private readonly float maxLifetime; + private readonly float maxDuration; private float currentTime; private float gravity; @@ -93,7 +97,7 @@ namespace osu.Game.Graphics : base(source) { particles = new FallingParticle[Source.particles.Length]; - maxLifetime = (float)Source.maxLifetime; + maxDuration = (float)Source.maxDuration; } public override void ApplyState() @@ -124,7 +128,7 @@ namespace osu.Game.Graphics var alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; - var pos = p.PositionAtTime(timeSinceStart, gravity, maxLifetime); + var pos = p.PositionAtTime(timeSinceStart, gravity, maxDuration); var scale = p.ScaleAtTime(timeSinceStart); var angle = p.AngleAtTime(timeSinceStart); @@ -174,7 +178,7 @@ namespace osu.Game.Graphics #endregion - protected struct FallingParticle + public struct FallingParticle { public float StartTime; public Vector2 StartPosition; From af4c3727d77a16e2534df9bbf452336b5c544342 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 04:39:35 +0200 Subject: [PATCH 58/92] Fix build errors --- .../Skinning/Legacy/LegacyCursorParticles.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index d4e5bdd46f..ba5a03c1f1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -127,15 +127,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return base.OnMouseMove(e); } - public bool OnPressed(OsuAction action) + public bool OnPressed(KeyBindingPressEvent e) { - handleInput(action, true); + handleInput(e.Action, true); return false; } - public void OnReleased(OsuAction action) + public void OnReleased(KeyBindingReleaseEvent e) { - handleInput(action, false); + handleInput(e.Action, false); } private bool leftPressed; From 761da45f6a41b30d90931819e168ea1204571d99 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:00:56 +0200 Subject: [PATCH 59/92] Revert `af4c3727d77a16e2534df9bbf452336b5c544342` --- .idea/.idea.osu/.idea/indexLayout.xml | 2 +- .../Skinning/Legacy/LegacyCursorParticles.cs | 217 ++++++++++-------- .../Gameplay/TestSceneParticleSpewer.cs | 62 +++-- osu.Game/Graphics/ParticleSpewer.cs | 34 ++- 4 files changed, 178 insertions(+), 137 deletions(-) diff --git a/.idea/.idea.osu/.idea/indexLayout.xml b/.idea/.idea.osu/.idea/indexLayout.xml index 27ba142e96..7b08163ceb 100644 --- a/.idea/.idea.osu/.idea/indexLayout.xml +++ b/.idea/.idea.osu/.idea/indexLayout.xml @@ -1,6 +1,6 @@ - + diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index ba5a03c1f1..e1b7dbc3e3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -20,17 +21,12 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler, IRequireHighFrequencyMousePosition + public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler { - private const int particle_lifetime_min = 300; - private const int particle_lifetime_max = 1000; - private const float particle_gravity = 240; - public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true; - private Vector2 cursorVelocity; - private ParticleSpewer breakSpewer; - private ParticleSpewer kiaiSpewer; + private LegacyCursorParticleSpewer breakSpewer; + private LegacyCursorParticleSpewer kiaiSpewer; [Resolved(canBeNull: true)] private Player player { get; set; } @@ -50,25 +46,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy texture.ScaleAdjust *= 1.6f; } - RelativeSizeAxes = Axes.Both; - Anchor = Anchor.Centre; InternalChildren = new[] { - breakSpewer = new ParticleSpewer(texture, 20, particle_lifetime_max) + breakSpewer = new LegacyCursorParticleSpewer(texture, 20) { - RelativeSizeAxes = Axes.Both, - Size = Vector2.One, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = starBreakAdditive, - ParticleGravity = particle_gravity, - CreateParticle = createBreakParticle, + Direction = SpewDirection.None, }, - kiaiSpewer = new ParticleSpewer(texture, 60, particle_lifetime_max) + kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60) { - RelativeSizeAxes = Axes.Both, - Size = Vector2.One, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = starBreakAdditive, - ParticleGravity = particle_gravity, - CreateParticle = createParticle, + Direction = SpewDirection.None, }, }; @@ -94,39 +86,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy kiaiSpewer.Active.Value = kiaiHitObject != null; } - private Vector2? cursorScreenPosition; - - private const double max_velocity_frame_length = 15; - private double velocityFrameLength; - private Vector2 totalPosDifference; - - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - - protected override bool OnMouseMove(MouseMoveEvent e) - { - if (cursorScreenPosition == null) - { - cursorScreenPosition = e.ScreenSpaceMousePosition; - return base.OnMouseMove(e); - } - - // calculate cursor velocity. - totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; - cursorScreenPosition = e.ScreenSpaceMousePosition; - - velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime); - - if (velocityFrameLength > max_velocity_frame_length) - { - cursorVelocity = totalPosDifference / (float)velocityFrameLength; - - totalPosDifference = Vector2.Zero; - velocityFrameLength = 0; - } - - return base.OnMouseMove(e); - } - public bool OnPressed(KeyBindingPressEvent e) { handleInput(e.Action, true); @@ -153,53 +112,125 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy rightPressed = pressed; break; } - } - - private ParticleSpewer.FallingParticle? createParticle() - { - if (!cursorScreenPosition.HasValue) return null; - - return new ParticleSpewer.FallingParticle - { - StartPosition = ToLocalSpace(cursorScreenPosition.Value), - Duration = RNG.NextSingle(particle_lifetime_min, particle_lifetime_max), - StartAngle = (float)(RNG.NextDouble() * 4 - 2), - EndAngle = RNG.NextSingle(-2f, 2f), - EndScale = RNG.NextSingle(2f), - Velocity = cursorVelocity * 40, - }; - } - - private ParticleSpewer.FallingParticle? createBreakParticle() - { - var baseParticle = createParticle(); - if (!baseParticle.HasValue) return null; - - var p = baseParticle.Value; if (leftPressed && rightPressed) - { - p.Velocity += new Vector2( - RNG.NextSingle(-460f, 460f), - RNG.NextSingle(-160f, 160f) - ); - } + breakSpewer.Direction = SpewDirection.Omni; else if (leftPressed) - { - p.Velocity += new Vector2( - RNG.NextSingle(-460f, 0), - RNG.NextSingle(-40f, 40f) - ); - } + breakSpewer.Direction = SpewDirection.Left; else if (rightPressed) + breakSpewer.Direction = SpewDirection.Right; + else + breakSpewer.Direction = SpewDirection.None; + } + + private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition + { + private const int particle_duration_min = 300; + private const int particle_duration_max = 1000; + + public SpewDirection Direction { get; set; } + + protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue; + protected override float ParticleGravity => 240; + + public LegacyCursorParticleSpewer(Texture texture, int perSecond) + : base(texture, perSecond, particle_duration_max) { - p.Velocity += new Vector2( - RNG.NextSingle(0, 460f), - RNG.NextSingle(-40f, 40f) - ); + Active.BindValueChanged(_ => resetVelocityCalculation()); } - return p; + private Vector2? cursorScreenPosition; + private Vector2 cursorVelocity; + + private const double max_velocity_frame_length = 15; + private double velocityFrameLength; + private Vector2 totalPosDifference; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + if (cursorScreenPosition == null) + { + cursorScreenPosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); + } + + // calculate cursor velocity. + totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value; + cursorScreenPosition = e.ScreenSpaceMousePosition; + + velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime); + + if (velocityFrameLength > max_velocity_frame_length) + { + cursorVelocity = totalPosDifference / (float)velocityFrameLength; + + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; + } + + return base.OnMouseMove(e); + } + + private void resetVelocityCalculation() + { + cursorScreenPosition = null; + totalPosDifference = Vector2.Zero; + velocityFrameLength = 0; + } + + protected override FallingParticle CreateParticle() => + new FallingParticle + { + StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero), + Duration = RNG.NextSingle(particle_duration_min, particle_duration_max), + StartAngle = (float)(RNG.NextDouble() * 4 - 2), + EndAngle = RNG.NextSingle(-2f, 2f), + EndScale = RNG.NextSingle(2f), + Velocity = getVelocity(), + }; + + private Vector2 getVelocity() + { + Vector2 velocity = Vector2.Zero; + + switch (Direction) + { + case SpewDirection.Left: + velocity = new Vector2( + RNG.NextSingle(-460f, 0), + RNG.NextSingle(-40f, 40f) + ); + break; + + case SpewDirection.Right: + velocity = new Vector2( + RNG.NextSingle(0, 460f), + RNG.NextSingle(-40f, 40f) + ); + break; + + case SpewDirection.Omni: + velocity = new Vector2( + RNG.NextSingle(-460f, 460f), + RNG.NextSingle(-160f, 160f) + ); + break; + } + + velocity += cursorVelocity * 40; + + return velocity; + } + } + + private enum SpewDirection + { + None, + Left, + Right, + Omni, } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 390534745d..2f107c8300 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -5,6 +5,7 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Textures; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Graphics; @@ -16,12 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestFixture] public class TestSceneParticleSpewer : OsuTestScene { - private const int max_particle_duration = 1500; - - private float particleMaxVelocity = 0.5f; - private Vector2 particleSpawnPosition = new Vector2(0.5f); - - private ParticleSpewer spewer; + private TestParticleSpewer spewer; [Resolved] private SkinManager skinManager { get; set; } @@ -32,11 +28,11 @@ namespace osu.Game.Tests.Visual.Gameplay Child = spewer = createSpewer(); AddToggleStep("toggle spawning", value => spewer.Active.Value = value); - AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => particleMaxVelocity = value); - AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.ParticleGravity = value); + AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value); + AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value); AddStep("move to new location", () => { - this.TransformTo(nameof(particleSpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); + spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out); }); } @@ -59,29 +55,47 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } - private ParticleSpewer createSpewer() => - new ParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2"), 1500, max_particle_duration) + private TestParticleSpewer createSpewer() => + new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) { Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, Position = new Vector2(0.5f), Size = new Vector2(0.5f), - CreateParticle = createParticle, }; - private ParticleSpewer.FallingParticle? createParticle() => - new ParticleSpewer.FallingParticle + private class TestParticleSpewer : ParticleSpewer + { + private const int max_duration = 1500; + private const int rate = 250; + + public float Gravity; + + public float MaxVelocity = 0.25f; + + public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f); + + protected override float ParticleGravity => Gravity; + + public TestParticleSpewer(Texture texture) + : base(texture, rate, max_duration) { - Velocity = new Vector2( - RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity), - RNG.NextSingle(-particleMaxVelocity, particleMaxVelocity) - ), - StartPosition = particleSpawnPosition, - Duration = RNG.NextSingle(max_particle_duration), - StartAngle = RNG.NextSingle(MathF.PI * 2), - EndAngle = RNG.NextSingle(MathF.PI * 2), - EndScale = RNG.NextSingle(0.5f, 1.5f) - }; + } + + protected override FallingParticle CreateParticle() => + new FallingParticle + { + Velocity = new Vector2( + RNG.NextSingle(-MaxVelocity, MaxVelocity), + RNG.NextSingle(-MaxVelocity, MaxVelocity) + ), + StartPosition = SpawnPosition, + Duration = RNG.NextSingle(max_duration), + StartAngle = RNG.NextSingle(MathF.PI * 2), + EndAngle = RNG.NextSingle(MathF.PI * 2), + EndScale = RNG.NextSingle(0.5f, 1.5f) + }; + } } } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 911f5894e7..492e439352 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -13,7 +13,7 @@ using osuTK; namespace osu.Game.Graphics { - public class ParticleSpewer : Sprite + public abstract class ParticleSpewer : Sprite { private readonly FallingParticle[] particles; private int currentIndex; @@ -29,16 +29,12 @@ namespace osu.Game.Graphics public override bool IsPresent => base.IsPresent && hasActiveParticles; - /// - /// Called each time a new particle should be spawned. - /// - public Func CreateParticle = () => new FallingParticle(); - - public float ParticleGravity; + protected virtual bool CanSpawnParticles => true; + protected virtual float ParticleGravity => 0; private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxDuration) > Time.Current; - public ParticleSpewer(Texture texture, int perSecond, double maxDuration) + protected ParticleSpewer(Texture texture, int perSecond, double maxDuration) { Texture = texture; Blending = BlendingParameters.Additive; @@ -57,25 +53,25 @@ namespace osu.Game.Graphics // this can happen when seeking in replays. if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - if (Active.Value && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) { var newParticle = CreateParticle(); + newParticle.StartTime = (float)Time.Current; - if (newParticle.HasValue) - { - var particle = newParticle.Value; - particle.StartTime = (float)Time.Current; + particles[currentIndex] = newParticle; - particles[currentIndex] = particle; - - currentIndex = (currentIndex + 1) % particles.Length; - lastParticleAdded = Time.Current; - } + currentIndex = (currentIndex + 1) % particles.Length; + lastParticleAdded = Time.Current; } Invalidate(Invalidation.DrawNode); } + /// + /// Called each time a new particle should be spawned. + /// + protected virtual FallingParticle CreateParticle() => new FallingParticle(); + protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this); # region DrawNode @@ -178,7 +174,7 @@ namespace osu.Game.Graphics #endregion - public struct FallingParticle + protected struct FallingParticle { public float StartTime; public Vector2 StartPosition; From d5a10e922177f9cf7c7bc7f465f7c3e1d9a9807e Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:47:20 +0200 Subject: [PATCH 60/92] Fix particles not spawning if `Time.Current` is negative --- osu.Game/Graphics/ParticleSpewer.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 492e439352..a52f749f4a 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -49,11 +49,7 @@ namespace osu.Game.Graphics { base.Update(); - // reset cooldown if the clock was rewound. - // this can happen when seeking in replays. - if (lastParticleAdded > Time.Current) lastParticleAdded = 0; - - if (Active.Value && CanSpawnParticles && Time.Current > lastParticleAdded + cooldown) + if (Active.Value && CanSpawnParticles && Math.Abs(Time.Current - lastParticleAdded) > cooldown) { var newParticle = CreateParticle(); newParticle.StartTime = (float)Time.Current; @@ -112,9 +108,6 @@ namespace osu.Game.Graphics { foreach (var p in particles) { - // ignore particles that weren't initialized. - if (p.StartTime <= 0) continue; - var timeSinceStart = currentTime - p.StartTime; // ignore particles from the future. From 0b593fac5c243438d2f0c9200f74eb7d2abdddf3 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:49:09 +0200 Subject: [PATCH 61/92] Scope down DrawNode's `source` parameter --- osu.Game/Graphics/ParticleSpewer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index a52f749f4a..90216da85a 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -85,7 +85,7 @@ namespace osu.Game.Graphics private Axes relativePositionAxes; private Vector2 sourceSize; - public ParticleSpewerDrawNode(Sprite source) + public ParticleSpewerDrawNode(ParticleSpewer source) : base(source) { particles = new FallingParticle[Source.particles.Length]; From 9c90dd539f3e6842cc03027cad368da5a9bb5d57 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 15:06:15 +0200 Subject: [PATCH 62/92] Use `Interpolation.Lerp` --- osu.Game/Graphics/ParticleSpewer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 90216da85a..466bf04369 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; using osuTK; namespace osu.Game.Graphics @@ -179,9 +180,9 @@ namespace osu.Game.Graphics public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart); - public float ScaleAtTime(float timeSinceStart) => 1 + (EndScale - 1) * progressAtTime(timeSinceStart); + public float ScaleAtTime(float timeSinceStart) => (float)Interpolation.Lerp(1, EndScale, progressAtTime(timeSinceStart)); - public float AngleAtTime(float timeSinceStart) => StartAngle + (EndAngle - StartAngle) * progressAtTime(timeSinceStart); + public float AngleAtTime(float timeSinceStart) => (float)Interpolation.Lerp(StartAngle, EndAngle, progressAtTime(timeSinceStart)); public Vector2 PositionAtTime(float timeSinceStart, float gravity, float maxDuration) { From 366dbf5c3dbae9993b78f1f55c2ac73dd7806944 Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sun, 19 Sep 2021 15:35:03 +0200 Subject: [PATCH 63/92] Add test for time jumps --- .../Gameplay/TestSceneParticleSpewer.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs index 2f107c8300..ce5cd629e0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Testing; +using osu.Framework.Timing; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Skinning; @@ -55,6 +56,26 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("is not present", () => !spewer.IsPresent); } + [Test] + public void TestTimeJumps() + { + ManualClock testClock = new ManualClock(); + + AddStep("prepare clock", () => + { + testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -3; + spewer.Clock = new FramedClock(testClock); + }); + AddStep("start spewer", () => spewer.Active.Value = true); + AddAssert("spawned first particle", () => spewer.TotalCreatedParticles == 1); + + AddStep("move clock forward", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * 3); + AddAssert("spawned second particle", () => spewer.TotalCreatedParticles == 2); + + AddStep("move clock backwards", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -1); + AddAssert("spawned third particle", () => spewer.TotalCreatedParticles == 3); + } + private TestParticleSpewer createSpewer() => new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2")) { @@ -67,9 +88,11 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestParticleSpewer : ParticleSpewer { - private const int max_duration = 1500; + public const int MAX_DURATION = 1500; private const int rate = 250; + public int TotalCreatedParticles { get; private set; } + public float Gravity; public float MaxVelocity = 0.25f; @@ -79,23 +102,27 @@ namespace osu.Game.Tests.Visual.Gameplay protected override float ParticleGravity => Gravity; public TestParticleSpewer(Texture texture) - : base(texture, rate, max_duration) + : base(texture, rate, MAX_DURATION) { } - protected override FallingParticle CreateParticle() => - new FallingParticle + protected override FallingParticle CreateParticle() + { + TotalCreatedParticles++; + + return new FallingParticle { Velocity = new Vector2( RNG.NextSingle(-MaxVelocity, MaxVelocity), RNG.NextSingle(-MaxVelocity, MaxVelocity) ), StartPosition = SpawnPosition, - Duration = RNG.NextSingle(max_duration), + Duration = RNG.NextSingle(MAX_DURATION), StartAngle = RNG.NextSingle(MathF.PI * 2), EndAngle = RNG.NextSingle(MathF.PI * 2), EndScale = RNG.NextSingle(0.5f, 1.5f) }; + } } } } From 93ca615c022f4d00a972d97ae947426447b14333 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Sun, 19 Sep 2021 14:15:22 -0400 Subject: [PATCH 64/92] Add tests for clearing `HitErrorMeter` Works with both `BarHitErrorMeter` and `ColourHitErrorMeter` --- .../Visual/Gameplay/TestSceneHitErrorMeter.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs index 7accaef818..1ba0965ceb 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHitErrorMeter.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; @@ -137,6 +138,23 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("no circle added", () => !this.ChildrenOfType().Any()); } + [Test] + public void TestClear() + { + AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1)); + + AddStep("hit", () => newJudgement(0.2D)); + AddAssert("bar added", () => this.ChildrenOfType().All( + meter => meter.ChildrenOfType().Count() == 1)); + AddAssert("circle added", () => this.ChildrenOfType().All( + meter => meter.ChildrenOfType().Count() == 1)); + + AddStep("clear", () => this.ChildrenOfType().ForEach(meter => meter.Clear())); + + AddAssert("bar cleared", () => !this.ChildrenOfType().Any()); + AddAssert("colour cleared", () => !this.ChildrenOfType().Any()); + } + private void recreateDisplay(HitWindows hitWindows, float overallDifficulty) { hitWindows?.SetDifficulty(overallDifficulty); From ab213e20107da27a5b92f92f49dbd65d371ed97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Sep 2021 21:09:03 +0200 Subject: [PATCH 65/92] Add missing licence headers --- osu.Game/Overlays/Login/LoginForm.cs | 3 +++ osu.Game/Overlays/Login/UserAction.cs | 3 +++ osu.Game/Overlays/Login/UserDropdown.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/osu.Game/Overlays/Login/LoginForm.cs b/osu.Game/Overlays/Login/LoginForm.cs index 9d229c2b3e..e43b84d52a 100644 --- a/osu.Game/Overlays/Login/LoginForm.cs +++ b/osu.Game/Overlays/Login/LoginForm.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using osu.Framework.Allocation; using osu.Framework.Graphics; diff --git a/osu.Game/Overlays/Login/UserAction.cs b/osu.Game/Overlays/Login/UserAction.cs index 440d6ea456..07b6b4bf7e 100644 --- a/osu.Game/Overlays/Login/UserAction.cs +++ b/osu.Game/Overlays/Login/UserAction.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System.ComponentModel; namespace osu.Game.Overlays.Login diff --git a/osu.Game/Overlays/Login/UserDropdown.cs b/osu.Game/Overlays/Login/UserDropdown.cs index 80f6c7113b..ac4e7f8eda 100644 --- a/osu.Game/Overlays/Login/UserDropdown.cs +++ b/osu.Game/Overlays/Login/UserDropdown.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; From 20eeb36567c2c2afb232b7580729c4c52f4d2ec6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 18:35:47 +0900 Subject: [PATCH 66/92] Avoid `AliveObject` enumeration when not in kiai section --- .../Skinning/Legacy/LegacyCursorParticles.cs | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs index e1b7dbc3e3..2b0dfba1dd 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Textures; @@ -12,6 +13,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Screens.Play; @@ -32,7 +34,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private Player player { get; set; } [Resolved(canBeNull: true)] - private OsuPlayfield osuPlayfield { get; set; } + private OsuPlayfield playfield { get; set; } + + [Resolved(canBeNull: true)] + private GameplayBeatmap gameplayBeatmap { get; set; } + + [Resolved(canBeNull: true)] + private GameplayClock gameplayClock { get; set; } [BackgroundDependencyLoader] private void load(ISkinSource skin, OsuColour colours) @@ -65,27 +73,39 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy }; if (player != null) - { - breakSpewer.Active.BindTarget = player.IsBreakTime; - } + ((IBindable)breakSpewer.Active).BindTo(player.IsBreakTime); } protected override void Update() { - if (osuPlayfield == null) return; + if (playfield == null || gameplayBeatmap == null) return; - // find active kiai slider or spinner. - var kiaiHitObject = osuPlayfield.HitObjectContainer.AliveObjects.FirstOrDefault(h => - h.HitObject.Kiai && - ( - (h is DrawableSlider slider && slider.Tracking.Value) || - (h is DrawableSpinner spinner && spinner.RotationTracker.Tracking) - ) - ); + DrawableHitObject kiaiHitObject = null; + + // Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary. + if (gameplayBeatmap.ControlPointInfo.EffectPointAt(gameplayBeatmap.Time.Current).KiaiMode) + kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking); kiaiSpewer.Active.Value = kiaiHitObject != null; } + private bool isTracking(DrawableHitObject h) + { + if (!h.HitObject.Kiai) + return false; + + switch (h) + { + case DrawableSlider slider: + return slider.Tracking.Value; + + case DrawableSpinner spinner: + return spinner.RotationTracker.Tracking; + } + + return false; + } + public bool OnPressed(KeyBindingPressEvent e) { handleInput(e.Action, true); From 10fe2382b08abf113d0ca020f11307284041cb9e Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 10:07:42 -0400 Subject: [PATCH 67/92] Address most issues --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 5 ++++- osu.Game/Screens/Play/Player.cs | 5 ++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 2b5228cab0..604df0b774 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -280,6 +280,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - public override void Clear() => judgementsContainer.Clear(true); + protected override void Clear() => judgementsContainer.Clear(); } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index ea64d1f4d9..19ba1910e6 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } - public override void Clear() => judgementsFlow.Clear(true); + protected override void Clear() => judgementsFlow.Clear(); private class JudgementFlow : FillFlowContainer { diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 1871519ab5..f9d4d89d1b 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters /// Invoked by when the active seeks through the current beatmap. /// Any inheritors of should have this method clear their container that displays the hit error results. /// - public abstract void Clear(); + protected abstract void Clear(); protected override void Dispose(bool isDisposing) { @@ -85,6 +85,9 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (processor != null) processor.NewJudgement -= OnNewJudgement; + + if (player != null) + player.OnSeek -= Clear; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index e982d02baf..cde3cda369 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,7 +69,10 @@ namespace osu.Game.Screens.Play public Action RestartRequested; - public Action OnSeek; + /// + /// Invoked when a seek has been performed via + /// + public event Action OnSeek; public bool HasFailed { get; private set; } From 36a20ab0b365d77878a76c0fbf4444bd6b67380f Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 10:22:36 -0400 Subject: [PATCH 68/92] Resolve failed test compilation --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 2 +- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 2 +- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index 604df0b774..39dafaffad 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -280,6 +280,6 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - protected override void Clear() => judgementsContainer.Clear(); + public override void Clear() => judgementsContainer.Clear(); } } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 19ba1910e6..5012be7249 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } - protected override void Clear() => judgementsFlow.Clear(); + public override void Clear() => judgementsFlow.Clear(); private class JudgementFlow : FillFlowContainer { diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index f9d4d89d1b..a864753b0c 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters /// Invoked by when the active seeks through the current beatmap. /// Any inheritors of should have this method clear their container that displays the hit error results. /// - protected abstract void Clear(); + public abstract void Clear(); protected override void Dispose(bool isDisposing) { From 9a1db04920ea9034980d70bd9f0d26ad45862ca2 Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 10:28:58 -0400 Subject: [PATCH 69/92] Resolve `GameplayClockContainer` instead of `Player` --- osu.Game/Screens/Play/GameplayClockContainer.cs | 8 ++++++++ .../Play/HUD/HitErrorMeters/HitErrorMeter.cs | 14 +++++++------- osu.Game/Screens/Play/Player.cs | 6 ------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index f791da80c8..0c9b827a41 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -13,6 +13,7 @@ namespace osu.Game.Screens.Play /// /// Encapsulates gameplay timing logic and provides a via DI for gameplay components to use. /// + [Cached] public abstract class GameplayClockContainer : Container, IAdjustableClock { /// @@ -35,6 +36,11 @@ namespace osu.Game.Screens.Play /// protected IClock SourceClock { get; private set; } + /// + /// Invoked when a seek has been performed via + /// + public event Action OnSeek; + /// /// Creates a new . /// @@ -88,6 +94,8 @@ namespace osu.Game.Screens.Play // Manually process to make sure the gameplay clock is correctly updated after a seek. GameplayClock.UnderlyingClock.ProcessFrame(); + + OnSeek?.Invoke(); } /// diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index a864753b0c..c7b06a3a2c 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private OsuColour colours { get; set; } [Resolved(canBeNull: true)] - private Player player { get; set; } + private GameplayClockContainer gameplayClockContainer { get; set; } public bool UsesFixedAnchor { get; set; } @@ -37,8 +37,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters { base.LoadComplete(); - if (player != null) - player.OnSeek += Clear; + if (gameplayClockContainer != null) + gameplayClockContainer.OnSeek += Clear; processor.NewJudgement += OnNewJudgement; } @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } /// - /// Invoked by when the active seeks through the current beatmap. + /// Invoked by . /// Any inheritors of should have this method clear their container that displays the hit error results. /// public abstract void Clear(); @@ -86,8 +86,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (processor != null) processor.NewJudgement -= OnNewJudgement; - if (player != null) - player.OnSeek -= Clear; + if (gameplayClockContainer != null) + gameplayClockContainer.OnSeek -= Clear; } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cde3cda369..a9a74d30d4 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -69,11 +69,6 @@ namespace osu.Game.Screens.Play public Action RestartRequested; - /// - /// Invoked when a seek has been performed via - /// - public event Action OnSeek; - public bool HasFailed { get; private set; } private Bindable mouseWheelDisabled; @@ -592,7 +587,6 @@ namespace osu.Game.Screens.Play public void Seek(double time) { GameplayClockContainer.Seek(time); - OnSeek?.Invoke(); } private ScheduledDelegate frameStablePlaybackResetDelegate; From fb416c79e906c7fd95c031f3f6de00778ac36f7f Mon Sep 17 00:00:00 2001 From: sh0ckR6 Date: Mon, 20 Sep 2021 15:01:03 -0400 Subject: [PATCH 70/92] Fully revert `Player` --- osu.Game/Screens/Play/Player.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a9a74d30d4..e8a2790c94 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -584,10 +584,7 @@ namespace osu.Game.Screens.Play /// Seek to a specific time in gameplay. /// /// The destination time to seek to. - public void Seek(double time) - { - GameplayClockContainer.Seek(time); - } + public void Seek(double time) => GameplayClockContainer.Seek(time); private ScheduledDelegate frameStablePlaybackResetDelegate; From b715b89edc3c2c4ea82db029effca7cda912f6d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 19:54:05 +0000 Subject: [PATCH 71/92] Bump SharpCompress from 0.28.3 to 0.29.0 in /osu.Game Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.28.3 to 0.29.0. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.28.3...0.29) --- updated-dependencies: - dependency-name: SharpCompress dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0460de6d72..e6afbe383a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -39,7 +39,7 @@ - + From 9ea9fa5f20db5b48dcf453aa92854285929324e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 11:38:04 +0900 Subject: [PATCH 72/92] Remove issue template to avoid the average use submitting issues --- .github/ISSUE_TEMPLATE/01-bug-issues.md | 30 ------------------------- .github/ISSUE_TEMPLATE/config.yml | 8 +++---- 2 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/01-bug-issues.md diff --git a/.github/ISSUE_TEMPLATE/01-bug-issues.md b/.github/ISSUE_TEMPLATE/01-bug-issues.md deleted file mode 100644 index 7026179259..0000000000 --- a/.github/ISSUE_TEMPLATE/01-bug-issues.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: Bug Report -about: Report a bug or crash to desktop ---- - - - - -**Describe the bug:** - -**Screenshots or videos showing encountered issue:** - -**osu!lazer version:** - -**Logs:** - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c62231e8e0..47a6a4c3d3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,12 +1,12 @@ blank_issues_enabled: false contact_links: - - name: Suggestions or feature request - url: https://github.com/ppy/osu/discussions/categories/ideas - about: Got something you think should change or be added? Search for or start a new discussion! - name: Help url: https://github.com/ppy/osu/discussions/categories/q-a about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section! + - name: Suggestions or feature request + url: https://github.com/ppy/osu/discussions/categories/ideas + about: Got something you think should change or be added? Search for or start a new discussion! - name: osu!stable issues url: https://github.com/ppy/osu-stable-issues - about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports. + about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support. From e3542878045a16a72d0cc0a091c96c42eef8df9c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Sep 2021 14:36:49 +0900 Subject: [PATCH 73/92] Fix incorrect ruleset name --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 4274d01bab..92ca1e1a8b 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -33,7 +33,7 @@ jobs: ruleset: - { name: osu, id: 0 } - { name: taiko, id: 1 } - - { name: catch, id: 2 } + - { name: fruits, id: 2 } - { name: mania, id: 3 } services: From fa374e67e7cc001e77d7c2ff6fd49d1f25167b17 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Sep 2021 14:42:39 +0900 Subject: [PATCH 74/92] Single line condition --- .github/workflows/test-diffcalc.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 92ca1e1a8b..bfa4a3a8a0 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -22,10 +22,7 @@ jobs: runs-on: ubuntu-latest continue-on-error: true - if: | - github.event.issue.pull_request && - contains(github.event.comment.body, '!pp check') && - (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') strategy: fail-fast: false From 0f8e570b845f9266e03defc486c6fa2557ac3dd0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 12:18:19 +0900 Subject: [PATCH 75/92] Use top 1000 data --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index bfa4a3a8a0..0e53bd2c88 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -102,7 +102,7 @@ jobs: # Initial data imports - name: Download + import data run: | - PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') + PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top_1000 | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') # Set env variable for further steps. From fc5fd203d60c75bcc1ec0df3296a23d35e5d6aa3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 12:46:27 +0900 Subject: [PATCH 76/92] Output sql import process --- .github/workflows/test-diffcalc.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 0e53bd2c88..6a8ecbbae5 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -120,8 +120,9 @@ jobs: mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" - cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master - cat *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr + echo "Importing SQL..." + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' # Run diffcalc - name: Run diffcalc (master) From 6bfb31a63559358013447a6ec54609ebb6418112 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 14 Sep 2021 12:55:33 +0900 Subject: [PATCH 77/92] Install pv --- .github/workflows/test-diffcalc.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 6a8ecbbae5..54365ed5f6 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -49,6 +49,9 @@ jobs: echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" exit 1 + - name: Install dependencies + run: sudo apt-get install -y pv + - name: Verify MySQL connection from host run: | sudo apt-get install -y mysql-client From 9d4c5e9cb6bbf12efcc3a866621bbdfe778df1d3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Sep 2021 14:24:30 +0900 Subject: [PATCH 78/92] Add filename to output rows --- .github/workflows/test-diffcalc.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 54365ed5f6..79c1497dca 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -148,6 +148,7 @@ jobs: SELECT m.beatmap_id, m.mods, + b.filename, m.diff_unified as 'sr_master', p.diff_unified as 'sr_pr', (p.diff_unified - m.diff_unified) as 'diff' @@ -156,6 +157,8 @@ jobs: ON m.beatmap_id = p.beatmap_id AND m.mode = p.mode AND m.mods = p.mods + JOIN osu_pr.osu_beatmaps b + ON b.beatmap_id = p.beatmap_id WHERE abs(m.diff_unified - p.diff_unified) > 0.1 ORDER BY abs(m.diff_unified - p.diff_unified) DESC From 06e11484721f9cfa5c58003e9198008bab7387d0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Sep 2021 19:47:41 +0900 Subject: [PATCH 79/92] Run on self-hosted runner --- .github/workflows/test-diffcalc.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 79c1497dca..9c633a597d 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -12,6 +12,7 @@ on: env: DB_USER: root DB_HOST: 127.0.0.1 + DB_PORT: 33306:3306 CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 @@ -19,7 +20,7 @@ env: jobs: diffcalc: name: Diffcalc - runs-on: ubuntu-latest + runs-on: self-hosted continue-on-error: true if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') @@ -39,7 +40,7 @@ jobs: env: MYSQL_ALLOW_EMPTY_PASSWORD: yes ports: - - 3306:3306 + - 33306:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: @@ -54,8 +55,7 @@ jobs: - name: Verify MySQL connection from host run: | - sudo apt-get install -y mysql-client - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" - name: Create directory structure run: | @@ -120,12 +120,12 @@ jobs: cd $GITHUB_WORKSPACE/$PERFORMANCE_DATA_NAME - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" echo "Importing SQL..." - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' + { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' # Run diffcalc - name: Run diffcalc (master) @@ -144,7 +144,7 @@ jobs: # Print diffs - name: Print diffs run: | - mysql --host ${{ env.DB_HOST }} -u${{ env.DB_USER }} -e " + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e " SELECT m.beatmap_id, m.mods, From 6b3a37e262b1da27a58570178946609bbced85c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Sep 2021 03:49:30 +0900 Subject: [PATCH 80/92] Remove deps step --- .github/workflows/test-diffcalc.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 9c633a597d..565af6ad3c 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -50,9 +50,6 @@ jobs: echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" exit 1 - - name: Install dependencies - run: sudo apt-get install -y pv - - name: Verify MySQL connection from host run: | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" @@ -164,4 +161,4 @@ jobs: DESC LIMIT 10000;" - # Todo: Run ppcalc \ No newline at end of file + # Todo: Run ppcalc From 1d560ed1226e648d05bc003c6939273f21112b67 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Sep 2021 23:18:31 +0900 Subject: [PATCH 81/92] Use optimisation branch for now --- .github/workflows/test-diffcalc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 565af6ad3c..db1fa7a088 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -63,7 +63,8 @@ jobs: - name: Checkout osu (master) uses: actions/checkout@v2 with: - repository: ppy/osu + repository: peppy/osu + ref: 'diffcalc-optimisations' path: 'master/osu' - name: Checkout osu (pr) uses: actions/checkout@v2 From 7d5e4ae0a2386e82748219bf57d767610d0403de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Sep 2021 03:57:25 +0900 Subject: [PATCH 82/92] Drop service based mysql usage (use host instead) --- .github/workflows/test-diffcalc.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index db1fa7a088..188e074e07 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -12,7 +12,7 @@ on: env: DB_USER: root DB_HOST: 127.0.0.1 - DB_PORT: 33306:3306 + DB_PORT: 3306 CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 @@ -34,15 +34,6 @@ jobs: - { name: fruits, id: 2 } - { name: mania, id: 3 } - services: - mysql: - image: mysql:8.0 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - ports: - - 33306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - steps: - name: Verify ruleset if: contains(github.event.comment.body, matrix.ruleset.id) == false @@ -54,6 +45,11 @@ jobs: run: | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + - name: Drop previous databases + run: | + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_master" + mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_pr" + - name: Create directory structure run: | mkdir -p $GITHUB_WORKSPACE/master/ From 7c2b8fc650f02c25509d8cdf2ebf3db42b6777f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 00:37:12 +0900 Subject: [PATCH 83/92] Apply new skip insert rule --- .github/workflows/test-diffcalc.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 188e074e07..23b5c8bd2c 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -16,6 +16,7 @@ env: CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 + SKIP_INSERT_ATTRIBUTES: 1 jobs: diffcalc: @@ -71,12 +72,14 @@ jobs: - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 with: - repository: ppy/osu-difficulty-calculator + repository: peppy/osu-difficulty-calculator + ref: 'bypass-attrib-row-insert' path: 'master/osu-difficulty-calculator' - name: Checkout osu-difficulty-calculator (pr) uses: actions/checkout@v2 with: - repository: ppy/osu-difficulty-calculator + repository: peppy/osu-difficulty-calculator + ref: 'bypass-attrib-row-insert' path: 'pr/osu-difficulty-calculator' - name: Install .NET 5.0.x From a46fe5da75793867394fe1072268dc44b4a4a49c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 01:14:33 +0900 Subject: [PATCH 84/92] Simplify action --- .github/workflows/test-diffcalc.yml | 44 ++++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 23b5c8bd2c..b5937c1378 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -10,9 +10,6 @@ on: types: [ created ] env: - DB_USER: root - DB_HOST: 127.0.0.1 - DB_PORT: 3306 CONCURRENCY: 4 ALLOW_DOWNLOAD: 1 SAVE_DOWNLOADED: 1 @@ -44,12 +41,14 @@ jobs: - name: Verify MySQL connection from host run: | - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "SHOW DATABASES" + mysql -e "SHOW DATABASES" - name: Drop previous databases run: | - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_master" - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "DROP DATABASE IF EXISTS osu_pr" + for db in osu_master osu_pr + do + mysql -e "DROP DATABASE IF EXISTS $db" + done - name: Create directory structure run: | @@ -68,7 +67,6 @@ jobs: with: path: 'pr/osu' - # Checkout osu-difficulty-calculator - name: Checkout osu-difficulty-calculator (master) uses: actions/checkout@v2 with: @@ -99,7 +97,6 @@ jobs: ./UseLocalOsu.sh dotnet build - # Initial data imports - name: Download + import data run: | PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top_1000 | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g') @@ -112,19 +109,33 @@ jobs: wget https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 wget https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 tar -xf $BEATMAPS_DATA_NAME.tar.bz2 - cd $GITHUB_WORKSPACE/$PERFORMANCE_DATA_NAME + cd $PERFORMANCE_DATA_NAME - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_master" - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e "CREATE DATABASE osu_pr" + for db in osu_master osu_pr + do + echo "Setting up database $db.." - echo "Importing SQL..." - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_master; } 2>&1 | stdbuf -oL tr '\r' '\n' - { pv -f *.sql | mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} --database=osu_pr; } 2>&1 | stdbuf -oL tr '\r' '\n' + mysql -e "CREATE DATABASE $db" + + echo "Importing beatmaps..." + cat osu_beatmaps.sql | mysql $db + cat osu_beatmapsets.sql | mysql $db + + mysql $db -e "CREATE TABLE `osu_beatmap_difficulty` ( + `beatmap_id` int unsigned NOT NULL, + `mode` tinyint NOT NULL DEFAULT '0', + `mods` int unsigned NOT NULL, + `diff_unified` float NOT NULL, + `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`beatmap_id`,`mode`,`mods`), + KEY `diff_sort` (`mode`,`mods`,`diff_unified`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" + done - # Run diffcalc - name: Run diffcalc (master) env: DB_NAME: osu_master @@ -138,10 +149,9 @@ jobs: cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator/osu.Server.DifficultyCalculator dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }} - # Print diffs - name: Print diffs run: | - mysql --host ${{ env.DB_HOST }} --port ${{ env.DB_PORT }} -u${{ env.DB_USER }} -e " + mysql -e " SELECT m.beatmap_id, m.mods, From 5b9cab8b1fca1054a4eebea5af8613853b831cd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 01:44:07 +0900 Subject: [PATCH 85/92] Add more log output and quiet `wget` --- .github/workflows/test-diffcalc.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index b5937c1378..e9fd624168 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -107,10 +107,14 @@ jobs: cd $GITHUB_WORKSPACE - wget https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 - wget https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 - + echo "Downloading database dump $PERFORMANCE_DATA_NAME.." + wget -q -nc https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2 + echo "Extracting.." tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 + + echo "Downloading beatmap dump $BEATMAPS_DATA_NAME.." + wget -q https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + echo "Extracting.." tar -xf $BEATMAPS_DATA_NAME.tar.bz2 cd $PERFORMANCE_DATA_NAME @@ -121,19 +125,21 @@ jobs: mysql -e "CREATE DATABASE $db" - echo "Importing beatmaps..." + echo "Importing beatmaps.." cat osu_beatmaps.sql | mysql $db + echo "Importing beatmapsets.." cat osu_beatmapsets.sql | mysql $db - mysql $db -e "CREATE TABLE `osu_beatmap_difficulty` ( + echo "Creating table structure.." + mysql $db -e 'CREATE TABLE `osu_beatmap_difficulty` ( `beatmap_id` int unsigned NOT NULL, - `mode` tinyint NOT NULL DEFAULT '0', + `mode` tinyint NOT NULL DEFAULT 0, `mods` int unsigned NOT NULL, `diff_unified` float NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`beatmap_id`,`mode`,`mods`), KEY `diff_sort` (`mode`,`mods`,`diff_unified`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;' done - name: Run diffcalc (master) From f73ebfcea188b19de4b9f7f804229d15b6de4bbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 01:44:54 +0900 Subject: [PATCH 86/92] Don't redownload if file already exists --- .github/workflows/test-diffcalc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index e9fd624168..dd623f59ce 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -113,7 +113,7 @@ jobs: tar -xf $PERFORMANCE_DATA_NAME.tar.bz2 echo "Downloading beatmap dump $BEATMAPS_DATA_NAME.." - wget -q https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 + wget -q -nc https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2 echo "Extracting.." tar -xf $BEATMAPS_DATA_NAME.tar.bz2 From afcf3edec99d76c7937683abd249d9efcc5b0a39 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 02:27:21 +0900 Subject: [PATCH 87/92] Build build matrix dynamically --- .github/workflows/test-diffcalc.yml | 51 ++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index dd623f59ce..75742f2bde 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -16,29 +16,50 @@ env: SKIP_INSERT_ATTRIBUTES: 1 jobs: + metadata: + runs-on: self-hosted + if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + outputs: + matrix: ${{ steps.generate-matrix.outputs.matrix }} + continue: ${{ steps.generate-matrix.outputs.continue }} + steps: + - name: generate matrix + id: generate-matrix + run: | + if [[ "${{ github.event.comment.body }}" =~ "1" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "osu", "id": 0 },' + fi + if [[ "${{ github.event.comment.body }}" =~ "2" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "taiko", "id": 1 },' + fi + if [[ "${{ github.event.comment.body }}" =~ "3" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "catch", "id": 2 },' + fi + if [[ "${{ github.event.comment.body }}" =~ "4" ]] ; then + MATRIX_PROJECTS_JSON+='{ "name": "mania", "id": 3 },' + fi + + if [[ "${MATRIX_PROJECTS_JSON}" != "" ]]; then + MATRIX_JSON="{ \"ruleset\": [ ${MATRIX_PROJECTS_JSON} ] }" + echo "${MATRIX_JSON}" + CONTINUE="yes" + else + CONTINUE="no" + fi + + echo "::set-output name=continue::${CONTINUE}" + echo "::set-output name=matrix::${MATRIX_JSON}" diffcalc: name: Diffcalc runs-on: self-hosted continue-on-error: true - - if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + if: needs.metadata.outputs.continue == 'yes' + needs: metadata strategy: - fail-fast: false - matrix: - ruleset: - - { name: osu, id: 0 } - - { name: taiko, id: 1 } - - { name: fruits, id: 2 } - - { name: mania, id: 3 } + matrix: ${{ fromJson(needs.metadata.outputs.matrix) }} steps: - - name: Verify ruleset - if: contains(github.event.comment.body, matrix.ruleset.id) == false - run: | - echo "${{ github.event.comment.body }} doesn't contain ${{ matrix.ruleset.id }}" - exit 1 - - name: Verify MySQL connection from host run: | mysql -e "SHOW DATABASES" From f282bd6f42c758be1e111755ee33839e362f0638 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 02:47:27 +0900 Subject: [PATCH 88/92] Tidy up naming --- .github/workflows/test-diffcalc.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index 75742f2bde..c83cbf5d61 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -4,7 +4,7 @@ # !pp check 0 2 | Runs only the osu! and catch rulesets. # -name: Diffcalc Consistency Checks +name: Difficulty Calculation on: issue_comment: types: [ created ] @@ -17,13 +17,14 @@ env: jobs: metadata: + name: Check for requests runs-on: self-hosted if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') outputs: matrix: ${{ steps.generate-matrix.outputs.matrix }} continue: ${{ steps.generate-matrix.outputs.continue }} steps: - - name: generate matrix + - name: Construct build matrix id: generate-matrix run: | if [[ "${{ github.event.comment.body }}" =~ "1" ]] ; then @@ -50,15 +51,13 @@ jobs: echo "::set-output name=continue::${CONTINUE}" echo "::set-output name=matrix::${MATRIX_JSON}" diffcalc: - name: Diffcalc + name: Run runs-on: self-hosted continue-on-error: true if: needs.metadata.outputs.continue == 'yes' needs: metadata - strategy: matrix: ${{ fromJson(needs.metadata.outputs.matrix) }} - steps: - name: Verify MySQL connection from host run: | From 192089db61398096d61494b3f77d66687de0d43d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 02:54:37 +0900 Subject: [PATCH 89/92] Use keywords instead of IDs --- .github/workflows/test-diffcalc.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/test-diffcalc.yml index c83cbf5d61..edb9fbd323 100644 --- a/.github/workflows/test-diffcalc.yml +++ b/.github/workflows/test-diffcalc.yml @@ -27,16 +27,16 @@ jobs: - name: Construct build matrix id: generate-matrix run: | - if [[ "${{ github.event.comment.body }}" =~ "1" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "osu" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "osu", "id": 0 },' fi - if [[ "${{ github.event.comment.body }}" =~ "2" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "taiko" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "taiko", "id": 1 },' fi - if [[ "${{ github.event.comment.body }}" =~ "3" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "catch" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "catch", "id": 2 },' fi - if [[ "${{ github.event.comment.body }}" =~ "4" ]] ; then + if [[ "${{ github.event.comment.body }}" =~ "mania" ]] ; then MATRIX_PROJECTS_JSON+='{ "name": "mania", "id": 3 },' fi From a694d482ed1c2fee5d0a153f4b2009910a95e6cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 12:32:26 +0900 Subject: [PATCH 90/92] Rename file --- .github/workflows/{test-diffcalc.yml => diffcalc.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{test-diffcalc.yml => diffcalc.yml} (100%) diff --git a/.github/workflows/test-diffcalc.yml b/.github/workflows/diffcalc.yml similarity index 100% rename from .github/workflows/test-diffcalc.yml rename to .github/workflows/diffcalc.yml From b9c91111d2a855e6935b443a5871c6b2d990885e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Sep 2021 12:43:29 +0900 Subject: [PATCH 91/92] Add some whitespace --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 529f8b9672..cb3338126c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -39,14 +39,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000; double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000; double baseFlashlightPerformance = 0.0; + if (mods.Any(h => h is OsuModFlashlight)) baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0; + double basePerformance = Math.Pow( Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1) + Math.Pow(baseFlashlightPerformance, 1.1), 1.0 / 1.1 ); + double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0; HitWindows hitWindows = new OsuHitWindows(); From ea624489ca809327e37a9952b0d90cf96c168ec1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Sep 2021 12:48:10 +0900 Subject: [PATCH 92/92] Remove continue-on-error --- .github/workflows/diffcalc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index edb9fbd323..842522ae87 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -53,7 +53,6 @@ jobs: diffcalc: name: Run runs-on: self-hosted - continue-on-error: true if: needs.metadata.outputs.continue == 'yes' needs: metadata strategy: