Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ppy/osu
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2023.1223.0
Choose a base ref
...
head repository: ppy/osu
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2023.1224.0
Choose a head ref
Loading
61 changes: 58 additions & 3 deletions osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,80 @@
// 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;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;

namespace osu.Game.Rulesets.Osu.Scoring
{
public partial class OsuHealthProcessor : DrainingHealthProcessor
{
private ComboResult currentComboResult = ComboResult.Perfect;

public OsuHealthProcessor(double drainStartTime, double drainLenience = 0)
: base(drainStartTime, drainLenience)
{
}

protected override int? GetDensityGroup(HitObject hitObject) => (hitObject as IHasComboInformation)?.ComboIndex;

protected override double GetHealthIncreaseFor(JudgementResult result)
{
if (IsSimulating)
return getHealthIncreaseFor(result);

if (result.HitObject is not IHasComboInformation combo)
return getHealthIncreaseFor(result);

if (combo.NewCombo)
currentComboResult = ComboResult.Perfect;

switch (result.Type)
{
case HitResult.LargeTickMiss:
case HitResult.Ok:
setComboResult(ComboResult.Good);
break;

case HitResult.Meh:
case HitResult.Miss:
setComboResult(ComboResult.None);
break;
}

// The slider tail has a special judgement that can't accurately be described above.
if (result.HitObject is SliderTailCircle && !result.IsHit)
setComboResult(ComboResult.Good);

if (combo.LastInCombo && result.Type.IsHit())
{
switch (currentComboResult)
{
case ComboResult.Perfect:
return getHealthIncreaseFor(result) + 0.07;

case ComboResult.Good:
return getHealthIncreaseFor(result) + 0.05;

default:
return getHealthIncreaseFor(result) + 0.03;
}
}

return getHealthIncreaseFor(result);

void setComboResult(ComboResult comboResult) => currentComboResult = (ComboResult)Math.Min((int)currentComboResult, (int)comboResult);
}

protected override void Reset(bool storeResults)
{
base.Reset(storeResults);
currentComboResult = ComboResult.Perfect;
}

private double getHealthIncreaseFor(JudgementResult result)
{
switch (result.Type)
{
Original file line number Diff line number Diff line change
@@ -160,7 +160,7 @@ private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedSta
{
decimal? legacyVersion = skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value;

if (legacyVersion >= 2.0m)
if (legacyVersion > 1.0m)
// legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
hitCircleText.FadeOut(legacy_fade_duration / 4);
else
15 changes: 15 additions & 0 deletions osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs
Original file line number Diff line number Diff line change
@@ -81,6 +81,21 @@ public void TestBasicDisplay()
AddStep("End watching user presence", () => metadataClient.EndWatchingUserPresence());
}

[Test]
public void TestUserWasPlayingBeforeWatchingUserPresence()
{
AddStep("User began playing", () => spectatorClient.SendStartPlay(streamingUser.Id, 0));
AddStep("Begin watching user presence", () => metadataClient.BeginWatchingUserPresence());
AddStep("Add online user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.ChoosingBeatmap() }));
AddUntilStep("Panel loaded", () => currentlyOnline.ChildrenOfType<UserGridPanel>().FirstOrDefault()?.User.Id == 2);
AddAssert("Spectate button enabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.True);

AddStep("User finished playing", () => spectatorClient.SendEndPlay(streamingUser.Id));
AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType<PurpleRoundedButton>().First().Enabled.Value, () => Is.False);
AddStep("Remove playing user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, null));
AddStep("End watching user presence", () => metadataClient.EndWatchingUserPresence());
}

internal partial class TestUserLookupCache : UserLookupCache
{
private static readonly string[] usernames =
32 changes: 32 additions & 0 deletions osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@

#nullable disable

using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
@@ -14,6 +15,7 @@
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Extensions;
using osu.Game.Graphics.Sprites;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets;
@@ -194,6 +196,36 @@ private void checkDisplayedBPM(string target)
});
}

[TestCase]
public void TestLengthUpdates()
{
IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo);
double drain = beatmap.CalculateDrainLength();
beatmap.BeatmapInfo.Length = drain;

OsuModDoubleTime doubleTime = null;

selectBeatmap(beatmap);
checkDisplayedLength(drain);

AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() });
checkDisplayedLength(Math.Round(drain / 1.5f));

AddStep("change DT rate", () => doubleTime.SpeedChange.Value = 2);
checkDisplayedLength(Math.Round(drain / 2));
}

private void checkDisplayedLength(double drain)
{
var displayedLength = drain.ToFormattedDuration();

AddUntilStep($"check map drain ({displayedLength})", () =>
{
var label = infoWedge.DisplayedContent.ChildrenOfType<BeatmapInfoWedge.WedgeInfoText.InfoLabel>().Single(l => l.Statistic.Name == BeatmapsetsStrings.ShowStatsTotalLength(displayedLength));
return label.Statistic.Content == displayedLength.ToString();
});
}

private void setRuleset(RulesetInfo rulesetInfo)
{
Container containerBefore = null;
30 changes: 23 additions & 7 deletions osu.Game/Database/StandardisedScoreMigrationTools.cs
Original file line number Diff line number Diff line change
@@ -312,15 +312,21 @@ private static long convertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapCo

double legacyAccScore = maximumLegacyAccuracyScore * score.Accuracy;
// We can not separate the ComboScore from the BonusScore, so we keep the bonus in the ratio.
double comboProportion =
((double)score.LegacyTotalScore - legacyAccScore) / (maximumLegacyComboScore + maximumLegacyBonusScore);
// Note that `maximumLegacyComboScore + maximumLegacyBonusScore` can actually be 0
// when playing a beatmap with no bonus objects, with mods that have a 0.0x multiplier on stable (relax/autopilot).
// In such cases, just assume 0.
double comboProportion = maximumLegacyComboScore + maximumLegacyBonusScore > 0
? ((double)score.LegacyTotalScore - legacyAccScore) / (maximumLegacyComboScore + maximumLegacyBonusScore)
: 0;

// We assume the bonus proportion only makes up the rest of the score that exceeds maximumLegacyBaseScore.
long maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore;
double bonusProportion = Math.Max(0, ((long)score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio);

double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n);

long convertedTotalScore;

switch (score.Ruleset.OnlineID)
{
case 0:
@@ -417,32 +423,42 @@ double lowerEstimateOfComboPortionInStandardisedScore

double newComboScoreProportion = estimatedComboPortionInStandardisedScore / maximumAchievableComboPortionInStandardisedScore;

return (long)Math.Round((
convertedTotalScore = (long)Math.Round((
500000 * newComboScoreProportion * score.Accuracy
+ 500000 * Math.Pow(score.Accuracy, 5)
+ bonusProportion) * modMultiplier);
break;

case 1:
return (long)Math.Round((
convertedTotalScore = (long)Math.Round((
250000 * comboProportion
+ 750000 * Math.Pow(score.Accuracy, 3.6)
+ bonusProportion) * modMultiplier);
break;

case 2:
return (long)Math.Round((
convertedTotalScore = (long)Math.Round((
600000 * comboProportion
+ 400000 * score.Accuracy
+ bonusProportion) * modMultiplier);
break;

case 3:
return (long)Math.Round((
convertedTotalScore = (long)Math.Round((
850000 * comboProportion
+ 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy)
+ bonusProportion) * modMultiplier);
break;

default:
return score.TotalScore;
convertedTotalScore = score.TotalScore;
break;
}

if (convertedTotalScore < 0)
throw new InvalidOperationException($"Total score conversion operation returned invalid total of {convertedTotalScore}");

return convertedTotalScore;
}

public static double ComputeAccuracy(ScoreInfo scoreInfo)
8 changes: 8 additions & 0 deletions osu.Game/Input/Bindings/GlobalActionContainer.cs
Original file line number Diff line number Diff line change
@@ -160,6 +160,8 @@ public static IEnumerable<GlobalAction> GetGlobalActionsFor(GlobalActionCategory
new KeyBinding(InputKey.Enter, GlobalAction.ToggleChatFocus),
new KeyBinding(InputKey.F1, GlobalAction.SaveReplay),
new KeyBinding(InputKey.F2, GlobalAction.ExportReplay),
new KeyBinding(InputKey.Plus, GlobalAction.IncreaseOffset),
new KeyBinding(InputKey.Minus, GlobalAction.DecreaseOffset),
};

private static IEnumerable<KeyBinding> replayKeyBindings => new[]
@@ -404,6 +406,12 @@ public enum GlobalAction

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))]
EditorToggleRotateControl,

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))]
IncreaseOffset,

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseOffset))]
DecreaseOffset
}

