mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 07:33:55 +09:00
Merge branch 'master' into diffcalc/skill-mods
This commit is contained in:
@ -69,8 +69,8 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="beatmapInfo">The <see cref="BeatmapInfo"/> to get the difficulty of.</param>
|
||||
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="BeatmapInfo"/>.</param>
|
||||
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available.</returns>
|
||||
public IBindable<StarDifficulty> GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, CancellationToken cancellationToken = default)
|
||||
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated).</returns>
|
||||
public IBindable<StarDifficulty?> GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var bindable = createBindable(beatmapInfo, currentRuleset.Value, currentMods.Value, cancellationToken);
|
||||
|
||||
@ -90,9 +90,9 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="rulesetInfo">The <see cref="RulesetInfo"/> to get the difficulty with. If <c>null</c>, the <paramref name="beatmapInfo"/>'s ruleset is used.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s to get the difficulty with. If <c>null</c>, no mods will be assumed.</param>
|
||||
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="BeatmapInfo"/>.</param>
|
||||
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available.</returns>
|
||||
public IBindable<StarDifficulty> GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable<Mod> mods,
|
||||
CancellationToken cancellationToken = default)
|
||||
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state.</returns>
|
||||
public IBindable<StarDifficulty?> GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable<Mod> mods,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> createBindable(beatmapInfo, rulesetInfo, mods, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
@ -313,7 +313,7 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private class BindableStarDifficulty : Bindable<StarDifficulty>
|
||||
private class BindableStarDifficulty : Bindable<StarDifficulty?>
|
||||
{
|
||||
public readonly BeatmapInfo Beatmap;
|
||||
public readonly CancellationToken CancellationToken;
|
||||
|
@ -20,6 +20,7 @@ using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Statistics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Database;
|
||||
@ -311,6 +312,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
workingCache.Add(working = new BeatmapManagerWorkingBeatmap(beatmapInfo, this));
|
||||
|
||||
// best effort; may be higher than expected.
|
||||
GlobalStatistics.Get<int>(nameof(Beatmaps), $"Cached {nameof(WorkingBeatmap)}s").Value = workingCache.Count();
|
||||
|
||||
return working;
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
this.mods = mods;
|
||||
}
|
||||
|
||||
private IBindable<StarDifficulty> localStarDifficulty;
|
||||
private IBindable<StarDifficulty?> localStarDifficulty;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -160,7 +160,11 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
localStarDifficulty = ruleset != null
|
||||
? difficultyCache.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token)
|
||||
: difficultyCache.GetBindableDifficulty(beatmap, difficultyCancellation.Token);
|
||||
localStarDifficulty.BindValueChanged(difficulty => StarDifficulty.Value = difficulty.NewValue);
|
||||
localStarDifficulty.BindValueChanged(d =>
|
||||
{
|
||||
if (d.NewValue is StarDifficulty diff)
|
||||
StarDifficulty.Value = diff;
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
@ -348,8 +349,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (split.Length >= 8)
|
||||
{
|
||||
LegacyEffectFlags effectFlags = (LegacyEffectFlags)Parsing.ParseInt(split[7]);
|
||||
kiaiMode = effectFlags.HasFlag(LegacyEffectFlags.Kiai);
|
||||
omitFirstBarSignature = effectFlags.HasFlag(LegacyEffectFlags.OmitFirstBarLine);
|
||||
kiaiMode = effectFlags.HasFlagFast(LegacyEffectFlags.Kiai);
|
||||
omitFirstBarSignature = effectFlags.HasFlagFast(LegacyEffectFlags.OmitFirstBarLine);
|
||||
}
|
||||
|
||||
string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
|
||||
|
@ -12,7 +12,6 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Statistics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -34,8 +33,6 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
protected AudioManager AudioManager { get; }
|
||||
|
||||
private static readonly GlobalStatistic<int> total_count = GlobalStatistics.Get<int>(nameof(Beatmaps), $"Total {nameof(WorkingBeatmap)}s");
|
||||
|
||||
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
|
||||
{
|
||||
AudioManager = audioManager;
|
||||
@ -47,8 +44,6 @@ namespace osu.Game.Beatmaps
|
||||
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
||||
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
||||
skin = new RecyclableLazy<ISkin>(GetSkin);
|
||||
|
||||
total_count.Value++;
|
||||
}
|
||||
|
||||
protected virtual Track GetVirtualTrack(double emptyLength = 0)
|
||||
@ -331,11 +326,6 @@ namespace osu.Game.Beatmaps
|
||||
protected virtual ISkin GetSkin() => new DefaultSkin();
|
||||
private readonly RecyclableLazy<ISkin> skin;
|
||||
|
||||
~WorkingBeatmap()
|
||||
{
|
||||
total_count.Value--;
|
||||
}
|
||||
|
||||
public class RecyclableLazy<T>
|
||||
{
|
||||
private Lazy<T> lazy;
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -121,7 +122,7 @@ namespace osu.Game.Collections
|
||||
Current.TriggerChange();
|
||||
}
|
||||
|
||||
protected override string GenerateItemText(CollectionFilterMenuItem item) => item.CollectionName.Value;
|
||||
protected override LocalisableString GenerateItemText(CollectionFilterMenuItem item) => item.CollectionName.Value;
|
||||
|
||||
protected sealed override DropdownHeader CreateHeader() => CreateCollectionHeader().With(d =>
|
||||
{
|
||||
@ -139,7 +140,7 @@ namespace osu.Game.Collections
|
||||
public readonly Bindable<CollectionFilterMenuItem> SelectedItem = new Bindable<CollectionFilterMenuItem>();
|
||||
private readonly Bindable<string> collectionName = new Bindable<string>();
|
||||
|
||||
protected override string Label
|
||||
protected override LocalisableString Label
|
||||
{
|
||||
get => base.Label;
|
||||
set { } // See updateText().
|
||||
|
@ -36,7 +36,19 @@ namespace osu.Game.Collections
|
||||
}
|
||||
|
||||
public bool Equals(CollectionFilterMenuItem other)
|
||||
=> other != null && CollectionName.Value == other.CollectionName.Value;
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
// collections may have the same name, so compare first on reference equality.
|
||||
// this relies on the assumption that only one instance of the BeatmapCollection exists game-wide, managed by CollectionManager.
|
||||
if (Collection != null)
|
||||
return Collection == other.Collection;
|
||||
|
||||
// fallback to name-based comparison.
|
||||
// this is required for special dropdown items which don't have a collection (all beatmaps / manage collections items below).
|
||||
return CollectionName.Value == other.CollectionName.Value;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => CollectionName.Value.GetHashCode();
|
||||
}
|
||||
|
@ -138,10 +138,10 @@ namespace osu.Game.Collections
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
var collection = readCollections(stream, notification);
|
||||
await importCollections(collection);
|
||||
var collections = readCollections(stream, notification);
|
||||
await importCollections(collections);
|
||||
|
||||
notification.CompletionText = $"Imported {collection.Count} collections";
|
||||
notification.CompletionText = $"Imported {collections.Count} collections";
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ namespace osu.Game.Collections
|
||||
{
|
||||
foreach (var newCol in newCollections)
|
||||
{
|
||||
var existing = Collections.FirstOrDefault(c => c.Name == newCol.Name);
|
||||
var existing = Collections.FirstOrDefault(c => c.Name.Value == newCol.Name.Value);
|
||||
if (existing == null)
|
||||
Collections.Add(existing = new BeatmapCollection { Name = { Value = newCol.Name.Value } });
|
||||
|
||||
|
@ -8,6 +8,7 @@ using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
@ -22,9 +23,9 @@ namespace osu.Game.Configuration
|
||||
/// </remarks>
|
||||
[MeansImplicitUse]
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class SettingSourceAttribute : Attribute
|
||||
public class SettingSourceAttribute : Attribute, IComparable<SettingSourceAttribute>
|
||||
{
|
||||
public string Label { get; }
|
||||
public LocalisableString Label { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
@ -41,6 +42,21 @@ namespace osu.Game.Configuration
|
||||
{
|
||||
OrderPosition = orderPosition;
|
||||
}
|
||||
|
||||
public int CompareTo(SettingSourceAttribute other)
|
||||
{
|
||||
if (OrderPosition == other.OrderPosition)
|
||||
return 0;
|
||||
|
||||
// unordered items come last (are greater than any ordered items).
|
||||
if (OrderPosition == null)
|
||||
return 1;
|
||||
if (other.OrderPosition == null)
|
||||
return -1;
|
||||
|
||||
// ordered items are sorted by the order value.
|
||||
return OrderPosition.Value.CompareTo(other.OrderPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SettingSourceExtensions
|
||||
@ -136,14 +152,9 @@ namespace osu.Game.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetOrderedSettingsSourceProperties(this object obj)
|
||||
{
|
||||
var original = obj.GetSettingsSourceProperties();
|
||||
|
||||
var orderedRelative = original.Where(attr => attr.Item1.OrderPosition != null).OrderBy(attr => attr.Item1.OrderPosition);
|
||||
var unordered = original.Except(orderedRelative);
|
||||
|
||||
return orderedRelative.Concat(unordered);
|
||||
}
|
||||
public static ICollection<(SettingSourceAttribute, PropertyInfo)> GetOrderedSettingsSourceProperties(this object obj)
|
||||
=> obj.GetSettingsSourceProperties()
|
||||
.OrderBy(attr => attr.Item1)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +141,13 @@ namespace osu.Game.Database
|
||||
|
||||
protected async Task<IEnumerable<TModel>> Import(ProgressNotification notification, params ImportTask[] tasks)
|
||||
{
|
||||
if (tasks.Length == 0)
|
||||
{
|
||||
notification.CompletionText = $"No {HumanisedModelName}s were found to import!";
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
return Enumerable.Empty<TModel>();
|
||||
}
|
||||
|
||||
notification.Progress = 0;
|
||||
notification.Text = $"{HumanisedModelName.Humanize(LetterCasing.Title)} import is initialising...";
|
||||
|
||||
|
@ -54,10 +54,5 @@ namespace osu.Game.Database
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~DatabaseWriteUsage()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.Sprites
|
||||
@ -14,7 +15,7 @@ namespace osu.Game.Graphics.Sprites
|
||||
{
|
||||
private readonly OsuSpriteText spriteText, blurredText;
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => spriteText.Text;
|
||||
set => blurredText.Text = spriteText.Text = value;
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@ -24,11 +25,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
set
|
||||
{
|
||||
direction = value;
|
||||
base.Direction = direction.HasFlag(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal;
|
||||
base.Direction = direction.HasFlagFast(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal;
|
||||
|
||||
foreach (var bar in Children)
|
||||
{
|
||||
bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
|
||||
bar.Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
|
||||
bar.Direction = direction;
|
||||
}
|
||||
}
|
||||
@ -56,14 +57,14 @@ namespace osu.Game.Graphics.UserInterface
|
||||
if (bar.Bar != null)
|
||||
{
|
||||
bar.Bar.Length = length;
|
||||
bar.Bar.Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1);
|
||||
bar.Bar.Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(new Bar
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = direction.HasFlag(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1),
|
||||
Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1),
|
||||
Length = length,
|
||||
Direction = Direction,
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -105,7 +106,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected class TextContainer : Container, IHasText
|
||||
{
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => NormalText.Text;
|
||||
set
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -21,9 +22,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
/// </summary>
|
||||
public class OsuButton : Button
|
||||
{
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => SpriteText?.Text;
|
||||
get => SpriteText?.Text ?? default;
|
||||
set
|
||||
{
|
||||
if (SpriteText != null)
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
@ -168,7 +169,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected new class Content : FillFlowContainer, IHasText
|
||||
{
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => Label.Text;
|
||||
set => Label.Text = value;
|
||||
@ -215,7 +216,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
protected readonly SpriteText Text;
|
||||
|
||||
protected override string Label
|
||||
protected override LocalisableString Label
|
||||
{
|
||||
get => Text.Text;
|
||||
set => Text.Text = value;
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@ -35,7 +36,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => text.Text;
|
||||
set => text.Text = value;
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@ -18,7 +19,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
private const int duration = 200;
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => text.Text;
|
||||
set => text.Text = value;
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
});
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> FilterTerms => new[] { Text };
|
||||
public virtual IEnumerable<string> FilterTerms => new[] { Text.ToString() };
|
||||
|
||||
public bool MatchingFilter
|
||||
{
|
||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Framework.Audio.Track;
|
||||
using System;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
@ -56,15 +57,15 @@ namespace osu.Game.Graphics.UserInterface
|
||||
set
|
||||
{
|
||||
base.Origin = value;
|
||||
c1.Origin = c1.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight;
|
||||
c2.Origin = c2.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft;
|
||||
c1.Origin = c1.Anchor = value.HasFlagFast(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight;
|
||||
c2.Origin = c2.Anchor = value.HasFlagFast(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft;
|
||||
|
||||
X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shear.X * 0.5f : 0;
|
||||
X = value.HasFlagFast(Anchor.x2) ? SIZE_RETRACTED.X * shear.X * 0.5f : 0;
|
||||
|
||||
Remove(c1);
|
||||
Remove(c2);
|
||||
c1.Depth = value.HasFlag(Anchor.x2) ? 0 : 1;
|
||||
c2.Depth = value.HasFlag(Anchor.x2) ? 1 : 0;
|
||||
c1.Depth = value.HasFlagFast(Anchor.x2) ? 0 : 1;
|
||||
c2.Depth = value.HasFlagFast(Anchor.x2) ? 1 : 0;
|
||||
Add(c1);
|
||||
Add(c2);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ExceptionExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
@ -246,7 +247,14 @@ namespace osu.Game.Online.API
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public IHubClientConnector GetHubConnector(string clientName, string endpoint) => new HubClientConnector(clientName, endpoint, this, versionHash);
|
||||
public IHubClientConnector GetHubConnector(string clientName, string endpoint)
|
||||
{
|
||||
// disabled until the underlying runtime issue is resolved, see https://github.com/mono/mono/issues/20805.
|
||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.iOS)
|
||||
return null;
|
||||
|
||||
return new HubClientConnector(clientName, endpoint, this, versionHash);
|
||||
}
|
||||
|
||||
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
|
||||
{
|
||||
@ -373,7 +381,13 @@ namespace osu.Game.Online.API
|
||||
|
||||
public void Queue(APIRequest request)
|
||||
{
|
||||
lock (queue) queue.Enqueue(request);
|
||||
lock (queue)
|
||||
{
|
||||
if (state.Value == APIState.Offline)
|
||||
return;
|
||||
|
||||
queue.Enqueue(request);
|
||||
}
|
||||
}
|
||||
|
||||
private void flushQueue(bool failOldRequests = true)
|
||||
@ -394,8 +408,6 @@ namespace osu.Game.Online.API
|
||||
|
||||
public void Logout()
|
||||
{
|
||||
flushQueue();
|
||||
|
||||
password = null;
|
||||
authentication.Clear();
|
||||
|
||||
@ -407,6 +419,7 @@ namespace osu.Game.Online.API
|
||||
});
|
||||
|
||||
state.Value = APIState.Offline;
|
||||
flushQueue();
|
||||
}
|
||||
|
||||
private static User createGuestUser() => new GuestUser();
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.IO.Network;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
@ -15,6 +16,13 @@ namespace osu.Game.Online.API.Requests
|
||||
this.noVideo = noVideo;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Timeout = 60000;
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmapsets/{Model.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
createNewPrivateMessageRequest.Failure += exception =>
|
||||
{
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
handlePostException(exception);
|
||||
target.ReplaceMessage(message, null);
|
||||
dequeueAndRun();
|
||||
};
|
||||
@ -171,7 +171,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
req.Failure += exception =>
|
||||
{
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
handlePostException(exception);
|
||||
target.ReplaceMessage(message, null);
|
||||
dequeueAndRun();
|
||||
};
|
||||
@ -184,6 +184,14 @@ namespace osu.Game.Online.Chat
|
||||
dequeueAndRun();
|
||||
}
|
||||
|
||||
private static void handlePostException(Exception exception)
|
||||
{
|
||||
if (exception is APIException apiException)
|
||||
Logger.Log(apiException.Message, level: LogLevel.Important);
|
||||
else
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts a command locally. Commands like /help will result in a help message written in the current channel.
|
||||
/// </summary>
|
||||
|
@ -361,14 +361,6 @@ namespace osu.Game
|
||||
|
||||
PerformFromScreen(screen =>
|
||||
{
|
||||
// we might already be at song select, so a check is required before performing the load to solo.
|
||||
if (screen is MainMenu)
|
||||
menuScreen.LoadToSolo();
|
||||
|
||||
// we might even already be at the song
|
||||
if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash && (difficultyCriteria?.Invoke(Beatmap.Value.BeatmapInfo) ?? true))
|
||||
return;
|
||||
|
||||
// Find beatmaps that match our predicate.
|
||||
var beatmaps = databasedSet.Beatmaps.Where(b => difficultyCriteria?.Invoke(b) ?? true).ToList();
|
||||
|
||||
@ -381,9 +373,16 @@ namespace osu.Game
|
||||
?? beatmaps.FirstOrDefault(b => b.Ruleset.Equals(Ruleset.Value))
|
||||
?? beatmaps.First();
|
||||
|
||||
Ruleset.Value = selection.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection);
|
||||
}, validScreens: new[] { typeof(SongSelect) });
|
||||
if (screen is IHandlePresentBeatmap presentableScreen)
|
||||
{
|
||||
presentableScreen.PresentBeatmap(BeatmapManager.GetWorkingBeatmap(selection), selection.Ruleset);
|
||||
}
|
||||
else
|
||||
{
|
||||
Ruleset.Value = selection.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(selection);
|
||||
}
|
||||
}, validScreens: new[] { typeof(SongSelect), typeof(IHandlePresentBeatmap) });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -84,14 +84,14 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
|
||||
Text = new RomanisableString(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
|
||||
Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
},
|
||||
|
@ -107,14 +107,14 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
|
||||
Text = new RomanisableString(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
|
||||
Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
|
||||
},
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -96,7 +97,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
public string TooltipText { get; }
|
||||
|
||||
public string Value
|
||||
public LocalisableString Value
|
||||
{
|
||||
get => value.Text;
|
||||
set => this.value.Text = value;
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -204,7 +203,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public LocalisedString Text
|
||||
public string Text
|
||||
{
|
||||
set => text.Text = value;
|
||||
}
|
||||
|
@ -4,19 +4,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osuTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Chat;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Chat.Selection
|
||||
{
|
||||
public class ChannelSection : Container, IHasFilterableChildren
|
||||
{
|
||||
private readonly OsuSpriteText header;
|
||||
|
||||
public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
|
||||
|
||||
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
|
||||
@ -29,12 +27,6 @@ namespace osu.Game.Overlays.Chat.Selection
|
||||
|
||||
public bool FilteringActive { get; set; }
|
||||
|
||||
public string Header
|
||||
{
|
||||
get => header.Text;
|
||||
set => header.Text = value.ToUpperInvariant();
|
||||
}
|
||||
|
||||
public IEnumerable<Channel> Channels
|
||||
{
|
||||
set => ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c));
|
||||
@ -47,9 +39,10 @@ namespace osu.Game.Overlays.Chat.Selection
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
header = new OsuSpriteText
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold),
|
||||
Text = "All Channels".ToUpperInvariant()
|
||||
},
|
||||
ChannelFlow = new FillFlowContainer<ChannelListItem>
|
||||
{
|
||||
|
@ -131,11 +131,7 @@ namespace osu.Game.Overlays.Chat.Selection
|
||||
{
|
||||
sectionsFlow.ChildrenEnumerable = new[]
|
||||
{
|
||||
new ChannelSection
|
||||
{
|
||||
Header = "All Channels",
|
||||
Channels = channels,
|
||||
},
|
||||
new ChannelSection { Channels = channels, },
|
||||
};
|
||||
|
||||
foreach (ChannelSection s in sectionsFlow.Children)
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Comments.Buttons
|
||||
{
|
||||
public abstract class CommentRepliesButton : CompositeDrawable
|
||||
{
|
||||
protected string Text
|
||||
protected LocalisableString Text
|
||||
{
|
||||
get => text.Text;
|
||||
set => text.Text = value;
|
||||
|
42
osu.Game/Overlays/Dialog/ConfirmDialog.cs
Normal file
42
osu.Game/Overlays/Dialog/ConfirmDialog.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// 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.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// A dialog which confirms a user action.
|
||||
/// </summary>
|
||||
public class ConfirmDialog : PopupDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a new confirmation dialog.
|
||||
/// </summary>
|
||||
/// <param name="message">The description of the action to be displayed to the user.</param>
|
||||
/// <param name="onConfirm">An action to perform on confirmation.</param>
|
||||
/// <param name="onCancel">An optional action to perform on cancel.</param>
|
||||
public ConfirmDialog(string message, Action onConfirm, Action onCancel = null)
|
||||
{
|
||||
HeaderText = message;
|
||||
BodyText = "Last chance to turn back";
|
||||
|
||||
Icon = FontAwesome.Solid.ExclamationTriangle;
|
||||
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogOkButton
|
||||
{
|
||||
Text = @"Yes",
|
||||
Action = onConfirm
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"Cancel",
|
||||
Action = onCancel
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
private FillFlowContainer cancelAndClearButtons;
|
||||
private FillFlowContainer<KeyButton> buttons;
|
||||
|
||||
public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend((string)text.Text);
|
||||
public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text.ToString());
|
||||
|
||||
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
|
||||
{
|
||||
|
@ -23,13 +23,15 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
|
||||
|
||||
protected IReadOnlyList<ModButton> Buttons { get; private set; } = Array.Empty<ModButton>();
|
||||
|
||||
public Action<Mod> Action;
|
||||
|
||||
public Key[] ToggleKeys;
|
||||
|
||||
public readonly ModType ModType;
|
||||
|
||||
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
|
||||
public IEnumerable<Mod> SelectedMods => Buttons.Select(b => b.SelectedMod).Where(m => m != null);
|
||||
|
||||
private CancellationTokenSource modsLoadCts;
|
||||
|
||||
@ -77,7 +79,7 @@ namespace osu.Game.Overlays.Mods
|
||||
ButtonsContainer.ChildrenEnumerable = c;
|
||||
}, (modsLoadCts = new CancellationTokenSource()).Token);
|
||||
|
||||
buttons = modContainers.OfType<ModButton>().ToArray();
|
||||
Buttons = modContainers.OfType<ModButton>().ToArray();
|
||||
|
||||
header.FadeIn(200);
|
||||
this.FadeIn(200);
|
||||
@ -88,8 +90,6 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
}
|
||||
|
||||
private ModButton[] buttons = Array.Empty<ModButton>();
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.ControlPressed) return false;
|
||||
@ -97,8 +97,8 @@ namespace osu.Game.Overlays.Mods
|
||||
if (ToggleKeys != null)
|
||||
{
|
||||
var index = Array.IndexOf(ToggleKeys, e.Key);
|
||||
if (index > -1 && index < buttons.Length)
|
||||
buttons[index].SelectNext(e.ShiftPressed ? -1 : 1);
|
||||
if (index > -1 && index < Buttons.Count)
|
||||
Buttons[index].SelectNext(e.ShiftPressed ? -1 : 1);
|
||||
}
|
||||
|
||||
return base.OnKeyDown(e);
|
||||
@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
pendingSelectionOperations.Clear();
|
||||
|
||||
foreach (var button in buttons.Where(b => !b.Selected))
|
||||
foreach (var button in Buttons.Where(b => !b.Selected))
|
||||
pendingSelectionOperations.Enqueue(() => button.SelectAt(0));
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Mods
|
||||
public void DeselectAll()
|
||||
{
|
||||
pendingSelectionOperations.Clear();
|
||||
DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
|
||||
DeselectTypes(Buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -161,7 +161,7 @@ namespace osu.Game.Overlays.Mods
|
||||
/// <param name="immediate">Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow.</param>
|
||||
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
|
||||
{
|
||||
foreach (var button in buttons)
|
||||
foreach (var button in Buttons)
|
||||
{
|
||||
if (button.SelectedMod == null) continue;
|
||||
|
||||
@ -184,7 +184,7 @@ namespace osu.Game.Overlays.Mods
|
||||
/// <param name="newSelectedMods">The new list of selected mods to select.</param>
|
||||
public void UpdateSelectedButtons(IReadOnlyList<Mod> newSelectedMods)
|
||||
{
|
||||
foreach (var button in buttons)
|
||||
foreach (var button in Buttons)
|
||||
updateButtonSelection(button, newSelectedMods);
|
||||
}
|
||||
|
||||
|
@ -456,6 +456,7 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
|
||||
updateSelectedButtons();
|
||||
OnAvailableModsChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -533,6 +534,13 @@ namespace osu.Game.Overlays.Mods
|
||||
private void playSelectedSound() => sampleOn?.Play();
|
||||
private void playDeselectedSound() => sampleOff?.Play();
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after <see cref="availableMods"/> has changed.
|
||||
/// </summary>
|
||||
protected virtual void OnAvailableModsChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a new <see cref="Mod"/> has been selected.
|
||||
/// </summary>
|
||||
|
@ -48,8 +48,8 @@ namespace osu.Game.Overlays.Music
|
||||
artistColour = colours.Gray9;
|
||||
HandleColour = colours.Gray5;
|
||||
|
||||
title = localisation.GetLocalisedString(new LocalisedString((Model.Metadata.TitleUnicode, Model.Metadata.Title)));
|
||||
artist = localisation.GetLocalisedString(new LocalisedString((Model.Metadata.ArtistUnicode, Model.Metadata.Artist)));
|
||||
title = localisation.GetLocalisedString(new RomanisableString(Model.Metadata.TitleUnicode, Model.Metadata.Title));
|
||||
artist = localisation.GetLocalisedString(new RomanisableString(Model.Metadata.ArtistUnicode, Model.Metadata.Artist));
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -8,10 +8,11 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
@ -37,7 +38,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public NotificationSection(string title, string clearButtonText)
|
||||
{
|
||||
this.clearButtonText = clearButtonText;
|
||||
this.clearButtonText = clearButtonText.ToUpperInvariant();
|
||||
titleText = title;
|
||||
}
|
||||
|
||||
@ -121,7 +122,20 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
base.Update();
|
||||
|
||||
countDrawable.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString();
|
||||
countDrawable.Text = getVisibleCount().ToString();
|
||||
}
|
||||
|
||||
private int getVisibleCount()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
foreach (var c in notifications)
|
||||
{
|
||||
if (c.Alpha > 0.99f)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private class ClearAllButton : OsuClickableContainer
|
||||
@ -138,10 +152,10 @@ namespace osu.Game.Overlays.Notifications
|
||||
};
|
||||
}
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => text.Text;
|
||||
set => text.Text = value.ToUpperInvariant();
|
||||
set => text.Text = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,8 +293,8 @@ namespace osu.Game.Overlays
|
||||
else
|
||||
{
|
||||
BeatmapMetadata metadata = beatmap.Metadata;
|
||||
title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
|
||||
artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
|
||||
title.Text = new RomanisableString(metadata.TitleUnicode, metadata.Title);
|
||||
artist.Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -17,6 +17,7 @@ using osu.Game.Overlays.Comments;
|
||||
using JetBrains.Annotations;
|
||||
using System;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -30,7 +31,7 @@ namespace osu.Game.Overlays
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
public string Title
|
||||
public LocalisableString Title
|
||||
{
|
||||
get => text.Text;
|
||||
set => text.Text = value;
|
||||
|
@ -5,7 +5,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
@ -13,6 +12,7 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
{
|
||||
@ -129,14 +129,14 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((
|
||||
Text = new RomanisableString(
|
||||
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
|
||||
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")),
|
||||
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] "),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold)
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
|
||||
Text = "by " + new RomanisableString(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular)
|
||||
},
|
||||
};
|
||||
|
@ -256,16 +256,16 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Text = new LocalisedString((
|
||||
Text = new RomanisableString(
|
||||
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} ",
|
||||
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} ")),
|
||||
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} "),
|
||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
|
||||
Text = "by " + new RomanisableString(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
|
||||
Font = OsuFont.GetFont(size: 12, italics: true)
|
||||
},
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
@ -76,7 +77,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
|
||||
private class AudioDeviceDropdownControl : DropdownControl
|
||||
{
|
||||
protected override string GenerateItemText(string item)
|
||||
protected override LocalisableString GenerateItemText(string item)
|
||||
=> string.IsNullOrEmpty(item) ? "Default" : base.GenerateItemText(item);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -234,7 +235,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
|
||||
private class ResolutionDropdownControl : DropdownControl
|
||||
{
|
||||
protected override string GenerateItemText(Size item)
|
||||
protected override LocalisableString GenerateItemText(Size item)
|
||||
{
|
||||
if (item == new Size(9999, 9999))
|
||||
return "Default";
|
||||
|
@ -17,7 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
protected override string Header => "Mouse";
|
||||
|
||||
private readonly BindableBool rawInputToggle = new BindableBool();
|
||||
private Bindable<double> sensitivityBindable = new BindableDouble();
|
||||
|
||||
private Bindable<double> configSensitivity;
|
||||
|
||||
private Bindable<double> localSensitivity;
|
||||
|
||||
private Bindable<string> ignoredInputHandlers;
|
||||
|
||||
private Bindable<WindowMode> windowMode;
|
||||
@ -26,12 +30,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager osuConfig, FrameworkConfigManager config)
|
||||
{
|
||||
var configSensitivity = config.GetBindable<double>(FrameworkSetting.CursorSensitivity);
|
||||
|
||||
// use local bindable to avoid changing enabled state of game host's bindable.
|
||||
sensitivityBindable = configSensitivity.GetUnboundCopy();
|
||||
configSensitivity.BindValueChanged(val => sensitivityBindable.Value = val.NewValue);
|
||||
sensitivityBindable.BindValueChanged(val => configSensitivity.Value = val.NewValue);
|
||||
configSensitivity = config.GetBindable<double>(FrameworkSetting.CursorSensitivity);
|
||||
localSensitivity = configSensitivity.GetUnboundCopy();
|
||||
|
||||
windowMode = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode);
|
||||
ignoredInputHandlers = config.GetBindable<string>(FrameworkSetting.IgnoredInputHandlers);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -43,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
new SensitivitySetting
|
||||
{
|
||||
LabelText = "Cursor sensitivity",
|
||||
Current = sensitivityBindable
|
||||
Current = localSensitivity
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
@ -66,14 +70,35 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
windowMode = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode);
|
||||
windowMode.BindValueChanged(mode => confineMouseModeSetting.Alpha = mode.NewValue == WindowMode.Fullscreen ? 0 : 1, true);
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
configSensitivity.BindValueChanged(val => localSensitivity.Value = val.NewValue, true);
|
||||
localSensitivity.BindValueChanged(val => configSensitivity.Value = val.NewValue);
|
||||
|
||||
windowMode.BindValueChanged(mode =>
|
||||
{
|
||||
var isFullscreen = mode.NewValue == WindowMode.Fullscreen;
|
||||
|
||||
if (isFullscreen)
|
||||
{
|
||||
confineMouseModeSetting.Current.Disabled = true;
|
||||
confineMouseModeSetting.TooltipText = "Not applicable in full screen mode";
|
||||
}
|
||||
else
|
||||
{
|
||||
confineMouseModeSetting.Current.Disabled = false;
|
||||
confineMouseModeSetting.TooltipText = string.Empty;
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
|
||||
{
|
||||
rawInputToggle.Disabled = true;
|
||||
sensitivityBindable.Disabled = true;
|
||||
localSensitivity.Disabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -86,12 +111,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
ignoredInputHandlers.Value = enabled.NewValue ? standard_mouse_handlers : raw_mouse_handler;
|
||||
};
|
||||
|
||||
ignoredInputHandlers = config.GetBindable<string>(FrameworkSetting.IgnoredInputHandlers);
|
||||
ignoredInputHandlers.ValueChanged += handler =>
|
||||
{
|
||||
bool raw = !handler.NewValue.Contains("Raw");
|
||||
rawInputToggle.Value = raw;
|
||||
sensitivityBindable.Disabled = !raw;
|
||||
localSensitivity.Disabled = !raw;
|
||||
};
|
||||
|
||||
ignoredInputHandlers.TriggerChange();
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -178,7 +179,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
|
||||
private class SkinDropdownControl : DropdownControl
|
||||
{
|
||||
protected override string GenerateItemText(SkinInfo item) => item.ToString();
|
||||
protected override LocalisableString GenerateItemText(SkinInfo item) => item.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,20 +2,22 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public class SettingsCheckbox : SettingsItem<bool>
|
||||
{
|
||||
private string labelText;
|
||||
private LocalisableString labelText;
|
||||
|
||||
protected override Drawable CreateControl() => new OsuCheckbox();
|
||||
|
||||
public override string LabelText
|
||||
public override LocalisableString LabelText
|
||||
{
|
||||
get => labelText;
|
||||
set => ((OsuCheckbox)Control).LabelText = labelText = value;
|
||||
// checkbox doesn't properly support localisation yet.
|
||||
set => ((OsuCheckbox)Control).LabelText = (labelText = value).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
public string TooltipText { get; set; }
|
||||
|
||||
public virtual string LabelText
|
||||
public virtual LocalisableString LabelText
|
||||
{
|
||||
get => labelText?.Text ?? string.Empty;
|
||||
set
|
||||
@ -69,7 +70,7 @@ namespace osu.Game.Overlays.Settings
|
||||
set => controlWithCurrent.Current = value;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> FilterTerms => Keywords == null ? new[] { LabelText } : new List<string>(Keywords) { LabelText }.ToArray();
|
||||
public virtual IEnumerable<string> FilterTerms => Keywords == null ? new[] { LabelText.ToString() } : new List<string>(Keywords) { LabelText.ToString() }.ToArray();
|
||||
|
||||
public IEnumerable<string> Keywords { get; set; }
|
||||
|
||||
@ -120,7 +121,7 @@ namespace osu.Game.Overlays.Settings
|
||||
labelText.Alpha = controlWithCurrent.Current.Disabled ? 0.3f : 1;
|
||||
}
|
||||
|
||||
private class RestoreDefaultValueButton : Container, IHasTooltip
|
||||
protected internal class RestoreDefaultValueButton : Container, IHasTooltip
|
||||
{
|
||||
private Bindable<T> bindable;
|
||||
|
||||
@ -146,6 +147,7 @@ namespace osu.Game.Overlays.Settings
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = SettingsPanel.CONTENT_MARGINS;
|
||||
Alpha = 0f;
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -206,7 +208,9 @@ namespace osu.Game.Overlays.Settings
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
public void UpdateState()
|
||||
public void UpdateState() => Scheduler.AddOnce(updateState);
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
if (bindable == null)
|
||||
return;
|
||||
|
@ -37,6 +37,15 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Size = new Vector2(1, HEIGHT);
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
// this only needed to be set for the initial LoadComplete/Update, so layout completes and gets buttons in a state they can correctly handle keyboard input for hotkeys.
|
||||
AlwaysPresent = false;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
@ -12,6 +13,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -43,19 +45,19 @@ namespace osu.Game.Overlays.Toolbar
|
||||
Texture = textures.Get(texture),
|
||||
});
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => DrawableText.Text;
|
||||
set => DrawableText.Text = value;
|
||||
}
|
||||
|
||||
public string TooltipMain
|
||||
public LocalisableString TooltipMain
|
||||
{
|
||||
get => tooltip1.Text;
|
||||
set => tooltip1.Text = value;
|
||||
}
|
||||
|
||||
public string TooltipSub
|
||||
public LocalisableString TooltipSub
|
||||
{
|
||||
get => tooltip2.Text;
|
||||
set => tooltip2.Text = value;
|
||||
@ -127,9 +129,9 @@ namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.Both, // stops us being considered in parent's autosize
|
||||
Anchor = TooltipAnchor.HasFlag(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight,
|
||||
Anchor = TooltipAnchor.HasFlagFast(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight,
|
||||
Origin = TooltipAnchor,
|
||||
Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5),
|
||||
Position = new Vector2(TooltipAnchor.HasFlagFast(Anchor.x0) ? 5 : -5, 5),
|
||||
Alpha = 0,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
@ -5,14 +5,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Game
|
||||
@ -29,9 +28,6 @@ namespace osu.Game
|
||||
[Resolved]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
|
||||
@ -81,27 +77,45 @@ namespace osu.Game
|
||||
|
||||
game?.CloseAllOverlays(false);
|
||||
|
||||
// we may already be at the target screen type.
|
||||
findValidTarget(current);
|
||||
}
|
||||
|
||||
private bool findValidTarget(IScreen current)
|
||||
{
|
||||
var type = current.GetType();
|
||||
|
||||
if (validScreens.Any(t => t.IsAssignableFrom(type)) && !beatmap.Disabled)
|
||||
// check if we are already at a valid target screen.
|
||||
if (validScreens.Any(t => t.IsAssignableFrom(type)))
|
||||
{
|
||||
finalAction(current);
|
||||
Cancel();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
// if this has a sub stack, recursively check the screens within it.
|
||||
if (current is IHasSubScreenStack currentSubScreen)
|
||||
{
|
||||
if (findValidTarget(currentSubScreen.SubScreenStack.CurrentScreen))
|
||||
{
|
||||
// should be correct in theory, but currently untested/unused in existing implementations.
|
||||
current.MakeCurrent();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (validScreens.Any(t => t.IsAssignableFrom(type)))
|
||||
{
|
||||
current.MakeCurrent();
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
current = current.GetParentScreen();
|
||||
type = current?.GetType();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osuTK;
|
||||
|
||||
@ -31,19 +32,19 @@ namespace osu.Game.Replays.Legacy
|
||||
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
public bool MouseLeft1 => ButtonState.HasFlag(ReplayButtonState.Left1);
|
||||
public bool MouseLeft1 => ButtonState.HasFlagFast(ReplayButtonState.Left1);
|
||||
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
public bool MouseRight1 => ButtonState.HasFlag(ReplayButtonState.Right1);
|
||||
public bool MouseRight1 => ButtonState.HasFlagFast(ReplayButtonState.Right1);
|
||||
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
public bool MouseLeft2 => ButtonState.HasFlag(ReplayButtonState.Left2);
|
||||
public bool MouseLeft2 => ButtonState.HasFlagFast(ReplayButtonState.Left2);
|
||||
|
||||
[JsonIgnore]
|
||||
[IgnoreMember]
|
||||
public bool MouseRight2 => ButtonState.HasFlag(ReplayButtonState.Right2);
|
||||
public bool MouseRight2 => ButtonState.HasFlagFast(ReplayButtonState.Right2);
|
||||
|
||||
[Key(3)]
|
||||
public ReplayButtonState ButtonState;
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) };
|
||||
|
||||
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
||||
|
||||
|
25
osu.Game/Rulesets/Mods/ModFailCondition.cs
Normal file
25
osu.Game/Rulesets/Mods/ModFailCondition.cs
Normal file
@ -0,0 +1,25 @@
|
||||
// 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.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
||||
{
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
||||
|
||||
public virtual bool PerformFail() => true;
|
||||
|
||||
public virtual bool RestartOnFail => true;
|
||||
|
||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||
{
|
||||
healthProcessor.FailConditions += FailCondition;
|
||||
}
|
||||
|
||||
protected abstract bool FailCondition(HealthProcessor healthProcessor, JudgementResult result);
|
||||
}
|
||||
}
|
@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Description => "You can't fail, no matter what.";
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override bool Ranked => true;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModAutoplay) };
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -8,13 +10,18 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModPerfect : ModSuddenDeath
|
||||
public abstract class ModPerfect : ModFailCondition
|
||||
{
|
||||
public override string Name => "Perfect";
|
||||
public override string Acronym => "PF";
|
||||
public override IconUsage? Icon => OsuIcon.ModPerfect;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override bool Ranked => true;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override string Description => "SS or quit.";
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray();
|
||||
|
||||
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||
=> result.Type.AffectsAccuracy()
|
||||
&& result.Type != result.Judgement.MaxResult;
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public double ApplyToRate(double time, double rate) => rate * SpeedChange.Value;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
|
||||
|
||||
public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x";
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override IconUsage? Icon => OsuIcon.ModRelax;
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) };
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -9,7 +10,7 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModSuddenDeath : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
||||
public abstract class ModSuddenDeath : ModFailCondition
|
||||
{
|
||||
public override string Name => "Sudden Death";
|
||||
public override string Acronym => "SD";
|
||||
@ -18,18 +19,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Description => "Miss and fail.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
||||
|
||||
public bool PerformFail() => true;
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray();
|
||||
|
||||
public bool RestartOnFail => true;
|
||||
|
||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||
{
|
||||
healthProcessor.FailConditions += FailCondition;
|
||||
}
|
||||
|
||||
protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||
=> result.Type.AffectsCombo()
|
||||
&& !result.IsHit;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public abstract BindableBool AdjustPitch { get; }
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust) };
|
||||
|
||||
public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x";
|
||||
|
||||
private double finalRateTime;
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 1,
|
||||
MinValue = 0.51,
|
||||
MaxValue = 2,
|
||||
Default = 1,
|
||||
Value = 1,
|
||||
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MaxValue = 0.99,
|
||||
MaxValue = 1.99,
|
||||
Default = 0.75,
|
||||
Value = 0.75,
|
||||
Precision = 0.01,
|
||||
@ -45,5 +45,20 @@ namespace osu.Game.Rulesets.Mods
|
||||
};
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray();
|
||||
|
||||
public ModWindDown()
|
||||
{
|
||||
InitialRate.BindValueChanged(val =>
|
||||
{
|
||||
if (val.NewValue <= FinalRate.Value)
|
||||
FinalRate.Value = val.NewValue - FinalRate.Precision;
|
||||
});
|
||||
|
||||
FinalRate.BindValueChanged(val =>
|
||||
{
|
||||
if (val.NewValue >= InitialRate.Value)
|
||||
InitialRate.Value = val.NewValue + InitialRate.Precision;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MaxValue = 1,
|
||||
MaxValue = 1.99,
|
||||
Default = 1,
|
||||
Value = 1,
|
||||
Precision = 0.01,
|
||||
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 1.01,
|
||||
MinValue = 0.51,
|
||||
MaxValue = 2,
|
||||
Default = 1.5,
|
||||
Value = 1.5,
|
||||
@ -45,5 +45,20 @@ namespace osu.Game.Rulesets.Mods
|
||||
};
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray();
|
||||
|
||||
public ModWindUp()
|
||||
{
|
||||
InitialRate.BindValueChanged(val =>
|
||||
{
|
||||
if (val.NewValue >= FinalRate.Value)
|
||||
FinalRate.Value = val.NewValue + FinalRate.Precision;
|
||||
});
|
||||
|
||||
FinalRate.BindValueChanged(val =>
|
||||
{
|
||||
if (val.NewValue <= InitialRate.Value)
|
||||
InitialRate.Value = val.NewValue - InitialRate.Precision;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Audio;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Skinning;
|
||||
@ -54,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4;
|
||||
type &= ~LegacyHitObjectType.ComboOffset;
|
||||
|
||||
bool combo = type.HasFlag(LegacyHitObjectType.NewCombo);
|
||||
bool combo = type.HasFlagFast(LegacyHitObjectType.NewCombo);
|
||||
type &= ~LegacyHitObjectType.NewCombo;
|
||||
|
||||
var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]);
|
||||
@ -62,14 +63,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
HitObject result = null;
|
||||
|
||||
if (type.HasFlag(LegacyHitObjectType.Circle))
|
||||
if (type.HasFlagFast(LegacyHitObjectType.Circle))
|
||||
{
|
||||
result = CreateHit(pos, combo, comboOffset);
|
||||
|
||||
if (split.Length > 5)
|
||||
readCustomSampleBanks(split[5], bankInfo);
|
||||
}
|
||||
else if (type.HasFlag(LegacyHitObjectType.Slider))
|
||||
else if (type.HasFlagFast(LegacyHitObjectType.Slider))
|
||||
{
|
||||
double? length = null;
|
||||
|
||||
@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
result = CreateSlider(pos, combo, comboOffset, convertPathString(split[5], pos), length, repeatCount, nodeSamples);
|
||||
}
|
||||
else if (type.HasFlag(LegacyHitObjectType.Spinner))
|
||||
else if (type.HasFlagFast(LegacyHitObjectType.Spinner))
|
||||
{
|
||||
double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime);
|
||||
|
||||
@ -150,7 +151,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
if (split.Length > 6)
|
||||
readCustomSampleBanks(split[6], bankInfo);
|
||||
}
|
||||
else if (type.HasFlag(LegacyHitObjectType.Hold))
|
||||
else if (type.HasFlagFast(LegacyHitObjectType.Hold))
|
||||
{
|
||||
// Note: Hold is generated by BMS converts
|
||||
|
||||
@ -436,16 +437,16 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.Normal, bankInfo.Volume, bankInfo.CustomSampleBank,
|
||||
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
|
||||
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
|
||||
type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal))
|
||||
type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal))
|
||||
};
|
||||
|
||||
if (type.HasFlag(LegacyHitSoundType.Finish))
|
||||
if (type.HasFlagFast(LegacyHitSoundType.Finish))
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||
|
||||
if (type.HasFlag(LegacyHitSoundType.Whistle))
|
||||
if (type.HasFlagFast(LegacyHitSoundType.Whistle))
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||
|
||||
if (type.HasFlag(LegacyHitSoundType.Clap))
|
||||
if (type.HasFlagFast(LegacyHitSoundType.Clap))
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||
|
||||
return soundTypes;
|
||||
|
@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
~DrawableRulesetDependencies()
|
||||
{
|
||||
// required to potentially clean up sample store from audio hierarchy.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
@ -134,6 +135,10 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public IBindable<double> AggregateTempo => throw new NotSupportedException();
|
||||
|
||||
public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||
|
||||
public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||
|
||||
public int PlaybackConcurrency
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
|
@ -16,7 +16,6 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||
|
||||
namespace osu.Game.Rulesets.UI
|
||||
@ -109,9 +108,9 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case MouseDownEvent mouseDown when mouseDown.Button == MouseButton.Left || mouseDown.Button == MouseButton.Right:
|
||||
case MouseDownEvent _:
|
||||
if (mouseDisabled.Value)
|
||||
return false;
|
||||
return true; // importantly, block upwards propagation so global bindings also don't fire.
|
||||
|
||||
break;
|
||||
|
||||
|
@ -137,7 +137,7 @@ namespace osu.Game.Scoring
|
||||
ScoringMode.BindValueChanged(onScoringModeChanged, true);
|
||||
}
|
||||
|
||||
private IBindable<StarDifficulty> difficultyBindable;
|
||||
private IBindable<StarDifficulty?> difficultyBindable;
|
||||
private CancellationTokenSource difficultyCancellationSource;
|
||||
|
||||
private void onScoringModeChanged(ValueChangedEvent<ScoringMode> mode)
|
||||
@ -168,7 +168,11 @@ namespace osu.Game.Scoring
|
||||
|
||||
// We can compute the max combo locally after the async beatmap difficulty computation.
|
||||
difficultyBindable = difficulties().GetBindableDifficulty(score.Beatmap, score.Ruleset, score.Mods, (difficultyCancellationSource = new CancellationTokenSource()).Token);
|
||||
difficultyBindable.BindValueChanged(d => updateScore(d.NewValue.MaxCombo), true);
|
||||
difficultyBindable.BindValueChanged(d =>
|
||||
{
|
||||
if (d.NewValue is StarDifficulty diff)
|
||||
updateScore(diff.MaxCombo);
|
||||
}, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -495,8 +495,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
// Apply the start time at the newly snapped-to position
|
||||
double offset = result.Time.Value - movementBlueprints.First().HitObject.StartTime;
|
||||
|
||||
foreach (HitObject obj in Beatmap.SelectedHitObjects)
|
||||
obj.StartTime += offset;
|
||||
Beatmap.PerformOnSelection(obj => obj.StartTime += offset);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -320,18 +320,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <param name="sampleName">The name of the hit sample.</param>
|
||||
public void AddHitSample(string sampleName)
|
||||
{
|
||||
EditorBeatmap.BeginChange();
|
||||
|
||||
foreach (var h in EditorBeatmap.SelectedHitObjects)
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
// Make sure there isn't already an existing sample
|
||||
if (h.Samples.Any(s => s.Name == sampleName))
|
||||
continue;
|
||||
return;
|
||||
|
||||
h.Samples.Add(new HitSampleInfo(sampleName));
|
||||
}
|
||||
|
||||
EditorBeatmap.EndChange();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -341,19 +337,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <exception cref="InvalidOperationException">Throws if any selected object doesn't implement <see cref="IHasComboInformation"/></exception>
|
||||
public void SetNewCombo(bool state)
|
||||
{
|
||||
EditorBeatmap.BeginChange();
|
||||
|
||||
foreach (var h in EditorBeatmap.SelectedHitObjects)
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
var comboInfo = h as IHasComboInformation;
|
||||
|
||||
if (comboInfo == null || comboInfo.NewCombo == state) continue;
|
||||
if (comboInfo == null || comboInfo.NewCombo == state) return;
|
||||
|
||||
comboInfo.NewCombo = state;
|
||||
EditorBeatmap.Update(h);
|
||||
}
|
||||
|
||||
EditorBeatmap.EndChange();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -362,12 +354,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <param name="sampleName">The name of the hit sample.</param>
|
||||
public void RemoveHitSample(string sampleName)
|
||||
{
|
||||
EditorBeatmap.BeginChange();
|
||||
|
||||
foreach (var h in EditorBeatmap.SelectedHitObjects)
|
||||
h.SamplesBindable.RemoveAll(s => s.Name == sampleName);
|
||||
|
||||
EditorBeatmap.EndChange();
|
||||
EditorBeatmap.PerformOnSelection(h => h.SamplesBindable.RemoveAll(s => s.Name == sampleName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -100,6 +100,22 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private readonly HashSet<HitObject> batchPendingUpdates = new HashSet<HitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Perform the provided action on every selected hitobject.
|
||||
/// Changes will be grouped as one history action.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
public void PerformOnSelection(Action<HitObject> action)
|
||||
{
|
||||
if (SelectedHitObjects.Count == 0)
|
||||
return;
|
||||
|
||||
BeginChange();
|
||||
foreach (var h in SelectedHitObjects)
|
||||
action(h);
|
||||
EndChange();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of <see cref="HitObject"/>s to this <see cref="EditorBeatmap"/>.
|
||||
/// </summary>
|
||||
|
23
osu.Game/Screens/IHandlePresentBeatmap.cs
Normal file
23
osu.Game/Screens/IHandlePresentBeatmap.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
{
|
||||
/// <summary>
|
||||
/// Denotes a screen which can handle beatmap / ruleset selection via local logic.
|
||||
/// This is used in the <see cref="OsuGame.PresentBeatmap"/> flow to handle cases which require custom logic,
|
||||
/// for instance, if a lease is held on the Beatmap.
|
||||
/// </summary>
|
||||
public interface IHandlePresentBeatmap
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked with a requested beatmap / ruleset for selection.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap to be selected.</param>
|
||||
/// <param name="ruleset">The ruleset to be selected.</param>
|
||||
void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset);
|
||||
}
|
||||
}
|
15
osu.Game/Screens/IHasSubScreenStack.cs
Normal file
15
osu.Game/Screens/IHasSubScreenStack.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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.Screens;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
{
|
||||
/// <summary>
|
||||
/// A screen which manages a nested stack of screens within itself.
|
||||
/// </summary>
|
||||
public interface IHasSubScreenStack
|
||||
{
|
||||
ScreenStack SubScreenStack { get; }
|
||||
}
|
||||
}
|
@ -172,6 +172,18 @@ namespace osu.Game.Screens.Menu
|
||||
return;
|
||||
}
|
||||
|
||||
// disabled until the underlying runtime issue is resolved, see https://github.com/mono/mono/issues/20805.
|
||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.iOS)
|
||||
{
|
||||
notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = "Multiplayer is temporarily unavailable on iOS as we figure out some low level issues.",
|
||||
Icon = FontAwesome.Solid.AppleAlt,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
OnMultiplayer?.Invoke();
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,15 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class ConfirmExitDialog : PopupDialog
|
||||
{
|
||||
public ConfirmExitDialog(Action confirm, Action cancel)
|
||||
/// <summary>
|
||||
/// Construct a new exit confirmation dialog.
|
||||
/// </summary>
|
||||
/// <param name="onConfirm">An action to perform on confirmation.</param>
|
||||
/// <param name="onCancel">An optional action to perform on cancel.</param>
|
||||
public ConfirmExitDialog(Action onConfirm, Action onCancel = null)
|
||||
{
|
||||
HeaderText = "Are you sure you want to exit?";
|
||||
BodyText = "Last chance to back out.";
|
||||
HeaderText = "Are you sure you want to exit osu!?";
|
||||
BodyText = "Last chance to turn back";
|
||||
|
||||
Icon = FontAwesome.Solid.ExclamationTriangle;
|
||||
|
||||
@ -20,13 +25,13 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
new PopupDialogOkButton
|
||||
{
|
||||
Text = @"Goodbye",
|
||||
Action = confirm
|
||||
Text = @"Let me out!",
|
||||
Action = onConfirm
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"Just a little more",
|
||||
Action = cancel
|
||||
Text = @"Just a little more...",
|
||||
Action = onCancel
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -9,12 +9,14 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
@ -23,7 +25,7 @@ using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class MainMenu : OsuScreen
|
||||
public class MainMenu : OsuScreen, IHandlePresentBeatmap
|
||||
{
|
||||
public const float FADE_IN_DURATION = 300;
|
||||
|
||||
@ -104,7 +106,7 @@ namespace osu.Game.Screens.Menu
|
||||
Beatmap.SetDefault();
|
||||
this.Push(new Editor());
|
||||
},
|
||||
OnSolo = onSolo,
|
||||
OnSolo = loadSoloSongSelect,
|
||||
OnMultiplayer = () => this.Push(new Multiplayer()),
|
||||
OnPlaylists = () => this.Push(new Playlists()),
|
||||
OnExit = confirmAndExit,
|
||||
@ -160,9 +162,7 @@ namespace osu.Game.Screens.Menu
|
||||
LoadComponentAsync(songSelect = new PlaySongSelect());
|
||||
}
|
||||
|
||||
public void LoadToSolo() => Schedule(onSolo);
|
||||
|
||||
private void onSolo() => this.Push(consumeSongSelect());
|
||||
private void loadSoloSongSelect() => this.Push(consumeSongSelect());
|
||||
|
||||
private Screen consumeSongSelect()
|
||||
{
|
||||
@ -289,5 +289,13 @@ namespace osu.Game.Screens.Menu
|
||||
this.FadeOut(3000);
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
|
||||
{
|
||||
Beatmap.Value = beatmap;
|
||||
Ruleset.Value = ruleset;
|
||||
|
||||
Schedule(loadSoloSongSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@ -61,8 +61,8 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
var metadata = beatmap.Value.Metadata;
|
||||
|
||||
title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
|
||||
artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
|
||||
title.Text = new RomanisableString(metadata.TitleUnicode, metadata.Title);
|
||||
artist.Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist);
|
||||
|
||||
this.FadeInFromZero(fade_duration / 2f)
|
||||
.Delay(4000)
|
||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((beatmap.Value.Metadata.ArtistUnicode, beatmap.Value.Metadata.Artist)),
|
||||
Text = new RomanisableString(beatmap.Value.Metadata.ArtistUnicode, beatmap.Value.Metadata.Artist),
|
||||
Font = OsuFont.GetFont(size: TextSize),
|
||||
},
|
||||
new OsuSpriteText
|
||||
@ -83,7 +83,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((beatmap.Value.Metadata.TitleUnicode, beatmap.Value.Metadata.Title)),
|
||||
Text = new RomanisableString(beatmap.Value.Metadata.TitleUnicode, beatmap.Value.Metadata.Title),
|
||||
Font = OsuFont.GetFont(size: TextSize),
|
||||
}
|
||||
}, LinkAction.OpenBeatmap, beatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap");
|
||||
|
@ -75,6 +75,14 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
section.DeselectAll();
|
||||
}
|
||||
|
||||
protected override void OnAvailableModsChanged()
|
||||
{
|
||||
base.OnAvailableModsChanged();
|
||||
|
||||
foreach (var section in ModSectionsContainer.Children)
|
||||
((FreeModSection)section).UpdateCheckboxState();
|
||||
}
|
||||
|
||||
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
|
||||
|
||||
private class FreeModSection : ModSection
|
||||
@ -108,10 +116,14 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
protected override void ModButtonStateChanged(Mod mod)
|
||||
{
|
||||
base.ModButtonStateChanged(mod);
|
||||
UpdateCheckboxState();
|
||||
}
|
||||
|
||||
public void UpdateCheckboxState()
|
||||
{
|
||||
if (!SelectionAnimationRunning)
|
||||
{
|
||||
var validButtons = ButtonsContainer.OfType<ModButton>().Where(b => b.Mod.HasImplementation);
|
||||
var validButtons = Buttons.Where(b => b.Mod.HasImplementation);
|
||||
checkbox.Current.Value = validButtons.All(b => b.Selected);
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,11 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
@ -19,6 +21,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
private LoadingLayer loadingLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new instance of multiplayer song select.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">An optional initial beatmap selection to perform.</param>
|
||||
/// <param name="ruleset">An optional initial ruleset selection to perform.</param>
|
||||
public MultiplayerMatchSongSelect(WorkingBeatmap beatmap = null, RulesetInfo ruleset = null)
|
||||
{
|
||||
if (beatmap != null || ruleset != null)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
if (beatmap != null) Beatmap.Value = beatmap;
|
||||
if (ruleset != null) Ruleset.Value = ruleset;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
|
@ -12,9 +12,13 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
@ -29,7 +33,7 @@ using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.Pa
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
[Cached]
|
||||
public class MultiplayerMatchSubScreen : RoomSubScreen
|
||||
public class MultiplayerMatchSubScreen : RoomSubScreen, IHandlePresentBeatmap
|
||||
{
|
||||
public override string Title { get; }
|
||||
|
||||
@ -279,14 +283,36 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList();
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
private bool exitConfirmed;
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
if (client.Room != null && settingsOverlay.State.Value == Visibility.Visible)
|
||||
if (client.Room == null)
|
||||
{
|
||||
// room has not been created yet; exit immediately.
|
||||
return base.OnBackButton();
|
||||
}
|
||||
|
||||
if (settingsOverlay.State.Value == Visibility.Visible)
|
||||
{
|
||||
settingsOverlay.Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!exitConfirmed && dialogOverlay != null)
|
||||
{
|
||||
dialogOverlay.Push(new ConfirmDialog("Are you sure you want to leave this multiplayer match?", () =>
|
||||
{
|
||||
exitConfirmed = true;
|
||||
this.Exit();
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnBackButton();
|
||||
}
|
||||
|
||||
@ -394,5 +420,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
modSettingChangeTracker?.Dispose();
|
||||
}
|
||||
|
||||
public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
|
||||
{
|
||||
if (!this.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
if (!client.IsHost)
|
||||
{
|
||||
// todo: should handle this when the request queue is implemented.
|
||||
// if we decide that the presentation should exit the user from the multiplayer game, the PresentBeatmap
|
||||
// flow may need to change to support an "unable to present" return value.
|
||||
return;
|
||||
}
|
||||
|
||||
this.Push(new MultiplayerMatchSongSelect(beatmap, ruleset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ using osuTK;
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
[Cached]
|
||||
public abstract class OnlinePlayScreen : OsuScreen
|
||||
public abstract class OnlinePlayScreen : OsuScreen, IHasSubScreenStack
|
||||
{
|
||||
public override bool CursorVisible => (screenStack.CurrentScreen as IOnlinePlaySubScreen)?.CursorVisible ?? true;
|
||||
|
||||
@ -355,5 +355,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
protected override double TransformDuration => 200;
|
||||
}
|
||||
}
|
||||
|
||||
ScreenStack IHasSubScreenStack.SubScreenStack => screenStack;
|
||||
}
|
||||
}
|
||||
|
@ -75,9 +75,18 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||
FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||
|
||||
Mods.BindValueChanged(onModsChanged);
|
||||
Ruleset.BindValueChanged(onRulesetChanged);
|
||||
}
|
||||
|
||||
private void onModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||
{
|
||||
FreeMods.Value = FreeMods.Value.Where(checkCompatibleFreeMod).ToList();
|
||||
|
||||
// Reset the validity delegate to update the overlay's display.
|
||||
freeModSelectOverlay.IsValidMod = IsValidFreeMod;
|
||||
}
|
||||
|
||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
||||
{
|
||||
FreeMods.Value = Array.Empty<Mod>();
|
||||
@ -155,6 +164,10 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
/// </summary>
|
||||
/// <param name="mod">The <see cref="Mod"/> to check.</param>
|
||||
/// <returns>Whether <paramref name="mod"/> is a selectable free-mod.</returns>
|
||||
protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod);
|
||||
protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod) && checkCompatibleFreeMod(mod);
|
||||
|
||||
private bool checkCompatibleFreeMod(Mod mod)
|
||||
=> Mods.Value.All(m => m.Acronym != mod.Acronym) // Mod must not be contained in the required mods.
|
||||
&& ModUtils.CheckCompatibleSet(Mods.Value.Append(mod).ToArray()); // Mod must be compatible with all the required mods.
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -362,7 +363,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
Menu.MaxHeight = 100;
|
||||
}
|
||||
|
||||
protected override string GenerateItemText(TimeSpan item) => item.Humanize();
|
||||
protected override LocalisableString GenerateItemText(TimeSpan item) => item.Humanize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Play
|
||||
}),
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)),
|
||||
Text = new RomanisableString(metadata.TitleUnicode, metadata.Title),
|
||||
Font = OsuFont.GetFont(size: 36, italics: true),
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
@ -81,7 +81,7 @@ namespace osu.Game.Screens.Play
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)),
|
||||
Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist),
|
||||
Font = OsuFont.GetFont(size: 26, italics: true),
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
|
@ -427,11 +427,18 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void updatePauseOnFocusLostState()
|
||||
{
|
||||
if (!PauseOnFocusLost || breakTracker.IsBreakTime.Value)
|
||||
if (!PauseOnFocusLost || !pausingSupportedByCurrentState || breakTracker.IsBreakTime.Value)
|
||||
return;
|
||||
|
||||
if (gameActive.Value == false)
|
||||
Pause();
|
||||
{
|
||||
bool paused = Pause();
|
||||
|
||||
// if the initial pause could not be satisfied, the pause cooldown may be active.
|
||||
// reschedule the pause attempt until it can be achieved.
|
||||
if (!paused)
|
||||
Scheduler.AddOnce(updatePauseOnFocusLostState);
|
||||
}
|
||||
}
|
||||
|
||||
private IBeatmap loadPlayableBeatmap()
|
||||
@ -674,6 +681,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private double? lastPauseActionTime;
|
||||
|
||||
protected bool PauseCooldownActive =>
|
||||
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
|
||||
|
||||
/// <summary>
|
||||
/// A set of conditionals which defines whether the current game state and configuration allows for
|
||||
/// pausing to be attempted via <see cref="Pause"/>. If false, the game should generally exit if a user pause
|
||||
@ -684,11 +694,9 @@ namespace osu.Game.Screens.Play
|
||||
LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume
|
||||
// replays cannot be paused and exit immediately
|
||||
&& !DrawableRuleset.HasReplayLoaded.Value
|
||||
// cannot pause if we are already in a fail state
|
||||
&& !HasFailed;
|
||||
|
||||
private bool pauseCooldownActive =>
|
||||
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
|
||||
|
||||
private bool canResume =>
|
||||
// cannot resume from a non-paused state
|
||||
GameplayClockContainer.IsPaused.Value
|
||||
@ -697,12 +705,12 @@ namespace osu.Game.Screens.Play
|
||||
// already resuming
|
||||
&& !IsResuming;
|
||||
|
||||
public void Pause()
|
||||
public bool Pause()
|
||||
{
|
||||
if (!pausingSupportedByCurrentState) return;
|
||||
if (!pausingSupportedByCurrentState) return false;
|
||||
|
||||
if (!IsResuming && pauseCooldownActive)
|
||||
return;
|
||||
if (!IsResuming && PauseCooldownActive)
|
||||
return false;
|
||||
|
||||
if (IsResuming)
|
||||
{
|
||||
@ -713,6 +721,7 @@ namespace osu.Game.Screens.Play
|
||||
GameplayClockContainer.Stop();
|
||||
PauseOverlay.Show();
|
||||
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)),
|
||||
Text = new RomanisableString(metadata.TitleUnicode, metadata.Title),
|
||||
Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold),
|
||||
MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2,
|
||||
Truncate = true,
|
||||
@ -110,7 +110,7 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)),
|
||||
Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist),
|
||||
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
|
||||
MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2,
|
||||
Truncate = true,
|
||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Screens.Ranking
|
||||
{
|
||||
public class SoloResultsScreen : ResultsScreen
|
||||
{
|
||||
private GetScoresRequest getScoreRequest;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
@ -28,9 +30,16 @@ namespace osu.Game.Screens.Ranking
|
||||
if (Score.Beatmap.OnlineBeatmapID == null || Score.Beatmap.Status <= BeatmapSetOnlineStatus.Pending)
|
||||
return null;
|
||||
|
||||
var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset);
|
||||
req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
|
||||
return req;
|
||||
getScoreRequest = new GetScoresRequest(Score.Beatmap, Score.Ruleset);
|
||||
getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
|
||||
return getScoreRequest;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
getScoreRequest?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
@ -25,6 +24,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@ -38,12 +38,16 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
||||
|
||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
|
||||
private IBindable<StarDifficulty> beatmapDifficulty;
|
||||
private IBindable<StarDifficulty?> beatmapDifficulty;
|
||||
|
||||
protected BufferedWedgeInfo Info;
|
||||
|
||||
@ -63,11 +67,10 @@ namespace osu.Game.Screens.Select
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] Bindable<RulesetInfo> parentRuleset)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
ruleset.BindTo(parentRuleset);
|
||||
ruleset.ValueChanged += _ => updateDisplay();
|
||||
ruleset.BindValueChanged(_ => updateDisplay());
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
@ -132,7 +135,7 @@ namespace osu.Game.Screens.Select
|
||||
return;
|
||||
}
|
||||
|
||||
LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, beatmapDifficulty.Value)
|
||||
LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value, mods.Value, beatmapDifficulty.Value ?? new StarDifficulty())
|
||||
{
|
||||
Shear = -Shear,
|
||||
Depth = Info?.Depth + 1 ?? 0
|
||||
@ -160,20 +163,25 @@ namespace osu.Game.Screens.Select
|
||||
public OsuSpriteText ArtistLabel { get; private set; }
|
||||
public BeatmapSetOnlineStatusPill StatusPill { get; private set; }
|
||||
public FillFlowContainer MapperContainer { get; private set; }
|
||||
public FillFlowContainer InfoLabelContainer { get; private set; }
|
||||
|
||||
private ILocalisedBindableString titleBinding;
|
||||
private ILocalisedBindableString artistBinding;
|
||||
private FillFlowContainer infoLabelContainer;
|
||||
private Container bpmLabelContainer;
|
||||
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
private readonly RulesetInfo ruleset;
|
||||
private readonly IReadOnlyList<Mod> mods;
|
||||
private readonly StarDifficulty starDifficulty;
|
||||
|
||||
public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, StarDifficulty difficulty)
|
||||
private ModSettingChangeTracker settingChangeTracker;
|
||||
|
||||
public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset, IReadOnlyList<Mod> mods, StarDifficulty difficulty)
|
||||
: base(pixelSnapping: true)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset;
|
||||
this.mods = mods;
|
||||
starDifficulty = difficulty;
|
||||
}
|
||||
|
||||
@ -184,11 +192,10 @@ namespace osu.Game.Screens.Select
|
||||
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||
|
||||
CacheDrawnFrameBuffer = true;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title)));
|
||||
artistBinding = localisation.GetLocalisedString(new LocalisedString((metadata.ArtistUnicode, metadata.Artist)));
|
||||
titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title));
|
||||
artistBinding = localisation.GetLocalisedString(new RomanisableString(metadata.ArtistUnicode, metadata.Artist));
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -302,12 +309,11 @@ namespace osu.Game.Screens.Select
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = getMapper(metadata)
|
||||
},
|
||||
InfoLabelContainer = new FillFlowContainer
|
||||
infoLabelContainer = new FillFlowContainer
|
||||
{
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Spacing = new Vector2(20, 0),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = getInfoLabels()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -319,6 +325,8 @@ namespace osu.Game.Screens.Select
|
||||
// no difficulty means it can't have a status to show
|
||||
if (beatmapInfo.Version == null)
|
||||
StatusPill.Hide();
|
||||
|
||||
addInfoLabels();
|
||||
}
|
||||
|
||||
private static Drawable createStarRatingDisplay(StarDifficulty difficulty) => difficulty.Stars > 0
|
||||
@ -335,63 +343,91 @@ namespace osu.Game.Screens.Select
|
||||
ForceRedraw();
|
||||
}
|
||||
|
||||
private InfoLabel[] getInfoLabels()
|
||||
private void addInfoLabels()
|
||||
{
|
||||
var b = beatmap.Beatmap;
|
||||
if (beatmap.Beatmap?.HitObjects?.Any() != true)
|
||||
return;
|
||||
|
||||
List<InfoLabel> labels = new List<InfoLabel>();
|
||||
|
||||
if (b?.HitObjects?.Any() == true)
|
||||
infoLabelContainer.Children = new Drawable[]
|
||||
{
|
||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
||||
new InfoLabel(new BeatmapStatistic
|
||||
{
|
||||
Name = "Length",
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
|
||||
Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"),
|
||||
}));
|
||||
|
||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
||||
Content = TimeSpan.FromMilliseconds(beatmap.BeatmapInfo.Length).ToString(@"m\:ss"),
|
||||
}),
|
||||
bpmLabelContainer = new Container
|
||||
{
|
||||
Name = "BPM",
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm),
|
||||
Content = getBPMRange(b),
|
||||
}));
|
||||
AutoSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(20, 0),
|
||||
Children = getRulesetInfoLabels()
|
||||
}
|
||||
};
|
||||
|
||||
settingChangeTracker = new ModSettingChangeTracker(mods);
|
||||
settingChangeTracker.SettingChanged += _ => refreshBPMLabel();
|
||||
|
||||
refreshBPMLabel();
|
||||
}
|
||||
|
||||
private InfoLabel[] getRulesetInfoLabels()
|
||||
{
|
||||
try
|
||||
{
|
||||
IBeatmap playableBeatmap;
|
||||
|
||||
try
|
||||
{
|
||||
IBeatmap playableBeatmap;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to get the beatmap with the user's ruleset
|
||||
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
|
||||
playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty<Mod>());
|
||||
}
|
||||
|
||||
labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)));
|
||||
// Try to get the beatmap with the user's ruleset
|
||||
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
Logger.Error(e, "Could not load beatmap successfully!");
|
||||
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
|
||||
playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty<Mod>());
|
||||
}
|
||||
|
||||
return playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)).ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Could not load beatmap successfully!");
|
||||
}
|
||||
|
||||
return labels.ToArray();
|
||||
return Array.Empty<InfoLabel>();
|
||||
}
|
||||
|
||||
private string getBPMRange(IBeatmap beatmap)
|
||||
private void refreshBPMLabel()
|
||||
{
|
||||
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
|
||||
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
|
||||
var b = beatmap.Beatmap;
|
||||
if (b == null)
|
||||
return;
|
||||
|
||||
if (Precision.AlmostEquals(bpmMin, bpmMax))
|
||||
return $"{bpmMin:0}";
|
||||
// this doesn't consider mods which apply variable rates, yet.
|
||||
double rate = 1;
|
||||
foreach (var mod in mods.OfType<IApplicableToRate>())
|
||||
rate = mod.ApplyToRate(0, rate);
|
||||
|
||||
return $"{bpmMin:0}-{bpmMax:0} (mostly {60000 / beatmap.GetMostCommonBeatLength():0})";
|
||||
double bpmMax = b.ControlPointInfo.BPMMaximum * rate;
|
||||
double bpmMin = b.ControlPointInfo.BPMMinimum * rate;
|
||||
double mostCommonBPM = 60000 / b.GetMostCommonBeatLength() * rate;
|
||||
|
||||
string labelText = Precision.AlmostEquals(bpmMin, bpmMax)
|
||||
? $"{bpmMin:0}"
|
||||
: $"{bpmMin:0}-{bpmMax:0} (mostly {mostCommonBPM:0})";
|
||||
|
||||
bpmLabelContainer.Child = new InfoLabel(new BeatmapStatistic
|
||||
{
|
||||
Name = "BPM",
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm),
|
||||
Content = labelText
|
||||
});
|
||||
|
||||
ForceRedraw();
|
||||
}
|
||||
|
||||
private OsuSpriteText[] getMapper(BeatmapMetadata metadata)
|
||||
@ -414,6 +450,12 @@ namespace osu.Game.Screens.Select
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
settingChangeTracker?.Dispose();
|
||||
}
|
||||
|
||||
public class InfoLabel : Container, IHasTooltip
|
||||
{
|
||||
public string TooltipText { get; }
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
|
||||
|
||||
private IBindable<StarDifficulty> starDifficultyBindable;
|
||||
private IBindable<StarDifficulty?> starDifficultyBindable;
|
||||
private CancellationTokenSource starDifficultyCancellationSource;
|
||||
|
||||
public DrawableCarouselBeatmap(CarouselBeatmap panel)
|
||||
@ -217,7 +217,10 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
// We've potentially cancelled the computation above so a new bindable is required.
|
||||
starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, (starDifficultyCancellationSource = new CancellationTokenSource()).Token);
|
||||
starDifficultyBindable.BindValueChanged(d => starCounter.Current = (float)d.NewValue.Stars, true);
|
||||
starDifficultyBindable.BindValueChanged(d =>
|
||||
{
|
||||
starCounter.Current = (float)(d.NewValue?.Stars ?? 0);
|
||||
}, true);
|
||||
}
|
||||
|
||||
base.ApplyState();
|
||||
|
@ -41,13 +41,13 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title)),
|
||||
Text = new RomanisableString(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 22, italics: true),
|
||||
Shadow = true,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = new LocalisedString((beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist)),
|
||||
Text = new RomanisableString(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true),
|
||||
Shadow = true,
|
||||
},
|
||||
|
@ -15,6 +15,8 @@ using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
@ -137,8 +139,6 @@ namespace osu.Game.Screens.Select.Details
|
||||
updateStarDifficulty();
|
||||
}
|
||||
|
||||
private IBindable<StarDifficulty> normalStarDifficulty;
|
||||
private IBindable<StarDifficulty> moddedStarDifficulty;
|
||||
private CancellationTokenSource starDifficultyCancellationSource;
|
||||
|
||||
private void updateStarDifficulty()
|
||||
@ -150,13 +150,13 @@ namespace osu.Game.Screens.Select.Details
|
||||
|
||||
starDifficultyCancellationSource = new CancellationTokenSource();
|
||||
|
||||
normalStarDifficulty = difficultyCache.GetBindableDifficulty(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token);
|
||||
moddedStarDifficulty = difficultyCache.GetBindableDifficulty(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
|
||||
var normalStarDifficulty = difficultyCache.GetDifficultyAsync(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token);
|
||||
var moddedStarDifficulty = difficultyCache.GetDifficultyAsync(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
|
||||
|
||||
normalStarDifficulty.BindValueChanged(_ => updateDisplay());
|
||||
moddedStarDifficulty.BindValueChanged(_ => updateDisplay(), true);
|
||||
|
||||
void updateDisplay() => starDifficulty.Value = ((float)normalStarDifficulty.Value.Stars, (float)moddedStarDifficulty.Value.Stars);
|
||||
Task.WhenAll(normalStarDifficulty, moddedStarDifficulty).ContinueWith(_ => Schedule(() =>
|
||||
{
|
||||
starDifficulty.Value = ((float)normalStarDifficulty.Result.Stars, (float)moddedStarDifficulty.Result.Stars);
|
||||
}), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@ -180,7 +180,7 @@ namespace osu.Game.Screens.Select.Details
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
public string Title
|
||||
public LocalisableString Title
|
||||
{
|
||||
get => name.Text;
|
||||
set => name.Text = value;
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
@ -21,9 +22,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
protected static readonly Vector2 SHEAR = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0);
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => SpriteText?.Text;
|
||||
get => SpriteText?.Text ?? default;
|
||||
set
|
||||
{
|
||||
if (SpriteText != null)
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
@ -39,13 +40,13 @@ namespace osu.Game.Screens.Select.Options
|
||||
set => iconText.Icon = value;
|
||||
}
|
||||
|
||||
public string FirstLineText
|
||||
public LocalisableString FirstLineText
|
||||
{
|
||||
get => firstLine.Text;
|
||||
set => firstLine.Text = value;
|
||||
}
|
||||
|
||||
public string SecondLineText
|
||||
public LocalisableString SecondLineText
|
||||
{
|
||||
get => secondLine.Text;
|
||||
set => secondLine.Text = value;
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// A sample corresponding to an <see cref="ISampleInfo"/> that supports being pooled and responding to skin changes.
|
||||
/// </summary>
|
||||
public class PoolableSkinnableSample : SkinReloadableDrawable, IAggregateAudioAdjustment, IAdjustableAudioComponent
|
||||
public class PoolableSkinnableSample : SkinReloadableDrawable, IAdjustableAudioComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently-loaded <see cref="DrawableSample"/>.
|
||||
@ -165,6 +165,10 @@ namespace osu.Game.Skinning
|
||||
|
||||
public BindableNumber<double> Tempo => sampleContainer.Tempo;
|
||||
|
||||
public void BindAdjustments(IAggregateAudioAdjustment component) => sampleContainer.BindAdjustments(component);
|
||||
|
||||
public void UnbindAdjustments(IAggregateAudioAdjustment component) => sampleContainer.UnbindAdjustments(component);
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => sampleContainer.AddAdjustment(type, adjustBindable);
|
||||
|
||||
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => sampleContainer.RemoveAdjustment(type, adjustBindable);
|
||||
|
@ -36,6 +36,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
~Skin()
|
||||
{
|
||||
// required to potentially clean up sample store from audio hierarchy.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
|
@ -176,6 +176,16 @@ namespace osu.Game.Skinning
|
||||
|
||||
public BindableNumber<double> Tempo => samplesContainer.Tempo;
|
||||
|
||||
public void BindAdjustments(IAggregateAudioAdjustment component)
|
||||
{
|
||||
samplesContainer.BindAdjustments(component);
|
||||
}
|
||||
|
||||
public void UnbindAdjustments(IAggregateAudioAdjustment component)
|
||||
{
|
||||
samplesContainer.UnbindAdjustments(component);
|
||||
}
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable)
|
||||
=> samplesContainer.AddAdjustment(type, adjustBindable);
|
||||
|
||||
@ -192,6 +202,14 @@ namespace osu.Game.Skinning
|
||||
|
||||
public bool IsPlayed => samplesContainer.Any(s => s.Played);
|
||||
|
||||
public IBindable<double> AggregateVolume => samplesContainer.AggregateVolume;
|
||||
|
||||
public IBindable<double> AggregateBalance => samplesContainer.AggregateBalance;
|
||||
|
||||
public IBindable<double> AggregateFrequency => samplesContainer.AggregateFrequency;
|
||||
|
||||
public IBindable<double> AggregateTempo => samplesContainer.AggregateTempo;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -21,9 +22,9 @@ namespace osu.Game.Skinning
|
||||
textDrawable.Text = Text;
|
||||
}
|
||||
|
||||
private string text;
|
||||
private LocalisableString text;
|
||||
|
||||
public string Text
|
||||
public LocalisableString Text
|
||||
{
|
||||
get => text;
|
||||
set
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -80,17 +81,17 @@ namespace osu.Game.Storyboards.Drawables
|
||||
|
||||
if (FlipH)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.x0))
|
||||
if (origin.HasFlagFast(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlag(Anchor.x2))
|
||||
else if (origin.HasFlagFast(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
if (FlipV)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.y0))
|
||||
if (origin.HasFlagFast(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlag(Anchor.y2))
|
||||
else if (origin.HasFlagFast(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -80,17 +81,17 @@ namespace osu.Game.Storyboards.Drawables
|
||||
|
||||
if (FlipH)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.x0))
|
||||
if (origin.HasFlagFast(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlag(Anchor.x2))
|
||||
else if (origin.HasFlagFast(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
if (FlipV)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.y0))
|
||||
if (origin.HasFlagFast(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlag(Anchor.y2))
|
||||
else if (origin.HasFlagFast(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
public new HealthProcessor HealthProcessor => base.HealthProcessor;
|
||||
|
||||
public new bool PauseCooldownActive => base.PauseCooldownActive;
|
||||
|
||||
public readonly List<JudgementResult> Results = new List<JudgementResult>();
|
||||
|
||||
public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
|
||||
|
@ -86,11 +86,6 @@ namespace osu.Game.Utils
|
||||
|
||||
#region Disposal
|
||||
|
||||
~SentryLogger()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user