mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 20:13:21 +08:00
Merge branch 'ppy:master' into catch-hide-in-relax
This commit is contained in:
commit
c73195fa77
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -4,6 +4,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
inspect-code:
|
||||
name: Code Quality
|
||||
|
4
.github/workflows/report-nunit.yml
vendored
4
.github/workflows/report-nunit.yml
vendored
@ -8,8 +8,12 @@ on:
|
||||
workflows: ["Continuous Integration"]
|
||||
types:
|
||||
- completed
|
||||
permissions: {}
|
||||
jobs:
|
||||
annotate:
|
||||
permissions:
|
||||
checks: write # to create checks (dorny/test-reporter)
|
||||
|
||||
name: Annotate CI run with test results
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion != 'cancelled' }}
|
||||
|
3
.github/workflows/sentry-release.yml
vendored
3
.github/workflows/sentry-release.yml
vendored
@ -5,6 +5,9 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
sentry_release:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.831.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.922.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1005.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -137,12 +137,13 @@ namespace osu.Desktop
|
||||
{
|
||||
base.SetHost(host);
|
||||
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
|
||||
var desktopWindow = (SDL2DesktopWindow)host.Window;
|
||||
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
if (iconStream != null)
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
|
||||
desktopWindow.CursorState |= CursorState.Hidden;
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
desktopWindow.Title = Name;
|
||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -16,22 +15,14 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||
|
||||
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1)
|
||||
{
|
||||
MinValue = 0.5f,
|
||||
MaxValue = 1.5f,
|
||||
Default = 1f,
|
||||
Value = 1f,
|
||||
Precision = 0.1f
|
||||
};
|
||||
|
||||
[SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")]
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||
|
||||
public override float DefaultFlashlightSize => 350;
|
||||
|
||||
|
@ -6,8 +6,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
@ -17,15 +15,8 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public override LocalisableString Description => "Where's the catcher?";
|
||||
|
||||
[SettingSource(
|
||||
"Hidden at combo",
|
||||
"The combo count at which the catcher becomes completely hidden",
|
||||
SettingControlType = typeof(SettingsSlider<int, HiddenComboSlider>)
|
||||
)]
|
||||
public override BindableInt HiddenComboCount { get; } = new BindableInt
|
||||
public override BindableInt HiddenComboCount { get; } = new BindableInt(10)
|
||||
{
|
||||
Default = 10,
|
||||
Value = 10,
|
||||
MinValue = 0,
|
||||
MaxValue = 50,
|
||||
};
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
@ -17,22 +16,14 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
|
||||
|
||||
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1)
|
||||
{
|
||||
MinValue = 0.5f,
|
||||
MaxValue = 3f,
|
||||
Default = 1f,
|
||||
Value = 1f,
|
||||
Precision = 0.1f
|
||||
};
|
||||
|
||||
[SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")]
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool
|
||||
{
|
||||
Default = false,
|
||||
Value = false
|
||||
};
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool();
|
||||
|
||||
public override float DefaultFlashlightSize => 50;
|
||||
|
||||
|
@ -54,10 +54,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateStartTimeStateTransforms() => this.FadeOut(150);
|
||||
}
|
||||
}
|
||||
|
@ -30,14 +30,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
public bool UpdateResult() => base.UpdateResult(true);
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
// This hitobject should never expire, so this is just a safe maximum.
|
||||
LifetimeEnd = LifetimeStart + 30000;
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
{
|
||||
// suppress the base call explicitly.
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
public void UpdateResult() => base.UpdateResult(true);
|
||||
|
||||
protected override double MaximumJudgementOffset => base.MaximumJudgementOffset * release_window_lenience;
|
||||
public override double MaximumJudgementOffset => base.MaximumJudgementOffset * release_window_lenience;
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
|
@ -23,10 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
// Leaving the default (10s) makes hitobjects not appear, as this offset is used for the initial state transforms.
|
||||
// Calculated as DrawableManiaRuleset.MAX_TIME_RANGE + some additional allowance for velocity < 1.
|
||||
protected override double InitialLifetimeOffset => 30000;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private ManiaPlayfield playfield { get; set; }
|
||||
|
||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
||||
|
||||
if (!(currentObj.BaseObject is Spinner))
|
||||
{
|
||||
double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.EndPosition).Length;
|
||||
double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.StackedEndPosition).Length;
|
||||
|
||||
cumulativeStrainTime += lastObj.StrainTime;
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@ -18,13 +17,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override LocalisableString Description => "Hit them at the right size!";
|
||||
|
||||
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
||||
public override BindableNumber<float> StartScale { get; } = new BindableFloat
|
||||
public override BindableNumber<float> StartScale { get; } = new BindableFloat(2)
|
||||
{
|
||||
MinValue = 1f,
|
||||
MaxValue = 25f,
|
||||
Default = 2f,
|
||||
Value = 2f,
|
||||
Precision = 0.1f,
|
||||
};
|
||||
}
|
||||
|
@ -32,22 +32,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
Precision = default_follow_delay,
|
||||
};
|
||||
|
||||
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1)
|
||||
{
|
||||
MinValue = 0.5f,
|
||||
MaxValue = 2f,
|
||||
Default = 1f,
|
||||
Value = 1f,
|
||||
Precision = 0.1f
|
||||
};
|
||||
|
||||
[SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")]
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||
|
||||
public override float DefaultFlashlightSize => 180;
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@ -18,13 +17,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override LocalisableString Description => "Hit them at the right size!";
|
||||
|
||||
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
||||
public override BindableNumber<float> StartScale { get; } = new BindableFloat
|
||||
public override BindableNumber<float> StartScale { get; } = new BindableFloat(0.5f)
|
||||
{
|
||||
MinValue = 0f,
|
||||
MaxValue = 0.99f,
|
||||
Default = 0.5f,
|
||||
Value = 0.5f,
|
||||
Precision = 0.01f,
|
||||
};
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@ -22,15 +20,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
private PeriodTracker spinnerPeriods = null!;
|
||||
|
||||
[SettingSource(
|
||||
"Hidden at combo",
|
||||
"The combo count at which the cursor becomes completely hidden",
|
||||
SettingControlType = typeof(SettingsSlider<int, HiddenComboSlider>)
|
||||
)]
|
||||
public override BindableInt HiddenComboCount { get; } = new BindableInt
|
||||
public override BindableInt HiddenComboCount { get; } = new BindableInt(10)
|
||||
{
|
||||
Default = 10,
|
||||
Value = 10,
|
||||
MinValue = 0,
|
||||
MaxValue = 50,
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -20,6 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
||||
public abstract BindableNumber<float> StartScale { get; }
|
||||
|
||||
protected virtual float EndScale => 1;
|
||||
|
@ -29,10 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray();
|
||||
|
||||
[SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider<float>))]
|
||||
public BindableFloat AngleSharpness { get; } = new BindableFloat
|
||||
public BindableFloat AngleSharpness { get; } = new BindableFloat(7)
|
||||
{
|
||||
Default = 7,
|
||||
Value = 7,
|
||||
MinValue = 1,
|
||||
MaxValue = 10,
|
||||
Precision = 0.1f
|
||||
|
@ -53,11 +53,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
}).ToArray();
|
||||
|
||||
[SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))]
|
||||
public Bindable<int?> Seed { get; } = new Bindable<int?>
|
||||
{
|
||||
Default = null,
|
||||
Value = null
|
||||
};
|
||||
public Bindable<int?> Seed { get; } = new Bindable<int?>();
|
||||
|
||||
[SettingSource("Metronome ticks", "Whether a metronome beat should play in the background")]
|
||||
public Bindable<bool> Metronome { get; } = new BindableBool(true);
|
||||
|
@ -204,12 +204,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
// todo: temporary / arbitrary, used for lifetime optimisation.
|
||||
this.Delay(800).FadeOut();
|
||||
|
||||
// in the case of an early state change, the fade should be expedited to the current point in time.
|
||||
if (HitStateUpdateTime < HitObject.StartTime)
|
||||
ApproachCircle.FadeOut(50);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
ApproachCircle.FadeOut();
|
||||
break;
|
||||
|
||||
case ArmedState.Idle:
|
||||
HitArea.HitAction = null;
|
||||
break;
|
||||
|
@ -11,7 +11,9 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -64,6 +66,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
ScaleBindable.UnbindFrom(HitObject.ScaleBindable);
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
// Dim should only be applied at a top level, as it will be implicitly applied to nested objects.
|
||||
if (ParentHitObject == null)
|
||||
{
|
||||
// Of note, no one noticed this was missing for years, but it definitely feels like it should still exist.
|
||||
// For now this is applied across all skins, and matches stable.
|
||||
// For simplicity, dim colour is applied to the DrawableHitObject itself.
|
||||
// We may need to make a nested container setup if this even causes a usage conflict (ie. with a mod).
|
||||
this.FadeColour(new Color4(195, 195, 195, 255));
|
||||
using (BeginDelayedSequence(InitialLifetimeOffset - OsuHitWindows.MISS_WINDOW))
|
||||
this.FadeColour(Color4.White, 100);
|
||||
}
|
||||
}
|
||||
|
||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||
|
||||
private OsuInputManager osuActionInputManager;
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
protected override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration;
|
||||
public override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration;
|
||||
|
||||
/// <summary>
|
||||
/// Apply a judgement result.
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
// This is so on repeats ticks don't appear too late to be visually processed by the player.
|
||||
offset = 200;
|
||||
else
|
||||
offset = TimeFadeIn * 0.66f;
|
||||
offset = TimePreempt * 0.66f;
|
||||
|
||||
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
|
||||
}
|
||||
|
@ -1,20 +1,23 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
public class OsuHitWindows : HitWindows
|
||||
{
|
||||
/// <summary>
|
||||
/// osu! ruleset has a fixed miss window regardless of difficulty settings.
|
||||
/// </summary>
|
||||
public const double MISS_WINDOW = 400;
|
||||
|
||||
private static readonly DifficultyRange[] osu_ranges =
|
||||
{
|
||||
new DifficultyRange(HitResult.Great, 80, 50, 20),
|
||||
new DifficultyRange(HitResult.Ok, 140, 100, 60),
|
||||
new DifficultyRange(HitResult.Meh, 200, 150, 100),
|
||||
new DifficultyRange(HitResult.Miss, 400, 400, 400),
|
||||
new DifficultyRange(HitResult.Miss, MISS_WINDOW, MISS_WINDOW, MISS_WINDOW),
|
||||
};
|
||||
|
||||
public override bool IsHitResultAllowed(HitResult result)
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
};
|
||||
|
||||
accentColour = hitObject.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(accent => BorderColour = accent.NewValue);
|
||||
accentColour.BindValueChanged(accent => BorderColour = accent.NewValue, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -129,5 +130,32 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
AssertResult<Hit>(0, HitResult.Miss);
|
||||
AssertResult<StrongNestedHitObject>(0, HitResult.IgnoreMiss);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHighVelocityHit()
|
||||
{
|
||||
const double hit_time = 1000;
|
||||
|
||||
var beatmap = CreateBeatmap(new Hit
|
||||
{
|
||||
Type = HitType.Centre,
|
||||
StartTime = hit_time,
|
||||
});
|
||||
|
||||
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 6 });
|
||||
beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 10 });
|
||||
|
||||
var hitWindows = new HitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(hit_time - hitWindows.WindowFor(HitResult.Great), TaikoAction.LeftCentre),
|
||||
}, beatmap);
|
||||
|
||||
AssertJudgementCount(1);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
private int countOk;
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
private double accuracy;
|
||||
|
||||
private double effectiveMissCount;
|
||||
|
||||
@ -36,6 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
|
||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||
accuracy = customAccuracy;
|
||||
|
||||
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
|
||||
if (totalSuccessfulHits > 0)
|
||||
@ -87,7 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
||||
difficultyValue *= 1.050 * lengthBonus;
|
||||
|
||||
return difficultyValue * Math.Pow(score.Accuracy, 2.0);
|
||||
return difficultyValue * Math.Pow(accuracy, 2.0);
|
||||
}
|
||||
|
||||
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||
@ -95,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
if (attributes.GreatHitWindow <= 0)
|
||||
return 0;
|
||||
|
||||
double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0;
|
||||
double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0;
|
||||
|
||||
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||
accuracyValue *= lengthBonus;
|
||||
@ -110,5 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
||||
|
||||
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
||||
|
||||
private double customAccuracy => totalHits > 0 ? (countGreat * 300 + countOk * 150) / (totalHits * 300.0) : 0;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
@ -17,22 +16,14 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||
|
||||
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat(1)
|
||||
{
|
||||
MinValue = 0.5f,
|
||||
MaxValue = 1.5f,
|
||||
Default = 1f,
|
||||
Value = 1f,
|
||||
Precision = 0.1f
|
||||
};
|
||||
|
||||
[SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")]
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||
|
||||
public override float DefaultFlashlightSize => 250;
|
||||
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
Filled = HitObject.FirstTick
|
||||
});
|
||||
|
||||
protected override double MaximumJudgementOffset => HitObject.HitWindow;
|
||||
public override double MaximumJudgementOffset => HitObject.HitWindow;
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
|
@ -204,31 +204,23 @@ namespace osu.Game.Tests.Online
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble(1.5)
|
||||
{
|
||||
MinValue = 1,
|
||||
MaxValue = 2,
|
||||
Default = 1.5,
|
||||
Value = 1.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble(0.5)
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Default = 0.5,
|
||||
Value = 0.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool(true);
|
||||
}
|
||||
|
||||
private class TestModDifficultyAdjust : ModDifficultyAdjust
|
||||
|
@ -124,31 +124,23 @@ namespace osu.Game.Tests.Online
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble(1.5)
|
||||
{
|
||||
MinValue = 1,
|
||||
MaxValue = 2,
|
||||
Default = 1.5,
|
||||
Value = 1.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble(0.5)
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Default = 0.5,
|
||||
Value = 0.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool(true);
|
||||
}
|
||||
|
||||
private class TestModEnum : Mod
|
||||
|
@ -8,7 +8,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -78,7 +77,7 @@ namespace osu.Game.Tests.Online
|
||||
}
|
||||
};
|
||||
|
||||
beatmaps.AllowImport = new TaskCompletionSource<bool>();
|
||||
beatmaps.AllowImport.Reset();
|
||||
|
||||
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
@ -132,7 +131,7 @@ namespace osu.Game.Tests.Online
|
||||
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.TriggerSuccess(testBeatmapFile));
|
||||
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
|
||||
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
AddUntilStep("wait for import", () => beatmaps.CurrentImport != null);
|
||||
AddUntilStep("ensure beatmap available", () => beatmaps.IsAvailableLocally(testBeatmapSet));
|
||||
addAvailabilityCheckStep("state is locally available", BeatmapAvailability.LocallyAvailable);
|
||||
@ -141,7 +140,7 @@ namespace osu.Game.Tests.Online
|
||||
[Test]
|
||||
public void TestTrackerRespectsSoftDeleting()
|
||||
{
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely());
|
||||
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
|
||||
|
||||
@ -155,7 +154,7 @@ namespace osu.Game.Tests.Online
|
||||
[Test]
|
||||
public void TestTrackerRespectsChecksum()
|
||||
{
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.Set());
|
||||
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).WaitSafely());
|
||||
addAvailabilityCheckStep("initially locally available", BeatmapAvailability.LocallyAvailable);
|
||||
|
||||
@ -202,7 +201,7 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
private class TestBeatmapManager : BeatmapManager
|
||||
{
|
||||
public TaskCompletionSource<bool> AllowImport = new TaskCompletionSource<bool>();
|
||||
public readonly ManualResetEventSlim AllowImport = new ManualResetEventSlim();
|
||||
|
||||
public Live<BeatmapSetInfo> CurrentImport { get; private set; }
|
||||
|
||||
@ -229,7 +228,9 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
public override Live<BeatmapSetInfo> ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
testBeatmapManager.AllowImport.Task.WaitSafely();
|
||||
if (!testBeatmapManager.AllowImport.Wait(TimeSpan.FromSeconds(10), cancellationToken))
|
||||
throw new TimeoutException("Timeout waiting for import to be allowed.");
|
||||
|
||||
return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, batchImport, cancellationToken));
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddUntilStep("wait for some scores not masked away",
|
||||
() => leaderboard.ChildrenOfType<GameplayLeaderboardScore>().Any(s => leaderboard.ScreenSpaceDrawQuad.Contains(s.ScreenSpaceDrawQuad.Centre)));
|
||||
|
||||
AddUntilStep("wait for tracked score fully visible", () => leaderboard.ScreenSpaceDrawQuad.Intersects(leaderboard.TrackedScore!.ScreenSpaceDrawQuad));
|
||||
|
||||
AddStep("change score to middle", () => playerScore.Value = 1000000);
|
||||
AddWaitStep("wait for movement", 5);
|
||||
AddUntilStep("wait for tracked score fully visible", () => leaderboard.ScreenSpaceDrawQuad.Intersects(leaderboard.TrackedScore!.ScreenSpaceDrawQuad));
|
||||
|
||||
AddStep("change score to first", () => playerScore.Value = 5000000);
|
||||
AddWaitStep("wait for movement", 5);
|
||||
AddUntilStep("wait for tracked score fully visible", () => leaderboard.ScreenSpaceDrawQuad.Intersects(leaderboard.TrackedScore!.ScreenSpaceDrawQuad));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -11,8 +11,10 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -167,14 +169,39 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("add control points", () => addControlPoints(testControlPoints, Time.Current));
|
||||
}
|
||||
|
||||
private void addHitObject(double time)
|
||||
[Test]
|
||||
public void TestVeryFlowScroll()
|
||||
{
|
||||
const double long_time_range = 100000;
|
||||
var manualClock = new ManualClock();
|
||||
|
||||
AddStep("set manual clock", () =>
|
||||
{
|
||||
manualClock.CurrentTime = 0;
|
||||
scrollContainers.ForEach(c => c.Clock = new FramedClock(manualClock));
|
||||
|
||||
setScrollAlgorithm(ScrollVisualisationMethod.Constant);
|
||||
scrollContainers.ForEach(c => c.TimeRange = long_time_range);
|
||||
});
|
||||
|
||||
AddStep("add hit objects", () =>
|
||||
{
|
||||
addHitObject(long_time_range);
|
||||
addHitObject(long_time_range + 100, 250);
|
||||
});
|
||||
|
||||
AddAssert("hit objects are alive", () => playfields.All(p => p.HitObjectContainer.AliveObjects.Count() == 2));
|
||||
}
|
||||
|
||||
private void addHitObject(double time, float size = 75)
|
||||
{
|
||||
playfields.ForEach(p =>
|
||||
{
|
||||
var hitObject = new TestDrawableHitObject(time);
|
||||
setAnchor(hitObject, p);
|
||||
var hitObject = new TestHitObject(size) { StartTime = time };
|
||||
var drawable = new TestDrawableHitObject(hitObject);
|
||||
|
||||
p.Add(hitObject);
|
||||
setAnchor(drawable, p);
|
||||
p.Add(drawable);
|
||||
});
|
||||
}
|
||||
|
||||
@ -248,6 +275,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override ScrollingHitObjectContainer CreateScrollingHitObjectContainer() => new TestScrollingHitObjectContainer();
|
||||
}
|
||||
|
||||
private class TestDrawableControlPoint : DrawableHitObject<HitObject>
|
||||
@ -281,22 +310,41 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject<HitObject>
|
||||
private class TestHitObject : HitObject
|
||||
{
|
||||
public TestDrawableHitObject(double time)
|
||||
: base(new HitObject { StartTime = time, HitWindows = HitWindows.Empty })
|
||||
{
|
||||
Origin = Anchor.Custom;
|
||||
OriginPosition = new Vector2(75 / 4.0f);
|
||||
public readonly float Size;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
public TestHitObject(float size)
|
||||
{
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject<TestHitObject>
|
||||
{
|
||||
public TestDrawableHitObject(TestHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2(hitObject.Size);
|
||||
|
||||
AddInternal(new Box
|
||||
{
|
||||
Size = new Vector2(75),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class TestScrollingHitObjectContainer : ScrollingHitObjectContainer
|
||||
{
|
||||
protected override RectangleF GetConservativeBoundingBox(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
if (entry.HitObject is TestHitObject testObject)
|
||||
return new RectangleF().Inflate(testObject.Size / 2);
|
||||
|
||||
return base.GetConservativeBoundingBox(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -24,6 +25,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private readonly BindableList<ScoreInfo> scores = new BindableList<ScoreInfo>();
|
||||
|
||||
private readonly Bindable<bool> configVisibility = new Bindable<bool>();
|
||||
|
||||
private SoloGameplayLeaderboard leaderboard = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility);
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
@ -37,11 +48,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Id = 2,
|
||||
};
|
||||
|
||||
Child = new SoloGameplayLeaderboard(trackingUser)
|
||||
Child = leaderboard = new SoloGameplayLeaderboard(trackingUser)
|
||||
{
|
||||
Scores = { BindTarget = scores },
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AlwaysVisible = { Value = false },
|
||||
Expanded = { Value = true },
|
||||
};
|
||||
});
|
||||
@ -54,7 +66,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddSliderStep("score", 0, 1000000, 500000, v => scoreProcessor.TotalScore.Value = v);
|
||||
AddSliderStep("accuracy", 0f, 1f, 0.5f, v => scoreProcessor.Accuracy.Value = v);
|
||||
AddSliderStep("combo", 0, 1000, 0, v => scoreProcessor.Combo.Value = v);
|
||||
AddSliderStep("combo", 0, 10000, 0, v => scoreProcessor.HighestCombo.Value = v);
|
||||
AddStep("toggle expanded", () => leaderboard.Expanded.Value = !leaderboard.Expanded.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVisibility()
|
||||
{
|
||||
AddStep("set config visible true", () => configVisibility.Value = true);
|
||||
AddUntilStep("leaderboard visible", () => leaderboard.Alpha == 1);
|
||||
|
||||
AddStep("set config visible false", () => configVisibility.Value = false);
|
||||
AddUntilStep("leaderboard not visible", () => leaderboard.Alpha == 0);
|
||||
|
||||
AddStep("set always visible", () => leaderboard.AlwaysVisible.Value = true);
|
||||
AddUntilStep("leaderboard visible", () => leaderboard.Alpha == 1);
|
||||
|
||||
AddStep("set config visible true", () => configVisibility.Value = true);
|
||||
AddAssert("leaderboard still visible", () => leaderboard.Alpha == 1);
|
||||
}
|
||||
|
||||
private static List<ScoreInfo> createSampleScores()
|
||||
|
@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected new OutroPlayer Player => (OutroPlayer)base.Player;
|
||||
|
||||
private double currentBeatmapDuration;
|
||||
private double currentStoryboardDuration;
|
||||
|
||||
private bool showResults = true;
|
||||
@ -45,7 +46,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("enable storyboard", () => LocalConfig.SetValue(OsuSetting.ShowStoryboard, true));
|
||||
AddStep("set dim level to 0", () => LocalConfig.SetValue<double>(OsuSetting.DimLevel, 0));
|
||||
AddStep("reset fail conditions", () => currentFailConditions = (_, _) => false);
|
||||
AddStep("set storyboard duration to 2s", () => currentStoryboardDuration = 2000);
|
||||
AddStep("set beatmap duration to 0s", () => currentBeatmapDuration = 0);
|
||||
AddStep("set storyboard duration to 8s", () => currentStoryboardDuration = 8000);
|
||||
AddStep("set ShowResults = true", () => showResults = true);
|
||||
}
|
||||
|
||||
@ -151,6 +153,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("player exited", () => Stack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformExitAfterOutro()
|
||||
{
|
||||
CreateTest(() =>
|
||||
{
|
||||
AddStep("set beatmap duration to 4s", () => currentBeatmapDuration = 4000);
|
||||
AddStep("set storyboard duration to 1s", () => currentStoryboardDuration = 1000);
|
||||
});
|
||||
|
||||
AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.CurrentTime >= currentStoryboardDuration);
|
||||
AddStep("exit via pause", () => Player.ExitViaPause());
|
||||
AddAssert("player paused", () => !Player.IsResuming);
|
||||
|
||||
AddStep("resume player", () => Player.Resume());
|
||||
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
|
||||
AddUntilStep("wait for score shown", () => Player.IsScoreShown);
|
||||
}
|
||||
|
||||
protected override bool AllowFail => true;
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
@ -160,7 +180,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(new HitCircle());
|
||||
beatmap.HitObjects.Add(new HitCircle { StartTime = currentBeatmapDuration });
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
@ -189,7 +209,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private event Func<HealthProcessor, JudgementResult, bool> failConditions;
|
||||
|
||||
public OutroPlayer(Func<HealthProcessor, JudgementResult, bool> failConditions, bool showResults = true)
|
||||
: base(false, showResults)
|
||||
: base(showResults: showResults)
|
||||
{
|
||||
this.failConditions = failConditions;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
[Test]
|
||||
public void TestEditDefaultSkin()
|
||||
{
|
||||
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.TRIANGLES_SKIN);
|
||||
AddAssert("is default skin", () => skinManager.CurrentSkinInfo.Value.ID == SkinInfo.ARGON_SKIN);
|
||||
|
||||
AddStep("open settings", () => { Game.Settings.Show(); });
|
||||
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddStep("open skin editor", () => skinEditor.Show());
|
||||
|
||||
// Until step required as the skin editor may take time to load (and an extra scheduled frame for the mutable part).
|
||||
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.TRIANGLES_SKIN);
|
||||
AddUntilStep("is modified default skin", () => skinManager.CurrentSkinInfo.Value.ID != SkinInfo.ARGON_SKIN);
|
||||
AddAssert("is not protected", () => skinManager.CurrentSkinInfo.Value.PerformRead(s => !s.Protected));
|
||||
|
||||
AddUntilStep("export button enabled", () => Game.Settings.ChildrenOfType<SkinSection.ExportSkinButton>().SingleOrDefault()?.Enabled.Value == true);
|
||||
|
@ -9,8 +9,10 @@ using NUnit.Framework;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
@ -92,6 +94,31 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
returnToMenu();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromSongSelectWithFilter([Values] ScorePresentType type)
|
||||
{
|
||||
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo.Invoke());
|
||||
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
|
||||
|
||||
AddStep("filter to nothing", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).FilterControl.CurrentTextSearch.Value = "fdsajkl;fgewq");
|
||||
AddUntilStep("wait for no results", () => Beatmap.IsDefault);
|
||||
|
||||
var firstImport = importScore(1, new CatchRuleset().RulesetInfo);
|
||||
presentAndConfirm(firstImport, type);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromSongSelectWithConvertRulesetChange([Values] ScorePresentType type)
|
||||
{
|
||||
AddStep("enter song select", () => Game.ChildrenOfType<ButtonSystem>().Single().OnSolo.Invoke());
|
||||
AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded);
|
||||
|
||||
AddStep("set convert to false", () => Game.LocalConfig.SetValue(OsuSetting.ShowConvertedBeatmaps, false));
|
||||
|
||||
var firstImport = importScore(1, new CatchRuleset().RulesetInfo);
|
||||
presentAndConfirm(firstImport, type);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFromSongSelect([Values] ScorePresentType type)
|
||||
{
|
||||
|
@ -29,11 +29,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
Child = textBox = new SettingsTextBox
|
||||
{
|
||||
Current = new Bindable<string>
|
||||
{
|
||||
Default = "test",
|
||||
Value = "test"
|
||||
}
|
||||
Current = new Bindable<string>("test")
|
||||
};
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => textBox.IsLoaded);
|
||||
@ -59,11 +55,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
Child = textBox = new SettingsTextBox
|
||||
{
|
||||
Current = new Bindable<string>
|
||||
{
|
||||
Default = "test",
|
||||
Value = "test"
|
||||
}
|
||||
Current = new Bindable<string>("test")
|
||||
};
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => textBox.IsLoaded);
|
||||
|
@ -67,11 +67,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
};
|
||||
|
||||
[SettingSource("Sample number textbox", "Textbox number entry", SettingControlType = typeof(SettingsNumberBox))]
|
||||
public Bindable<int?> IntTextBoxBindable { get; } = new Bindable<int?>
|
||||
{
|
||||
Default = null,
|
||||
Value = null
|
||||
};
|
||||
public Bindable<int?> IntTextBoxBindable { get; } = new Bindable<int?>();
|
||||
}
|
||||
|
||||
private enum TestEnum
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -36,10 +34,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Cached(typeof(IDialogOverlay))]
|
||||
private readonly DialogOverlay dialogOverlay;
|
||||
|
||||
private ScoreManager scoreManager;
|
||||
|
||||
private RulesetStore rulesetStore;
|
||||
private BeatmapManager beatmapManager;
|
||||
private ScoreManager scoreManager = null!;
|
||||
private RulesetStore rulesetStore = null!;
|
||||
private BeatmapManager beatmapManager = null!;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
@ -74,7 +71,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
[Test]
|
||||
public void TestLocalScoresDisplay()
|
||||
{
|
||||
BeatmapInfo beatmapInfo = null;
|
||||
BeatmapInfo beatmapInfo = null!;
|
||||
|
||||
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local);
|
||||
|
||||
@ -387,7 +384,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
private class FailableLeaderboard : BeatmapLeaderboard
|
||||
{
|
||||
public new void SetErrorState(LeaderboardState state) => base.SetErrorState(state);
|
||||
public new void SetScores(IEnumerable<ScoreInfo> scores, ScoreInfo userScore = default) => base.SetScores(scores, userScore);
|
||||
public new void SetScores(IEnumerable<ScoreInfo>? scores, ScoreInfo? userScore = null) => base.SetScores(scores, userScore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
|
||||
AddUntilStep("wait for fetch", () => leaderboard.Scores.Any());
|
||||
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != scoreBeingDeleted.OnlineID));
|
||||
|
||||
// "Clean up"
|
||||
@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
public void TestDeleteViaDatabase()
|
||||
{
|
||||
AddStep("delete top score", () => scoreManager.Delete(importedScores[0]));
|
||||
AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
|
||||
AddUntilStep("wait for fetch", () => leaderboard.Scores.Any());
|
||||
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != importedScores[0].OnlineID));
|
||||
}
|
||||
}
|
||||
|
@ -107,9 +107,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
AddStep("start drag", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(notification.ChildrenOfType<Notification>().Single());
|
||||
InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType<Notification>().Single());
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.MoveMouseTo(notification.ChildrenOfType<Notification>().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0));
|
||||
InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType<Notification>().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0));
|
||||
});
|
||||
|
||||
AddStep("fling away", () =>
|
||||
@ -123,6 +123,45 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddAssert("unread count zero", () => notificationOverlay.UnreadCount.Value == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestProgressNotificationCantBeFlung()
|
||||
{
|
||||
bool activated = false;
|
||||
ProgressNotification notification = null!;
|
||||
|
||||
AddStep("post", () =>
|
||||
{
|
||||
activated = false;
|
||||
notificationOverlay.Post(notification = new ProgressNotification
|
||||
{
|
||||
Text = @"Uploading to BSS...",
|
||||
CompletionText = "Uploaded to BSS!",
|
||||
Activated = () => activated = true,
|
||||
});
|
||||
|
||||
progressingNotifications.Add(notification);
|
||||
});
|
||||
|
||||
AddStep("start drag", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType<Notification>().Single());
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
InputManager.MoveMouseTo(notificationOverlay.ChildrenOfType<Notification>().Single().ScreenSpaceDrawQuad.Centre + new Vector2(-500, 0));
|
||||
});
|
||||
|
||||
AddStep("attempt fling", () =>
|
||||
{
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("was not closed", () => !notification.WasClosed);
|
||||
AddUntilStep("was not cancelled", () => notification.State == ProgressNotificationState.Active);
|
||||
AddAssert("was not activated", () => !activated);
|
||||
AddStep("reset mouse position", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||
|
||||
AddUntilStep("was completed", () => notification.State == ProgressNotificationState.Completed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDismissWithoutActivationCloseButton()
|
||||
{
|
||||
@ -465,7 +504,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
base.Update();
|
||||
|
||||
progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed);
|
||||
progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed && n.WasClosed);
|
||||
|
||||
if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
|
||||
{
|
||||
|
@ -24,7 +24,6 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
public readonly BindableDouble SliderVelocityBindable = new BindableDouble(1)
|
||||
{
|
||||
Precision = 0.01,
|
||||
Default = 1,
|
||||
MinValue = 0.1,
|
||||
MaxValue = 10
|
||||
};
|
||||
|
@ -28,7 +28,6 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
public readonly BindableDouble ScrollSpeedBindable = new BindableDouble(1)
|
||||
{
|
||||
Precision = 0.01,
|
||||
Default = 1,
|
||||
MinValue = 0.01,
|
||||
MaxValue = 10
|
||||
};
|
||||
|
@ -45,7 +45,6 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 100,
|
||||
Default = 100
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -49,7 +49,6 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
/// </summary>
|
||||
public readonly BindableDouble BeatLengthBindable = new BindableDouble(DEFAULT_BEAT_LENGTH)
|
||||
{
|
||||
Default = DEFAULT_BEAT_LENGTH,
|
||||
MinValue = 6,
|
||||
MaxValue = 60000
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Configuration
|
||||
{
|
||||
// UI/selection defaults
|
||||
SetDefault(OsuSetting.Ruleset, string.Empty);
|
||||
SetDefault(OsuSetting.Skin, SkinInfo.TRIANGLES_SKIN.ToString());
|
||||
SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString());
|
||||
|
||||
SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details);
|
||||
SetDefault(OsuSetting.BeatmapDetailModsFilter, false);
|
||||
@ -131,6 +131,7 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
|
||||
SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
|
||||
SetDefault(OsuSetting.KeyOverlay, false);
|
||||
SetDefault(OsuSetting.GameplayLeaderboard, true);
|
||||
SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true);
|
||||
|
||||
SetDefault(OsuSetting.FloatingComments, false);
|
||||
@ -294,6 +295,7 @@ namespace osu.Game.Configuration
|
||||
LightenDuringBreaks,
|
||||
ShowStoryboard,
|
||||
KeyOverlay,
|
||||
GameplayLeaderboard,
|
||||
PositionalHitsounds,
|
||||
PositionalHitsoundsLevel,
|
||||
AlwaysPlayFirstComboBreak,
|
||||
|
@ -89,6 +89,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections");
|
||||
|
||||
/// <summary>
|
||||
/// "Mod presets"
|
||||
/// </summary>
|
||||
public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Mod presets");
|
||||
|
||||
/// <summary>
|
||||
/// "Name"
|
||||
/// </summary>
|
||||
|
@ -44,11 +44,6 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString ClearAllCaches => new TranslatableString(getKey(@"clear_all_caches"), @"Clear all caches");
|
||||
|
||||
/// <summary>
|
||||
/// "Compact realm"
|
||||
/// </summary>
|
||||
public static LocalisableString CompactRealm => new TranslatableString(getKey(@"compact_realm"), @"Compact realm");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString AlwaysShowKeyOverlay => new TranslatableString(getKey(@"key_overlay"), @"Always show key overlay");
|
||||
|
||||
/// <summary>
|
||||
/// "Always show gameplay leaderboard"
|
||||
/// </summary>
|
||||
public static LocalisableString AlwaysShowGameplayLeaderboard => new TranslatableString(getKey(@"gameplay_leaderboard"), @"Always show gameplay leaderboard");
|
||||
|
||||
/// <summary>
|
||||
/// "Always play first combo break sound"
|
||||
/// </summary>
|
||||
|
@ -64,6 +64,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString RunSetupWizard => new TranslatableString(getKey(@"run_setup_wizard"), @"Run setup wizard");
|
||||
|
||||
/// <summary>
|
||||
/// "You are running the latest release ({0})"
|
||||
/// </summary>
|
||||
public static LocalisableString RunningLatestRelease(string version) => new TranslatableString(getKey(@"running_latest_release"), @"You are running the latest release ({0})", version);
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,41 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString SelectDirectory => new TranslatableString(getKey(@"select_directory"), @"Select directory");
|
||||
|
||||
/// <summary>
|
||||
/// "Migration in progress"
|
||||
/// </summary>
|
||||
public static LocalisableString MigrationInProgress => new TranslatableString(getKey(@"migration_in_progress"), @"Migration in progress");
|
||||
|
||||
/// <summary>
|
||||
/// "This could take a few minutes depending on the speed of your disk(s)."
|
||||
/// </summary>
|
||||
public static LocalisableString MigrationDescription => new TranslatableString(getKey(@"migration_description"), @"This could take a few minutes depending on the speed of your disk(s).");
|
||||
|
||||
/// <summary>
|
||||
/// "Please avoid interacting with the game!"
|
||||
/// </summary>
|
||||
public static LocalisableString ProhibitedInteractDuringMigration => new TranslatableString(getKey(@"prohibited_interact_during_migration"), @"Please avoid interacting with the game!");
|
||||
|
||||
/// <summary>
|
||||
/// "Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up."
|
||||
/// </summary>
|
||||
public static LocalisableString FailedCleanupNotification => new TranslatableString(getKey(@"failed_cleanup_notification"), @"Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up.");
|
||||
|
||||
/// <summary>
|
||||
/// "Please select a new location"
|
||||
/// </summary>
|
||||
public static LocalisableString SelectNewLocation => new TranslatableString(getKey(@"select_new_location"), @"Please select a new location");
|
||||
|
||||
/// <summary>
|
||||
/// "The target directory already seems to have an osu! install. Use that data instead?"
|
||||
/// </summary>
|
||||
public static LocalisableString TargetDirectoryAlreadyInstalledOsu => new TranslatableString(getKey(@"target_directory_already_installed_osu"), @"The target directory already seems to have an osu! install. Use that data instead?");
|
||||
|
||||
/// <summary>
|
||||
/// "To complete this operation, osu! will close. Please open it again to use the new data location."
|
||||
/// </summary>
|
||||
public static LocalisableString RestartAndReOpenRequiredForCompletion => new TranslatableString(getKey(@"restart_and_re_open_required_for_completion"), @"To complete this operation, osu! will close. Please open it again to use the new data location.");
|
||||
|
||||
/// <summary>
|
||||
/// "Import beatmaps from stable"
|
||||
/// </summary>
|
||||
@ -84,6 +119,26 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString RestoreAllRecentlyDeletedModPresets => new TranslatableString(getKey(@"restore_all_recently_deleted_mod_presets"), @"Restore all recently deleted mod presets");
|
||||
|
||||
/// <summary>
|
||||
/// "Deleted all collections!"
|
||||
/// </summary>
|
||||
public static LocalisableString DeletedAllCollections => new TranslatableString(getKey(@"deleted_all_collections"), @"Deleted all collections!");
|
||||
|
||||
/// <summary>
|
||||
/// "Deleted all mod presets!"
|
||||
/// </summary>
|
||||
public static LocalisableString DeletedAllModPresets => new TranslatableString(getKey(@"deleted_all_mod_presets"), @"Deleted all mod presets!");
|
||||
|
||||
/// <summary>
|
||||
/// "Restored all deleted mod presets!"
|
||||
/// </summary>
|
||||
public static LocalisableString RestoredAllDeletedModPresets => new TranslatableString(getKey(@"restored_all_deleted_mod_presets"), @"Restored all deleted mod presets!");
|
||||
|
||||
/// <summary>
|
||||
/// "Please select your osu!stable install location"
|
||||
/// </summary>
|
||||
public static LocalisableString StableDirectorySelectHeader => new TranslatableString(getKey(@"stable_directory_select_header"), @"Please select your osu!stable install location");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString NoTabletDetected => new TranslatableString(getKey(@"no_tablet_detected"), @"No tablet detected!");
|
||||
|
||||
/// <summary>
|
||||
/// "If your tablet is not detected, please read [this FAQ]({0}) for troubleshooting steps."
|
||||
/// </summary>
|
||||
public static LocalisableString NoTabletDetectedDescription(string url) => new TranslatableString(getKey(@"no_tablet_detected_description"), @"If your tablet is not detected, please read [this FAQ]({0}) for troubleshooting steps.", url);
|
||||
|
||||
/// <summary>
|
||||
/// "Reset to full area"
|
||||
/// </summary>
|
||||
|
@ -9,27 +9,25 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetBeatmapRequest : APIRequest<APIBeatmap>
|
||||
{
|
||||
private readonly IBeatmapInfo beatmapInfo;
|
||||
|
||||
private readonly string filename;
|
||||
public readonly IBeatmapInfo BeatmapInfo;
|
||||
public readonly string Filename;
|
||||
|
||||
public GetBeatmapRequest(IBeatmapInfo beatmapInfo)
|
||||
{
|
||||
this.beatmapInfo = beatmapInfo;
|
||||
|
||||
filename = (beatmapInfo as BeatmapInfo)?.Path ?? string.Empty;
|
||||
BeatmapInfo = beatmapInfo;
|
||||
Filename = (beatmapInfo as BeatmapInfo)?.Path ?? string.Empty;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var request = base.CreateWebRequest();
|
||||
|
||||
if (beatmapInfo.OnlineID > 0)
|
||||
request.AddParameter(@"id", beatmapInfo.OnlineID.ToString());
|
||||
if (!string.IsNullOrEmpty(beatmapInfo.MD5Hash))
|
||||
request.AddParameter(@"checksum", beatmapInfo.MD5Hash);
|
||||
if (!string.IsNullOrEmpty(filename))
|
||||
request.AddParameter(@"filename", filename);
|
||||
if (BeatmapInfo.OnlineID > 0)
|
||||
request.AddParameter(@"id", BeatmapInfo.OnlineID.ToString());
|
||||
if (!string.IsNullOrEmpty(BeatmapInfo.MD5Hash))
|
||||
request.AddParameter(@"checksum", BeatmapInfo.MD5Hash);
|
||||
if (!string.IsNullOrEmpty(Filename))
|
||||
request.AddParameter(@"filename", Filename);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty(@"rank_history")]
|
||||
private APIRankHistory rankHistory
|
||||
{
|
||||
set => statistics.RankHistory = value;
|
||||
set => Statistics.RankHistory = value;
|
||||
}
|
||||
|
||||
[JsonProperty("badges")]
|
||||
|
@ -1,14 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Development;
|
||||
@ -54,23 +51,23 @@ namespace osu.Game.Online.Leaderboards
|
||||
private readonly Container placeholderContainer;
|
||||
private readonly UserTopScoreContainer<TScoreInfo> userScoreContainer;
|
||||
|
||||
private FillFlowContainer<LeaderboardScore> scoreFlowContainer;
|
||||
private FillFlowContainer<LeaderboardScore>? scoreFlowContainer;
|
||||
|
||||
private readonly LoadingSpinner loading;
|
||||
|
||||
private CancellationTokenSource currentFetchCancellationSource;
|
||||
private CancellationTokenSource currentScoresAsyncLoadCancellationSource;
|
||||
private CancellationTokenSource? currentFetchCancellationSource;
|
||||
private CancellationTokenSource? currentScoresAsyncLoadCancellationSource;
|
||||
|
||||
private APIRequest fetchScoresRequest;
|
||||
private APIRequest? fetchScoresRequest;
|
||||
|
||||
private LeaderboardState state;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider? api { get; set; }
|
||||
|
||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
||||
|
||||
private TScope scope;
|
||||
private TScope scope = default!;
|
||||
|
||||
public TScope Scope
|
||||
{
|
||||
@ -179,16 +176,21 @@ namespace osu.Game.Online.Leaderboards
|
||||
/// </summary>
|
||||
/// <param name="scores">The scores to display.</param>
|
||||
/// <param name="userScore">The user top score, if any.</param>
|
||||
protected void SetScores(IEnumerable<TScoreInfo> scores, TScoreInfo userScore = default)
|
||||
protected void SetScores(IEnumerable<TScoreInfo>? scores, TScoreInfo? userScore = default)
|
||||
{
|
||||
this.scores.Clear();
|
||||
if (scores != null)
|
||||
this.scores.AddRange(scores);
|
||||
|
||||
// Schedule needs to be non-delayed here for the weird logic in refetchScores to work.
|
||||
// If it is removed, the placeholder will be incorrectly updated to "no scores" rather than "retrieving".
|
||||
// This whole flow should be refactored in the future.
|
||||
Scheduler.Add(applyNewScores, false);
|
||||
// Non-delayed schedule may potentially run inline (due to IsMainThread check passing) after leaderboard is disposed.
|
||||
// This is guarded against in BeatmapLeaderboard via web request cancellation, but let's be extra safe.
|
||||
if (!IsDisposed)
|
||||
{
|
||||
// Schedule needs to be non-delayed here for the weird logic in refetchScores to work.
|
||||
// If it is removed, the placeholder will be incorrectly updated to "no scores" rather than "retrieving".
|
||||
// This whole flow should be refactored in the future.
|
||||
Scheduler.Add(applyNewScores, false);
|
||||
}
|
||||
|
||||
void applyNewScores()
|
||||
{
|
||||
@ -208,8 +210,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
||||
[CanBeNull]
|
||||
protected abstract APIRequest FetchScores(CancellationToken cancellationToken);
|
||||
protected abstract APIRequest? FetchScores(CancellationToken cancellationToken);
|
||||
|
||||
protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index);
|
||||
|
||||
@ -293,7 +294,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
|
||||
#region Placeholder handling
|
||||
|
||||
private Placeholder placeholder;
|
||||
private Placeholder? placeholder;
|
||||
|
||||
private void setState(LeaderboardState state)
|
||||
{
|
||||
@ -320,7 +321,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private Placeholder getPlaceholderFor(LeaderboardState state)
|
||||
private Placeholder? getPlaceholderFor(LeaderboardState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using osu.Framework.Bindables;
|
||||
@ -18,13 +16,15 @@ namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
private const int duration = 500;
|
||||
|
||||
public Bindable<TScoreInfo> Score = new Bindable<TScoreInfo>();
|
||||
public Bindable<TScoreInfo?> Score = new Bindable<TScoreInfo?>();
|
||||
|
||||
private readonly Container scoreContainer;
|
||||
private readonly Func<TScoreInfo, LeaderboardScore> createScoreDelegate;
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
|
||||
private CancellationTokenSource? loadScoreCancellation;
|
||||
|
||||
public UserTopScoreContainer(Func<TScoreInfo, LeaderboardScore> createScoreDelegate)
|
||||
{
|
||||
this.createScoreDelegate = createScoreDelegate;
|
||||
@ -65,9 +65,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
Score.BindValueChanged(onScoreChanged);
|
||||
}
|
||||
|
||||
private CancellationTokenSource loadScoreCancellation;
|
||||
|
||||
private void onScoreChanged(ValueChangedEvent<TScoreInfo> score)
|
||||
private void onScoreChanged(ValueChangedEvent<TScoreInfo?> score)
|
||||
{
|
||||
var newScore = score.NewValue;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
// 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 System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -58,7 +58,7 @@ namespace osu.Game.Online.Spectator
|
||||
{
|
||||
await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), state);
|
||||
}
|
||||
catch (HubException exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception.GetHubExceptionMessage() == HubClientConnector.SERVER_SHUTDOWN_MESSAGE)
|
||||
{
|
||||
|
@ -561,9 +561,11 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
// This should be able to be performed from song select, but that is disabled for now
|
||||
// due to the weird decoupled ruleset logic (which can cause a crash in certain filter scenarios).
|
||||
PerformFromScreen(screen =>
|
||||
{
|
||||
Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset} to match score");
|
||||
Logger.Log($"{nameof(PresentScore)} updating beatmap ({databasedBeatmap}) and ruleset ({databasedScore.ScoreInfo.Ruleset}) to match score");
|
||||
|
||||
Ruleset.Value = databasedScore.ScoreInfo.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
||||
@ -578,7 +580,7 @@ namespace osu.Game
|
||||
screen.Push(new SoloResultsScreen(databasedScore.ScoreInfo, false));
|
||||
break;
|
||||
}
|
||||
}, validScreens: new[] { typeof(PlaySongSelect) });
|
||||
});
|
||||
}
|
||||
|
||||
public override Task Import(params ImportTask[] imports)
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dialog
|
||||
@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
/// <param name="message">The description of the action to be displayed to the user.</param>
|
||||
/// <param name="onConfirm">An action to perform on confirmation.</param>
|
||||
/// <param name="onCancel">An optional action to perform on cancel.</param>
|
||||
public ConfirmDialog(string message, Action onConfirm, Action onCancel = null)
|
||||
public ConfirmDialog(LocalisableString message, Action onConfirm, Action onCancel = null)
|
||||
{
|
||||
HeaderText = message;
|
||||
BodyText = "Last chance to turn back";
|
||||
|
@ -123,7 +123,7 @@ namespace osu.Game.Overlays.FirstRunSetup
|
||||
beatmapSubscription?.Dispose();
|
||||
}
|
||||
|
||||
private void beatmapsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet? changes, Exception error)
|
||||
private void beatmapsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet? changes, Exception error) => Schedule(() =>
|
||||
{
|
||||
currentlyLoadedBeatmaps.Text = FirstRunSetupBeatmapScreenStrings.CurrentlyLoadedBeatmaps(sender.Count);
|
||||
|
||||
@ -139,7 +139,7 @@ namespace osu.Game.Overlays.FirstRunSetup
|
||||
currentlyLoadedBeatmaps.ScaleTo(1.1f)
|
||||
.ScaleTo(1, 1500, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private void downloadTutorial()
|
||||
{
|
||||
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
@ -24,7 +25,7 @@ namespace osu.Game.Overlays.Music
|
||||
public class PlaylistOverlay : VisibilityContainer
|
||||
{
|
||||
private const float transition_duration = 600;
|
||||
private const float playlist_height = 510;
|
||||
public const float PLAYLIST_HEIGHT = 510;
|
||||
|
||||
private readonly BindableList<Live<BeatmapSetInfo>> beatmapSets = new BindableList<Live<BeatmapSetInfo>>();
|
||||
|
||||
@ -130,7 +131,7 @@ namespace osu.Game.Overlays.Music
|
||||
filter.Search.HoldFocus = true;
|
||||
Schedule(() => filter.Search.TakeFocus());
|
||||
|
||||
this.ResizeTo(new Vector2(1, playlist_height), transition_duration, Easing.OutQuint);
|
||||
this.ResizeTo(new Vector2(1, RelativeSizeAxes.HasFlagFast(Axes.Y) ? 1f : PLAYLIST_HEIGHT), transition_duration, Easing.OutQuint);
|
||||
this.FadeIn(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
|
@ -58,12 +58,11 @@ namespace osu.Game.Overlays
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
// Todo: These binds really shouldn't be here, but are unlikely to cause any issues for now.
|
||||
// They are placed here for now since some tests rely on setting the beatmap _and_ their hierarchies inside their load(), which runs before the MusicController's load().
|
||||
beatmap.BindValueChanged(beatmapChanged, true);
|
||||
base.LoadComplete();
|
||||
|
||||
beatmap.BindValueChanged(b => changeBeatmap(b.NewValue), true);
|
||||
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
|
||||
}
|
||||
|
||||
@ -263,8 +262,6 @@ namespace osu.Game.Overlays
|
||||
|
||||
private IQueryable<BeatmapSetInfo> getBeatmapSets() => realm.Realm.All<BeatmapSetInfo>().Where(s => !s.DeletePending);
|
||||
|
||||
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap) => changeBeatmap(beatmap.NewValue);
|
||||
|
||||
private void changeBeatmap(WorkingBeatmap newWorking)
|
||||
{
|
||||
// This method can potentially be triggered multiple times as it is eagerly fired in next() / prev() to ensure correct execution order
|
||||
|
@ -113,9 +113,12 @@ namespace osu.Game.Overlays
|
||||
|
||||
if (enabled)
|
||||
// we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
|
||||
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 100);
|
||||
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 250);
|
||||
else
|
||||
{
|
||||
processingPosts = false;
|
||||
toastTray.FlushAllToasts();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -68,6 +68,8 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public virtual bool Read { get; set; }
|
||||
|
||||
protected virtual bool AllowFlingDismiss => true;
|
||||
|
||||
public new bool IsDragged => dragContainer.IsDragged;
|
||||
|
||||
protected virtual IconUsage CloseButtonIcon => FontAwesome.Solid.Check;
|
||||
@ -315,7 +317,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
if (Rotation < -10 || velocity.X < -0.3f)
|
||||
if (notification.AllowFlingDismiss && (Rotation < -10 || velocity.X < -0.3f))
|
||||
notification.Close(true);
|
||||
else if (X > 30 || velocity.X > 0.3f)
|
||||
notification.ForwardToOverlay?.Invoke();
|
||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public Func<bool>? CancelRequested { get; set; }
|
||||
|
||||
protected override bool AllowFlingDismiss => false;
|
||||
|
||||
/// <summary>
|
||||
/// The function to post completion notifications back to.
|
||||
/// </summary>
|
||||
|
@ -38,6 +38,7 @@ namespace osu.Game.Overlays
|
||||
private const float transition_length = 800;
|
||||
private const float progress_height = 10;
|
||||
private const float bottom_black_area_height = 55;
|
||||
private const float margin = 10;
|
||||
|
||||
private Drawable background;
|
||||
private ProgressBar progressBar;
|
||||
@ -53,6 +54,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private Container dragContainer;
|
||||
private Container playerContainer;
|
||||
private Container playlistContainer;
|
||||
|
||||
protected override string PopInSampleName => "UI/now-playing-pop-in";
|
||||
protected override string PopOutSampleName => "UI/now-playing-pop-out";
|
||||
@ -69,7 +71,7 @@ namespace osu.Game.Overlays
|
||||
public NowPlayingOverlay()
|
||||
{
|
||||
Width = 400;
|
||||
Margin = new MarginPadding(10);
|
||||
Margin = new MarginPadding(margin);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -82,7 +84,6 @@ namespace osu.Game.Overlays
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
playerContainer = new Container
|
||||
@ -182,8 +183,13 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
},
|
||||
},
|
||||
playlistContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = player_height + margin,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -193,11 +199,10 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
LoadComponentAsync(playlist = new PlaylistOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = player_height + 10,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}, _ =>
|
||||
{
|
||||
dragContainer.Add(playlist);
|
||||
playlistContainer.Add(playlist);
|
||||
|
||||
playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true);
|
||||
|
||||
@ -242,7 +247,18 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
Height = dragContainer.Height;
|
||||
playlistContainer.Height = MathF.Min(Parent.DrawHeight - margin * 3 - player_height, PlaylistOverlay.PLAYLIST_HEIGHT);
|
||||
|
||||
float height = player_height;
|
||||
|
||||
if (playlist != null)
|
||||
{
|
||||
height += playlist.DrawHeight;
|
||||
if (playlist.State.Value == Visibility.Visible)
|
||||
height += margin;
|
||||
}
|
||||
|
||||
Height = dragContainer.Height = height;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Text = DebugSettingsStrings.CompactRealm,
|
||||
Text = "Compact realm",
|
||||
Action = () =>
|
||||
{
|
||||
// Blocking operations implicitly causes a Compact().
|
||||
|
@ -38,6 +38,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
Current = config.GetBindable<bool>(OsuSetting.KeyOverlay),
|
||||
Keywords = new[] { "counter" },
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = GameplaySettingsStrings.AlwaysShowGameplayLeaderboard,
|
||||
Current = config.GetBindable<bool>(OsuSetting.GameplayLeaderboard),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = $"You are running the latest release ({game.Version})",
|
||||
Text = GeneralSettingsStrings.RunningLatestRelease(game.Version),
|
||||
Icon = FontAwesome.Solid.CheckCircle,
|
||||
});
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
windowModes.BindTo(host.Window.SupportedWindowModes);
|
||||
}
|
||||
|
||||
if (host.Window is WindowsWindow windowsWindow)
|
||||
fullscreenCapability.BindTo(windowsWindow.FullscreenCapability);
|
||||
if (host.Renderer is IWindowsRenderer windowsRenderer)
|
||||
fullscreenCapability.BindTo(windowsRenderer.FullscreenCapability);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(OsuColour colours, LocalisationManager localisation)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -110,11 +110,10 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows || RuntimeInfo.OS == RuntimeInfo.Platform.Linux)
|
||||
{
|
||||
t.NewLine();
|
||||
t.AddText("If your tablet is not detected, please read ");
|
||||
t.AddLink("this FAQ", LinkAction.External, RuntimeInfo.OS == RuntimeInfo.Platform.Windows
|
||||
var formattedSource = MessageFormatter.FormatText(localisation.GetLocalisedBindableString(TabletSettingsStrings.NoTabletDetectedDescription(RuntimeInfo.OS == RuntimeInfo.Platform.Windows
|
||||
? @"https://opentabletdriver.net/Wiki/FAQ/Windows"
|
||||
: @"https://opentabletdriver.net/Wiki/FAQ/Linux");
|
||||
t.AddText(" for troubleshooting steps.");
|
||||
: @"https://opentabletdriver.net/Wiki/FAQ/Linux")).Value);
|
||||
t.AddLinks(formattedSource.Text, formattedSource.Links);
|
||||
}
|
||||
}),
|
||||
}
|
||||
@ -215,21 +214,21 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
rotation.BindTo(tabletHandler.Rotation);
|
||||
|
||||
areaOffset.BindTo(tabletHandler.AreaOffset);
|
||||
areaOffset.BindValueChanged(val =>
|
||||
areaOffset.BindValueChanged(val => Schedule(() =>
|
||||
{
|
||||
offsetX.Value = val.NewValue.X;
|
||||
offsetY.Value = val.NewValue.Y;
|
||||
}, true);
|
||||
}), true);
|
||||
|
||||
offsetX.BindValueChanged(val => areaOffset.Value = new Vector2(val.NewValue, areaOffset.Value.Y));
|
||||
offsetY.BindValueChanged(val => areaOffset.Value = new Vector2(areaOffset.Value.X, val.NewValue));
|
||||
|
||||
areaSize.BindTo(tabletHandler.AreaSize);
|
||||
areaSize.BindValueChanged(val =>
|
||||
areaSize.BindValueChanged(val => Schedule(() =>
|
||||
{
|
||||
sizeX.Value = val.NewValue.X;
|
||||
sizeY.Value = val.NewValue.Y;
|
||||
}, true);
|
||||
}), true);
|
||||
|
||||
sizeX.BindValueChanged(val =>
|
||||
{
|
||||
@ -255,7 +254,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
});
|
||||
|
||||
tablet.BindTo(tabletHandler.Tablet);
|
||||
tablet.BindValueChanged(val =>
|
||||
tablet.BindValueChanged(val => Schedule(() =>
|
||||
{
|
||||
Scheduler.AddOnce(updateVisibility);
|
||||
|
||||
@ -274,7 +273,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
sizeY.Default = sizeY.MaxValue = tab.Size.Y;
|
||||
|
||||
areaSize.Default = new Vector2(sizeX.Default, sizeY.Default);
|
||||
}, true);
|
||||
}), true);
|
||||
}
|
||||
|
||||
private void updateVisibility()
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class BeatmapSettings : SettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => "Beatmaps";
|
||||
protected override LocalisableString Header => CommonStrings.Beatmaps;
|
||||
|
||||
private SettingsButton importBeatmapsButton = null!;
|
||||
private SettingsButton deleteBeatmapsButton = null!;
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class CollectionsSettings : SettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => "Collections";
|
||||
protected override LocalisableString Header => CommonStrings.Collections;
|
||||
|
||||
private SettingsButton importCollectionsButton = null!;
|
||||
|
||||
@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
private void deleteAllCollections()
|
||||
{
|
||||
realm.Write(r => r.RemoveAll<BeatmapCollection>());
|
||||
notificationOverlay?.Post(new ProgressCompletionNotification { Text = "Deleted all collections!" });
|
||||
notificationOverlay?.Post(new ProgressCompletionNotification { Text = MaintenanceSettingsStrings.DeletedAllCollections });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Screens;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Screens;
|
||||
using osuTK;
|
||||
@ -71,14 +72,14 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Migration in progress",
|
||||
Text = MaintenanceSettingsStrings.MigrationInProgress,
|
||||
Font = OsuFont.Default.With(size: 40)
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "This could take a few minutes depending on the speed of your disk(s).",
|
||||
Text = MaintenanceSettingsStrings.MigrationDescription,
|
||||
Font = OsuFont.Default.With(size: 30)
|
||||
},
|
||||
new LoadingSpinner(true)
|
||||
@ -89,7 +90,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Please avoid interacting with the game!",
|
||||
Text = MaintenanceSettingsStrings.ProhibitedInteractDuringMigration,
|
||||
Font = OsuFont.Default.With(size: 30)
|
||||
},
|
||||
}
|
||||
@ -111,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
notifications.Post(new SimpleNotification
|
||||
{
|
||||
Text = "Some files couldn't be cleaned up during migration. Clicking this notification will open the folder so you can manually clean things up.",
|
||||
Text = MaintenanceSettingsStrings.FailedCleanupNotification,
|
||||
Activated = () =>
|
||||
{
|
||||
originalStorage.PresentExternally();
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
|
||||
public override LocalisableString HeaderText => "Please select a new location";
|
||||
public override LocalisableString HeaderText => MaintenanceSettingsStrings.SelectNewLocation;
|
||||
|
||||
protected override void OnSelection(DirectoryInfo directory)
|
||||
{
|
||||
@ -51,9 +52,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
// Quick test for whether there's already an osu! install at the target path.
|
||||
if (fileInfos.Any(f => f.Name == OsuGameBase.CLIENT_DATABASE_FILENAME))
|
||||
{
|
||||
dialogOverlay.Push(new ConfirmDialog("The target directory already seems to have an osu! install. Use that data instead?", () =>
|
||||
dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.TargetDirectoryAlreadyInstalledOsu, () =>
|
||||
{
|
||||
dialogOverlay.Push(new ConfirmDialog("To complete this operation, osu! will close. Please open it again to use the new data location.", () =>
|
||||
dialogOverlay.Push(new ConfirmDialog(MaintenanceSettingsStrings.RestartAndReOpenRequiredForCompletion, () =>
|
||||
{
|
||||
(storage as OsuStorage)?.ChangeDataPath(target.FullName);
|
||||
game.Exit();
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class ModPresetSettings : SettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => "Mod presets";
|
||||
protected override LocalisableString Header => CommonStrings.ModPresets;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
deleteAllButton.Enabled.Value = true;
|
||||
|
||||
if (deletionTask.IsCompletedSuccessfully)
|
||||
notificationOverlay?.Post(new ProgressCompletionNotification { Text = "Deleted all mod presets!" });
|
||||
notificationOverlay?.Post(new ProgressCompletionNotification { Text = MaintenanceSettingsStrings.DeletedAllModPresets });
|
||||
else if (deletionTask.IsFaulted)
|
||||
Logger.Error(deletionTask.Exception, "Failed to delete all mod presets");
|
||||
}
|
||||
@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
undeleteButton.Enabled.Value = true;
|
||||
|
||||
if (undeletionTask.IsCompletedSuccessfully)
|
||||
notificationOverlay?.Post(new ProgressCompletionNotification { Text = "Restored all deleted mod presets!" });
|
||||
notificationOverlay?.Post(new ProgressCompletionNotification { Text = MaintenanceSettingsStrings.RestoredAllDeletedModPresets });
|
||||
else if (undeletionTask.IsFaulted)
|
||||
Logger.Error(undeletionTask.Exception, "Failed to restore mod presets");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class ScoreSettings : SettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => "Scores";
|
||||
protected override LocalisableString Header => CommonStrings.Scores;
|
||||
|
||||
private SettingsButton importScoresButton = null!;
|
||||
private SettingsButton deleteScoresButton = null!;
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public class SkinSettings : SettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => "Skins";
|
||||
protected override LocalisableString Header => CommonStrings.Skins;
|
||||
|
||||
private SettingsButton importSkinsButton = null!;
|
||||
private SettingsButton deleteSkinsButton = null!;
|
||||
|
@ -58,6 +58,9 @@ namespace osu.Game.Rulesets.Configuration
|
||||
pendingWrites.Clear();
|
||||
}
|
||||
|
||||
if (!changed.Any())
|
||||
return true;
|
||||
|
||||
realm?.Write(r =>
|
||||
{
|
||||
foreach (var c in changed)
|
||||
|
@ -36,32 +36,24 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp), typeof(ModAutoplay) };
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
public BindableNumber<double> InitialRate { get; } = new BindableDouble(1)
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MaxValue = 2,
|
||||
Default = 1,
|
||||
Value = 1,
|
||||
Precision = 0.01
|
||||
};
|
||||
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public BindableBool AdjustPitch { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public BindableBool AdjustPitch { get; } = new BindableBool(true);
|
||||
|
||||
/// <summary>
|
||||
/// The instantaneous rate of the track.
|
||||
/// Every frame this mod will attempt to smoothly adjust this to meet <see cref="targetRate"/>.
|
||||
/// </summary>
|
||||
public BindableNumber<double> SpeedChange { get; } = new BindableDouble
|
||||
public BindableNumber<double> SpeedChange { get; } = new BindableDouble(1)
|
||||
{
|
||||
MinValue = min_allowable_rate,
|
||||
MaxValue = max_allowable_rate,
|
||||
Default = 1,
|
||||
Value = 1
|
||||
};
|
||||
|
||||
// The two constants below denote the maximum allowable range of rates that `SpeedChange` can take.
|
||||
|
@ -18,12 +18,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override LocalisableString Description => "Zoooooooooom...";
|
||||
|
||||
[SettingSource("Speed increase", "The actual increase to apply")]
|
||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble
|
||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(1.5)
|
||||
{
|
||||
MinValue = 1.01,
|
||||
MaxValue = 2,
|
||||
Default = 1.5,
|
||||
Value = 1.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
}
|
||||
|
@ -18,12 +18,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override LocalisableString Description => "Less zoom...";
|
||||
|
||||
[SettingSource("Speed decrease", "The actual decrease to apply")]
|
||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble
|
||||
public override BindableNumber<double> SpeedChange { get; } = new BindableDouble(0.75)
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MaxValue = 0.99,
|
||||
Default = 0.75,
|
||||
Value = 0.75,
|
||||
Precision = 0.01,
|
||||
};
|
||||
}
|
||||
|
@ -36,34 +36,20 @@ namespace osu.Game.Rulesets.Mods
|
||||
private readonly BindableNumber<int> currentCombo = new BindableInt();
|
||||
|
||||
[SettingSource("Enable metronome", "Add a metronome beat to help you keep track of the rhythm.")]
|
||||
public BindableBool EnableMetronome { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public BindableBool EnableMetronome { get; } = new BindableBool(true);
|
||||
|
||||
[SettingSource("Final volume at combo", "The combo count at which point the track reaches its final volume.", SettingControlType = typeof(SettingsSlider<int, MuteComboSlider>))]
|
||||
public BindableInt MuteComboCount { get; } = new BindableInt
|
||||
public BindableInt MuteComboCount { get; } = new BindableInt(100)
|
||||
{
|
||||
Default = 100,
|
||||
Value = 100,
|
||||
MinValue = 0,
|
||||
MaxValue = 500,
|
||||
};
|
||||
|
||||
[SettingSource("Start muted", "Increase volume as combo builds.")]
|
||||
public BindableBool InverseMuting { get; } = new BindableBool
|
||||
{
|
||||
Default = false,
|
||||
Value = false
|
||||
};
|
||||
public BindableBool InverseMuting { get; } = new BindableBool();
|
||||
|
||||
[SettingSource("Mute hit sounds", "Hit sounds are also muted alongside the track.")]
|
||||
public BindableBool AffectsHitSounds { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public BindableBool AffectsHitSounds { get; } = new BindableBool(true);
|
||||
|
||||
protected ModMuted()
|
||||
{
|
||||
|
@ -6,7 +6,9 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -34,6 +36,11 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
protected float ComboBasedAlpha;
|
||||
|
||||
[SettingSource(
|
||||
"Hidden at combo",
|
||||
"The combo count at which the cursor becomes completely hidden",
|
||||
SettingControlType = typeof(SettingsSlider<int, HiddenComboSlider>)
|
||||
)]
|
||||
public abstract BindableInt HiddenComboCount { get; }
|
||||
|
||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||
|
@ -18,10 +18,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))]
|
||||
public Bindable<int?> Seed { get; } = new Bindable<int?>
|
||||
{
|
||||
Default = null,
|
||||
Value = null
|
||||
};
|
||||
public Bindable<int?> Seed { get; } = new Bindable<int?>();
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
private double finalRateTime;
|
||||
private double beginRampTime;
|
||||
|
||||
public BindableNumber<double> SpeedChange { get; } = new BindableDouble
|
||||
public BindableNumber<double> SpeedChange { get; } = new BindableDouble(1)
|
||||
{
|
||||
Default = 1,
|
||||
Value = 1,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@ -17,32 +16,21 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override LocalisableString Description => "Sloooow doooown...";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown;
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble(1)
|
||||
{
|
||||
MinValue = 0.51,
|
||||
MaxValue = 2,
|
||||
Default = 1,
|
||||
Value = 1,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble(0.75)
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MaxValue = 1.99,
|
||||
Default = 0.75,
|
||||
Value = 0.75,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool(true);
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray();
|
||||
|
||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@ -17,32 +16,21 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override LocalisableString Description => "Can you keep up?";
|
||||
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp;
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble(1)
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MaxValue = 1.99,
|
||||
Default = 1,
|
||||
Value = 1,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble(1.5)
|
||||
{
|
||||
MinValue = 0.51,
|
||||
MaxValue = 2,
|
||||
Default = 1.5,
|
||||
Value = 1.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool(true);
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray();
|
||||
|
||||
|
@ -651,7 +651,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// <remarks>
|
||||
/// This does not affect the time offset provided to invocations of <see cref="CheckForResult"/>.
|
||||
/// </remarks>
|
||||
protected virtual double MaximumJudgementOffset => HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? 0;
|
||||
public virtual double MaximumJudgementOffset => HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Applies the <see cref="Result"/> of this <see cref="DrawableHitObject"/>, notifying responders such as
|
||||
|
@ -60,7 +60,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// </summary>
|
||||
protected readonly BindableDouble TimeRange = new BindableDouble(time_span_default)
|
||||
{
|
||||
Default = time_span_default,
|
||||
MinValue = time_span_min,
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -126,6 +127,16 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
||||
private float scrollLength => scrollingAxis == Direction.Horizontal ? DrawWidth : DrawHeight;
|
||||
|
||||
public override void Add(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
// Scroll info is not available until loaded.
|
||||
// The lifetime of all entries will be updated in the first Update.
|
||||
if (IsLoaded)
|
||||
setComputedLifetimeStart(entry);
|
||||
|
||||
base.Add(entry);
|
||||
}
|
||||
|
||||
protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable)
|
||||
{
|
||||
base.AddDrawable(entry, drawable);
|
||||
@ -144,7 +155,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
||||
private void invalidateHitObject(DrawableHitObject hitObject)
|
||||
{
|
||||
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
|
||||
layoutComputed.Remove(hitObject);
|
||||
}
|
||||
|
||||
@ -156,10 +166,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
||||
layoutComputed.Clear();
|
||||
|
||||
// Reset lifetime to the conservative estimation.
|
||||
// If a drawable becomes alive by this lifetime, its lifetime will be updated to a more precise lifetime in the next update.
|
||||
foreach (var entry in Entries)
|
||||
entry.SetInitialLifetime();
|
||||
setComputedLifetimeStart(entry);
|
||||
|
||||
scrollingInfo.Algorithm.Reset();
|
||||
|
||||
@ -186,35 +194,46 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
}
|
||||
}
|
||||
|
||||
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
|
||||
/// <summary>
|
||||
/// Get a conservative maximum bounding box of a <see cref="DrawableHitObject"/> corresponding to <paramref name="entry"/>.
|
||||
/// It is used to calculate when the hit object appears.
|
||||
/// </summary>
|
||||
protected virtual RectangleF GetConservativeBoundingBox(HitObjectLifetimeEntry entry) => new RectangleF().Inflate(100);
|
||||
|
||||
private double computeDisplayStartTime(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
// Origin position may be relative to the parent size
|
||||
Debug.Assert(hitObject.Parent != null);
|
||||
RectangleF boundingBox = GetConservativeBoundingBox(entry);
|
||||
float startOffset = 0;
|
||||
|
||||
float originAdjustment = 0.0f;
|
||||
|
||||
// calculate the dimension of the part of the hitobject that should already be visible
|
||||
// when the hitobject origin first appears inside the scrolling container
|
||||
switch (direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
originAdjustment = hitObject.OriginPosition.Y;
|
||||
case ScrollingDirection.Right:
|
||||
startOffset = boundingBox.Right;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Down:
|
||||
originAdjustment = hitObject.DrawHeight - hitObject.OriginPosition.Y;
|
||||
startOffset = boundingBox.Bottom;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Left:
|
||||
originAdjustment = hitObject.OriginPosition.X;
|
||||
startOffset = -boundingBox.Left;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Right:
|
||||
originAdjustment = hitObject.DrawWidth - hitObject.OriginPosition.X;
|
||||
case ScrollingDirection.Up:
|
||||
startOffset = -boundingBox.Top;
|
||||
break;
|
||||
}
|
||||
|
||||
return scrollingInfo.Algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, originAdjustment, timeRange.Value, scrollLength);
|
||||
return scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength);
|
||||
}
|
||||
|
||||
private void setComputedLifetimeStart(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
double computedStartTime = computeDisplayStartTime(entry);
|
||||
|
||||
// always load the hitobject before its first judgement offset
|
||||
double judgementOffset = entry.HitObject.HitWindows?.WindowFor(Scoring.HitResult.Miss) ?? 0;
|
||||
entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - judgementOffset, computedStartTime);
|
||||
}
|
||||
|
||||
private void updateLayoutRecursive(DrawableHitObject hitObject)
|
||||
@ -232,8 +251,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
updateLayoutRecursive(obj);
|
||||
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions and start lifetime
|
||||
updatePosition(obj, hitObject.HitObject.StartTime);
|
||||
setComputedLifetimeStart(obj.Entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// </summary>
|
||||
public virtual Vector2 ScreenSpacePositionAtTime(double time) => HitObjectContainer.ScreenSpacePositionAtTime(time);
|
||||
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer();
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => CreateScrollingHitObjectContainer();
|
||||
|
||||
protected virtual ScrollingHitObjectContainer CreateScrollingHitObjectContainer() => new ScrollingHitObjectContainer();
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,12 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
@ -34,8 +35,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private static readonly int highest_divisor = BindableBeatDivisor.PREDEFINED_DIVISORS.Last();
|
||||
|
||||
public TimelineTickDisplay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@ -80,20 +79,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (timeline != null)
|
||||
if (timeline == null || DrawWidth <= 0) return;
|
||||
|
||||
(float, float) newRange = (
|
||||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X,
|
||||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X);
|
||||
|
||||
if (visibleRange != newRange)
|
||||
{
|
||||
var newRange = (
|
||||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X,
|
||||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X);
|
||||
visibleRange = newRange;
|
||||
|
||||
if (visibleRange != newRange)
|
||||
{
|
||||
visibleRange = newRange;
|
||||
|
||||
// actual regeneration only needs to occur if we've passed one of the known next min/max tick boundaries.
|
||||
if (nextMinTick == null || nextMaxTick == null || (visibleRange.min < nextMinTick || visibleRange.max > nextMaxTick))
|
||||
tickCache.Invalidate();
|
||||
}
|
||||
// actual regeneration only needs to occur if we've passed one of the known next min/max tick boundaries.
|
||||
if (nextMinTick == null || nextMaxTick == null || (visibleRange.min < nextMinTick || visibleRange.max > nextMaxTick))
|
||||
tickCache.Invalidate();
|
||||
}
|
||||
|
||||
if (!tickCache.IsValid)
|
||||
@ -151,6 +149,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
}
|
||||
|
||||
if (Children.Count > 512)
|
||||
{
|
||||
// There should always be a sanely small number of ticks rendered.
|
||||
// If this assertion triggers, either the zoom logic is broken or a beatmap is
|
||||
// probably doing weird things...
|
||||
//
|
||||
// Let's hope the latter never happens.
|
||||
// If it does, we can choose to either fix it or ignore it as an outlier.
|
||||
string message = $"Timeline is rendering many ticks ({Children.Count})";
|
||||
|
||||
Logger.Log(message);
|
||||
Debug.Fail(message);
|
||||
}
|
||||
|
||||
int usedDrawables = drawableIndex;
|
||||
|
||||
// save a few drawables beyond the currently used for edge cases.
|
||||
|
@ -56,7 +56,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
protected ZoomableScrollContainer()
|
||||
: base(Direction.Horizontal)
|
||||
{
|
||||
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
|
||||
base.Content.Add(zoomedContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
// We must hide content until SetupZoom is called.
|
||||
// If not, a child component that relies on its DrawWidth (via RelativeSizeAxes) may see a very incorrect value
|
||||
// momentarily, as noticed in the TimelineTickDisplay, which would render thousands of ticks incorrectly.
|
||||
Alpha = 0,
|
||||
});
|
||||
|
||||
AddLayout(zoomedContentWidthCache);
|
||||
}
|
||||
@ -94,6 +101,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
maxZoom = maximum;
|
||||
CurrentZoom = zoomTarget = initial;
|
||||
isZoomSetUp = true;
|
||||
|
||||
zoomedContent.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -118,9 +127,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
CurrentZoom = zoomTarget = newZoom;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.Update();
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
if (!zoomedContentWidthCache.IsValid)
|
||||
updateZoomedContentWidth();
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -16,7 +14,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
public class MatchLeaderboard : Leaderboard<MatchLeaderboardScope, APIUserScoreAggregate>
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||
private Bindable<long?> roomId { get; set; }
|
||||
private Bindable<long?> roomId { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -33,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
|
||||
protected override bool IsOnlineScope => true;
|
||||
|
||||
protected override APIRequest FetchScores(CancellationToken cancellationToken)
|
||||
protected override APIRequest? FetchScores(CancellationToken cancellationToken)
|
||||
{
|
||||
if (roomId.Value == null)
|
||||
return null;
|
||||
|
@ -54,6 +54,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
private const float disabled_alpha = 0.2f;
|
||||
|
||||
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
||||
|
||||
public Action? SettingsApplied;
|
||||
|
||||
public OsuTextBox NameField = null!;
|
||||
@ -424,7 +426,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
private void hideError() => ErrorText.FadeOut(50);
|
||||
|
||||
private void onSuccess(Room room)
|
||||
private void onSuccess(Room room) => Schedule(() =>
|
||||
{
|
||||
Debug.Assert(applyingSettingsOperation != null);
|
||||
|
||||
@ -432,9 +434,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
applyingSettingsOperation.Dispose();
|
||||
applyingSettingsOperation = null;
|
||||
}
|
||||
});
|
||||
|
||||
private void onError(string text)
|
||||
private void onError(string text) => Schedule(() =>
|
||||
{
|
||||
Debug.Assert(applyingSettingsOperation != null);
|
||||
|
||||
@ -455,7 +457,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
applyingSettingsOperation.Dispose();
|
||||
applyingSettingsOperation = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class CreateOrUpdateButton : TriangleButton
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user