public enum GlobalActionCategory
10 changes: 10 additions & 0 deletions osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
Original file line number Diff line number Diff line change
@@ -344,6 +344,16 @@ public static class GlobalActionKeyBindingStrings
/// </summary>
public static LocalisableString ExportReplay => new TranslatableString(getKey(@"export_replay"), @"Export replay");

/// <summary>
/// "Increase offset"
/// </summary>
public static LocalisableString IncreaseOffset => new TranslatableString(getKey(@"increase_offset"), @"Increase offset");

/// <summary>
/// "Decrease offset"
/// </summary>
public static LocalisableString DecreaseOffset => new TranslatableString(getKey(@"decrease_offset"), @"Decrease offset");

/// <summary>
/// "Toggle rotate control"
/// </summary>
11 changes: 9 additions & 2 deletions osu.Game/OsuGameBase.cs
Original file line number Diff line number Diff line change
@@ -527,14 +527,21 @@ public bool Migrate(string path)
{
ManualResetEventSlim readyToRun = new ManualResetEventSlim();

bool success = false;

Scheduler.Add(() =>
{
realmBlocker = realm.BlockAllOperations("migration");
try
{
realmBlocker = realm.BlockAllOperations("migration");
success = true;
}
catch { }

readyToRun.Set();
}, false);

if (!readyToRun.Wait(30000))
if (!readyToRun.Wait(30000) || !success)
throw new TimeoutException("Attempting to block for migration took too long.");

bool? cleanupSucceded = (Storage as OsuStorage)?.Migrate(Host.GetStorage(path));
2 changes: 2 additions & 0 deletions osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
@@ -218,6 +219,7 @@ private OnlineUserPanel createUserPanel(APIUser user) =>
{
panel.Anchor = Anchor.TopCentre;
panel.Origin = Anchor.TopCentre;
panel.CanSpectate.Value = playingUsers.Contains(user.Id);
});

