Skip to content

Commit

Permalink
Merge branch 'master' into sharpen
Browse files Browse the repository at this point in the history
  • Loading branch information
smoogipoo authored Nov 27, 2019
2 parents eba8657 + 9494a47 commit c6a85a1
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 81 deletions.
2 changes: 1 addition & 1 deletion osu.Android.props
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1122.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1126.0" />
</ItemGroup>
</Project>
36 changes: 17 additions & 19 deletions osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,55 +47,53 @@ public override double Calculate(Dictionary<string, double> categoryDifficulty =
return 0;

// We are heavily relying on aim in catch the beat
double value = Math.Pow(5.0f * Math.Max(1.0f, Attributes.StarRating / 0.0049f) - 4.0f, 2.0f) / 100000.0f;
double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0;

// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
int numTotalHits = totalComboHits();

// Longer maps are worth more
float lengthBonus =
0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) +
(numTotalHits > 3000 ? MathF.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f);
double lengthBonus =
0.95 + 0.4 * Math.Min(1.0, numTotalHits / 3000.0) +
(numTotalHits > 3000 ? Math.Log10(numTotalHits / 3000.0) * 0.5 : 0.0);

// Longer maps are worth more
value *= lengthBonus;

// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
value *= Math.Pow(0.97f, misses);
value *= Math.Pow(0.97, misses);

// Combo scaling
float beatmapMaxCombo = Attributes.MaxCombo;
if (beatmapMaxCombo > 0)
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
if (Attributes.MaxCombo > 0)
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);

float approachRate = (float)Attributes.ApproachRate;
float approachRateFactor = 1.0f;
if (approachRate > 9.0f)
approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9
else if (approachRate < 8.0f)
approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8
double approachRateFactor = 1.0;
if (Attributes.ApproachRate > 9.0)
approachRateFactor += 0.1 * (Attributes.ApproachRate - 9.0); // 10% for each AR above 9
else if (Attributes.ApproachRate < 8.0)
approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); // 2.5% for each AR below 8

value *= approachRateFactor;

if (mods.Any(m => m is ModHidden))
// Hiddens gives nothing on max approach rate, and more the lower it is
value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10
value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10

if (mods.Any(m => m is ModFlashlight))
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
value *= 1.35f * lengthBonus;
value *= 1.35 * lengthBonus;

// Scale the aim value with accuracy _slightly_
value *= Math.Pow(accuracy(), 5.5f);
value *= Math.Pow(accuracy(), 5.5);

// Custom multipliers for NoFail. SpunOut is not applicable.
if (mods.Any(m => m is ModNoFail))
value *= 0.90f;
value *= 0.90;

return value;
}

private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0f, 1f);
private float accuracy() => totalHits() == 0 ? 0 : Math.Clamp((float)totalSuccessfulHits() / totalHits(), 0, 1);
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
private int totalComboHits() => misses + ticksHit + fruitsHit;
Expand Down
76 changes: 38 additions & 38 deletions osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,22 @@ public override double Calculate(Dictionary<string, double> categoryRatings = nu
return 0;

// Custom multipliers for NoFail and SpunOut.
double multiplier = 1.12f; // 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 *= 0.90f;
multiplier *= 0.90;

if (mods.Any(m => m is OsuModSpunOut))
multiplier *= 0.95f;
multiplier *= 0.95;

double aimValue = computeAimValue();
double speedValue = computeSpeedValue();
double accuracyValue = computeAccuracyValue();
double totalValue =
Math.Pow(
Math.Pow(aimValue, 1.1f) +
Math.Pow(speedValue, 1.1f) +
Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
Math.Pow(aimValue, 1.1) +
Math.Pow(speedValue, 1.1) +
Math.Pow(accuracyValue, 1.1), 1.0 / 1.1
) * multiplier;

if (categoryRatings != null)
Expand All @@ -93,82 +93,82 @@ private double computeAimValue()
if (mods.Any(m => m is OsuModTouchDevice))
rawAim = Math.Pow(rawAim, 0.8);

double aimValue = Math.Pow(5.0f * Math.Max(1.0f, rawAim / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
double aimValue = Math.Pow(5.0 * Math.Max(1.0, rawAim / 0.0675) - 4.0, 3.0) / 100000.0;

// Longer maps are worth more
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);

aimValue *= lengthBonus;

// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
aimValue *= Math.Pow(0.97f, countMiss);
aimValue *= Math.Pow(0.97, countMiss);

// Combo scaling
if (beatmapMaxCombo > 0)
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0);

double approachRateFactor = 1.0f;
double approachRateFactor = 1.0;

if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
else if (Attributes.ApproachRate < 8.0f)
if (Attributes.ApproachRate > 10.33)
approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);
else if (Attributes.ApproachRate < 8.0)
{
approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
}

aimValue *= approachRateFactor;

// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden))
aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
aimValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);

if (mods.Any(h => h is OsuModFlashlight))
{
// Apply object-based bonus for flashlight.
aimValue *= 1.0f + 0.35f * Math.Min(1.0f, totalHits / 200.0f) +
aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) +
(totalHits > 200
? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
(totalHits > 500 ? (totalHits - 500) / 1200.0f : 0.0f)
: 0.0f);
? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) +
(totalHits > 500 ? (totalHits - 500) / 1200.0 : 0.0)
: 0.0);
}

