mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 08:27:49 +08:00
Merge remote-tracking branch 'ppy/master' into colour-encoding-2
This commit is contained in:
commit
17d418d319
@ -19,3 +19,7 @@ P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResult
|
|||||||
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
|
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
|
||||||
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
||||||
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
||||||
|
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
|
||||||
|
M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
|
||||||
|
M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
|
||||||
|
M:Humanizer.InflectorExtensions.Kebaberize(System.String);Humanizer's .Kebaberize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToKebabCase() instead.
|
||||||
|
@ -51,8 +51,8 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.716.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.715.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.720.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
76
osu.Android/AndroidJoystickSettings.cs
Normal file
76
osu.Android/AndroidJoystickSettings.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Android.Input;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
|
namespace osu.Android
|
||||||
|
{
|
||||||
|
public class AndroidJoystickSettings : SettingsSubsection
|
||||||
|
{
|
||||||
|
protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad;
|
||||||
|
|
||||||
|
private readonly AndroidJoystickHandler joystickHandler;
|
||||||
|
|
||||||
|
private readonly Bindable<bool> enabled = new BindableBool(true);
|
||||||
|
|
||||||
|
private SettingsSlider<float> deadzoneSlider = null!;
|
||||||
|
|
||||||
|
private Bindable<float> handlerDeadzone = null!;
|
||||||
|
|
||||||
|
private Bindable<float> localDeadzone = null!;
|
||||||
|
|
||||||
|
public AndroidJoystickSettings(AndroidJoystickHandler joystickHandler)
|
||||||
|
{
|
||||||
|
this.joystickHandler = joystickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
// use local bindable to avoid changing enabled state of game host's bindable.
|
||||||
|
handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy();
|
||||||
|
localDeadzone = handlerDeadzone.GetUnboundCopy();
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = CommonStrings.Enabled,
|
||||||
|
Current = enabled
|
||||||
|
},
|
||||||
|
deadzoneSlider = new SettingsSlider<float>
|
||||||
|
{
|
||||||
|
LabelText = JoystickSettingsStrings.DeadzoneThreshold,
|
||||||
|
KeyboardStep = 0.01f,
|
||||||
|
DisplayAsPercentage = true,
|
||||||
|
Current = localDeadzone,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
enabled.BindTo(joystickHandler.Enabled);
|
||||||
|
enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true);
|
||||||
|
|
||||||
|
handlerDeadzone.BindValueChanged(val =>
|
||||||
|
{
|
||||||
|
bool disabled = localDeadzone.Disabled;
|
||||||
|
|
||||||
|
localDeadzone.Disabled = false;
|
||||||
|
localDeadzone.Value = val.NewValue;
|
||||||
|
localDeadzone.Disabled = disabled;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
localDeadzone.BindValueChanged(val => handlerDeadzone.Value = val.NewValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -96,6 +96,9 @@ namespace osu.Android
|
|||||||
case AndroidMouseHandler mh:
|
case AndroidMouseHandler mh:
|
||||||
return new AndroidMouseSettings(mh);
|
return new AndroidMouseSettings(mh);
|
||||||
|
|
||||||
|
case AndroidJoystickHandler jh:
|
||||||
|
return new AndroidJoystickSettings(jh);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return base.CreateSettingsSubsectionFor(handler);
|
return base.CreateSettingsSubsectionFor(handler);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="AndroidJoystickSettings.cs" />
|
||||||
<Compile Include="AndroidMouseSettings.cs" />
|
<Compile Include="AndroidMouseSettings.cs" />
|
||||||
<Compile Include="GameplayScreenRotationLocker.cs" />
|
<Compile Include="GameplayScreenRotationLocker.cs" />
|
||||||
<Compile Include="OsuGameActivity.cs" />
|
<Compile Include="OsuGameActivity.cs" />
|
||||||
|
@ -22,10 +22,12 @@ using osu.Framework.Input.Handlers;
|
|||||||
using osu.Framework.Input.Handlers.Joystick;
|
using osu.Framework.Input.Handlers.Joystick;
|
||||||
using osu.Framework.Input.Handlers.Mouse;
|
using osu.Framework.Input.Handlers.Mouse;
|
||||||
using osu.Framework.Input.Handlers.Tablet;
|
using osu.Framework.Input.Handlers.Tablet;
|
||||||
|
using osu.Framework.Input.Handlers.Touch;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Overlays.Settings.Sections;
|
||||||
using osu.Game.Overlays.Settings.Sections.Input;
|
using osu.Game.Overlays.Settings.Sections.Input;
|
||||||
|
|
||||||
namespace osu.Desktop
|
namespace osu.Desktop
|
||||||
@ -156,6 +158,9 @@ namespace osu.Desktop
|
|||||||
case JoystickHandler jh:
|
case JoystickHandler jh:
|
||||||
return new JoystickSettings(jh);
|
return new JoystickSettings(jh);
|
||||||
|
|
||||||
|
case TouchHandler th:
|
||||||
|
return new InputSection.HandlerSection(th);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return base.CreateSettingsSubsectionFor(handler);
|
return base.CreateSettingsSubsectionFor(handler);
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,15 @@ namespace osu.Desktop
|
|||||||
// See https://www.mongodb.com/docs/realm/sdk/dotnet/#supported-platforms
|
// See https://www.mongodb.com/docs/realm/sdk/dotnet/#supported-platforms
|
||||||
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
|
if (windowsVersion.Major < 6 || (windowsVersion.Major == 6 && windowsVersion.Minor <= 2))
|
||||||
{
|
{
|
||||||
|
// If users running in compatibility mode becomes more of a common thing, we may want to provide better guidance or even consider
|
||||||
|
// disabling it ourselves.
|
||||||
|
// We could also better detect compatibility mode if required:
|
||||||
|
// https://stackoverflow.com/questions/10744651/how-i-can-detect-if-my-application-is-running-under-compatibility-mode#comment58183249_10744730
|
||||||
SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
|
SDL.SDL_ShowSimpleMessageBox(SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
|
||||||
"Your operating system is too old to run osu!",
|
"Your operating system is too old to run osu!",
|
||||||
"This version of osu! requires at least Windows 8.1 to run.\nPlease upgrade your operating system or consider using an older version of osu!.", IntPtr.Zero);
|
"This version of osu! requires at least Windows 8.1 to run.\n"
|
||||||
|
+ "Please upgrade your operating system or consider using an older version of osu!.\n\n"
|
||||||
|
+ "If you are running a newer version of windows, please check you don't have \"Compatibility mode\" turned on for osu!", IntPtr.Zero);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
166
osu.Game.Benchmarks/BenchmarkHitObject.cs
Normal file
166
osu.Game.Benchmarks/BenchmarkHitObject.cs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// 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 BenchmarkDotNet.Attributes;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Benchmarks
|
||||||
|
{
|
||||||
|
public class BenchmarkHitObject : BenchmarkTest
|
||||||
|
{
|
||||||
|
[Params(1, 100, 1000)]
|
||||||
|
public int Count { get; set; }
|
||||||
|
|
||||||
|
[Params(false, true)]
|
||||||
|
public bool WithBindableAccess { get; set; }
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public HitCircle[] OsuCircle()
|
||||||
|
{
|
||||||
|
var circles = new HitCircle[Count];
|
||||||
|
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
circles[i] = new HitCircle();
|
||||||
|
|
||||||
|
if (WithBindableAccess)
|
||||||
|
{
|
||||||
|
_ = circles[i].PositionBindable;
|
||||||
|
_ = circles[i].ScaleBindable;
|
||||||
|
_ = circles[i].ComboIndexBindable;
|
||||||
|
_ = circles[i].ComboOffsetBindable;
|
||||||
|
_ = circles[i].StackHeightBindable;
|
||||||
|
_ = circles[i].LastInComboBindable;
|
||||||
|
_ = circles[i].ComboIndexWithOffsetsBindable;
|
||||||
|
_ = circles[i].IndexInCurrentComboBindable;
|
||||||
|
_ = circles[i].SamplesBindable;
|
||||||
|
_ = circles[i].StartTimeBindable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = circles[i].Position;
|
||||||
|
_ = circles[i].Scale;
|
||||||
|
_ = circles[i].ComboIndex;
|
||||||
|
_ = circles[i].ComboOffset;
|
||||||
|
_ = circles[i].StackHeight;
|
||||||
|
_ = circles[i].LastInCombo;
|
||||||
|
_ = circles[i].ComboIndexWithOffsets;
|
||||||
|
_ = circles[i].IndexInCurrentCombo;
|
||||||
|
_ = circles[i].Samples;
|
||||||
|
_ = circles[i].StartTime;
|
||||||
|
_ = circles[i].Position;
|
||||||
|
_ = circles[i].Scale;
|
||||||
|
_ = circles[i].ComboIndex;
|
||||||
|
_ = circles[i].ComboOffset;
|
||||||
|
_ = circles[i].StackHeight;
|
||||||
|
_ = circles[i].LastInCombo;
|
||||||
|
_ = circles[i].ComboIndexWithOffsets;
|
||||||
|
_ = circles[i].IndexInCurrentCombo;
|
||||||
|
_ = circles[i].Samples;
|
||||||
|
_ = circles[i].StartTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return circles;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public Hit[] TaikoHit()
|
||||||
|
{
|
||||||
|
var hits = new Hit[Count];
|
||||||
|
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
hits[i] = new Hit();
|
||||||
|
|
||||||
|
if (WithBindableAccess)
|
||||||
|
{
|
||||||
|
_ = hits[i].TypeBindable;
|
||||||
|
_ = hits[i].IsStrongBindable;
|
||||||
|
_ = hits[i].SamplesBindable;
|
||||||
|
_ = hits[i].StartTimeBindable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = hits[i].Type;
|
||||||
|
_ = hits[i].IsStrong;
|
||||||
|
_ = hits[i].Samples;
|
||||||
|
_ = hits[i].StartTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hits;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public Fruit[] CatchFruit()
|
||||||
|
{
|
||||||
|
var fruit = new Fruit[Count];
|
||||||
|
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
fruit[i] = new Fruit();
|
||||||
|
|
||||||
|
if (WithBindableAccess)
|
||||||
|
{
|
||||||
|
_ = fruit[i].OriginalXBindable;
|
||||||
|
_ = fruit[i].XOffsetBindable;
|
||||||
|
_ = fruit[i].ScaleBindable;
|
||||||
|
_ = fruit[i].ComboIndexBindable;
|
||||||
|
_ = fruit[i].HyperDashBindable;
|
||||||
|
_ = fruit[i].LastInComboBindable;
|
||||||
|
_ = fruit[i].ComboIndexWithOffsetsBindable;
|
||||||
|
_ = fruit[i].IndexInCurrentComboBindable;
|
||||||
|
_ = fruit[i].IndexInBeatmapBindable;
|
||||||
|
_ = fruit[i].SamplesBindable;
|
||||||
|
_ = fruit[i].StartTimeBindable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = fruit[i].OriginalX;
|
||||||
|
_ = fruit[i].XOffset;
|
||||||
|
_ = fruit[i].Scale;
|
||||||
|
_ = fruit[i].ComboIndex;
|
||||||
|
_ = fruit[i].HyperDash;
|
||||||
|
_ = fruit[i].LastInCombo;
|
||||||
|
_ = fruit[i].ComboIndexWithOffsets;
|
||||||
|
_ = fruit[i].IndexInCurrentCombo;
|
||||||
|
_ = fruit[i].IndexInBeatmap;
|
||||||
|
_ = fruit[i].Samples;
|
||||||
|
_ = fruit[i].StartTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fruit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public Note[] ManiaNote()
|
||||||
|
{
|
||||||
|
var notes = new Note[Count];
|
||||||
|
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
notes[i] = new Note();
|
||||||
|
|
||||||
|
if (WithBindableAccess)
|
||||||
|
{
|
||||||
|
_ = notes[i].ColumnBindable;
|
||||||
|
_ = notes[i].SamplesBindable;
|
||||||
|
_ = notes[i].StartTimeBindable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = notes[i].Column;
|
||||||
|
_ = notes[i].Samples;
|
||||||
|
_ = notes[i].StartTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDoubleTime : ModDoubleTime
|
public class CatchModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModFlashlight : ModFlashlight<CatchHitObject>
|
public class CatchModFlashlight : ModFlashlight<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
|
|
||||||
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
||||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModHardRock : ModHardRock, IApplicableToBeatmapProcessor
|
public class CatchModHardRock : ModHardRock, IApplicableToBeatmapProcessor
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
|
|
||||||
public void ApplyToBeatmapProcessor(IBeatmapProcessor beatmapProcessor)
|
public void ApplyToBeatmapProcessor(IBeatmapProcessor beatmapProcessor)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public class CatchModHidden : ModHidden, IApplicableToDrawableRuleset<CatchHitObject>
|
public class CatchModHidden : ModHidden, IApplicableToDrawableRuleset<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override string Description => @"Play with fading fruits.";
|
public override string Description => @"Play with fading fruits.";
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
private const double fade_out_offset_multiplier = 0.6;
|
private const double fade_out_offset_multiplier = 0.6;
|
||||||
private const double fade_out_duration_multiplier = 0.44;
|
private const double fade_out_duration_multiplier = 0.44;
|
||||||
|
@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
public const float OBJECT_RADIUS = 64;
|
public const float OBJECT_RADIUS = 64;
|
||||||
|
|
||||||
public readonly Bindable<float> OriginalXBindable = new Bindable<float>();
|
private HitObjectProperty<float> originalX;
|
||||||
|
|
||||||
|
public Bindable<float> OriginalXBindable => originalX.Bindable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||||
@ -31,18 +33,20 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public float X
|
public float X
|
||||||
{
|
{
|
||||||
set => OriginalXBindable.Value = value;
|
set => originalX.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
|
private HitObjectProperty<float> xOffset;
|
||||||
|
|
||||||
|
public Bindable<float> XOffsetBindable => xOffset.Bindable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A random offset applied to the horizontal position, set by the beatmap processing.
|
/// A random offset applied to the horizontal position, set by the beatmap processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float XOffset
|
public float XOffset
|
||||||
{
|
{
|
||||||
get => XOffsetBindable.Value;
|
get => xOffset.Value;
|
||||||
set => XOffsetBindable.Value = value;
|
set => xOffset.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -54,8 +58,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public float OriginalX
|
public float OriginalX
|
||||||
{
|
{
|
||||||
get => OriginalXBindable.Value;
|
get => originalX.Value;
|
||||||
set => OriginalXBindable.Value = value;
|
set => originalX.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -69,59 +73,71 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public double TimePreempt { get; set; } = 1000;
|
public double TimePreempt { get; set; } = 1000;
|
||||||
|
|
||||||
public readonly Bindable<int> IndexInBeatmapBindable = new Bindable<int>();
|
private HitObjectProperty<int> indexInBeatmap;
|
||||||
|
|
||||||
|
public Bindable<int> IndexInBeatmapBindable => indexInBeatmap.Bindable;
|
||||||
|
|
||||||
public int IndexInBeatmap
|
public int IndexInBeatmap
|
||||||
{
|
{
|
||||||
get => IndexInBeatmapBindable.Value;
|
get => indexInBeatmap.Value;
|
||||||
set => IndexInBeatmapBindable.Value = value;
|
set => indexInBeatmap.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
public int ComboOffset { get; set; }
|
public int ComboOffset { get; set; }
|
||||||
|
|
||||||
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
|
private HitObjectProperty<int> indexInCurrentCombo;
|
||||||
|
|
||||||
|
public Bindable<int> IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
|
||||||
|
|
||||||
public int IndexInCurrentCombo
|
public int IndexInCurrentCombo
|
||||||
{
|
{
|
||||||
get => IndexInCurrentComboBindable.Value;
|
get => indexInCurrentCombo.Value;
|
||||||
set => IndexInCurrentComboBindable.Value = value;
|
set => indexInCurrentCombo.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<int> ComboIndexBindable { get; } = new Bindable<int>();
|
private HitObjectProperty<int> comboIndex;
|
||||||
|
|
||||||
|
public Bindable<int> ComboIndexBindable => comboIndex.Bindable;
|
||||||
|
|
||||||
public int ComboIndex
|
public int ComboIndex
|
||||||
{
|
{
|
||||||
get => ComboIndexBindable.Value;
|
get => comboIndex.Value;
|
||||||
set => ComboIndexBindable.Value = value;
|
set => comboIndex.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<int> ComboIndexWithOffsetsBindable { get; } = new Bindable<int>();
|
private HitObjectProperty<int> comboIndexWithOffsets;
|
||||||
|
|
||||||
|
public Bindable<int> ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
|
||||||
|
|
||||||
public int ComboIndexWithOffsets
|
public int ComboIndexWithOffsets
|
||||||
{
|
{
|
||||||
get => ComboIndexWithOffsetsBindable.Value;
|
get => comboIndexWithOffsets.Value;
|
||||||
set => ComboIndexWithOffsetsBindable.Value = value;
|
set => comboIndexWithOffsets.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
|
private HitObjectProperty<bool> lastInCombo;
|
||||||
|
|
||||||
|
public Bindable<bool> LastInComboBindable => lastInCombo.Bindable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The next fruit starts a new combo. Used for explodey.
|
/// The next fruit starts a new combo. Used for explodey.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool LastInCombo
|
public virtual bool LastInCombo
|
||||||
{
|
{
|
||||||
get => LastInComboBindable.Value;
|
get => lastInCombo.Value;
|
||||||
set => LastInComboBindable.Value = value;
|
set => lastInCombo.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
|
private HitObjectProperty<float> scale = new HitObjectProperty<float>(1);
|
||||||
|
|
||||||
|
public Bindable<float> ScaleBindable => scale.Bindable;
|
||||||
|
|
||||||
public float Scale
|
public float Scale
|
||||||
{
|
{
|
||||||
get => ScaleBindable.Value;
|
get => scale.Value;
|
||||||
set => ScaleBindable.Value = value;
|
set => scale.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -24,12 +25,14 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float DistanceToHyperDash { get; set; }
|
public float DistanceToHyperDash { get; set; }
|
||||||
|
|
||||||
public readonly Bindable<bool> HyperDashBindable = new Bindable<bool>();
|
private HitObjectProperty<bool> hyperDash;
|
||||||
|
|
||||||
|
public Bindable<bool> HyperDashBindable => hyperDash.Bindable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this fruit can initiate a hyperdash.
|
/// Whether this fruit can initiate a hyperdash.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HyperDash => HyperDashBindable.Value;
|
public bool HyperDash => hyperDash.Value;
|
||||||
|
|
||||||
private CatchHitObject hyperDashTarget;
|
private CatchHitObject hyperDashTarget;
|
||||||
|
|
||||||
|
@ -13,12 +13,14 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
{
|
{
|
||||||
public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
|
public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
|
||||||
{
|
{
|
||||||
public readonly Bindable<int> ColumnBindable = new Bindable<int>();
|
private HitObjectProperty<int> column;
|
||||||
|
|
||||||
|
public Bindable<int> ColumnBindable => column.Bindable;
|
||||||
|
|
||||||
public virtual int Column
|
public virtual int Column
|
||||||
{
|
{
|
||||||
get => ColumnBindable.Value;
|
get => column.Value;
|
||||||
set => ColumnBindable.Value = value;
|
set => column.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
||||||
|
@ -17,18 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.6972307565739273d, 206, "diffcalc-test")]
|
[TestCase(6.6369583000323935d, 206, "diffcalc-test")]
|
||||||
[TestCase(1.4484754139145539d, 45, "zero-length-sliders")]
|
[TestCase(1.4476531024675374d, 45, "zero-length-sliders")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(8.9382559208689809d, 206, "diffcalc-test")]
|
[TestCase(8.8816128335486386d, 206, "diffcalc-test")]
|
||||||
[TestCase(1.7548875851757628d, 45, "zero-length-sliders")]
|
[TestCase(1.7540389962596916d, 45, "zero-length-sliders")]
|
||||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
[TestCase(6.6972307218715166d, 239, "diffcalc-test")]
|
[TestCase(6.6369583000323935d, 239, "diffcalc-test")]
|
||||||
[TestCase(1.4484754139145537d, 54, "zero-length-sliders")]
|
[TestCase(1.4476531024675374d, 54, "zero-length-sliders")]
|
||||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||||
|
|
||||||
|
@ -108,13 +108,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
|
|||||||
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
|
||||||
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
|
||||||
|
|
||||||
// Reward for % distance slowed down compared to previous, paying attention to not award overlap
|
velocityChangeBonus = overlapVelocityBuff * distRatio;
|
||||||
double nonOverlapVelocityBuff = Math.Abs(prevVelocity - currVelocity)
|
|
||||||
// do not award overlap
|
|
||||||
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, Math.Min(osuCurrObj.LazyJumpDistance, osuLastObj.LazyJumpDistance) / 100)), 2);
|
|
||||||
|
|
||||||
// Choose the largest bonus, multiplied by ratio.
|
|
||||||
velocityChangeBonus = Math.Max(overlapVelocityBuff, nonOverlapVelocityBuff) * distRatio;
|
|
||||||
|
|
||||||
// Penalize for rhythm changes.
|
// Penalize for rhythm changes.
|
||||||
velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
velocityChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2);
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
public class SliderPlacementBlueprint : PlacementBlueprint
|
public class SliderPlacementBlueprint : PlacementBlueprint
|
||||||
{
|
{
|
||||||
public new Objects.Slider HitObject => (Objects.Slider)base.HitObject;
|
public new Slider HitObject => (Slider)base.HitObject;
|
||||||
|
|
||||||
private SliderBodyPiece bodyPiece;
|
private SliderBodyPiece bodyPiece;
|
||||||
private HitCirclePiece headCirclePiece;
|
private HitCirclePiece headCirclePiece;
|
||||||
@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
private IDistanceSnapProvider snapProvider { get; set; }
|
private IDistanceSnapProvider snapProvider { get; set; }
|
||||||
|
|
||||||
public SliderPlacementBlueprint()
|
public SliderPlacementBlueprint()
|
||||||
: base(new Objects.Slider())
|
: base(new Slider())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -82,7 +83,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
case SliderPlacementState.Initial:
|
case SliderPlacementState.Initial:
|
||||||
BeginPlacement();
|
BeginPlacement();
|
||||||
|
|
||||||
var nearestDifficultyPoint = editorBeatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
|
var nearestDifficultyPoint = editorBeatmap.HitObjects
|
||||||
|
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime)?
|
||||||
|
.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
|
||||||
|
|
||||||
HitObject.DifficultyControlPoint = nearestDifficultyPoint ?? new DifficultyControlPoint();
|
HitObject.DifficultyControlPoint = nearestDifficultyPoint ?? new DifficultyControlPoint();
|
||||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
|
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
|
||||||
public override ModType Type => ModType.DifficultyIncrease;
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
|
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) };
|
||||||
|
|
||||||
private DrawableOsuBlinds blinds;
|
private DrawableOsuBlinds blinds;
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModCinema : ModCinema<OsuHitObject>
|
public class OsuModCinema : ModCinema<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap), typeof(OsuModRepel) }).ToArray();
|
||||||
|
|
||||||
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
||||||
|
@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDoubleTime : ModDoubleTime
|
public class OsuModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModFlashlight : ModFlashlight<OsuHitObject>, IApplicableToDrawableHitObject
|
public class OsuModFlashlight : ModFlashlight<OsuHitObject>, IApplicableToDrawableHitObject
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModBlinds)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModBlinds)).ToArray();
|
||||||
|
|
||||||
private const double default_follow_delay = 120;
|
private const double default_follow_delay = 120;
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModHardRock : ModHardRock, IApplicableToHitObject
|
public class OsuModHardRock : ModHardRock, IApplicableToHitObject
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray();
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
|
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
|
||||||
|
|
||||||
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) };
|
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) };
|
||||||
|
|
||||||
|
@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModPerfect : ModPerfect
|
public class OsuModPerfect : ModPerfect
|
||||||
{
|
{
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public class OsuModSingleTap : InputBlockingMod
|
public class OsuModSingleTap : InputBlockingMod
|
||||||
{
|
{
|
||||||
public override string Name => @"Single Tap";
|
public override string Name => @"Single Tap";
|
||||||
public override string Acronym => @"ST";
|
public override string Acronym => @"SG";
|
||||||
public override string Description => @"You must only use one key!";
|
public override string Description => @"You must only use one key!";
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
||||||
|
|
||||||
|
@ -7,12 +7,12 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osuTK;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
@ -36,12 +36,14 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
public double TimePreempt = 600;
|
public double TimePreempt = 600;
|
||||||
public double TimeFadeIn = 400;
|
public double TimeFadeIn = 400;
|
||||||
|
|
||||||
public readonly Bindable<Vector2> PositionBindable = new Bindable<Vector2>();
|
private HitObjectProperty<Vector2> position;
|
||||||
|
|
||||||
|
public Bindable<Vector2> PositionBindable => position.Bindable;
|
||||||
|
|
||||||
public virtual Vector2 Position
|
public virtual Vector2 Position
|
||||||
{
|
{
|
||||||
get => PositionBindable.Value;
|
get => position.Value;
|
||||||
set => PositionBindable.Value = value;
|
set => position.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float X => Position.X;
|
public float X => Position.X;
|
||||||
@ -53,66 +55,80 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public Vector2 StackedEndPosition => EndPosition + StackOffset;
|
public Vector2 StackedEndPosition => EndPosition + StackOffset;
|
||||||
|
|
||||||
public readonly Bindable<int> StackHeightBindable = new Bindable<int>();
|
private HitObjectProperty<int> stackHeight;
|
||||||
|
|
||||||
|
public Bindable<int> StackHeightBindable => stackHeight.Bindable;
|
||||||
|
|
||||||
public int StackHeight
|
public int StackHeight
|
||||||
{
|
{
|
||||||
get => StackHeightBindable.Value;
|
get => stackHeight.Value;
|
||||||
set => StackHeightBindable.Value = value;
|
set => stackHeight.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
|
public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
|
||||||
|
|
||||||
public double Radius => OBJECT_RADIUS * Scale;
|
public double Radius => OBJECT_RADIUS * Scale;
|
||||||
|
|
||||||
public readonly Bindable<float> ScaleBindable = new BindableFloat(1);
|
private HitObjectProperty<float> scale = new HitObjectProperty<float>(1);
|
||||||
|
|
||||||
|
public Bindable<float> ScaleBindable => scale.Bindable;
|
||||||
|
|
||||||
public float Scale
|
public float Scale
|
||||||
{
|
{
|
||||||
get => ScaleBindable.Value;
|
get => scale.Value;
|
||||||
set => ScaleBindable.Value = value;
|
set => scale.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
public readonly Bindable<int> ComboOffsetBindable = new Bindable<int>();
|
private HitObjectProperty<int> comboOffset;
|
||||||
|
|
||||||
|
public Bindable<int> ComboOffsetBindable => comboOffset.Bindable;
|
||||||
|
|
||||||
public int ComboOffset
|
public int ComboOffset
|
||||||
{
|
{
|
||||||
get => ComboOffsetBindable.Value;
|
get => comboOffset.Value;
|
||||||
set => ComboOffsetBindable.Value = value;
|
set => comboOffset.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
|
private HitObjectProperty<int> indexInCurrentCombo;
|
||||||
|
|
||||||
|
public Bindable<int> IndexInCurrentComboBindable => indexInCurrentCombo.Bindable;
|
||||||
|
|
||||||
public virtual int IndexInCurrentCombo
|
public virtual int IndexInCurrentCombo
|
||||||
{
|
{
|
||||||
get => IndexInCurrentComboBindable.Value;
|
get => indexInCurrentCombo.Value;
|
||||||
set => IndexInCurrentComboBindable.Value = value;
|
set => indexInCurrentCombo.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<int> ComboIndexBindable { get; } = new Bindable<int>();
|
private HitObjectProperty<int> comboIndex;
|
||||||
|
|
||||||
|
public Bindable<int> ComboIndexBindable => comboIndex.Bindable;
|
||||||
|
|
||||||
public virtual int ComboIndex
|
public virtual int ComboIndex
|
||||||
{
|
{
|
||||||
get => ComboIndexBindable.Value;
|
get => comboIndex.Value;
|
||||||
set => ComboIndexBindable.Value = value;
|
set => comboIndex.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<int> ComboIndexWithOffsetsBindable { get; } = new Bindable<int>();
|
private HitObjectProperty<int> comboIndexWithOffsets;
|
||||||
|
|
||||||
|
public Bindable<int> ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable;
|
||||||
|
|
||||||
public int ComboIndexWithOffsets
|
public int ComboIndexWithOffsets
|
||||||
{
|
{
|
||||||
get => ComboIndexWithOffsetsBindable.Value;
|
get => comboIndexWithOffsets.Value;
|
||||||
set => ComboIndexWithOffsetsBindable.Value = value;
|
set => comboIndexWithOffsets.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
|
private HitObjectProperty<bool> lastInCombo;
|
||||||
|
|
||||||
|
public Bindable<bool> LastInComboBindable => lastInCombo.Bindable;
|
||||||
|
|
||||||
public bool LastInCombo
|
public bool LastInCombo
|
||||||
{
|
{
|
||||||
get => LastInComboBindable.Value;
|
get => lastInCombo.Value;
|
||||||
set => LastInComboBindable.Value = value;
|
set => lastInCombo.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OsuHitObject()
|
protected OsuHitObject()
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -32,19 +34,35 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
|
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
|
||||||
{
|
{
|
||||||
const float scale_duration = 300f;
|
Debug.Assert(ParentObject != null);
|
||||||
const float fade_duration = 300f;
|
|
||||||
|
|
||||||
this.ScaleTo(tracking.NewValue ? DrawableSliderBall.FOLLOW_AREA : 1f, scale_duration, Easing.OutQuint)
|
const float duration = 300f;
|
||||||
.FadeTo(tracking.NewValue ? 1f : 0, fade_duration, Easing.OutQuint);
|
|
||||||
|
if (ParentObject.Judged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (tracking.NewValue)
|
||||||
|
{
|
||||||
|
if (Precision.AlmostEquals(0, Alpha))
|
||||||
|
this.ScaleTo(1);
|
||||||
|
|
||||||
|
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA, duration, Easing.OutQuint)
|
||||||
|
.FadeTo(1f, duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.2f, duration / 2, Easing.OutQuint)
|
||||||
|
.FadeTo(0, duration / 2, Easing.OutQuint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSliderEnd()
|
protected override void OnSliderEnd()
|
||||||
{
|
{
|
||||||
const float fade_duration = 450f;
|
const float fade_duration = 300;
|
||||||
|
|
||||||
// intentionally pile on an extra FadeOut to make it happen much faster
|
// intentionally pile on an extra FadeOut to make it happen much faster
|
||||||
this.FadeOut(fade_duration / 4, Easing.Out);
|
this.ScaleTo(1, fade_duration, Easing.OutQuint);
|
||||||
|
this.FadeOut(fade_duration / 2, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
// 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 osu.Game.Rulesets.Taiko.Mods;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneTaikoPlayerLegacySkin : LegacySkinPlayerTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset();
|
||||||
|
|
||||||
|
protected override TestPlayer CreatePlayer(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new[] { new TaikoModClassic() };
|
||||||
|
return base.CreatePlayer(ruleset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,13 +35,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
||||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||||
|
|
||||||
double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModNoFail))
|
|
||||||
multiplier *= 0.90;
|
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModHidden))
|
if (score.Mods.Any(m => m is ModHidden))
|
||||||
multiplier *= 1.10;
|
multiplier *= 1.075;
|
||||||
|
|
||||||
|
if (score.Mods.Any(m => m is ModEasy))
|
||||||
|
multiplier *= 0.975;
|
||||||
|
|
||||||
double difficultyValue = computeDifficultyValue(score, taikoAttributes);
|
double difficultyValue = computeDifficultyValue(score, taikoAttributes);
|
||||||
double accuracyValue = computeAccuracyValue(score, taikoAttributes);
|
double accuracyValue = computeAccuracyValue(score, taikoAttributes);
|
||||||
@ -61,12 +61,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||||
{
|
{
|
||||||
double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.175) - 4.0, 2.25) / 450.0;
|
double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.115) - 4.0, 2.25) / 1150.0;
|
||||||
|
|
||||||
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
||||||
difficultyValue *= lengthBonus;
|
difficultyValue *= lengthBonus;
|
||||||
|
|
||||||
difficultyValue *= Math.Pow(0.985, countMiss);
|
difficultyValue *= Math.Pow(0.986, countMiss);
|
||||||
|
|
||||||
|
if (score.Mods.Any(m => m is ModEasy))
|
||||||
|
difficultyValue *= 0.980;
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModHidden))
|
if (score.Mods.Any(m => m is ModHidden))
|
||||||
difficultyValue *= 1.025;
|
difficultyValue *= 1.025;
|
||||||
@ -74,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
||||||
difficultyValue *= 1.05 * lengthBonus;
|
difficultyValue *= 1.05 * lengthBonus;
|
||||||
|
|
||||||
return difficultyValue * score.Accuracy;
|
return difficultyValue * Math.Pow(score.Accuracy, 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||||
@ -82,10 +85,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
if (attributes.GreatHitWindow <= 0)
|
if (attributes.GreatHitWindow <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
double accValue = Math.Pow(150.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 15) * 22.0;
|
double accuracyValue = Math.Pow(140.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 12.0) * 27;
|
||||||
|
|
||||||
// Bonus for many objects - it's harder to keep good accuracy up for longer
|
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||||
return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
accuracyValue *= lengthBonus;
|
||||||
|
|
||||||
|
// Slight HDFL Bonus for accuracy.
|
||||||
|
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>) && score.Mods.Any(m => m is ModHidden))
|
||||||
|
accuracyValue *= 1.10 * lengthBonus;
|
||||||
|
|
||||||
|
return accuracyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
||||||
|
@ -18,6 +18,9 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
||||||
drawableTaikoRuleset.LockPlayfieldAspect.Value = false;
|
drawableTaikoRuleset.LockPlayfieldAspect.Value = false;
|
||||||
|
|
||||||
|
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
||||||
|
playfield.ClassicHitTargetPosition.Value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(Playfield playfield)
|
public void Update(Playfield playfield)
|
||||||
|
@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModDoubleTime : ModDoubleTime
|
public class TaikoModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModFlashlight : ModFlashlight<TaikoHitObject>
|
public class TaikoModFlashlight : ModFlashlight<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
|
|
||||||
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
||||||
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
public override BindableFloat SizeMultiplier { get; } = new BindableFloat
|
||||||
|
@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModHardRock : ModHardRock
|
public class TaikoModHardRock : ModHardRock
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Multiplier factor added to the scrolling speed.
|
/// Multiplier factor added to the scrolling speed.
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
public class TaikoModHidden : ModHidden, IApplicableToDrawableRuleset<TaikoHitObject>
|
public class TaikoModHidden : ModHidden, IApplicableToDrawableRuleset<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override string Description => @"Beats fade out before you hit them!";
|
public override string Description => @"Beats fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How far away from the hit target should hitobjects start to fade out.
|
/// How far away from the hit target should hitobjects start to fade out.
|
||||||
|
@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,16 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
{
|
{
|
||||||
public class BarLine : TaikoHitObject, IBarLine
|
public class BarLine : TaikoHitObject, IBarLine
|
||||||
{
|
{
|
||||||
|
private HitObjectProperty<bool> major;
|
||||||
|
|
||||||
|
public Bindable<bool> MajorBindable => major.Bindable;
|
||||||
|
|
||||||
public bool Major
|
public bool Major
|
||||||
{
|
{
|
||||||
get => MajorBindable.Value;
|
get => major.Value;
|
||||||
set => MajorBindable.Value = value;
|
set => major.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Bindable<bool> MajorBindable = new BindableBool();
|
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -14,19 +15,21 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
{
|
{
|
||||||
public class Hit : TaikoStrongableHitObject, IHasDisplayColour
|
public class Hit : TaikoStrongableHitObject, IHasDisplayColour
|
||||||
{
|
{
|
||||||
public readonly Bindable<HitType> TypeBindable = new Bindable<HitType>();
|
private HitObjectProperty<HitType> type;
|
||||||
|
|
||||||
public Bindable<Color4> DisplayColour { get; } = new Bindable<Color4>(COLOUR_CENTRE);
|
public Bindable<HitType> TypeBindable => type.Bindable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="HitType"/> that actuates this <see cref="Hit"/>.
|
/// The <see cref="HitType"/> that actuates this <see cref="Hit"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HitType Type
|
public HitType Type
|
||||||
{
|
{
|
||||||
get => TypeBindable.Value;
|
get => type.Value;
|
||||||
set => TypeBindable.Value = value;
|
set => type.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bindable<Color4> DisplayColour { get; } = new Bindable<Color4>(COLOUR_CENTRE);
|
||||||
|
|
||||||
public static readonly Color4 COLOUR_CENTRE = Color4Extensions.FromHex(@"bb1177");
|
public static readonly Color4 COLOUR_CENTRE = Color4Extensions.FromHex(@"bb1177");
|
||||||
public static readonly Color4 COLOUR_RIM = Color4Extensions.FromHex(@"2299bb");
|
public static readonly Color4 COLOUR_RIM = Color4Extensions.FromHex(@"2299bb");
|
||||||
|
|
||||||
|
@ -22,13 +22,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class LegacyInputDrum : Container
|
internal class LegacyInputDrum : Container
|
||||||
{
|
{
|
||||||
|
private Container content;
|
||||||
private LegacyHalfDrum left;
|
private LegacyHalfDrum left;
|
||||||
private LegacyHalfDrum right;
|
private LegacyHalfDrum right;
|
||||||
private Container content;
|
|
||||||
|
|
||||||
public LegacyInputDrum()
|
public LegacyInputDrum()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -33,7 +34,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
sampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer);
|
sampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer);
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.X;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -41,12 +43,32 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new DefaultInputDrum())
|
||||||
{
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
sampleTriggerSource
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultInputDrum : AspectContainer
|
||||||
|
{
|
||||||
|
public DefaultInputDrum()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Scale = new Vector2(0.9f),
|
Scale = new Vector2(0.9f),
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new TaikoHalfDrum(false)
|
new TaikoHalfDrum(false)
|
||||||
{
|
{
|
||||||
@ -71,131 +93,130 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
CentreAction = TaikoAction.RightCentre
|
CentreAction = TaikoAction.RightCentre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
sampleTriggerSource
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A half-drum. Contains one centre and one rim hit.
|
|
||||||
/// </summary>
|
|
||||||
private class TaikoHalfDrum : Container, IKeyBindingHandler<TaikoAction>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The key to be used for the rim of the half-drum.
|
|
||||||
/// </summary>
|
|
||||||
public TaikoAction RimAction;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The key to be used for the centre of the half-drum.
|
|
||||||
/// </summary>
|
|
||||||
public TaikoAction CentreAction;
|
|
||||||
|
|
||||||
private readonly Sprite rim;
|
|
||||||
private readonly Sprite rimHit;
|
|
||||||
private readonly Sprite centre;
|
|
||||||
private readonly Sprite centreHit;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private DrumSampleTriggerSource sampleTriggerSource { get; set; }
|
|
||||||
|
|
||||||
public TaikoHalfDrum(bool flipped)
|
|
||||||
{
|
|
||||||
Masking = true;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
rim = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both
|
|
||||||
},
|
|
||||||
rimHit = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
},
|
|
||||||
centre = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(0.7f)
|
|
||||||
},
|
|
||||||
centreHit = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(0.7f),
|
|
||||||
Alpha = 0,
|
|
||||||
Blending = BlendingParameters.Additive
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
/// <summary>
|
||||||
private void load(TextureStore textures, OsuColour colours)
|
/// A half-drum. Contains one centre and one rim hit.
|
||||||
|
/// </summary>
|
||||||
|
private class TaikoHalfDrum : Container, IKeyBindingHandler<TaikoAction>
|
||||||
{
|
{
|
||||||
rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer");
|
/// <summary>
|
||||||
rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit");
|
/// The key to be used for the rim of the half-drum.
|
||||||
centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner");
|
/// </summary>
|
||||||
centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit");
|
public TaikoAction RimAction;
|
||||||
|
|
||||||
rimHit.Colour = colours.Blue;
|
/// <summary>
|
||||||
centreHit.Colour = colours.Pink;
|
/// The key to be used for the centre of the half-drum.
|
||||||
}
|
/// </summary>
|
||||||
|
public TaikoAction CentreAction;
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
private readonly Sprite rim;
|
||||||
{
|
private readonly Sprite rimHit;
|
||||||
Drawable target = null;
|
private readonly Sprite centre;
|
||||||
Drawable back = null;
|
private readonly Sprite centreHit;
|
||||||
|
|
||||||
if (e.Action == CentreAction)
|
[Resolved]
|
||||||
|
private DrumSampleTriggerSource sampleTriggerSource { get; set; }
|
||||||
|
|
||||||
|
public TaikoHalfDrum(bool flipped)
|
||||||
{
|
{
|
||||||
target = centreHit;
|
Masking = true;
|
||||||
back = centre;
|
|
||||||
|
|
||||||
sampleTriggerSource.Play(HitType.Centre);
|
Children = new Drawable[]
|
||||||
}
|
{
|
||||||
else if (e.Action == RimAction)
|
rim = new Sprite
|
||||||
{
|
{
|
||||||
target = rimHit;
|
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
||||||
back = rim;
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
sampleTriggerSource.Play(HitType.Rim);
|
},
|
||||||
|
rimHit = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
},
|
||||||
|
centre = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = new Vector2(0.7f)
|
||||||
|
},
|
||||||
|
centreHit = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = new Vector2(0.7f),
|
||||||
|
Alpha = 0,
|
||||||
|
Blending = BlendingParameters.Additive
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target != null)
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures, OsuColour colours)
|
||||||
{
|
{
|
||||||
const float scale_amount = 0.05f;
|
rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer");
|
||||||
const float alpha_amount = 0.5f;
|
rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit");
|
||||||
|
centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner");
|
||||||
|
centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit");
|
||||||
|
|
||||||
const float down_time = 40;
|
rimHit.Colour = colours.Blue;
|
||||||
const float up_time = 1000;
|
centreHit.Colour = colours.Pink;
|
||||||
|
|
||||||
back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint)
|
|
||||||
.Then()
|
|
||||||
.ScaleTo(1, up_time, Easing.OutQuint);
|
|
||||||
|
|
||||||
target.Animate(
|
|
||||||
t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint),
|
|
||||||
t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint)
|
|
||||||
).Then(
|
|
||||||
t => t.ScaleTo(1, up_time, Easing.OutQuint),
|
|
||||||
t => t.FadeOut(up_time, Easing.OutQuint)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||||
}
|
{
|
||||||
|
Drawable target = null;
|
||||||
|
Drawable back = null;
|
||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
if (e.Action == CentreAction)
|
||||||
{
|
{
|
||||||
|
target = centreHit;
|
||||||
|
back = centre;
|
||||||
|
|
||||||
|
sampleTriggerSource.Play(HitType.Centre);
|
||||||
|
}
|
||||||
|
else if (e.Action == RimAction)
|
||||||
|
{
|
||||||
|
target = rimHit;
|
||||||
|
back = rim;
|
||||||
|
|
||||||
|
sampleTriggerSource.Play(HitType.Rim);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target != null)
|
||||||
|
{
|
||||||
|
const float scale_amount = 0.05f;
|
||||||
|
const float alpha_amount = 0.5f;
|
||||||
|
|
||||||
|
const float down_time = 40;
|
||||||
|
const float up_time = 1000;
|
||||||
|
|
||||||
|
back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.ScaleTo(1, up_time, Easing.OutQuint);
|
||||||
|
|
||||||
|
target.Animate(
|
||||||
|
t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint),
|
||||||
|
t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint)
|
||||||
|
).Then(
|
||||||
|
t => t.ScaleTo(1, up_time, Easing.OutQuint),
|
||||||
|
t => t.FadeOut(up_time, Easing.OutQuint)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
@ -34,6 +35,11 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DEFAULT_HEIGHT = 200;
|
public const float DEFAULT_HEIGHT = 200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the hit target should be nudged further towards the left area, matching the stable "classic" position.
|
||||||
|
/// </summary>
|
||||||
|
public Bindable<bool> ClassicHitTargetPosition = new BindableBool();
|
||||||
|
|
||||||
private Container<HitExplosion> hitExplosionContainer;
|
private Container<HitExplosion> hitExplosionContainer;
|
||||||
private Container<KiaiHitExplosion> kiaiExplosionContainer;
|
private Container<KiaiHitExplosion> kiaiExplosionContainer;
|
||||||
private JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
private JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
||||||
@ -45,8 +51,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
|
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
|
||||||
|
|
||||||
private ProxyContainer topLevelHitContainer;
|
private ProxyContainer topLevelHitContainer;
|
||||||
|
private InputDrum inputDrum;
|
||||||
private Container rightArea;
|
private Container rightArea;
|
||||||
private Container leftArea;
|
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <see cref="Playfield.AddNested"/> is purposefully not called on this to prevent i.e. being able to interact
|
/// <see cref="Playfield.AddNested"/> is purposefully not called on this to prevent i.e. being able to interact
|
||||||
@ -54,14 +60,43 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
private BarLinePlayfield barLinePlayfield;
|
private BarLinePlayfield barLinePlayfield;
|
||||||
|
|
||||||
private Container hitTargetOffsetContent;
|
private Container playfieldContent;
|
||||||
|
private Container playfieldOverlay;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
|
inputDrum = new InputDrum(HitObjectContainer)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
};
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()),
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()),
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = "Left overlay",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
BorderColour = colours.Gray0,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()),
|
||||||
|
inputDrum.CreateProxy(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Mascot), _ => Empty())
|
||||||
|
{
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
RelativePositionAxes = Axes.Y,
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Y = 0.2f
|
||||||
|
},
|
||||||
rightArea = new Container
|
rightArea = new Container
|
||||||
{
|
{
|
||||||
Name = "Right area",
|
Name = "Right area",
|
||||||
@ -71,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Masked elements before hit objects",
|
Name = "Elements before hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
FillMode = FillMode.Fit,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
@ -86,22 +121,28 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hitTargetOffsetContent = new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
Name = "Masked hit objects content",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Child = playfieldContent = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
barLinePlayfield = new BarLinePlayfield(),
|
||||||
|
HitObjectContainer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playfieldOverlay = new Container
|
||||||
|
{
|
||||||
|
Name = "Elements after hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
barLinePlayfield = new BarLinePlayfield(),
|
drumRollHitContainer = new DrumRollHitContainer(),
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Hit objects",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
HitObjectContainer,
|
|
||||||
drumRollHitContainer = new DrumRollHitContainer()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
kiaiExplosionContainer = new Container<KiaiHitExplosion>
|
kiaiExplosionContainer = new Container<KiaiHitExplosion>
|
||||||
{
|
{
|
||||||
Name = "Kiai hit explosions",
|
Name = "Kiai hit explosions",
|
||||||
@ -117,36 +158,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leftArea = new Container
|
|
||||||
{
|
|
||||||
Name = "Left overlay",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
BorderColour = colours.Gray0,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()),
|
|
||||||
new InputDrum(HitObjectContainer)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Mascot), _ => Empty())
|
|
||||||
{
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Anchor = Anchor.TopLeft,
|
|
||||||
RelativePositionAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.None,
|
|
||||||
Y = 0.2f
|
|
||||||
},
|
|
||||||
topLevelHitContainer = new ProxyContainer
|
topLevelHitContainer = new ProxyContainer
|
||||||
{
|
{
|
||||||
Name = "Top level hit objects",
|
Name = "Top level hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
drumRollHitContainer.CreateProxy(),
|
drumRollHitContainer.CreateProxy(),
|
||||||
|
// this is added at the end of the hierarchy to receive input before taiko objects.
|
||||||
|
// but is proxied below everything to not cover visual effects such as hit explosions.
|
||||||
|
inputDrum,
|
||||||
};
|
};
|
||||||
|
|
||||||
RegisterPool<Hit, DrawableHit>(50);
|
RegisterPool<Hit, DrawableHit>(50);
|
||||||
@ -193,8 +213,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
// Padding is required to be updated for elements which are based on "absolute" X sized elements.
|
// Padding is required to be updated for elements which are based on "absolute" X sized elements.
|
||||||
// This is basically allowing for correct alignment as relative pieces move around them.
|
// This is basically allowing for correct alignment as relative pieces move around them.
|
||||||
rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth };
|
rightArea.Padding = new MarginPadding { Left = inputDrum.Width };
|
||||||
hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
|
playfieldContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
|
||||||
|
playfieldOverlay.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
|
||||||
|
|
||||||
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
|
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
|
||||||
}
|
}
|
||||||
|
85
osu.Game.Tests/Extensions/StringDehumanizeExtensionsTest.cs
Normal file
85
osu.Game.Tests/Extensions/StringDehumanizeExtensionsTest.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// 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.Globalization;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Extensions
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class StringDehumanizeExtensionsTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
[TestCase("single", "Single")]
|
||||||
|
[TestCase("example word", "ExampleWord")]
|
||||||
|
[TestCase("mixed Casing test", "MixedCasingTest")]
|
||||||
|
[TestCase("PascalCase", "PascalCase")]
|
||||||
|
[TestCase("camelCase", "CamelCase")]
|
||||||
|
[TestCase("snake_case", "SnakeCase")]
|
||||||
|
[TestCase("kebab-case", "KebabCase")]
|
||||||
|
[TestCase("i will not break in a different culture", "IWillNotBreakInADifferentCulture", "tr-TR")]
|
||||||
|
public void TestToPascalCase(string input, string expectedOutput, string? culture = null)
|
||||||
|
{
|
||||||
|
using (temporaryCurrentCulture(culture))
|
||||||
|
Assert.That(input.ToPascalCase(), Is.EqualTo(expectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("single", "single")]
|
||||||
|
[TestCase("example word", "exampleWord")]
|
||||||
|
[TestCase("mixed Casing test", "mixedCasingTest")]
|
||||||
|
[TestCase("PascalCase", "pascalCase")]
|
||||||
|
[TestCase("camelCase", "camelCase")]
|
||||||
|
[TestCase("snake_case", "snakeCase")]
|
||||||
|
[TestCase("kebab-case", "kebabCase")]
|
||||||
|
[TestCase("I will not break in a different culture", "iWillNotBreakInADifferentCulture", "tr-TR")]
|
||||||
|
public void TestToCamelCase(string input, string expectedOutput, string? culture = null)
|
||||||
|
{
|
||||||
|
using (temporaryCurrentCulture(culture))
|
||||||
|
Assert.That(input.ToCamelCase(), Is.EqualTo(expectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("single", "single")]
|
||||||
|
[TestCase("example word", "example_word")]
|
||||||
|
[TestCase("mixed Casing test", "mixed_casing_test")]
|
||||||
|
[TestCase("PascalCase", "pascal_case")]
|
||||||
|
[TestCase("camelCase", "camel_case")]
|
||||||
|
[TestCase("snake_case", "snake_case")]
|
||||||
|
[TestCase("kebab-case", "kebab_case")]
|
||||||
|
[TestCase("I will not break in a different culture", "i_will_not_break_in_a_different_culture", "tr-TR")]
|
||||||
|
public void TestToSnakeCase(string input, string expectedOutput, string? culture = null)
|
||||||
|
{
|
||||||
|
using (temporaryCurrentCulture(culture))
|
||||||
|
Assert.That(input.ToSnakeCase(), Is.EqualTo(expectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("single", "single")]
|
||||||
|
[TestCase("example word", "example-word")]
|
||||||
|
[TestCase("mixed Casing test", "mixed-casing-test")]
|
||||||
|
[TestCase("PascalCase", "pascal-case")]
|
||||||
|
[TestCase("camelCase", "camel-case")]
|
||||||
|
[TestCase("snake_case", "snake-case")]
|
||||||
|
[TestCase("kebab-case", "kebab-case")]
|
||||||
|
[TestCase("I will not break in a different culture", "i-will-not-break-in-a-different-culture", "tr-TR")]
|
||||||
|
public void TestToKebabCase(string input, string expectedOutput, string? culture = null)
|
||||||
|
{
|
||||||
|
using (temporaryCurrentCulture(culture))
|
||||||
|
Assert.That(input.ToKebabCase(), Is.EqualTo(expectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDisposable temporaryCurrentCulture(string? cultureName)
|
||||||
|
{
|
||||||
|
var storedCulture = CultureInfo.CurrentCulture;
|
||||||
|
|
||||||
|
if (cultureName != null)
|
||||||
|
CultureInfo.CurrentCulture = new CultureInfo(cultureName);
|
||||||
|
|
||||||
|
return new InvokeOnDisposal(() => CultureInfo.CurrentCulture = storedCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -17,7 +15,7 @@ namespace osu.Game.Tests.Mods
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ModDifficultyAdjustTest
|
public class ModDifficultyAdjustTest
|
||||||
{
|
{
|
||||||
private TestModDifficultyAdjust testMod;
|
private TestModDifficultyAdjust testMod = null!;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
@ -148,7 +146,7 @@ namespace osu.Game.Tests.Mods
|
|||||||
yield return new TestModDifficultyAdjust();
|
yield return new TestModDifficultyAdjust();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new System.NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Moq;
|
using Moq;
|
||||||
@ -164,19 +162,19 @@ namespace osu.Game.Tests.Mods
|
|||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new InvalidMultiplayerMod() },
|
new Mod[] { new OsuModHidden(), new InvalidMultiplayerMod() },
|
||||||
null
|
Array.Empty<Type>()
|
||||||
},
|
},
|
||||||
// invalid free mod is valid for local.
|
// invalid free mod is valid for local.
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() },
|
new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() },
|
||||||
null
|
Array.Empty<Type>()
|
||||||
},
|
},
|
||||||
// valid pair.
|
// valid pair.
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
|
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
|
||||||
null
|
Array.Empty<Type>()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -216,13 +214,13 @@ namespace osu.Game.Tests.Mods
|
|||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() },
|
new Mod[] { new OsuModHidden(), new InvalidMultiplayerFreeMod() },
|
||||||
null
|
Array.Empty<Type>()
|
||||||
},
|
},
|
||||||
// valid pair.
|
// valid pair.
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
|
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
|
||||||
null
|
Array.Empty<Type>()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -256,19 +254,19 @@ namespace osu.Game.Tests.Mods
|
|||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new OsuModApproachDifferent() },
|
new Mod[] { new OsuModHidden(), new OsuModApproachDifferent() },
|
||||||
null,
|
Array.Empty<Type>(),
|
||||||
},
|
},
|
||||||
// incompatible pair with derived class is valid for free mods.
|
// incompatible pair with derived class is valid for free mods.
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModDeflate(), new OsuModSpinIn() },
|
new Mod[] { new OsuModDeflate(), new OsuModSpinIn() },
|
||||||
null,
|
Array.Empty<Type>(),
|
||||||
},
|
},
|
||||||
// valid pair.
|
// valid pair.
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
|
new Mod[] { new OsuModHidden(), new OsuModHardRock() },
|
||||||
null
|
Array.Empty<Type>()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -277,12 +275,12 @@ namespace osu.Game.Tests.Mods
|
|||||||
{
|
{
|
||||||
bool isValid = ModUtils.CheckValidForGameplay(inputMods, out var invalid);
|
bool isValid = ModUtils.CheckValidForGameplay(inputMods, out var invalid);
|
||||||
|
|
||||||
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
|
Assert.That(isValid, Is.EqualTo(expectedInvalid.Length == 0));
|
||||||
|
|
||||||
if (isValid)
|
if (isValid)
|
||||||
Assert.IsNull(invalid);
|
Assert.IsNull(invalid);
|
||||||
else
|
else
|
||||||
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCaseSource(nameof(invalid_multiplayer_mod_test_scenarios))]
|
[TestCaseSource(nameof(invalid_multiplayer_mod_test_scenarios))]
|
||||||
@ -290,12 +288,12 @@ namespace osu.Game.Tests.Mods
|
|||||||
{
|
{
|
||||||
bool isValid = ModUtils.CheckValidRequiredModsForMultiplayer(inputMods, out var invalid);
|
bool isValid = ModUtils.CheckValidRequiredModsForMultiplayer(inputMods, out var invalid);
|
||||||
|
|
||||||
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
|
Assert.That(isValid, Is.EqualTo(expectedInvalid.Length == 0));
|
||||||
|
|
||||||
if (isValid)
|
if (isValid)
|
||||||
Assert.IsNull(invalid);
|
Assert.IsNull(invalid);
|
||||||
else
|
else
|
||||||
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCaseSource(nameof(invalid_free_mod_test_scenarios))]
|
[TestCaseSource(nameof(invalid_free_mod_test_scenarios))]
|
||||||
@ -303,12 +301,12 @@ namespace osu.Game.Tests.Mods
|
|||||||
{
|
{
|
||||||
bool isValid = ModUtils.CheckValidFreeModsForMultiplayer(inputMods, out var invalid);
|
bool isValid = ModUtils.CheckValidFreeModsForMultiplayer(inputMods, out var invalid);
|
||||||
|
|
||||||
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
|
Assert.That(isValid, Is.EqualTo(expectedInvalid.Length == 0));
|
||||||
|
|
||||||
if (isValid)
|
if (isValid)
|
||||||
Assert.IsNull(invalid);
|
Assert.IsNull(invalid);
|
||||||
else
|
else
|
||||||
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class CustomMod1 : Mod, IModCompatibilitySpecification
|
public abstract class CustomMod1 : Mod, IModCompatibilitySpecification
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -29,10 +27,10 @@ namespace osu.Game.Tests.Mods
|
|||||||
[TestCase(typeof(ManiaRuleset))]
|
[TestCase(typeof(ManiaRuleset))]
|
||||||
public void TestAllMultiModsFromRulesetAreIncompatible(Type rulesetType)
|
public void TestAllMultiModsFromRulesetAreIncompatible(Type rulesetType)
|
||||||
{
|
{
|
||||||
var ruleset = (Ruleset)Activator.CreateInstance(rulesetType);
|
var ruleset = Activator.CreateInstance(rulesetType) as Ruleset;
|
||||||
Assert.That(ruleset, Is.Not.Null);
|
Assert.That(ruleset, Is.Not.Null);
|
||||||
|
|
||||||
var allMultiMods = getMultiMods(ruleset);
|
var allMultiMods = getMultiMods(ruleset!);
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -33,7 +31,7 @@ namespace osu.Game.Tests.Mods
|
|||||||
return Array.Empty<Mod>();
|
return Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod>? mods = null) => throw new NotImplementedException();
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
@ -62,9 +62,45 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
Assert.IsTrue(beatmapSetA.Beatmaps.Single().AudioEquals(beatmapSetB.Beatmaps.Single()));
|
Assert.IsTrue(beatmapSetA.Beatmaps.Single().AudioEquals(beatmapSetB.Beatmaps.Single()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addAudioFile(BeatmapSetInfo beatmapSetInfo, string hash = null)
|
[Test]
|
||||||
|
public void TestAudioEqualityBeatmapInfoSameHash()
|
||||||
{
|
{
|
||||||
beatmapSetInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = hash ?? Guid.NewGuid().ToString() }, "audio.mp3"));
|
var beatmapSet = TestResources.CreateTestBeatmapSetInfo(2);
|
||||||
|
|
||||||
|
addAudioFile(beatmapSet);
|
||||||
|
|
||||||
|
var beatmap1 = beatmapSet.Beatmaps.First();
|
||||||
|
var beatmap2 = beatmapSet.Beatmaps.Last();
|
||||||
|
|
||||||
|
Assert.AreNotEqual(beatmap1, beatmap2);
|
||||||
|
Assert.IsTrue(beatmap1.AudioEquals(beatmap2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAudioEqualityBeatmapInfoDifferentHash()
|
||||||
|
{
|
||||||
|
var beatmapSet = TestResources.CreateTestBeatmapSetInfo(2);
|
||||||
|
|
||||||
|
const string filename1 = "audio1.mp3";
|
||||||
|
const string filename2 = "audio2.mp3";
|
||||||
|
|
||||||
|
addAudioFile(beatmapSet, filename: filename1);
|
||||||
|
addAudioFile(beatmapSet, filename: filename2);
|
||||||
|
|
||||||
|
var beatmap1 = beatmapSet.Beatmaps.First();
|
||||||
|
var beatmap2 = beatmapSet.Beatmaps.Last();
|
||||||
|
|
||||||
|
Assert.AreNotEqual(beatmap1, beatmap2);
|
||||||
|
|
||||||
|
beatmap1.Metadata.AudioFile = filename1;
|
||||||
|
beatmap2.Metadata.AudioFile = filename2;
|
||||||
|
|
||||||
|
Assert.IsFalse(beatmap1.AudioEquals(beatmap2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addAudioFile(BeatmapSetInfo beatmapSetInfo, string hash = null, string filename = null)
|
||||||
|
{
|
||||||
|
beatmapSetInfo.Files.Add(new RealmNamedFileUsage(new RealmFile { Hash = hash ?? Guid.NewGuid().ToString() }, filename ?? "audio.mp3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -80,7 +80,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
AddStep("download beatmap", () => beatmaps.Download(test_db_model));
|
AddStep("download beatmap", () => beatmaps.Download(test_db_model));
|
||||||
|
|
||||||
AddStep("cancel download from request", () => beatmaps.GetExistingDownload(test_db_model).Cancel());
|
AddStep("cancel download from request", () => beatmaps.GetExistingDownload(test_db_model)!.Cancel());
|
||||||
|
|
||||||
AddUntilStep("is removed from download list", () => beatmaps.GetExistingDownload(test_db_model) == null);
|
AddUntilStep("is removed from download list", () => beatmaps.GetExistingDownload(test_db_model) == null);
|
||||||
AddAssert("is notification cancelled", () => recentNotification.State == ProgressNotificationState.Cancelled);
|
AddAssert("is notification cancelled", () => recentNotification.State == ProgressNotificationState.Cancelled);
|
||||||
|
@ -126,10 +126,10 @@ namespace osu.Game.Tests.Online
|
|||||||
AddStep("start downloading", () => beatmapDownloader.Download(testBeatmapSet));
|
AddStep("start downloading", () => beatmapDownloader.Download(testBeatmapSet));
|
||||||
addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f));
|
addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f));
|
||||||
|
|
||||||
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).SetProgress(0.4f));
|
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.SetProgress(0.4f));
|
||||||
addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f));
|
addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f));
|
||||||
|
|
||||||
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile));
|
AddStep("finish download", () => ((TestDownloadRequest)beatmapDownloader.GetExistingDownload(testBeatmapSet))!.TriggerSuccess(testBeatmapFile));
|
||||||
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
|
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
|
||||||
|
|
||||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||||
@ -246,7 +246,7 @@ namespace osu.Game.Tests.Online
|
|||||||
=> new TestDownloadRequest(set);
|
=> new TestDownloadRequest(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestDownloadRequest : ArchiveDownloadRequest<IBeatmapSetInfo>
|
internal class TestDownloadRequest : ArchiveDownloadRequest<IBeatmapSetInfo>
|
||||||
{
|
{
|
||||||
public new void SetProgress(float progress) => base.SetProgress(progress);
|
public new void SetProgress(float progress) => base.SetProgress(progress);
|
||||||
public new void TriggerSuccess(string filename) => base.TriggerSuccess(filename);
|
public new void TriggerSuccess(string filename) => base.TriggerSuccess(filename);
|
||||||
|
@ -138,7 +138,7 @@ namespace osu.Game.Tests.Resources
|
|||||||
BPM = bpm,
|
BPM = bpm,
|
||||||
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
Hash = Guid.NewGuid().ToString().ComputeMD5Hash(),
|
||||||
Ruleset = rulesetInfo,
|
Ruleset = rulesetInfo,
|
||||||
Metadata = metadata,
|
Metadata = metadata.DeepClone(),
|
||||||
Difficulty = new BeatmapDifficulty
|
Difficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
OverallDifficulty = diff,
|
OverallDifficulty = diff,
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -19,8 +17,8 @@ namespace osu.Game.Tests.Rulesets.Mods
|
|||||||
private const double start_time = 1000;
|
private const double start_time = 1000;
|
||||||
private const double duration = 9000;
|
private const double duration = 9000;
|
||||||
|
|
||||||
private TrackVirtual track;
|
private TrackVirtual track = null!;
|
||||||
private OsuPlayfield playfield;
|
private OsuPlayfield playfield = null!;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
@ -32,12 +31,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
|
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
|
||||||
|
|
||||||
AddStep("scale zoom", () => TimelineArea.Timeline.Zoom = 200);
|
AddStep("scale zoom", () => TimelineArea.Timeline.Zoom = 200);
|
||||||
AddAssert("range halved", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange / 2, 1));
|
AddStep("range halved", () => Assert.That(TimelineArea.Timeline.VisibleRange, Is.EqualTo(initialVisibleRange / 2).Within(1)));
|
||||||
AddStep("descale zoom", () => TimelineArea.Timeline.Zoom = 50);
|
AddStep("descale zoom", () => TimelineArea.Timeline.Zoom = 50);
|
||||||
AddAssert("range doubled", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange * 2, 1));
|
AddStep("range doubled", () => Assert.That(TimelineArea.Timeline.VisibleRange, Is.EqualTo(initialVisibleRange * 2).Within(1)));
|
||||||
|
|
||||||
AddStep("restore zoom", () => TimelineArea.Timeline.Zoom = 100);
|
AddStep("restore zoom", () => TimelineArea.Timeline.Zoom = 100);
|
||||||
AddAssert("range restored", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange, 1));
|
AddStep("range restored", () => Assert.That(TimelineArea.Timeline.VisibleRange, Is.EqualTo(initialVisibleRange).Within(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
[HeadlessTest]
|
||||||
|
public class TestSceneNoConflictingModAcronyms : TestSceneAllRulesetPlayers
|
||||||
|
{
|
||||||
|
protected override void AddCheckSteps()
|
||||||
|
{
|
||||||
|
AddStep("Check all mod acronyms are unique", () =>
|
||||||
|
{
|
||||||
|
var mods = Ruleset.Value.CreateInstance().AllMods;
|
||||||
|
|
||||||
|
IEnumerable<string> acronyms = mods.Select(m => m.Acronym);
|
||||||
|
|
||||||
|
Assert.That(acronyms, Is.Unique);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,14 +8,18 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
@ -58,14 +62,35 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
protected override bool HasCustomSteps => true;
|
protected override bool HasCustomSteps => true;
|
||||||
|
|
||||||
protected override bool AllowFail => false;
|
protected override bool AllowFail => allowFail;
|
||||||
|
|
||||||
|
private bool allowFail;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
allowFail = false;
|
||||||
|
customRuleset = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSaveFailedReplay()
|
||||||
|
{
|
||||||
|
AddStep("allow fail", () => allowFail = true);
|
||||||
|
|
||||||
|
CreateTest();
|
||||||
|
|
||||||
|
AddUntilStep("fail screen displayed", () => Player.ChildrenOfType<FailOverlay>().First().State.Value == Visibility.Visible);
|
||||||
|
AddUntilStep("score not in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) == null));
|
||||||
|
AddStep("click save button", () => Player.ChildrenOfType<SaveFailedScoreButton>().First().ChildrenOfType<OsuClickableContainer>().First().TriggerClick());
|
||||||
|
AddUntilStep("score not in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestLastPlayedUpdated()
|
public void TestLastPlayedUpdated()
|
||||||
{
|
{
|
||||||
DateTimeOffset? getLastPlayed() => Realm.Run(r => r.Find<BeatmapInfo>(Beatmap.Value.BeatmapInfo.ID)?.LastPlayed);
|
DateTimeOffset? getLastPlayed() => Realm.Run(r => r.Find<BeatmapInfo>(Beatmap.Value.BeatmapInfo.ID)?.LastPlayed);
|
||||||
|
|
||||||
AddStep("set no custom ruleset", () => customRuleset = null);
|
|
||||||
AddAssert("last played is null", () => getLastPlayed() == null);
|
AddAssert("last played is null", () => getLastPlayed() == null);
|
||||||
|
|
||||||
CreateTest();
|
CreateTest();
|
||||||
@ -77,8 +102,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestScoreStoredLocally()
|
public void TestScoreStoredLocally()
|
||||||
{
|
{
|
||||||
AddStep("set no custom ruleset", () => customRuleset = null);
|
|
||||||
|
|
||||||
CreateTest();
|
CreateTest();
|
||||||
|
|
||||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
@ -402,16 +402,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPlayStartsWithCorrectBeatmapWhileAtSongSelect()
|
public void TestPlayStartsWithCorrectBeatmapWhileAtSongSelect()
|
||||||
{
|
{
|
||||||
createRoom(() => new Room
|
PlaylistItem? item = null;
|
||||||
|
createRoom(() =>
|
||||||
{
|
{
|
||||||
Name = { Value = "Test Room" },
|
item = new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||||
Playlist =
|
|
||||||
{
|
{
|
||||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
{
|
};
|
||||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
return new Room
|
||||||
}
|
{
|
||||||
}
|
Name = { Value = "Test Room" },
|
||||||
|
Playlist = { item }
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
pressReadyButton();
|
pressReadyButton();
|
||||||
@ -419,7 +421,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep("Enter song select", () =>
|
AddStep("Enter song select", () =>
|
||||||
{
|
{
|
||||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
|
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||||
@ -440,16 +442,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPlayStartsWithCorrectRulesetWhileAtSongSelect()
|
public void TestPlayStartsWithCorrectRulesetWhileAtSongSelect()
|
||||||
{
|
{
|
||||||
createRoom(() => new Room
|
PlaylistItem? item = null;
|
||||||
|
createRoom(() =>
|
||||||
{
|
{
|
||||||
Name = { Value = "Test Room" },
|
item = new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||||
Playlist =
|
|
||||||
{
|
{
|
||||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
{
|
};
|
||||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
return new Room
|
||||||
}
|
{
|
||||||
}
|
Name = { Value = "Test Room" },
|
||||||
|
Playlist = { item }
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
pressReadyButton();
|
pressReadyButton();
|
||||||
@ -457,7 +461,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep("Enter song select", () =>
|
AddStep("Enter song select", () =>
|
||||||
{
|
{
|
||||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
|
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||||
@ -478,16 +482,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPlayStartsWithCorrectModsWhileAtSongSelect()
|
public void TestPlayStartsWithCorrectModsWhileAtSongSelect()
|
||||||
{
|
{
|
||||||
createRoom(() => new Room
|
PlaylistItem? item = null;
|
||||||
|
createRoom(() =>
|
||||||
{
|
{
|
||||||
Name = { Value = "Test Room" },
|
item = new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||||
Playlist =
|
|
||||||
{
|
{
|
||||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||||
{
|
};
|
||||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
return new Room
|
||||||
}
|
{
|
||||||
}
|
Name = { Value = "Test Room" },
|
||||||
|
Playlist = { item }
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
pressReadyButton();
|
pressReadyButton();
|
||||||
@ -495,7 +501,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep("Enter song select", () =>
|
AddStep("Enter song select", () =>
|
||||||
{
|
{
|
||||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(multiplayerClient.ClientRoom?.Settings.PlaylistItemId);
|
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||||
|
@ -108,6 +108,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Version = "2018.712.0",
|
Version = "2018.712.0",
|
||||||
DisplayVersion = "2018.712.0",
|
DisplayVersion = "2018.712.0",
|
||||||
UpdateStream = streams[OsuGameBase.CLIENT_STREAM_NAME],
|
UpdateStream = streams[OsuGameBase.CLIENT_STREAM_NAME],
|
||||||
|
CreatedAt = new DateTime(2018, 7, 12),
|
||||||
ChangelogEntries = new List<APIChangelogEntry>
|
ChangelogEntries = new List<APIChangelogEntry>
|
||||||
{
|
{
|
||||||
new APIChangelogEntry
|
new APIChangelogEntry
|
||||||
@ -171,6 +172,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Version = "2019.920.0",
|
Version = "2019.920.0",
|
||||||
DisplayVersion = "2019.920.0",
|
DisplayVersion = "2019.920.0",
|
||||||
|
CreatedAt = new DateTime(2019, 9, 20),
|
||||||
UpdateStream = new APIUpdateStream
|
UpdateStream = new APIUpdateStream
|
||||||
{
|
{
|
||||||
Name = "Test",
|
Name = "Test",
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Id = 3103765,
|
Id = 3103765,
|
||||||
IsOnline = true,
|
IsOnline = true,
|
||||||
Statistics = new UserStatistics { GlobalRank = 1111 },
|
Statistics = new UserStatistics { GlobalRank = 1111 },
|
||||||
Country = new Country { FlagName = "JP" },
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||||
},
|
},
|
||||||
new APIUser
|
new APIUser
|
||||||
@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Id = 2,
|
Id = 2,
|
||||||
IsOnline = false,
|
IsOnline = false,
|
||||||
Statistics = new UserStatistics { GlobalRank = 2222 },
|
Statistics = new UserStatistics { GlobalRank = 2222 },
|
||||||
Country = new Country { FlagName = "AU" },
|
CountryCode = CountryCode.AU,
|
||||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
IsSupporter = true,
|
IsSupporter = true,
|
||||||
SupportLevel = 3,
|
SupportLevel = 3,
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = "Evast",
|
Username = "Evast",
|
||||||
Id = 8195163,
|
Id = 8195163,
|
||||||
Country = new Country { FlagName = "BY" },
|
CountryCode = CountryCode.BY,
|
||||||
CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||||
IsOnline = false,
|
IsOnline = false,
|
||||||
LastVisit = DateTimeOffset.Now
|
LastVisit = DateTimeOffset.Now
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
public TestSceneRankingsCountryFilter()
|
public TestSceneRankingsCountryFilter()
|
||||||
{
|
{
|
||||||
var countryBindable = new Bindable<Country>();
|
var countryBindable = new Bindable<CountryCode>();
|
||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
@ -56,20 +56,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var country = new Country
|
const CountryCode country = CountryCode.BY;
|
||||||
{
|
const CountryCode unknown_country = CountryCode.CK;
|
||||||
FlagName = "BY",
|
|
||||||
FullName = "Belarus"
|
|
||||||
};
|
|
||||||
var unknownCountry = new Country
|
|
||||||
{
|
|
||||||
FlagName = "CK",
|
|
||||||
FullName = "Cook Islands"
|
|
||||||
};
|
|
||||||
|
|
||||||
AddStep("Set country", () => countryBindable.Value = country);
|
AddStep("Set country", () => countryBindable.Value = country);
|
||||||
AddStep("Set null country", () => countryBindable.Value = null);
|
AddStep("Set default country", () => countryBindable.Value = default);
|
||||||
AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
|
AddStep("Set country with no flag", () => countryBindable.Value = unknown_country);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
public TestSceneRankingsHeader()
|
public TestSceneRankingsHeader()
|
||||||
{
|
{
|
||||||
var countryBindable = new Bindable<Country>();
|
var countryBindable = new Bindable<CountryCode>();
|
||||||
var ruleset = new Bindable<RulesetInfo>();
|
var ruleset = new Bindable<RulesetInfo>();
|
||||||
var scope = new Bindable<RankingsScope>();
|
var scope = new Bindable<RankingsScope>();
|
||||||
|
|
||||||
@ -30,21 +30,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Ruleset = { BindTarget = ruleset }
|
Ruleset = { BindTarget = ruleset }
|
||||||
});
|
});
|
||||||
|
|
||||||
var country = new Country
|
const CountryCode country = CountryCode.BY;
|
||||||
{
|
const CountryCode unknown_country = CountryCode.CK;
|
||||||
FlagName = "BY",
|
|
||||||
FullName = "Belarus"
|
|
||||||
};
|
|
||||||
|
|
||||||
var unknownCountry = new Country
|
|
||||||
{
|
|
||||||
FlagName = "CK",
|
|
||||||
FullName = "Cook Islands"
|
|
||||||
};
|
|
||||||
|
|
||||||
AddStep("Set country", () => countryBindable.Value = country);
|
AddStep("Set country", () => countryBindable.Value = country);
|
||||||
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
|
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
|
||||||
AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry);
|
AddStep("Set country with no flag", () => countryBindable.Value = unknown_country);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private TestRankingsOverlay rankingsOverlay;
|
private TestRankingsOverlay rankingsOverlay;
|
||||||
|
|
||||||
private readonly Bindable<Country> countryBindable = new Bindable<Country>();
|
private readonly Bindable<CountryCode> countryBindable = new Bindable<CountryCode>();
|
||||||
private readonly Bindable<RankingsScope> scope = new Bindable<RankingsScope>();
|
private readonly Bindable<RankingsScope> scope = new Bindable<RankingsScope>();
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@ -48,15 +48,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
public void TestFlagScopeDependency()
|
public void TestFlagScopeDependency()
|
||||||
{
|
{
|
||||||
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
|
AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
|
||||||
AddAssert("Check country is Null", () => countryBindable.Value == null);
|
AddAssert("Check country is default", () => countryBindable.IsDefault);
|
||||||
AddStep("Set country", () => countryBindable.Value = us_country);
|
AddStep("Set country", () => countryBindable.Value = CountryCode.US);
|
||||||
AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
|
AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowCountry()
|
public void TestShowCountry()
|
||||||
{
|
{
|
||||||
AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country));
|
AddStep("Show US", () => rankingsOverlay.ShowCountry(CountryCode.US));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRankingsOverlay()
|
private void loadRankingsOverlay()
|
||||||
@ -69,15 +69,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Country us_country = new Country
|
|
||||||
{
|
|
||||||
FlagName = "US",
|
|
||||||
FullName = "United States"
|
|
||||||
};
|
|
||||||
|
|
||||||
private class TestRankingsOverlay : RankingsOverlay
|
private class TestRankingsOverlay : RankingsOverlay
|
||||||
{
|
{
|
||||||
public new Bindable<Country> Country => base.Country;
|
public new Bindable<CountryCode> Country => base.Country;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
new CountryStatistics
|
new CountryStatistics
|
||||||
{
|
{
|
||||||
Country = new Country { FlagName = "US", FullName = "United States" },
|
Code = CountryCode.US,
|
||||||
FlagName = "US",
|
|
||||||
ActiveUsers = 2_972_623,
|
ActiveUsers = 2_972_623,
|
||||||
PlayCount = 3_086_515_743,
|
PlayCount = 3_086_515_743,
|
||||||
RankedScore = 449_407_643_332_546,
|
RankedScore = 449_407_643_332_546,
|
||||||
@ -66,8 +65,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
new CountryStatistics
|
new CountryStatistics
|
||||||
{
|
{
|
||||||
Country = new Country { FlagName = "RU", FullName = "Russian Federation" },
|
Code = CountryCode.RU,
|
||||||
FlagName = "RU",
|
|
||||||
ActiveUsers = 1_609_989,
|
ActiveUsers = 1_609_989,
|
||||||
PlayCount = 1_637_052_841,
|
PlayCount = 1_637_052_841,
|
||||||
RankedScore = 221_660_827_473_004,
|
RankedScore = 221_660_827_473_004,
|
||||||
@ -86,7 +84,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Username = "first active user",
|
Username = "first active user",
|
||||||
Country = new Country { FlagName = "JP" },
|
CountryCode = CountryCode.JP,
|
||||||
Active = true,
|
Active = true,
|
||||||
},
|
},
|
||||||
Accuracy = 0.9972,
|
Accuracy = 0.9972,
|
||||||
@ -106,7 +104,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Username = "inactive user",
|
Username = "inactive user",
|
||||||
Country = new Country { FlagName = "AU" },
|
CountryCode = CountryCode.AU,
|
||||||
Active = false,
|
Active = false,
|
||||||
},
|
},
|
||||||
Accuracy = 0.9831,
|
Accuracy = 0.9831,
|
||||||
@ -126,7 +124,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
User = new APIUser
|
User = new APIUser
|
||||||
{
|
{
|
||||||
Username = "second active user",
|
Username = "second active user",
|
||||||
Country = new Country { FlagName = "PL" },
|
CountryCode = CountryCode.PL,
|
||||||
Active = true,
|
Active = true,
|
||||||
},
|
},
|
||||||
Accuracy = 0.9584,
|
Accuracy = 0.9584,
|
||||||
|
@ -157,11 +157,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
Username = @"waaiiru",
|
Username = @"waaiiru",
|
||||||
Country = new Country
|
CountryCode = CountryCode.ES,
|
||||||
{
|
|
||||||
FullName = @"Spain",
|
|
||||||
FlagName = @"ES",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Mods = new[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
@ -184,11 +180,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Id = 4608074,
|
Id = 4608074,
|
||||||
Username = @"Skycries",
|
Username = @"Skycries",
|
||||||
Country = new Country
|
CountryCode = CountryCode.BR,
|
||||||
{
|
|
||||||
FullName = @"Brazil",
|
|
||||||
FlagName = @"BR",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Mods = new[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
@ -210,11 +202,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Id = 1014222,
|
Id = 1014222,
|
||||||
Username = @"eLy",
|
Username = @"eLy",
|
||||||
Country = new Country
|
CountryCode = CountryCode.JP,
|
||||||
{
|
|
||||||
FullName = @"Japan",
|
|
||||||
FlagName = @"JP",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Mods = new[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
@ -235,11 +223,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Id = 1541390,
|
Id = 1541390,
|
||||||
Username = @"Toukai",
|
Username = @"Toukai",
|
||||||
Country = new Country
|
CountryCode = CountryCode.CA,
|
||||||
{
|
|
||||||
FullName = @"Canada",
|
|
||||||
FlagName = @"CA",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Mods = new[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
@ -259,11 +243,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Id = 7151382,
|
Id = 7151382,
|
||||||
Username = @"Mayuri Hana",
|
Username = @"Mayuri Hana",
|
||||||
Country = new Country
|
CountryCode = CountryCode.TH,
|
||||||
{
|
|
||||||
FullName = @"Thailand",
|
|
||||||
FlagName = @"TH",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.D,
|
Rank = ScoreRank.D,
|
||||||
PP = 160,
|
PP = 160,
|
||||||
@ -274,15 +254,26 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int initial_great_count = 2000;
|
||||||
|
const int initial_tick_count = 100;
|
||||||
|
|
||||||
|
int greatCount = initial_great_count;
|
||||||
|
int tickCount = initial_tick_count;
|
||||||
|
|
||||||
foreach (var s in scores.Scores)
|
foreach (var s in scores.Scores)
|
||||||
{
|
{
|
||||||
s.Statistics = new Dictionary<HitResult, int>
|
s.Statistics = new Dictionary<HitResult, int>
|
||||||
{
|
{
|
||||||
{ HitResult.Great, RNG.Next(2000) },
|
{ HitResult.Great, greatCount },
|
||||||
{ HitResult.Ok, RNG.Next(2000) },
|
{ HitResult.LargeTickHit, tickCount },
|
||||||
{ HitResult.Meh, RNG.Next(2000) },
|
{ HitResult.Ok, RNG.Next(100) },
|
||||||
{ HitResult.Miss, RNG.Next(2000) }
|
{ HitResult.Meh, RNG.Next(100) },
|
||||||
|
{ HitResult.Miss, initial_great_count - greatCount },
|
||||||
|
{ HitResult.LargeTickMiss, initial_tick_count - tickCount },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
greatCount -= 100;
|
||||||
|
tickCount -= RNG.Next(1, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scores;
|
return scores;
|
||||||
@ -298,11 +289,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Id = 7151382,
|
Id = 7151382,
|
||||||
Username = @"Mayuri Hana",
|
Username = @"Mayuri Hana",
|
||||||
Country = new Country
|
CountryCode = CountryCode.TH,
|
||||||
{
|
|
||||||
FullName = @"Thailand",
|
|
||||||
FlagName = @"TH",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.D,
|
Rank = ScoreRank.D,
|
||||||
PP = 160,
|
PP = 160,
|
||||||
|
@ -60,7 +60,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = @"flyte",
|
Username = @"flyte",
|
||||||
Id = 3103765,
|
Id = 3103765,
|
||||||
Country = new Country { FlagName = @"JP" },
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg",
|
||||||
Status = { Value = new UserStatusOnline() }
|
Status = { Value = new UserStatusOnline() }
|
||||||
}) { Width = 300 },
|
}) { Width = 300 },
|
||||||
@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = @"peppy",
|
Username = @"peppy",
|
||||||
Id = 2,
|
Id = 2,
|
||||||
Country = new Country { FlagName = @"AU" },
|
CountryCode = CountryCode.AU,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
IsSupporter = true,
|
IsSupporter = true,
|
||||||
SupportLevel = 3,
|
SupportLevel = 3,
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = @"Evast",
|
Username = @"Evast",
|
||||||
Id = 8195163,
|
Id = 8195163,
|
||||||
Country = new Country { FlagName = @"BY" },
|
CountryCode = CountryCode.BY,
|
||||||
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||||
IsOnline = false,
|
IsOnline = false,
|
||||||
LastVisit = DateTimeOffset.Now
|
LastVisit = DateTimeOffset.Now
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = @"Somebody",
|
Username = @"Somebody",
|
||||||
Id = 1,
|
Id = 1,
|
||||||
Country = new Country { FullName = @"Alien" },
|
CountryCode = CountryCode.Unknown,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
||||||
JoinDate = DateTimeOffset.Now.AddDays(-1),
|
JoinDate = DateTimeOffset.Now.AddDays(-1),
|
||||||
LastVisit = DateTimeOffset.Now,
|
LastVisit = DateTimeOffset.Now,
|
||||||
@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Username = @"peppy",
|
Username = @"peppy",
|
||||||
Id = 2,
|
Id = 2,
|
||||||
IsSupporter = true,
|
IsSupporter = true,
|
||||||
Country = new Country { FullName = @"Australia", FlagName = @"AU" },
|
CountryCode = CountryCode.AU,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Username = @"flyte",
|
Username = @"flyte",
|
||||||
Id = 3103765,
|
Id = 3103765,
|
||||||
Country = new Country { FullName = @"Japan", FlagName = @"JP" },
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Username = @"BanchoBot",
|
Username = @"BanchoBot",
|
||||||
Id = 3,
|
Id = 3,
|
||||||
IsBot = true,
|
IsBot = true,
|
||||||
Country = new Country { FullName = @"Saint Helena", FlagName = @"SH" },
|
CountryCode = CountryCode.SH,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg"
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
public TestSceneUserProfileScores()
|
public TestSceneUserProfileScores()
|
||||||
{
|
{
|
||||||
var firstScore = new APIScore
|
var firstScore = new SoloScoreInfo
|
||||||
{
|
{
|
||||||
PP = 1047.21,
|
PP = 1047.21,
|
||||||
Rank = ScoreRank.SH,
|
Rank = ScoreRank.SH,
|
||||||
@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
DifficultyName = "Extreme"
|
DifficultyName = "Extreme"
|
||||||
},
|
},
|
||||||
Date = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Mods = new[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new APIMod { Acronym = new OsuModHidden().Acronym },
|
new APIMod { Acronym = new OsuModHidden().Acronym },
|
||||||
@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Accuracy = 0.9813
|
Accuracy = 0.9813
|
||||||
};
|
};
|
||||||
|
|
||||||
var secondScore = new APIScore
|
var secondScore = new SoloScoreInfo
|
||||||
{
|
{
|
||||||
PP = 134.32,
|
PP = 134.32,
|
||||||
Rank = ScoreRank.A,
|
Rank = ScoreRank.A,
|
||||||
@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
DifficultyName = "[4K] Regret"
|
DifficultyName = "[4K] Regret"
|
||||||
},
|
},
|
||||||
Date = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Mods = new[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new APIMod { Acronym = new OsuModHardRock().Acronym },
|
new APIMod { Acronym = new OsuModHardRock().Acronym },
|
||||||
@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Accuracy = 0.998546
|
Accuracy = 0.998546
|
||||||
};
|
};
|
||||||
|
|
||||||
var thirdScore = new APIScore
|
var thirdScore = new SoloScoreInfo
|
||||||
{
|
{
|
||||||
PP = 96.83,
|
PP = 96.83,
|
||||||
Rank = ScoreRank.S,
|
Rank = ScoreRank.S,
|
||||||
@ -79,11 +79,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
DifficultyName = "Insane"
|
DifficultyName = "Insane"
|
||||||
},
|
},
|
||||||
Date = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Accuracy = 0.9726
|
Accuracy = 0.9726
|
||||||
};
|
};
|
||||||
|
|
||||||
var noPPScore = new APIScore
|
var noPPScore = new SoloScoreInfo
|
||||||
{
|
{
|
||||||
Rank = ScoreRank.B,
|
Rank = ScoreRank.B,
|
||||||
Beatmap = new APIBeatmap
|
Beatmap = new APIBeatmap
|
||||||
@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
DifficultyName = "[4K] Cataclysmic Hypernova"
|
DifficultyName = "[4K] Cataclysmic Hypernova"
|
||||||
},
|
},
|
||||||
Date = DateTimeOffset.Now,
|
EndedAt = DateTimeOffset.Now,
|
||||||
Accuracy = 0.55879
|
Accuracy = 0.55879
|
||||||
};
|
};
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -825,7 +825,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
checkVisibleItemCount(true, 15);
|
checkVisibleItemCount(true, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null, int? count = null, bool randomDifficulties = false)
|
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null, int? count = null,
|
||||||
|
bool randomDifficulties = false)
|
||||||
{
|
{
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
|
@ -140,11 +140,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
Username = @"waaiiru",
|
Username = @"waaiiru",
|
||||||
Country = new Country
|
CountryCode = CountryCode.ES,
|
||||||
{
|
|
||||||
FullName = @"Spain",
|
|
||||||
FlagName = @"ES",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -164,12 +160,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
Username = @"waaiiru",
|
Username = @"waaiiru",
|
||||||
Country = new Country
|
CountryCode = CountryCode.ES,
|
||||||
{
|
}
|
||||||
FullName = @"Spain",
|
|
||||||
FlagName = @"ES",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,11 +217,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
Username = @"waaiiru",
|
Username = @"waaiiru",
|
||||||
Country = new Country
|
CountryCode = CountryCode.ES,
|
||||||
{
|
|
||||||
FullName = @"Spain",
|
|
||||||
FlagName = @"ES",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -246,11 +234,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 4608074,
|
Id = 4608074,
|
||||||
Username = @"Skycries",
|
Username = @"Skycries",
|
||||||
Country = new Country
|
CountryCode = CountryCode.BR,
|
||||||
{
|
|
||||||
FullName = @"Brazil",
|
|
||||||
FlagName = @"BR",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -268,11 +252,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 1014222,
|
Id = 1014222,
|
||||||
Username = @"eLy",
|
Username = @"eLy",
|
||||||
Country = new Country
|
CountryCode = CountryCode.JP,
|
||||||
{
|
|
||||||
FullName = @"Japan",
|
|
||||||
FlagName = @"JP",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -290,11 +270,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 1541390,
|
Id = 1541390,
|
||||||
Username = @"Toukai",
|
Username = @"Toukai",
|
||||||
Country = new Country
|
CountryCode = CountryCode.CA,
|
||||||
{
|
|
||||||
FullName = @"Canada",
|
|
||||||
FlagName = @"CA",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -312,11 +288,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 2243452,
|
Id = 2243452,
|
||||||
Username = @"Satoruu",
|
Username = @"Satoruu",
|
||||||
Country = new Country
|
CountryCode = CountryCode.VE,
|
||||||
{
|
|
||||||
FullName = @"Venezuela",
|
|
||||||
FlagName = @"VE",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -334,11 +306,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 2705430,
|
Id = 2705430,
|
||||||
Username = @"Mooha",
|
Username = @"Mooha",
|
||||||
Country = new Country
|
CountryCode = CountryCode.FR,
|
||||||
{
|
|
||||||
FullName = @"France",
|
|
||||||
FlagName = @"FR",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -356,11 +324,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 7151382,
|
Id = 7151382,
|
||||||
Username = @"Mayuri Hana",
|
Username = @"Mayuri Hana",
|
||||||
Country = new Country
|
CountryCode = CountryCode.TH,
|
||||||
{
|
|
||||||
FullName = @"Thailand",
|
|
||||||
FlagName = @"TH",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -378,11 +342,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 2051389,
|
Id = 2051389,
|
||||||
Username = @"FunOrange",
|
Username = @"FunOrange",
|
||||||
Country = new Country
|
CountryCode = CountryCode.CA,
|
||||||
{
|
|
||||||
FullName = @"Canada",
|
|
||||||
FlagName = @"CA",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -400,11 +360,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 6169483,
|
Id = 6169483,
|
||||||
Username = @"-Hebel-",
|
Username = @"-Hebel-",
|
||||||
Country = new Country
|
CountryCode = CountryCode.MX,
|
||||||
{
|
|
||||||
FullName = @"Mexico",
|
|
||||||
FlagName = @"MX",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -422,11 +378,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 6702666,
|
Id = 6702666,
|
||||||
Username = @"prhtnsm",
|
Username = @"prhtnsm",
|
||||||
Country = new Country
|
CountryCode = CountryCode.DE,
|
||||||
{
|
|
||||||
FullName = @"Germany",
|
|
||||||
FlagName = @"DE",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Screens.Select.Carousel;
|
||||||
|
using osu.Game.Tests.Online;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneUpdateBeatmapSetButton : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private BeatmapCarousel carousel = null!;
|
||||||
|
|
||||||
|
private TestSceneOnlinePlayBeatmapAvailabilityTracker.TestBeatmapModelDownloader beatmapDownloader = null!;
|
||||||
|
|
||||||
|
private BeatmapSetInfo testBeatmapSetInfo = null!;
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
|
var importer = parent.Get<BeatmapManager>();
|
||||||
|
|
||||||
|
dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestSceneOnlinePlayBeatmapAvailabilityTracker.TestBeatmapModelDownloader(importer, API));
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdateBeatmapSetButton? getUpdateButton() => carousel.ChildrenOfType<UpdateBeatmapSetButton>().SingleOrDefault();
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("create carousel", () =>
|
||||||
|
{
|
||||||
|
Child = carousel = new BeatmapCarousel
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
BeatmapSets = new List<BeatmapSetInfo>
|
||||||
|
{
|
||||||
|
(testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for load", () => carousel.BeatmapSetsLoaded);
|
||||||
|
|
||||||
|
AddAssert("update button not visible", () => getUpdateButton() == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDownloadToCompletion()
|
||||||
|
{
|
||||||
|
ArchiveDownloadRequest<IBeatmapSetInfo>? downloadRequest = null;
|
||||||
|
|
||||||
|
AddStep("update online hash", () =>
|
||||||
|
{
|
||||||
|
testBeatmapSetInfo.Beatmaps.First().OnlineMD5Hash = "different hash";
|
||||||
|
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("only one set visible", () => carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().Count() == 1);
|
||||||
|
AddUntilStep("update button visible", () => getUpdateButton() != null);
|
||||||
|
|
||||||
|
AddStep("click button", () => getUpdateButton()?.TriggerClick());
|
||||||
|
|
||||||
|
AddUntilStep("wait for download started", () =>
|
||||||
|
{
|
||||||
|
downloadRequest = beatmapDownloader.GetExistingDownload(testBeatmapSetInfo);
|
||||||
|
return downloadRequest != null;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for button disabled", () => getUpdateButton()?.Enabled.Value == false);
|
||||||
|
|
||||||
|
AddUntilStep("progress download to completion", () =>
|
||||||
|
{
|
||||||
|
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||||
|
{
|
||||||
|
testRequest.SetProgress(testRequest.Progress + 0.1f);
|
||||||
|
|
||||||
|
if (testRequest.Progress >= 1)
|
||||||
|
{
|
||||||
|
testRequest.TriggerSuccess();
|
||||||
|
|
||||||
|
// usually this would be done by the import process.
|
||||||
|
testBeatmapSetInfo.Beatmaps.First().MD5Hash = "different hash";
|
||||||
|
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
// usually this would be done by a realm subscription.
|
||||||
|
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDownloadFailed()
|
||||||
|
{
|
||||||
|
ArchiveDownloadRequest<IBeatmapSetInfo>? downloadRequest = null;
|
||||||
|
|
||||||
|
AddStep("update online hash", () =>
|
||||||
|
{
|
||||||
|
testBeatmapSetInfo.Beatmaps.First().OnlineMD5Hash = "different hash";
|
||||||
|
testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
carousel.UpdateBeatmapSet(testBeatmapSetInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("only one set visible", () => carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().Count() == 1);
|
||||||
|
AddUntilStep("update button visible", () => getUpdateButton() != null);
|
||||||
|
|
||||||
|
AddStep("click button", () => getUpdateButton()?.TriggerClick());
|
||||||
|
|
||||||
|
AddUntilStep("wait for download started", () =>
|
||||||
|
{
|
||||||
|
downloadRequest = beatmapDownloader.GetExistingDownload(testBeatmapSetInfo);
|
||||||
|
return downloadRequest != null;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for button disabled", () => getUpdateButton()?.Enabled.Value == false);
|
||||||
|
|
||||||
|
AddUntilStep("progress download to failure", () =>
|
||||||
|
{
|
||||||
|
if (downloadRequest is TestSceneOnlinePlayBeatmapAvailabilityTracker.TestDownloadRequest testRequest)
|
||||||
|
{
|
||||||
|
testRequest.SetProgress(testRequest.Progress + 0.1f);
|
||||||
|
|
||||||
|
if (testRequest.Progress >= 0.5f)
|
||||||
|
{
|
||||||
|
testRequest.TriggerFailure(new Exception());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for button enabled", () => getUpdateButton()?.Enabled.Value == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -69,11 +69,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
Username = @"waaiiru",
|
Username = @"waaiiru",
|
||||||
Country = new Country
|
CountryCode = CountryCode.ES,
|
||||||
{
|
|
||||||
FullName = @"Spain",
|
|
||||||
FlagName = @"ES",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -88,11 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 4608074,
|
Id = 4608074,
|
||||||
Username = @"Skycries",
|
Username = @"Skycries",
|
||||||
Country = new Country
|
CountryCode = CountryCode.BR,
|
||||||
{
|
|
||||||
FullName = @"Brazil",
|
|
||||||
FlagName = @"BR",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
@ -107,11 +99,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
Id = 1541390,
|
Id = 1541390,
|
||||||
Username = @"Toukai",
|
Username = @"Toukai",
|
||||||
Country = new Country
|
CountryCode = CountryCode.CA,
|
||||||
{
|
|
||||||
FullName = @"Canada",
|
|
||||||
FlagName = @"CA",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
};
|
};
|
||||||
|
|
||||||
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
|
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
|
||||||
control.General.BindCollectionChanged((_, _) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().Underscore())) : "")}", true);
|
control.General.BindCollectionChanged((_, _) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().ToSnakeCase())) : "")}", true);
|
||||||
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
|
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
|
||||||
control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
|
control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
|
||||||
control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true);
|
control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true);
|
||||||
|
51
osu.Game.Tests/Visual/UserInterface/TestSceneFPSCounter.cs
Normal file
51
osu.Game.Tests/Visual/UserInterface/TestSceneFPSCounter.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneFPSCounter : OsuTestScene
|
||||||
|
{
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("create display", () =>
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.White,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FPSCounter(),
|
||||||
|
new FPSCounter { Scale = new Vector2(2) },
|
||||||
|
new FPSCounter { Scale = new Vector2(4) },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasic()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
770
osu.Game.Tournament/CountryExtensions.cs
Normal file
770
osu.Game.Tournament/CountryExtensions.cs
Normal file
@ -0,0 +1,770 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Tournament
|
||||||
|
{
|
||||||
|
public static class CountryExtensions
|
||||||
|
{
|
||||||
|
public static string GetAcronym(this CountryCode country)
|
||||||
|
{
|
||||||
|
switch (country)
|
||||||
|
{
|
||||||
|
case CountryCode.BD:
|
||||||
|
return "BGD";
|
||||||
|
|
||||||
|
case CountryCode.BE:
|
||||||
|
return "BEL";
|
||||||
|
|
||||||
|
case CountryCode.BF:
|
||||||
|
return "BFA";
|
||||||
|
|
||||||
|
case CountryCode.BG:
|
||||||
|
return "BGR";
|
||||||
|
|
||||||
|
case CountryCode.BA:
|
||||||
|
return "BIH";
|
||||||
|
|
||||||
|
case CountryCode.BB:
|
||||||
|
return "BRB";
|
||||||
|
|
||||||
|
case CountryCode.WF:
|
||||||
|
return "WLF";
|
||||||
|
|
||||||
|
case CountryCode.BL:
|
||||||
|
return "BLM";
|
||||||
|
|
||||||
|
case CountryCode.BM:
|
||||||
|
return "BMU";
|
||||||
|
|
||||||
|
case CountryCode.BN:
|
||||||
|
return "BRN";
|
||||||
|
|
||||||
|
case CountryCode.BO:
|
||||||
|
return "BOL";
|
||||||
|
|
||||||
|
case CountryCode.BH:
|
||||||
|
return "BHR";
|
||||||
|
|
||||||
|
case CountryCode.BI:
|
||||||
|
return "BDI";
|
||||||
|
|
||||||
|
case CountryCode.BJ:
|
||||||
|
return "BEN";
|
||||||
|
|
||||||
|
case CountryCode.BT:
|
||||||
|
return "BTN";
|
||||||
|
|
||||||
|
case CountryCode.JM:
|
||||||
|
return "JAM";
|
||||||
|
|
||||||
|
case CountryCode.BV:
|
||||||
|
return "BVT";
|
||||||
|
|
||||||
|
case CountryCode.BW:
|
||||||
|
return "BWA";
|
||||||
|
|
||||||
|
case CountryCode.WS:
|
||||||
|
return "WSM";
|
||||||
|
|
||||||
|
case CountryCode.BQ:
|
||||||
|
return "BES";
|
||||||
|
|
||||||
|
case CountryCode.BR:
|
||||||
|
return "BRA";
|
||||||
|
|
||||||
|
case CountryCode.BS:
|
||||||
|
return "BHS";
|
||||||
|
|
||||||
|
case CountryCode.JE:
|
||||||
|
return "JEY";
|
||||||
|
|
||||||
|
case CountryCode.BY:
|
||||||
|
return "BLR";
|
||||||
|
|
||||||
|
case CountryCode.BZ:
|
||||||
|
return "BLZ";
|
||||||
|
|
||||||
|
case CountryCode.RU:
|
||||||
|
return "RUS";
|
||||||
|
|
||||||
|
case CountryCode.RW:
|
||||||
|
return "RWA";
|
||||||
|
|
||||||
|
case CountryCode.RS:
|
||||||
|
return "SRB";
|
||||||
|
|
||||||
|
case CountryCode.TL:
|
||||||
|
return "TLS";
|
||||||
|
|
||||||
|
case CountryCode.RE:
|
||||||
|
return "REU";
|
||||||
|
|
||||||
|
case CountryCode.TM:
|
||||||
|
return "TKM";
|
||||||
|
|
||||||
|
case CountryCode.TJ:
|
||||||
|
return "TJK";
|
||||||
|
|
||||||
|
case CountryCode.RO:
|
||||||
|
return "ROU";
|
||||||
|
|
||||||
|
case CountryCode.TK:
|
||||||
|
return "TKL";
|
||||||
|
|
||||||
|
case CountryCode.GW:
|
||||||
|
return "GNB";
|
||||||
|
|
||||||
|
case CountryCode.GU:
|
||||||
|
return "GUM";
|
||||||
|
|
||||||
|
case CountryCode.GT:
|
||||||
|
return "GTM";
|
||||||
|
|
||||||
|
case CountryCode.GS:
|
||||||
|
return "SGS";
|
||||||
|
|
||||||
|
case CountryCode.GR:
|
||||||
|
return "GRC";
|
||||||
|
|
||||||
|
case CountryCode.GQ:
|
||||||
|
return "GNQ";
|
||||||
|
|
||||||
|
case CountryCode.GP:
|
||||||
|
return "GLP";
|
||||||
|
|
||||||
|
case CountryCode.JP:
|
||||||
|
return "JPN";
|
||||||
|
|
||||||
|
case CountryCode.GY:
|
||||||
|
return "GUY";
|
||||||
|
|
||||||
|
case CountryCode.GG:
|
||||||
|
return "GGY";
|
||||||
|
|
||||||
|
case CountryCode.GF:
|
||||||
|
return "GUF";
|
||||||
|
|
||||||
|
case CountryCode.GE:
|
||||||
|
return "GEO";
|
||||||
|
|
||||||
|
case CountryCode.GD:
|
||||||
|
return "GRD";
|
||||||
|
|
||||||
|
case CountryCode.GB:
|
||||||
|
return "GBR";
|
||||||
|
|
||||||
|
case CountryCode.GA:
|
||||||
|
return "GAB";
|
||||||
|
|
||||||
|
case CountryCode.SV:
|
||||||
|
return "SLV";
|
||||||
|
|
||||||
|
case CountryCode.GN:
|
||||||
|
return "GIN";
|
||||||
|
|
||||||
|
case CountryCode.GM:
|
||||||
|
return "GMB";
|
||||||
|
|
||||||
|
case CountryCode.GL:
|
||||||
|
return "GRL";
|
||||||
|
|
||||||
|
case CountryCode.GI:
|
||||||
|
return "GIB";
|
||||||
|
|
||||||
|
case CountryCode.GH:
|
||||||
|
return "GHA";
|
||||||
|
|
||||||
|
case CountryCode.OM:
|
||||||
|
return "OMN";
|
||||||
|
|
||||||
|
case CountryCode.TN:
|
||||||
|
return "TUN";
|
||||||
|
|
||||||
|
case CountryCode.JO:
|
||||||
|
return "JOR";
|
||||||
|
|
||||||
|
case CountryCode.HR:
|
||||||
|
return "HRV";
|
||||||
|
|
||||||
|
case CountryCode.HT:
|
||||||
|
return "HTI";
|
||||||
|
|
||||||
|
case CountryCode.HU:
|
||||||
|
return "HUN";
|
||||||
|
|
||||||
|
case CountryCode.HK:
|
||||||
|
return "HKG";
|
||||||
|
|
||||||
|
case CountryCode.HN:
|
||||||
|
return "HND";
|
||||||
|
|
||||||
|
case CountryCode.HM:
|
||||||
|
return "HMD";
|
||||||
|
|
||||||
|
case CountryCode.VE:
|
||||||
|
return "VEN";
|
||||||
|
|
||||||
|
case CountryCode.PR:
|
||||||
|
return "PRI";
|
||||||
|
|
||||||
|
case CountryCode.PS:
|
||||||
|
return "PSE";
|
||||||
|
|
||||||
|
case CountryCode.PW:
|
||||||
|
return "PLW";
|
||||||
|
|
||||||
|
case CountryCode.PT:
|
||||||
|
return "PRT";
|
||||||
|
|
||||||
|
case CountryCode.SJ:
|
||||||
|
return "SJM";
|
||||||
|
|
||||||
|
case CountryCode.PY:
|
||||||
|
return "PRY";
|
||||||
|
|
||||||
|
case CountryCode.IQ:
|
||||||
|
return "IRQ";
|
||||||
|
|
||||||
|
case CountryCode.PA:
|
||||||
|
return "PAN";
|
||||||
|
|
||||||
|
case CountryCode.PF:
|
||||||
|
return "PYF";
|
||||||
|
|
||||||
|
case CountryCode.PG:
|
||||||
|
return "PNG";
|
||||||
|
|
||||||
|
case CountryCode.PE:
|
||||||
|
return "PER";
|
||||||
|
|
||||||
|
case CountryCode.PK:
|
||||||
|
return "PAK";
|
||||||
|
|
||||||
|
case CountryCode.PH:
|
||||||
|
return "PHL";
|
||||||
|
|
||||||
|
case CountryCode.PN:
|
||||||
|
return "PCN";
|
||||||
|
|
||||||
|
case CountryCode.PL:
|
||||||
|
return "POL";
|
||||||
|
|
||||||
|
case CountryCode.PM:
|
||||||
|
return "SPM";
|
||||||
|
|
||||||
|
case CountryCode.ZM:
|
||||||
|
return "ZMB";
|
||||||
|
|
||||||
|
case CountryCode.EH:
|
||||||
|
return "ESH";
|
||||||
|
|
||||||
|
case CountryCode.EE:
|
||||||
|
return "EST";
|
||||||
|
|
||||||
|
case CountryCode.EG:
|
||||||
|
return "EGY";
|
||||||
|
|
||||||
|
case CountryCode.ZA:
|
||||||
|
return "ZAF";
|
||||||
|
|
||||||
|
case CountryCode.EC:
|
||||||
|
return "ECU";
|
||||||
|
|
||||||
|
case CountryCode.IT:
|
||||||
|
return "ITA";
|
||||||
|
|
||||||
|
case CountryCode.VN:
|
||||||
|
return "VNM";
|
||||||
|
|
||||||
|
case CountryCode.SB:
|
||||||
|
return "SLB";
|
||||||
|
|
||||||
|
case CountryCode.ET:
|
||||||
|
return "ETH";
|
||||||
|
|
||||||
|
case CountryCode.SO:
|
||||||
|
return "SOM";
|
||||||
|
|
||||||
|
case CountryCode.ZW:
|
||||||
|
return "ZWE";
|
||||||
|
|
||||||
|
case CountryCode.SA:
|
||||||
|
return "SAU";
|
||||||
|
|
||||||
|
case CountryCode.ES:
|
||||||
|
return "ESP";
|
||||||
|
|
||||||
|
case CountryCode.ER:
|
||||||
|
return "ERI";
|
||||||
|
|
||||||
|
case CountryCode.ME:
|
||||||
|
return "MNE";
|
||||||
|
|
||||||
|
case CountryCode.MD:
|
||||||
|
return "MDA";
|
||||||
|
|
||||||
|
case CountryCode.MG:
|
||||||
|
return "MDG";
|
||||||
|
|
||||||
|
case CountryCode.MF:
|
||||||
|
return "MAF";
|
||||||
|
|
||||||
|
case CountryCode.MA:
|
||||||
|
return "MAR";
|
||||||
|
|
||||||
|
case CountryCode.MC:
|
||||||
|
return "MCO";
|
||||||
|
|
||||||
|
case CountryCode.UZ:
|
||||||
|
return "UZB";
|
||||||
|
|
||||||
|
case CountryCode.MM:
|
||||||
|
return "MMR";
|
||||||
|
|
||||||
|
case CountryCode.ML:
|
||||||
|
return "MLI";
|
||||||
|
|
||||||
|
case CountryCode.MO:
|
||||||
|
return "MAC";
|
||||||
|
|
||||||
|
case CountryCode.MN:
|
||||||
|
return "MNG";
|
||||||
|
|
||||||
|
case CountryCode.MH:
|
||||||
|
return "MHL";
|
||||||
|
|
||||||
|
case CountryCode.MK:
|
||||||
|
return "MKD";
|
||||||
|
|
||||||
|
case CountryCode.MU:
|
||||||
|
return "MUS";
|
||||||
|
|
||||||
|
case CountryCode.MT:
|
||||||
|
return "MLT";
|
||||||
|
|
||||||
|
case CountryCode.MW:
|
||||||
|
return "MWI";
|
||||||
|
|
||||||
|
case CountryCode.MV:
|
||||||
|
return "MDV";
|
||||||
|
|
||||||
|
case CountryCode.MQ:
|
||||||
|
return "MTQ";
|
||||||
|
|
||||||
|
case CountryCode.MP:
|
||||||
|
return "MNP";
|
||||||
|
|
||||||
|
case CountryCode.MS:
|
||||||
|
return "MSR";
|
||||||
|
|
||||||
|
case CountryCode.MR:
|
||||||
|
return "MRT";
|
||||||
|
|
||||||
|
case CountryCode.IM:
|
||||||
|
return "IMN";
|
||||||
|
|
||||||
|
case CountryCode.UG:
|
||||||
|
return "UGA";
|
||||||
|
|
||||||
|
case CountryCode.TZ:
|
||||||
|
return "TZA";
|
||||||
|
|
||||||
|
case CountryCode.MY:
|
||||||
|
return "MYS";
|
||||||
|
|
||||||
|
case CountryCode.MX:
|
||||||
|
return "MEX";
|
||||||
|
|
||||||
|
case CountryCode.IL:
|
||||||
|
return "ISR";
|
||||||
|
|
||||||
|
case CountryCode.FR:
|
||||||
|
return "FRA";
|
||||||
|
|
||||||
|
case CountryCode.IO:
|
||||||
|
return "IOT";
|
||||||
|
|
||||||
|
case CountryCode.SH:
|
||||||
|
return "SHN";
|
||||||
|
|
||||||
|
case CountryCode.FI:
|
||||||
|
return "FIN";
|
||||||
|
|
||||||
|
case CountryCode.FJ:
|
||||||
|
return "FJI";
|
||||||
|
|
||||||
|
case CountryCode.FK:
|
||||||
|
return "FLK";
|
||||||
|
|
||||||
|
case CountryCode.FM:
|
||||||
|
return "FSM";
|
||||||
|
|
||||||
|
case CountryCode.FO:
|
||||||
|
return "FRO";
|
||||||
|
|
||||||
|
case CountryCode.NI:
|
||||||
|
return "NIC";
|
||||||
|
|
||||||
|
case CountryCode.NL:
|
||||||
|
return "NLD";
|
||||||
|
|
||||||
|
case CountryCode.NO:
|
||||||
|
return "NOR";
|
||||||
|
|
||||||
|
case CountryCode.NA:
|
||||||
|
return "NAM";
|
||||||
|
|
||||||
|
case CountryCode.VU:
|
||||||
|
return "VUT";
|
||||||
|
|
||||||
|
case CountryCode.NC:
|
||||||
|
return "NCL";
|
||||||
|
|
||||||
|
case CountryCode.NE:
|
||||||
|
return "NER";
|
||||||
|
|
||||||
|
case CountryCode.NF:
|
||||||
|
return "NFK";
|
||||||
|
|
||||||
|
case CountryCode.NG:
|
||||||
|
return "NGA";
|
||||||
|
|
||||||
|
case CountryCode.NZ:
|
||||||
|
return "NZL";
|
||||||
|
|
||||||
|
case CountryCode.NP:
|
||||||
|
return "NPL";
|
||||||
|
|
||||||
|
case CountryCode.NR:
|
||||||
|
return "NRU";
|
||||||
|
|
||||||
|
case CountryCode.NU:
|
||||||
|
return "NIU";
|
||||||
|
|
||||||
|
case CountryCode.CK:
|
||||||
|
return "COK";
|
||||||
|
|
||||||
|
case CountryCode.XK:
|
||||||
|
return "XKX";
|
||||||
|
|
||||||
|
case CountryCode.CI:
|
||||||
|
return "CIV";
|
||||||
|
|
||||||
|
case CountryCode.CH:
|
||||||
|
return "CHE";
|
||||||
|
|
||||||
|
case CountryCode.CO:
|
||||||
|
return "COL";
|
||||||
|
|
||||||
|
case CountryCode.CN:
|
||||||
|
return "CHN";
|
||||||
|
|
||||||
|
case CountryCode.CM:
|
||||||
|
return "CMR";
|
||||||
|
|
||||||
|
case CountryCode.CL:
|
||||||
|
return "CHL";
|
||||||
|
|
||||||
|
case CountryCode.CC:
|
||||||
|
return "CCK";
|
||||||
|
|
||||||
|
case CountryCode.CA:
|
||||||
|
return "CAN";
|
||||||
|
|
||||||
|
case CountryCode.CG:
|
||||||
|
return "COG";
|
||||||
|
|
||||||
|
case CountryCode.CF:
|
||||||
|
return "CAF";
|
||||||
|
|
||||||
|
case CountryCode.CD:
|
||||||
|
return "COD";
|
||||||
|
|
||||||
|
case CountryCode.CZ:
|
||||||
|
return "CZE";
|
||||||
|
|
||||||
|
case CountryCode.CY:
|
||||||
|
return "CYP";
|
||||||
|
|
||||||
|
case CountryCode.CX:
|
||||||
|
return "CXR";
|
||||||
|
|
||||||
|
case CountryCode.CR:
|
||||||
|
return "CRI";
|
||||||
|
|
||||||
|
case CountryCode.CW:
|
||||||
|
return "CUW";
|
||||||
|
|
||||||
|
case CountryCode.CV:
|
||||||
|
return "CPV";
|
||||||
|
|
||||||
|
case CountryCode.CU:
|
||||||
|
return "CUB";
|
||||||
|
|
||||||
|
case CountryCode.SZ:
|
||||||
|
return "SWZ";
|
||||||
|
|
||||||
|
case CountryCode.SY:
|
||||||
|
return "SYR";
|
||||||
|
|
||||||
|
case CountryCode.SX:
|
||||||
|
return "SXM";
|
||||||
|
|
||||||
|
case CountryCode.KG:
|
||||||
|
return "KGZ";
|
||||||
|
|
||||||
|
case CountryCode.KE:
|
||||||
|
return "KEN";
|
||||||
|
|
||||||
|
case CountryCode.SS:
|
||||||
|
return "SSD";
|
||||||
|
|
||||||
|
case CountryCode.SR:
|
||||||
|
return "SUR";
|
||||||
|
|
||||||
|
case CountryCode.KI:
|
||||||
|
return "KIR";
|
||||||
|
|
||||||
|
case CountryCode.KH:
|
||||||
|
return "KHM";
|
||||||
|
|
||||||
|
case CountryCode.KN:
|
||||||
|
return "KNA";
|
||||||
|
|
||||||
|
case CountryCode.KM:
|
||||||
|
return "COM";
|
||||||
|
|
||||||
|
case CountryCode.ST:
|
||||||
|
return "STP";
|
||||||
|
|
||||||
|
case CountryCode.SK:
|
||||||
|
return "SVK";
|
||||||
|
|
||||||
|
case CountryCode.KR:
|
||||||
|
return "KOR";
|
||||||
|
|
||||||
|
case CountryCode.SI:
|
||||||
|
return "SVN";
|
||||||
|
|
||||||
|
case CountryCode.KP:
|
||||||
|
return "PRK";
|
||||||
|
|
||||||
|
case CountryCode.KW:
|
||||||
|
return "KWT";
|
||||||
|
|
||||||
|
case CountryCode.SN:
|
||||||
|
return "SEN";
|
||||||
|
|
||||||
|
case CountryCode.SM:
|
||||||
|
return "SMR";
|
||||||
|
|
||||||
|
case CountryCode.SL:
|
||||||
|
return "SLE";
|
||||||
|
|
||||||
|
case CountryCode.SC:
|
||||||
|
return "SYC";
|
||||||
|
|
||||||
|
case CountryCode.KZ:
|
||||||
|
return "KAZ";
|
||||||
|
|
||||||
|
case CountryCode.KY:
|
||||||
|
return "CYM";
|
||||||
|
|
||||||
|
case CountryCode.SG:
|
||||||
|
return "SGP";
|
||||||
|
|
||||||
|
case CountryCode.SE:
|
||||||
|
return "SWE";
|
||||||
|
|
||||||
|
case CountryCode.SD:
|
||||||
|
return "SDN";
|
||||||
|
|
||||||
|
case CountryCode.DO:
|
||||||
|
return "DOM";
|
||||||
|
|
||||||
|
case CountryCode.DM:
|
||||||
|
return "DMA";
|
||||||
|
|
||||||
|
case CountryCode.DJ:
|
||||||
|
return "DJI";
|
||||||
|
|
||||||
|
case CountryCode.DK:
|
||||||
|
return "DNK";
|
||||||
|
|
||||||
|
case CountryCode.VG:
|
||||||
|
return "VGB";
|
||||||
|
|
||||||
|
case CountryCode.DE:
|
||||||
|
return "DEU";
|
||||||
|
|
||||||
|
case CountryCode.YE:
|
||||||
|
return "YEM";
|
||||||
|
|
||||||
|
case CountryCode.DZ:
|
||||||
|
return "DZA";
|
||||||
|
|
||||||
|
case CountryCode.US:
|
||||||
|
return "USA";
|
||||||
|
|
||||||
|
case CountryCode.UY:
|
||||||
|
return "URY";
|
||||||
|
|
||||||
|
case CountryCode.YT:
|
||||||
|
return "MYT";
|
||||||
|
|
||||||
|
case CountryCode.UM:
|
||||||
|
return "UMI";
|
||||||
|
|
||||||
|
case CountryCode.LB:
|
||||||
|
return "LBN";
|
||||||
|
|
||||||
|
case CountryCode.LC:
|
||||||
|
return "LCA";
|
||||||
|
|
||||||
|
case CountryCode.LA:
|
||||||
|
return "LAO";
|
||||||
|
|
||||||
|
case CountryCode.TV:
|
||||||
|
return "TUV";
|
||||||
|
|
||||||
|
case CountryCode.TW:
|
||||||
|
return "TWN";
|
||||||
|
|
||||||
|
case CountryCode.TT:
|
||||||
|
return "TTO";
|
||||||
|
|
||||||
|
case CountryCode.TR:
|
||||||
|
return "TUR";
|
||||||
|
|
||||||
|
case CountryCode.LK:
|
||||||
|
return "LKA";
|
||||||
|
|
||||||
|
case CountryCode.LI:
|
||||||
|
return "LIE";
|
||||||
|
|
||||||
|
case CountryCode.LV:
|
||||||
|
return "LVA";
|
||||||
|
|
||||||
|
case CountryCode.TO:
|
||||||
|
return "TON";
|
||||||
|
|
||||||
|
case CountryCode.LT:
|
||||||
|
return "LTU";
|
||||||
|
|
||||||
|
case CountryCode.LU:
|
||||||
|
return "LUX";
|
||||||
|
|
||||||
|
case CountryCode.LR:
|
||||||
|
return "LBR";
|
||||||
|
|
||||||
|
case CountryCode.LS:
|
||||||
|
return "LSO";
|
||||||
|
|
||||||
|
case CountryCode.TH:
|
||||||
|
return "THA";
|
||||||
|
|
||||||
|
case CountryCode.TF:
|
||||||
|
return "ATF";
|
||||||
|
|
||||||
|
case CountryCode.TG:
|
||||||
|
return "TGO";
|
||||||
|
|
||||||
|
case CountryCode.TD:
|
||||||
|
return "TCD";
|
||||||
|
|
||||||
|
case CountryCode.TC:
|
||||||
|
return "TCA";
|
||||||
|
|
||||||
|
case CountryCode.LY:
|
||||||
|
return "LBY";
|
||||||
|
|
||||||
|
case CountryCode.VA:
|
||||||
|
return "VAT";
|
||||||
|
|
||||||
|
case CountryCode.VC:
|
||||||
|
return "VCT";
|
||||||
|
|
||||||
|
case CountryCode.AE:
|
||||||
|
return "ARE";
|
||||||
|
|
||||||
|
case CountryCode.AD:
|
||||||
|
return "AND";
|
||||||
|
|
||||||
|
case CountryCode.AG:
|
||||||
|
return "ATG";
|
||||||
|
|
||||||
|
case CountryCode.AF:
|
||||||
|
return "AFG";
|
||||||
|
|
||||||
|
case CountryCode.AI:
|
||||||
|
return "AIA";
|
||||||
|
|
||||||
|
case CountryCode.VI:
|
||||||
|
return "VIR";
|
||||||
|
|
||||||
|
case CountryCode.IS:
|
||||||
|
return "ISL";
|
||||||
|
|
||||||
|
case CountryCode.IR:
|
||||||
|
return "IRN";
|
||||||
|
|
||||||
|
case CountryCode.AM:
|
||||||
|
return "ARM";
|
||||||
|
|
||||||
|
case CountryCode.AL:
|
||||||
|
return "ALB";
|
||||||
|
|
||||||
|
case CountryCode.AO:
|
||||||
|
return "AGO";
|
||||||
|
|
||||||
|
case CountryCode.AQ:
|
||||||
|
return "ATA";
|
||||||
|
|
||||||
|
case CountryCode.AS:
|
||||||
|
return "ASM";
|
||||||
|
|
||||||
|
case CountryCode.AR:
|
||||||
|
return "ARG";
|
||||||
|
|
||||||
|
case CountryCode.AU:
|
||||||
|
return "AUS";
|
||||||
|
|
||||||
|
case CountryCode.AT:
|
||||||
|
return "AUT";
|
||||||
|
|
||||||
|
case CountryCode.AW:
|
||||||
|
return "ABW";
|
||||||
|
|
||||||
|
case CountryCode.IN:
|
||||||
|
return "IND";
|
||||||
|
|
||||||
|
case CountryCode.AX:
|
||||||
|
return "ALA";
|
||||||
|
|
||||||
|
case CountryCode.AZ:
|
||||||
|
return "AZE";
|
||||||
|
|
||||||
|
case CountryCode.IE:
|
||||||
|
return "IRL";
|
||||||
|
|
||||||
|
case CountryCode.ID:
|
||||||
|
return "IDN";
|
||||||
|
|
||||||
|
case CountryCode.UA:
|
||||||
|
return "UKR";
|
||||||
|
|
||||||
|
case CountryCode.QA:
|
||||||
|
return "QAT";
|
||||||
|
|
||||||
|
case CountryCode.MZ:
|
||||||
|
return "MOZ";
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(country));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,8 @@ namespace osu.Game.Tournament.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The player's country.
|
/// The player's country.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Country? Country { get; set; }
|
[JsonProperty("country_code")]
|
||||||
|
public CountryCode CountryCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The player's global rank, or null if not available.
|
/// The player's global rank, or null if not available.
|
||||||
@ -40,7 +41,7 @@ namespace osu.Game.Tournament.Models
|
|||||||
{
|
{
|
||||||
Id = OnlineID,
|
Id = OnlineID,
|
||||||
Username = Username,
|
Username = Username,
|
||||||
Country = Country,
|
CountryCode = CountryCode,
|
||||||
CoverUrl = CoverUrl,
|
CoverUrl = CoverUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -25,9 +25,6 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
{
|
{
|
||||||
public class TeamEditorScreen : TournamentEditorScreen<TeamEditorScreen.TeamRow, TournamentTeam>
|
public class TeamEditorScreen : TournamentEditorScreen<TeamEditorScreen.TeamRow, TournamentTeam>
|
||||||
{
|
{
|
||||||
[Resolved]
|
|
||||||
private TournamentGameBase game { get; set; }
|
|
||||||
|
|
||||||
protected override BindableList<TournamentTeam> Storage => LadderInfo.Teams;
|
protected override BindableList<TournamentTeam> Storage => LadderInfo.Teams;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -45,11 +42,17 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
|
|
||||||
private void addAllCountries()
|
private void addAllCountries()
|
||||||
{
|
{
|
||||||
List<TournamentTeam> countries;
|
var countries = new List<TournamentTeam>();
|
||||||
|
|
||||||
using (Stream stream = game.Resources.GetStream("Resources/countries.json"))
|
foreach (var country in Enum.GetValues(typeof(CountryCode)).Cast<CountryCode>().Skip(1))
|
||||||
using (var sr = new StreamReader(stream))
|
{
|
||||||
countries = JsonConvert.DeserializeObject<List<TournamentTeam>>(sr.ReadToEnd());
|
countries.Add(new TournamentTeam
|
||||||
|
{
|
||||||
|
FlagName = { Value = country.ToString() },
|
||||||
|
FullName = { Value = country.GetDescription() },
|
||||||
|
Acronym = { Value = country.GetAcronym() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Debug.Assert(countries != null);
|
Debug.Assert(countries != null);
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
using osu.Game.Tournament.IO;
|
using osu.Game.Tournament.IO;
|
||||||
using osu.Game.Tournament.IPC;
|
using osu.Game.Tournament.IPC;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
|
using osu.Game.Users;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tournament
|
namespace osu.Game.Tournament
|
||||||
@ -186,7 +187,9 @@ namespace osu.Game.Tournament
|
|||||||
{
|
{
|
||||||
var playersRequiringPopulation = ladder.Teams
|
var playersRequiringPopulation = ladder.Teams
|
||||||
.SelectMany(t => t.Players)
|
.SelectMany(t => t.Players)
|
||||||
.Where(p => string.IsNullOrEmpty(p.Username) || p.Rank == null).ToList();
|
.Where(p => string.IsNullOrEmpty(p.Username)
|
||||||
|
|| p.CountryCode == CountryCode.Unknown
|
||||||
|
|| p.Rank == null).ToList();
|
||||||
|
|
||||||
if (playersRequiringPopulation.Count == 0)
|
if (playersRequiringPopulation.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
@ -288,7 +291,7 @@ namespace osu.Game.Tournament
|
|||||||
|
|
||||||
user.Username = res.Username;
|
user.Username = res.Username;
|
||||||
user.CoverUrl = res.CoverUrl;
|
user.CoverUrl = res.CoverUrl;
|
||||||
user.Country = res.Country;
|
user.CountryCode = res.CountryCode;
|
||||||
user.Rank = res.Statistics?.GlobalRank;
|
user.Rank = res.Statistics?.GlobalRank;
|
||||||
|
|
||||||
success?.Invoke();
|
success?.Invoke();
|
||||||
|
@ -14,15 +14,15 @@ namespace osu.Game.Audio
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class HitSampleInfo : ISampleInfo, IEquatable<HitSampleInfo>
|
public class HitSampleInfo : ISampleInfo, IEquatable<HitSampleInfo>
|
||||||
{
|
{
|
||||||
|
public const string HIT_NORMAL = @"hitnormal";
|
||||||
public const string HIT_WHISTLE = @"hitwhistle";
|
public const string HIT_WHISTLE = @"hitwhistle";
|
||||||
public const string HIT_FINISH = @"hitfinish";
|
public const string HIT_FINISH = @"hitfinish";
|
||||||
public const string HIT_NORMAL = @"hitnormal";
|
|
||||||
public const string HIT_CLAP = @"hitclap";
|
public const string HIT_CLAP = @"hitclap";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All valid sample addition constants.
|
/// All valid sample addition constants.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_CLAP, HIT_FINISH };
|
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_FINISH, HIT_CLAP };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the sample to load.
|
/// The name of the sample to load.
|
||||||
|
@ -92,6 +92,16 @@ namespace osu.Game.Beatmaps
|
|||||||
[Indexed]
|
[Indexed]
|
||||||
public string MD5Hash { get; set; } = string.Empty;
|
public string MD5Hash { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string OnlineMD5Hash { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTimeOffset? LastOnlineUpdate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this beatmap matches the online version, based on fetched online metadata.
|
||||||
|
/// Will return <c>true</c> if no online metadata is available.
|
||||||
|
/// </summary>
|
||||||
|
public bool MatchesOnlineVersion => LastOnlineUpdate == null || MD5Hash == OnlineMD5Hash;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool Hidden { get; set; }
|
public bool Hidden { get; set; }
|
||||||
|
|
||||||
@ -169,8 +179,8 @@ namespace osu.Game.Beatmaps
|
|||||||
Debug.Assert(x.BeatmapSet != null);
|
Debug.Assert(x.BeatmapSet != null);
|
||||||
Debug.Assert(y.BeatmapSet != null);
|
Debug.Assert(y.BeatmapSet != null);
|
||||||
|
|
||||||
string? fileHashX = x.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(x.BeatmapSet.Metadata))?.File.Hash;
|
string? fileHashX = x.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(x.Metadata))?.File.Hash;
|
||||||
string? fileHashY = y.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(y.BeatmapSet.Metadata))?.File.Hash;
|
string? fileHashY = y.BeatmapSet.Files.FirstOrDefault(f => f.Filename == getFilename(y.Metadata))?.File.Hash;
|
||||||
|
|
||||||
return fileHashX == fileHashY;
|
return fileHashX == fileHashY;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
@ -14,7 +12,7 @@ namespace osu.Game.Beatmaps
|
|||||||
protected override ArchiveDownloadRequest<IBeatmapSetInfo> CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) =>
|
protected override ArchiveDownloadRequest<IBeatmapSetInfo> CreateDownloadRequest(IBeatmapSetInfo set, bool minimiseDownloadSize) =>
|
||||||
new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
|
new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
|
||||||
|
|
||||||
public override ArchiveDownloadRequest<IBeatmapSetInfo> GetExistingDownload(IBeatmapSetInfo model)
|
public override ArchiveDownloadRequest<IBeatmapSetInfo>? GetExistingDownload(IBeatmapSetInfo model)
|
||||||
=> CurrentDownloads.Find(r => r.Model.OnlineID == model.OnlineID);
|
=> CurrentDownloads.Find(r => r.Model.OnlineID == model.OnlineID);
|
||||||
|
|
||||||
public BeatmapModelDownloader(IModelImporter<BeatmapSetInfo> beatmapImporter, IAPIProvider api)
|
public BeatmapModelDownloader(IModelImporter<BeatmapSetInfo> beatmapImporter, IAPIProvider api)
|
||||||
|
50
osu.Game/Beatmaps/BeatmapOnlineChangeIngest.cs
Normal file
50
osu.Game/Beatmaps/BeatmapOnlineChangeIngest.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Online.Metadata;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ingests any changes that happen externally to the client, reprocessing as required.
|
||||||
|
/// </summary>
|
||||||
|
public class BeatmapOnlineChangeIngest : Component
|
||||||
|
{
|
||||||
|
private readonly BeatmapUpdater beatmapUpdater;
|
||||||
|
private readonly RealmAccess realm;
|
||||||
|
private readonly MetadataClient metadataClient;
|
||||||
|
|
||||||
|
public BeatmapOnlineChangeIngest(BeatmapUpdater beatmapUpdater, RealmAccess realm, MetadataClient metadataClient)
|
||||||
|
{
|
||||||
|
this.beatmapUpdater = beatmapUpdater;
|
||||||
|
this.realm = realm;
|
||||||
|
this.metadataClient = metadataClient;
|
||||||
|
|
||||||
|
metadataClient.ChangedBeatmapSetsArrived += changesDetected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changesDetected(int[] beatmapSetIds)
|
||||||
|
{
|
||||||
|
// May want to batch incoming updates further if the background realm operations ever becomes a concern.
|
||||||
|
realm.Run(r =>
|
||||||
|
{
|
||||||
|
foreach (int id in beatmapSetIds)
|
||||||
|
{
|
||||||
|
var matchingSet = r.All<BeatmapSetInfo>().FirstOrDefault(s => s.OnlineID == id);
|
||||||
|
|
||||||
|
if (matchingSet != null)
|
||||||
|
beatmapUpdater.Queue(matchingSet.ToLive(realm));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
metadataClient.ChangedBeatmapSetsArrived -= changesDetected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -102,6 +102,12 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapOnlineStatus.None;
|
beatmapInfo.BeatmapSet.Status = res.BeatmapSet?.Status ?? BeatmapOnlineStatus.None;
|
||||||
beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID;
|
beatmapInfo.BeatmapSet.OnlineID = res.OnlineBeatmapSetID;
|
||||||
|
beatmapInfo.BeatmapSet.DateRanked = res.BeatmapSet?.Ranked;
|
||||||
|
beatmapInfo.BeatmapSet.DateSubmitted = res.BeatmapSet?.Submitted;
|
||||||
|
|
||||||
|
beatmapInfo.OnlineMD5Hash = res.MD5Hash;
|
||||||
|
beatmapInfo.LastOnlineUpdate = res.LastUpdated;
|
||||||
|
|
||||||
beatmapInfo.OnlineID = res.OnlineID;
|
beatmapInfo.OnlineID = res.OnlineID;
|
||||||
|
|
||||||
beatmapInfo.Metadata.Author.OnlineID = res.AuthorID;
|
beatmapInfo.Metadata.Author.OnlineID = res.AuthorID;
|
||||||
@ -190,7 +196,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
using (var cmd = db.CreateCommand())
|
using (var cmd = db.CreateCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = "SELECT beatmapset_id, beatmap_id, approved, user_id FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineID OR filename = @Path";
|
cmd.CommandText =
|
||||||
|
"SELECT beatmapset_id, beatmap_id, approved, user_id, checksum, last_update FROM osu_beatmaps WHERE checksum = @MD5Hash OR beatmap_id = @OnlineID OR filename = @Path";
|
||||||
|
|
||||||
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmapInfo.MD5Hash));
|
cmd.Parameters.Add(new SqliteParameter("@MD5Hash", beatmapInfo.MD5Hash));
|
||||||
cmd.Parameters.Add(new SqliteParameter("@OnlineID", beatmapInfo.OnlineID));
|
cmd.Parameters.Add(new SqliteParameter("@OnlineID", beatmapInfo.OnlineID));
|
||||||
@ -208,10 +215,13 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
beatmapInfo.BeatmapSet.Status = status;
|
beatmapInfo.BeatmapSet.Status = status;
|
||||||
beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0);
|
beatmapInfo.BeatmapSet.OnlineID = reader.GetInt32(0);
|
||||||
|
// TODO: DateSubmitted and DateRanked are not provided by local cache.
|
||||||
beatmapInfo.OnlineID = reader.GetInt32(1);
|
beatmapInfo.OnlineID = reader.GetInt32(1);
|
||||||
|
|
||||||
beatmapInfo.Metadata.Author.OnlineID = reader.GetInt32(3);
|
beatmapInfo.Metadata.Author.OnlineID = reader.GetInt32(3);
|
||||||
|
|
||||||
|
beatmapInfo.OnlineMD5Hash = reader.GetString(4);
|
||||||
|
beatmapInfo.LastOnlineUpdate = reader.GetDateTimeOffset(5);
|
||||||
|
|
||||||
logForModel(set, $"Cached local retrieval for {beatmapInfo}.");
|
logForModel(set, $"Cached local retrieval for {beatmapInfo}.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,16 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public DateTimeOffset DateAdded { get; set; }
|
public DateTimeOffset DateAdded { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The date this beatmap set was first submitted.
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? DateSubmitted { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The date this beatmap set was ranked.
|
||||||
|
/// </summary>
|
||||||
|
public DateTimeOffset? DateRanked { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IBeatmapMetadataInfo Metadata => Beatmaps.FirstOrDefault()?.Metadata ?? new BeatmapMetadata();
|
public IBeatmapMetadataInfo Metadata => Beatmaps.FirstOrDefault()?.Metadata ?? new BeatmapMetadata();
|
||||||
|
|
||||||
@ -93,5 +103,7 @@ namespace osu.Game.Beatmaps
|
|||||||
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
|
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
|
||||||
|
|
||||||
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
||||||
|
|
||||||
|
public bool AllBeatmapsUpToDate => Beatmaps.All(b => b.MatchesOnlineVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using Humanizer;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TextureStore textures)
|
private void load(TextureStore textures)
|
||||||
{
|
{
|
||||||
Texture = textures.Get($"Icons/BeatmapDetails/{iconType.ToString().Kebaberize()}");
|
Texture = textures.Get($"Icons/BeatmapDetails/{iconType.ToString().ToKebabCase()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -30,21 +31,12 @@ namespace osu.Game.Beatmaps
|
|||||||
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
|
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queue a beatmap for background processing.
|
|
||||||
/// </summary>
|
|
||||||
public void Queue(int beatmapSetId)
|
|
||||||
{
|
|
||||||
// TODO: implement
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue a beatmap for background processing.
|
/// Queue a beatmap for background processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Queue(Live<BeatmapSetInfo> beatmap)
|
public void Queue(Live<BeatmapSetInfo> beatmap)
|
||||||
{
|
{
|
||||||
// For now, just fire off a task.
|
Logger.Log($"Queueing change for local beatmap {beatmap}");
|
||||||
// TODO: Add actual queueing probably.
|
|
||||||
Task.Factory.StartNew(() => beatmap.PerformRead(Process));
|
Task.Factory.StartNew(() => beatmap.PerformRead(Process));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +48,8 @@ namespace osu.Game.Beatmaps
|
|||||||
// Before we use below, we want to invalidate.
|
// Before we use below, we want to invalidate.
|
||||||
workingBeatmapCache.Invalidate(beatmapSet);
|
workingBeatmapCache.Invalidate(beatmapSet);
|
||||||
|
|
||||||
|
// TODO: this call currently uses the local `online.db` lookup.
|
||||||
|
// We probably don't want this to happen after initial import (as the data may be stale).
|
||||||
onlineLookupQueue.Update(beatmapSet);
|
onlineLookupQueue.Update(beatmapSet);
|
||||||
|
|
||||||
foreach (var beatmap in beatmapSet.Beatmaps)
|
foreach (var beatmap in beatmapSet.Beatmaps)
|
||||||
|
@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
Logger.Log($"Beatmap background failed to load (file {Metadata.BackgroundFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
Logger.Log($"Beatmap background failed to load (file {Metadata.BackgroundFile} not found on disk at expected location {fileStorePath}).");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +224,12 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
return new TrackedSettings
|
return new TrackedSettings
|
||||||
{
|
{
|
||||||
|
new TrackedSetting<bool>(OsuSetting.ShowFpsDisplay, state => new SettingDescription(
|
||||||
|
rawValue: state,
|
||||||
|
name: GlobalActionKeyBindingStrings.ToggleFPSCounter,
|
||||||
|
value: state ? CommonStrings.Enabled.ToLower() : CommonStrings.Disabled.ToLower(),
|
||||||
|
shortcut: LookupKeyBindings(GlobalAction.ToggleFPSDisplay))
|
||||||
|
),
|
||||||
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, disabledState => new SettingDescription(
|
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, disabledState => new SettingDescription(
|
||||||
rawValue: !disabledState,
|
rawValue: !disabledState,
|
||||||
name: GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons,
|
name: GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons,
|
||||||
|
@ -443,7 +443,6 @@ namespace osu.Game.Database
|
|||||||
TotalScore = score.TotalScore,
|
TotalScore = score.TotalScore,
|
||||||
MaxCombo = score.MaxCombo,
|
MaxCombo = score.MaxCombo,
|
||||||
Accuracy = score.Accuracy,
|
Accuracy = score.Accuracy,
|
||||||
HasReplay = ((IScoreInfo)score).HasReplay,
|
|
||||||
Date = score.Date,
|
Date = score.Date,
|
||||||
PP = score.PP,
|
PP = score.PP,
|
||||||
Rank = score.Rank,
|
Rank = score.Rank,
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -19,18 +17,18 @@ namespace osu.Game.Database
|
|||||||
where TModel : class, IHasGuidPrimaryKey, ISoftDelete, IEquatable<TModel>, T
|
where TModel : class, IHasGuidPrimaryKey, ISoftDelete, IEquatable<TModel>, T
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
public Action<Notification> PostNotification { protected get; set; }
|
public Action<Notification>? PostNotification { protected get; set; }
|
||||||
|
|
||||||
public event Action<ArchiveDownloadRequest<T>> DownloadBegan;
|
public event Action<ArchiveDownloadRequest<T>>? DownloadBegan;
|
||||||
|
|
||||||
public event Action<ArchiveDownloadRequest<T>> DownloadFailed;
|
public event Action<ArchiveDownloadRequest<T>>? DownloadFailed;
|
||||||
|
|
||||||
private readonly IModelImporter<TModel> importer;
|
private readonly IModelImporter<TModel> importer;
|
||||||
private readonly IAPIProvider api;
|
private readonly IAPIProvider? api;
|
||||||
|
|
||||||
protected readonly List<ArchiveDownloadRequest<T>> CurrentDownloads = new List<ArchiveDownloadRequest<T>>();
|
protected readonly List<ArchiveDownloadRequest<T>> CurrentDownloads = new List<ArchiveDownloadRequest<T>>();
|
||||||
|
|
||||||
protected ModelDownloader(IModelImporter<TModel> importer, IAPIProvider api)
|
protected ModelDownloader(IModelImporter<TModel> importer, IAPIProvider? api)
|
||||||
{
|
{
|
||||||
this.importer = importer;
|
this.importer = importer;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
@ -87,7 +85,7 @@ namespace osu.Game.Database
|
|||||||
CurrentDownloads.Add(request);
|
CurrentDownloads.Add(request);
|
||||||
PostNotification?.Invoke(notification);
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
api.PerformAsync(request);
|
api?.PerformAsync(request);
|
||||||
|
|
||||||
DownloadBegan?.Invoke(request);
|
DownloadBegan?.Invoke(request);
|
||||||
return true;
|
return true;
|
||||||
@ -105,7 +103,7 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ArchiveDownloadRequest<T> GetExistingDownload(T model);
|
public abstract ArchiveDownloadRequest<T>? GetExistingDownload(T model);
|
||||||
|
|
||||||
private bool canDownload(T model) => GetExistingDownload(model) == null && api != null;
|
private bool canDownload(T model) => GetExistingDownload(model) == null && api != null;
|
||||||
|
|
||||||
|
@ -59,8 +59,12 @@ namespace osu.Game.Database
|
|||||||
/// 13 2022-01-13 Final migration of beatmaps and scores to realm (multiple new storage fields).
|
/// 13 2022-01-13 Final migration of beatmaps and scores to realm (multiple new storage fields).
|
||||||
/// 14 2022-03-01 Added BeatmapUserSettings to BeatmapInfo.
|
/// 14 2022-03-01 Added BeatmapUserSettings to BeatmapInfo.
|
||||||
/// 15 2022-07-13 Added LastPlayed to BeatmapInfo.
|
/// 15 2022-07-13 Added LastPlayed to BeatmapInfo.
|
||||||
|
/// 16 2022-07-15 Removed HasReplay from ScoreInfo.
|
||||||
|
/// 17 2022-07-16 Added CountryCode to RealmUser.
|
||||||
|
/// 18 2022-07-19 Added OnlineMD5Hash and LastOnlineUpdate to BeatmapInfo.
|
||||||
|
/// 19 2022-07-19 Added DateSubmitted and DateRanked to BeatmapSetInfo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int schema_version = 15;
|
private const int schema_version = 19;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using Humanizer;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -67,7 +66,7 @@ namespace osu.Game.Extensions
|
|||||||
|
|
||||||
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
||||||
{
|
{
|
||||||
if (!info.Settings.TryGetValue(property.Name.Underscore(), out object settingValue))
|
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object settingValue))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
skinnable.CopyAdjustedSetting((IBindable)property.GetValue(component), settingValue);
|
skinnable.CopyAdjustedSetting((IBindable)property.GetValue(component), settingValue);
|
||||||
|
94
osu.Game/Extensions/StringDehumanizeExtensions.cs
Normal file
94
osu.Game/Extensions/StringDehumanizeExtensions.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Based on code from the Humanizer library (https://github.com/Humanizr/Humanizer/blob/606e958cb83afc9be5b36716ac40d4daa9fa73a7/src/Humanizer/InflectorExtensions.cs)
|
||||||
|
//
|
||||||
|
// Humanizer is licenced under the MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) .NET Foundation and Contributors
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace osu.Game.Extensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class with extension methods used to turn human-readable strings to casing conventions frequently used in code.
|
||||||
|
/// Often used for communicating with other systems (web API, spectator server).
|
||||||
|
/// All of the operations in this class are intentionally culture-invariant.
|
||||||
|
/// </summary>
|
||||||
|
public static class StringDehumanizeExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the string to "Pascal case" (also known as "upper camel case").
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// "this is a test string".ToPascalCase() == "ThisIsATestString"
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
public static string ToPascalCase(this string input)
|
||||||
|
{
|
||||||
|
return Regex.Replace(input, "(?:^|_|-| +)(.)", match => match.Groups[1].Value.ToUpperInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the string to (lower) "camel case".
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// "this is a test string".ToCamelCase() == "thisIsATestString"
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
public static string ToCamelCase(this string input)
|
||||||
|
{
|
||||||
|
string word = input.ToPascalCase();
|
||||||
|
return word.Length > 0 ? word.Substring(0, 1).ToLowerInvariant() + word.Substring(1) : word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the string to "snake case".
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// "this is a test string".ToSnakeCase() == "this_is_a_test_string"
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
public static string ToSnakeCase(this string input)
|
||||||
|
{
|
||||||
|
return Regex.Replace(
|
||||||
|
Regex.Replace(
|
||||||
|
Regex.Replace(input, @"([\p{Lu}]+)([\p{Lu}][\p{Ll}])", "$1_$2"), @"([\p{Ll}\d])([\p{Lu}])", "$1_$2"), @"[-\s]", "_").ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the string to "kebab case".
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// "this is a test string".ToKebabCase() == "this-is-a-test-string"
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
public static string ToKebabCase(this string input)
|
||||||
|
{
|
||||||
|
return ToSnakeCase(input).Replace('_', '-');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
public class BeatSyncedContainer : Container
|
public class BeatSyncedContainer : Container
|
||||||
{
|
{
|
||||||
private int lastBeat;
|
private int lastBeat;
|
||||||
private TimingControlPoint lastTimingPoint;
|
protected TimingControlPoint LastTimingPoint { get; private set; }
|
||||||
|
protected EffectControlPoint LastEffectPoint { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of time before a beat we should fire <see cref="OnNewBeat(int, TimingControlPoint, EffectControlPoint, ChannelAmplitudes)"/>.
|
/// The amount of time before a beat we should fire <see cref="OnNewBeat(int, TimingControlPoint, EffectControlPoint, ChannelAmplitudes)"/>.
|
||||||
@ -127,7 +128,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
TimeSinceLastBeat = beatLength - TimeUntilNextBeat;
|
TimeSinceLastBeat = beatLength - TimeUntilNextBeat;
|
||||||
|
|
||||||
if (ReferenceEquals(timingPoint, lastTimingPoint) && beatIndex == lastBeat)
|
if (ReferenceEquals(timingPoint, LastTimingPoint) && beatIndex == lastBeat)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat.
|
// as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat.
|
||||||
@ -139,7 +140,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastBeat = beatIndex;
|
lastBeat = beatIndex;
|
||||||
lastTimingPoint = timingPoint;
|
LastTimingPoint = timingPoint;
|
||||||
|
LastEffectPoint = effectPoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,21 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using osuTK;
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using System;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osu.Framework.Audio;
|
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
{
|
{
|
||||||
@ -35,6 +35,7 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
private Vector2 positionMouseDown;
|
private Vector2 positionMouseDown;
|
||||||
|
|
||||||
private Sample tapSample;
|
private Sample tapSample;
|
||||||
|
private Vector2 lastMovePosition;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager, AudioManager audio)
|
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager, AudioManager audio)
|
||||||
@ -47,16 +48,25 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
tapSample = audio.Samples.Get(@"UI/cursor-tap");
|
tapSample = audio.Samples.Get(@"UI/cursor-tap");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (dragRotationState != DragRotationState.NotDragging
|
||||||
|
&& Vector2.Distance(positionMouseDown, lastMovePosition) > 60)
|
||||||
|
{
|
||||||
|
// make the rotation centre point floating.
|
||||||
|
positionMouseDown = Interpolation.ValueAt(0.04f, positionMouseDown, lastMovePosition, 0, Clock.ElapsedFrameTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
if (dragRotationState != DragRotationState.NotDragging)
|
if (dragRotationState != DragRotationState.NotDragging)
|
||||||
{
|
{
|
||||||
// make the rotation centre point floating.
|
lastMovePosition = e.MousePosition;
|
||||||
if (Vector2.Distance(positionMouseDown, e.MousePosition) > 60)
|
|
||||||
positionMouseDown = Interpolation.ValueAt(0.005f, positionMouseDown, e.MousePosition, 0, Clock.ElapsedFrameTime);
|
|
||||||
|
|
||||||
var position = e.MousePosition;
|
float distance = Vector2Extensions.Distance(lastMovePosition, positionMouseDown);
|
||||||
float distance = Vector2Extensions.Distance(position, positionMouseDown);
|
|
||||||
|
|
||||||
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
||||||
// else it can have an annoying effect.
|
// else it can have an annoying effect.
|
||||||
|
282
osu.Game/Graphics/UserInterface/FPSCounter.cs
Normal file
282
osu.Game/Graphics/UserInterface/FPSCounter.cs
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public class FPSCounter : VisibilityContainer, IHasCustomTooltip
|
||||||
|
{
|
||||||
|
private OsuSpriteText counterUpdateFrameTime = null!;
|
||||||
|
private OsuSpriteText counterDrawFPS = null!;
|
||||||
|
|
||||||
|
private Container mainContent = null!;
|
||||||
|
|
||||||
|
private Container background = null!;
|
||||||
|
|
||||||
|
private Container counters = null!;
|
||||||
|
|
||||||
|
private const double min_time_between_updates = 10;
|
||||||
|
|
||||||
|
private const double spike_time_ms = 20;
|
||||||
|
|
||||||
|
private const float idle_background_alpha = 0.4f;
|
||||||
|
|
||||||
|
private readonly BindableBool showFpsDisplay = new BindableBool(true);
|
||||||
|
|
||||||
|
private double displayedFpsCount;
|
||||||
|
private double displayedFrameTime;
|
||||||
|
|
||||||
|
private bool isDisplayed;
|
||||||
|
|
||||||
|
private ScheduledDelegate? fadeOutDelegate;
|
||||||
|
|
||||||
|
private double aimDrawFPS;
|
||||||
|
private double aimUpdateFPS;
|
||||||
|
|
||||||
|
private double lastUpdate;
|
||||||
|
private ThrottledFrameClock drawClock = null!;
|
||||||
|
private ThrottledFrameClock updateClock = null!;
|
||||||
|
private ThrottledFrameClock inputClock = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
public FPSCounter()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config, GameHost gameHost)
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
mainContent = new Container
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
Height = 26,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CornerRadius = 5,
|
||||||
|
CornerExponent = 5f,
|
||||||
|
Masking = true,
|
||||||
|
Alpha = idle_background_alpha,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.Gray0,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
counters = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
counterUpdateFrameTime = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Margin = new MarginPadding(1),
|
||||||
|
Font = OsuFont.Default.With(fixedWidth: true, size: 16, weight: FontWeight.SemiBold),
|
||||||
|
Spacing = new Vector2(-1),
|
||||||
|
Y = -2,
|
||||||
|
},
|
||||||
|
counterDrawFPS = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Margin = new MarginPadding(2),
|
||||||
|
Font = OsuFont.Default.With(fixedWidth: true, size: 13, weight: FontWeight.SemiBold),
|
||||||
|
Spacing = new Vector2(-2),
|
||||||
|
Y = 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
config.BindWith(OsuSetting.ShowFpsDisplay, showFpsDisplay);
|
||||||
|
|
||||||
|
drawClock = gameHost.DrawThread.Clock;
|
||||||
|
updateClock = gameHost.UpdateThread.Clock;
|
||||||
|
inputClock = gameHost.InputThread.Clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
displayTemporarily();
|
||||||
|
|
||||||
|
showFpsDisplay.BindValueChanged(showFps =>
|
||||||
|
{
|
||||||
|
State.Value = showFps.NewValue ? Visibility.Visible : Visibility.Hidden;
|
||||||
|
if (showFps.NewValue)
|
||||||
|
displayTemporarily();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
State.BindValueChanged(state => showFpsDisplay.Value = state.NewValue == Visibility.Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(100);
|
||||||
|
|
||||||
|
protected override void PopOut() => this.FadeOut(100);
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
background.FadeTo(1, 200);
|
||||||
|
displayTemporarily();
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
background.FadeTo(idle_background_alpha, 200);
|
||||||
|
displayTemporarily();
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayTemporarily()
|
||||||
|
{
|
||||||
|
if (!isDisplayed)
|
||||||
|
{
|
||||||
|
mainContent.FadeTo(1, 300, Easing.OutQuint);
|
||||||
|
isDisplayed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fadeOutDelegate?.Cancel();
|
||||||
|
fadeOutDelegate = null;
|
||||||
|
|
||||||
|
if (!IsHovered)
|
||||||
|
{
|
||||||
|
fadeOutDelegate = Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
|
mainContent.FadeTo(0, 300, Easing.OutQuint);
|
||||||
|
isDisplayed = false;
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
mainContent.Width = Math.Max(mainContent.Width, counters.DrawWidth);
|
||||||
|
|
||||||
|
// Handle the case where the window has become inactive or the user changed the
|
||||||
|
// frame limiter (we want to show the FPS as it's changing, even if it isn't an outlier).
|
||||||
|
bool aimRatesChanged = updateAimFPS();
|
||||||
|
|
||||||
|
bool hasUpdateSpike = displayedFrameTime < spike_time_ms && updateClock.ElapsedFrameTime > spike_time_ms;
|
||||||
|
// use elapsed frame time rather then FramesPerSecond to better catch stutter frames.
|
||||||
|
bool hasDrawSpike = displayedFpsCount > (1000 / spike_time_ms) && drawClock.ElapsedFrameTime > spike_time_ms;
|
||||||
|
|
||||||
|
// note that we use an elapsed time here of 1 intentionally.
|
||||||
|
// this weights all updates equally. if we passed in the elapsed time, longer frames would be weighted incorrectly lower.
|
||||||
|
displayedFrameTime = Interpolation.DampContinuously(displayedFrameTime, updateClock.ElapsedFrameTime, hasUpdateSpike ? 0 : 100, 1);
|
||||||
|
|
||||||
|
if (hasDrawSpike)
|
||||||
|
// show spike time using raw elapsed value, to account for `FramesPerSecond` being so averaged spike frames don't show.
|
||||||
|
displayedFpsCount = 1000 / drawClock.ElapsedFrameTime;
|
||||||
|
else
|
||||||
|
displayedFpsCount = Interpolation.DampContinuously(displayedFpsCount, drawClock.FramesPerSecond, 100, Time.Elapsed);
|
||||||
|
|
||||||
|
if (Time.Current - lastUpdate > min_time_between_updates)
|
||||||
|
{
|
||||||
|
updateFpsDisplay();
|
||||||
|
updateFrameTimeDisplay();
|
||||||
|
|
||||||
|
lastUpdate = Time.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasSignificantChanges = aimRatesChanged
|
||||||
|
|| hasDrawSpike
|
||||||
|
|| hasUpdateSpike
|
||||||
|
|| displayedFpsCount < aimDrawFPS * 0.8
|
||||||
|
|| 1000 / displayedFrameTime < aimUpdateFPS * 0.8;
|
||||||
|
|
||||||
|
if (hasSignificantChanges)
|
||||||
|
displayTemporarily();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFpsDisplay()
|
||||||
|
{
|
||||||
|
counterDrawFPS.Colour = getColour(displayedFpsCount / aimDrawFPS);
|
||||||
|
counterDrawFPS.Text = $"{displayedFpsCount:#,0}fps";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFrameTimeDisplay()
|
||||||
|
{
|
||||||
|
counterUpdateFrameTime.Text = displayedFrameTime < 5
|
||||||
|
? $"{displayedFrameTime:N1}ms"
|
||||||
|
: $"{displayedFrameTime:N0}ms";
|
||||||
|
|
||||||
|
counterUpdateFrameTime.Colour = getColour((1000 / displayedFrameTime) / aimUpdateFPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool updateAimFPS()
|
||||||
|
{
|
||||||
|
if (updateClock.Throttling)
|
||||||
|
{
|
||||||
|
double newAimDrawFPS = drawClock.MaximumUpdateHz;
|
||||||
|
double newAimUpdateFPS = updateClock.MaximumUpdateHz;
|
||||||
|
|
||||||
|
if (aimDrawFPS != newAimDrawFPS || aimUpdateFPS != newAimUpdateFPS)
|
||||||
|
{
|
||||||
|
aimDrawFPS = newAimDrawFPS;
|
||||||
|
aimUpdateFPS = newAimUpdateFPS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double newAimFPS = inputClock.MaximumUpdateHz;
|
||||||
|
|
||||||
|
if (aimDrawFPS != newAimFPS || aimUpdateFPS != newAimFPS)
|
||||||
|
{
|
||||||
|
aimUpdateFPS = aimDrawFPS = newAimFPS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ColourInfo getColour(double performanceRatio)
|
||||||
|
{
|
||||||
|
if (performanceRatio < 0.5f)
|
||||||
|
return Interpolation.ValueAt(performanceRatio, colours.Red, colours.Orange2, 0, 0.5);
|
||||||
|
|
||||||
|
return Interpolation.ValueAt(performanceRatio, colours.Orange2, colours.Lime0, 0.5, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITooltip GetCustomTooltip() => new FPSCounterTooltip();
|
||||||
|
|
||||||
|
public object TooltipContent => this;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user