public partial class OnlineUserPanel : CompositeDrawable, IFilterable
8 changes: 8 additions & 0 deletions osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs
Original file line number Diff line number Diff line change
@@ -232,6 +232,14 @@ private void updateCoverState()
bool expanded = coverToggle.CoverExpanded.Value;

cover.ResizeHeightTo(expanded ? 250 : 0, transition_duration, Easing.OutQuint);

// Without this a very tiny slither of the cover will be visible even with a size of zero.
// Integer masking woes, no doubt.
if (expanded)
cover.FadeIn(transition_duration, Easing.OutQuint);
else
cover.FadeOut(transition_duration, Easing.InQuint);

avatar.ResizeTo(new Vector2(expanded ? 120 : content_height), transition_duration, Easing.OutQuint);
avatar.TransformTo(nameof(avatar.CornerRadius), expanded ? 40f : 20f, transition_duration, Easing.OutQuint);
flow.TransformTo(nameof(flow.Spacing), new Vector2(expanded ? 20f : 10f), transition_duration, Easing.OutQuint);
27 changes: 14 additions & 13 deletions osu.Game/Overlays/Toolbar/ToolbarButton.cs
Original file line number Diff line number Diff line change
@@ -157,6 +157,15 @@ protected ToolbarButton()
};
}

[BackgroundDependencyLoader]
private void load()
{
if (Hotkey != null)
{
realm.SubscribeToPropertyChanged(r => r.All<RealmKeyBinding>().FirstOrDefault(rkb => rkb.RulesetName == null && rkb.ActionInt == (int)Hotkey.Value), kb => kb.KeyCombinationString, updateKeyBindingTooltip);
}
}

protected override bool OnMouseDown(MouseDownEvent e) => false;

protected override bool OnClick(ClickEvent e)
@@ -168,8 +177,6 @@ protected override bool OnClick(ClickEvent e)

protected override bool OnHover(HoverEvent e)
{
updateKeyBindingTooltip();

HoverBackground.FadeIn(200);
tooltipContainer.FadeIn(100);

@@ -197,19 +204,13 @@ public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
}

private void updateKeyBindingTooltip()
private void updateKeyBindingTooltip(string keyCombination)
{
if (Hotkey == null) return;

var realmKeyBinding = realm.Realm.All<RealmKeyBinding>().FirstOrDefault(rkb => rkb.RulesetName == null && rkb.ActionInt == (int)Hotkey.Value);
string keyBindingString = keyCombinationProvider.GetReadableString(keyCombination);

if (realmKeyBinding != null)
{
string keyBindingString = keyCombinationProvider.GetReadableString(realmKeyBinding.KeyCombination);

if (!string.IsNullOrEmpty(keyBindingString))
keyBindingTooltip.Text = $" ({keyBindingString})";
}
keyBindingTooltip.Text = !string.IsNullOrEmpty(keyBindingString)
? $" ({keyBindingString})"
: string.Empty;
}
}

Loading