// Scale the aim value with accuracy _slightly_
aimValue *= 0.5f + accuracy / 2.0f;
aimValue *= 0.5 + accuracy / 2.0;
// It is important to also consider accuracy difficulty when doing that
aimValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
aimValue *= 0.98 + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;

return aimValue;
}

private double computeSpeedValue()
{
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.SpeedStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
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
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
speedValue *= 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);

// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
speedValue *= Math.Pow(0.97f, countMiss);
speedValue *= Math.Pow(0.97, countMiss);

// Combo scaling
if (beatmapMaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0);

double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
double approachRateFactor = 1.0;
if (Attributes.ApproachRate > 10.33)
approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33);

speedValue *= approachRateFactor;

if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);

// Scale the speed value with accuracy _slightly_
speedValue *= 0.02f + accuracy;
speedValue *= 0.02 + accuracy;
// It is important to also consider accuracy difficulty when doing that
speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
speedValue *= 0.96 + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;

return speedValue;
}
Expand All @@ -190,15 +190,15 @@ private double computeAccuracyValue()

// Lots of arbitrary values from testing.
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
double accuracyValue = Math.Pow(1.52163f, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
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
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
accuracyValue *= Math.Min(1.15, Math.Pow(amountHitObjectsWithAccuracy / 1000.0, 0.3));

if (mods.Any(m => m is OsuModHidden))
accuracyValue *= 1.08f;
accuracyValue *= 1.08;
if (mods.Any(m => m is OsuModFlashlight))
accuracyValue *= 1.02f;
accuracyValue *= 1.02;

return accuracyValue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private double computeStrainValue()
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;

// Longer maps are worth more
double lengthBonus = 1 + 0.1f * Math.Min(1.0, totalHits / 1500.0);
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
strainValue *= lengthBonus;

// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
Expand Down
13 changes: 13 additions & 0 deletions osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ public void TestSkipBreaks()
seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false);
}

[TestCase(true)]
[TestCase(false)]
public void TestBeforeGameplayStart(bool withBreaks)
{
setClock(true);

if (withBreaks)
loadBreaksStep("multiple breaks", testBreaks);

seekAndAssertBreak("seek to break intro time", -100, true);
seekAndAssertBreak("seek to break intro time", 0, false);
}

private void addShowBreakStep(double seconds)
{
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List<BreakPeriod>
Expand Down
47 changes: 30 additions & 17 deletions osu.Game/Screens/Play/BreakOverlay.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
Expand All @@ -16,6 +16,8 @@ namespace osu.Game.Screens.Play
{
public class BreakOverlay : Container
{
private readonly ScoreProcessor scoreProcessor;

/// <summary>
/// The duration of the break overlay fading.
/// </summary>
Expand Down Expand Up @@ -60,9 +62,12 @@ public IReadOnlyList<BreakPeriod> Breaks
private readonly RemainingTimeCounter remainingTimeCounter;
private readonly BreakInfo info;
private readonly BreakArrows breakArrows;
private readonly double gameplayStartTime;

public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor = null)
public BreakOverlay(bool letterboxing, double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
{
this.gameplayStartTime = gameplayStartTime;
this.scoreProcessor = scoreProcessor;
RelativeSizeAxes = Axes.Both;
Child = fadeContainer = new Container
{
Expand Down Expand Up @@ -135,26 +140,34 @@ protected override void Update()
updateBreakTimeBindable();
}

private void updateBreakTimeBindable()
private void updateBreakTimeBindable() =>
isBreakTime.Value = getCurrentBreak()?.HasEffect == true
|| Clock.CurrentTime < gameplayStartTime
|| scoreProcessor?.HasCompleted == true;

private BreakPeriod getCurrentBreak()
{
if (breaks == null || breaks.Count == 0)
return;
if (breaks?.Count > 0)
{
var time = Clock.CurrentTime;

var time = Clock.CurrentTime;
if (time > breaks[CurrentBreakIndex].EndTime)
{
while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
CurrentBreakIndex++;
}
else
{
while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
CurrentBreakIndex--;
}

if (time > breaks[CurrentBreakIndex].EndTime)
{
while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
CurrentBreakIndex++;
}
else
{
while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
CurrentBreakIndex--;
var closest = breaks[CurrentBreakIndex];

return closest.Contains(time) ? closest : null;
}

var currentBreak = breaks[CurrentBreakIndex];
isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time);
return null;
}

private void initializeBreaks()
Expand Down
4 changes: 2 additions & 2 deletions osu.Game/Screens/Play/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ private void addOverlayComponents(Container target, WorkingBeatmap working)
{
target.AddRange(new[]
{
breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, DrawableRuleset.GameplayStartTime, ScoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Expand Down Expand Up @@ -468,7 +468,7 @@ public void Resume()
PauseOverlay.Hide();

// breaks and time-based conditions may allow instant resume.
if (breakOverlay.IsBreakTime.Value || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
if (breakOverlay.IsBreakTime.Value)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/osu.Game.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1122.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1126.0" />
<PackageReference Include="Sentry" Version="1.2.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
Expand Down
4 changes: 2 additions & 2 deletions osu.iOS.props
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1122.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1126.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
<PackageReference Include="Humanizer" Version="2.7.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1122.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1126.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />
Expand Down

0 comments on commit c6a85a1

Please sign in to comment.