mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 23:24:04 +09:00
Merge remote-tracking branch 'upstream/master' into smoogipoo-disable-mouse-buttons
This commit is contained in:
@ -15,21 +15,27 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// A Beatmap containing converted HitObjects.
|
||||
/// </summary>
|
||||
public class Beatmap<T> : IJsonSerializable
|
||||
public class Beatmap<T> : IBeatmap
|
||||
where T : HitObject
|
||||
{
|
||||
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
||||
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||
public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = @"Unknown",
|
||||
Title = @"Unknown",
|
||||
AuthorString = @"Unknown Creator",
|
||||
},
|
||||
Version = @"Normal",
|
||||
BaseDifficulty = new BeatmapDifficulty()
|
||||
};
|
||||
|
||||
[JsonIgnore]
|
||||
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
|
||||
|
||||
/// <summary>
|
||||
/// The HitObjects this Beatmap contains.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
||||
public List<T> HitObjects = new List<T>();
|
||||
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
|
||||
|
||||
public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>();
|
||||
|
||||
/// <summary>
|
||||
/// Total amount of break time in the beatmap.
|
||||
@ -38,51 +44,28 @@ namespace osu.Game.Beatmaps
|
||||
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new beatmap.
|
||||
/// The HitObjects this Beatmap contains.
|
||||
/// </summary>
|
||||
/// <param name="original">The original beatmap to use the parameters of.</param>
|
||||
public Beatmap(Beatmap<T> original = null)
|
||||
{
|
||||
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
|
||||
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
|
||||
Breaks = original?.Breaks ?? Breaks;
|
||||
HitObjects = original?.HitObjects ?? HitObjects;
|
||||
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
||||
public List<T> HitObjects = new List<T>();
|
||||
|
||||
if (original == null && Metadata == null)
|
||||
{
|
||||
// we may have no metadata in cases we weren't sourced from the database.
|
||||
// let's fill it (and other related fields) so we don't need to null-check it in future usages.
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = @"Unknown",
|
||||
Title = @"Unknown",
|
||||
AuthorString = @"Unknown Creator",
|
||||
},
|
||||
Version = @"Normal",
|
||||
BaseDifficulty = new BeatmapDifficulty()
|
||||
};
|
||||
}
|
||||
IEnumerable<HitObject> IBeatmap.HitObjects => HitObjects;
|
||||
|
||||
public virtual IEnumerable<BeatmapStatistic> GetStatistics() => Enumerable.Empty<BeatmapStatistic>();
|
||||
|
||||
IBeatmap IBeatmap.Clone() => Clone();
|
||||
|
||||
public Beatmap<T> Clone()
|
||||
{
|
||||
var newInstance = (Beatmap<T>)MemberwiseClone();
|
||||
newInstance.BeatmapInfo = BeatmapInfo.DeepClone();
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Beatmap containing un-converted HitObjects.
|
||||
/// </summary>
|
||||
public class Beatmap : Beatmap<HitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new beatmap.
|
||||
/// </summary>
|
||||
/// <param name="original">The original beatmap to use the parameters of.</param>
|
||||
public Beatmap(Beatmap original)
|
||||
: base(original)
|
||||
{
|
||||
}
|
||||
|
||||
public Beatmap()
|
||||
{
|
||||
}
|
||||
public Beatmap Clone() => (Beatmap)base.Clone();
|
||||
}
|
||||
}
|
||||
|
@ -22,32 +22,34 @@ namespace osu.Game.Beatmaps
|
||||
remove => ObjectConverted -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a Beatmap can be converted using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The Beatmap to check.</param>
|
||||
/// <returns>Whether the Beatmap can be converted using this Beatmap Converter.</returns>
|
||||
public bool CanConvert(Beatmap beatmap) => ValidConversionTypes.All(t => beatmap.HitObjects.Any(t.IsInstanceOfType));
|
||||
public IBeatmap Beatmap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Beatmap using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="original">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted Beatmap.</returns>
|
||||
public Beatmap<T> Convert(Beatmap original)
|
||||
protected BeatmapConverter(IBeatmap beatmap)
|
||||
{
|
||||
// We always operate on a clone of the original beatmap, to not modify it game-wide
|
||||
return ConvertBeatmap(new Beatmap(original));
|
||||
Beatmap = beatmap;
|
||||
}
|
||||
|
||||
void IBeatmapConverter.Convert(Beatmap original) => Convert(original);
|
||||
/// <summary>
|
||||
/// Whether <see cref="Beatmap"/> can be converted by this <see cref="BeatmapConverter{T}"/>.
|
||||
/// </summary>
|
||||
public bool CanConvert => !Beatmap.HitObjects.Any() || ValidConversionTypes.All(t => Beatmap.HitObjects.Any(t.IsInstanceOfType));
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="Beatmap"/>.
|
||||
/// </summary>
|
||||
/// <returns>The converted Beatmap.</returns>
|
||||
public IBeatmap Convert()
|
||||
{
|
||||
// We always operate on a clone of the original beatmap, to not modify it game-wide
|
||||
return ConvertBeatmap(Beatmap.Clone());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the conversion of a Beatmap using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="original">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted Beatmap.</returns>
|
||||
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
|
||||
protected virtual Beatmap<T> ConvertBeatmap(IBeatmap original)
|
||||
{
|
||||
var beatmap = CreateBeatmap();
|
||||
|
||||
@ -67,7 +69,7 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="original">The hit object to convert.</param>
|
||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted hit object.</returns>
|
||||
private IEnumerable<T> convert(HitObject original, Beatmap beatmap)
|
||||
private IEnumerable<T> convert(HitObject original, IBeatmap beatmap)
|
||||
{
|
||||
// Check if the hitobject is already the converted type
|
||||
T tObject = original as T;
|
||||
@ -107,6 +109,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="original">The hit object to convert.</param>
|
||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted hit object.</returns>
|
||||
protected abstract IEnumerable<T> ConvertHitObject(HitObject original, Beatmap beatmap);
|
||||
protected abstract IEnumerable<T> ConvertHitObject(HitObject original, IBeatmap beatmap);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
@ -333,7 +335,7 @@ namespace osu.Game.Beatmaps
|
||||
ms.Position = 0;
|
||||
|
||||
var decoder = Decoder.GetDecoder<Beatmap>(sr);
|
||||
Beatmap beatmap = decoder.Decode(sr);
|
||||
IBeatmap beatmap = decoder.Decode(sr);
|
||||
|
||||
beatmap.BeatmapInfo.Path = name;
|
||||
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
||||
@ -341,9 +343,16 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
||||
|
||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
||||
beatmap.BeatmapInfo.Ruleset = ruleset;
|
||||
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
|
||||
|
||||
if (ruleset != null)
|
||||
{
|
||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
||||
var converted = new DummyConversionBeatmap(beatmap).GetPlayableBeatmap(ruleset);
|
||||
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(converted).Calculate();
|
||||
}
|
||||
else
|
||||
beatmap.BeatmapInfo.StarDifficulty = 0;
|
||||
|
||||
beatmapInfos.Add(beatmap.BeatmapInfo);
|
||||
}
|
||||
@ -351,5 +360,23 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
return beatmapInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
|
||||
/// </summary>
|
||||
private class DummyConversionBeatmap : WorkingBeatmap
|
||||
{
|
||||
private readonly IBeatmap beatmap;
|
||||
|
||||
public DummyConversionBeatmap(IBeatmap beatmap)
|
||||
: base(beatmap.BeatmapInfo)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
protected override IBeatmap GetBeatmap() => beatmap;
|
||||
protected override Texture GetBackground() => null;
|
||||
protected override Track GetTrack() => null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Beatmaps
|
||||
this.audioManager = audioManager;
|
||||
}
|
||||
|
||||
protected override Beatmap GetBeatmap()
|
||||
protected override IBeatmap GetBeatmap()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -2,30 +2,47 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public interface IBeatmapProcessor
|
||||
{
|
||||
IBeatmap Beatmap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Post-processes <see cref="Beatmap"/> to add mode-specific components that aren't added during conversion.
|
||||
/// <para>
|
||||
/// An example of such a usage is for combo colours.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
void PostProcess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a post-converted Beatmap.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam>
|
||||
public class BeatmapProcessor<TObject>
|
||||
where TObject : HitObject
|
||||
public class BeatmapProcessor : IBeatmapProcessor
|
||||
{
|
||||
public IBeatmap Beatmap { get; }
|
||||
|
||||
public BeatmapProcessor(IBeatmap beatmap)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post-processes a Beatmap to add mode-specific components that aren't added during conversion.
|
||||
/// <para>
|
||||
/// An example of such a usage is for combo colours.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The Beatmap to process.</param>
|
||||
public virtual void PostProcess(Beatmap<TObject> beatmap)
|
||||
public virtual void PostProcess()
|
||||
{
|
||||
IHasComboInformation lastObj = null;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects.OfType<IHasComboInformation>())
|
||||
foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>())
|
||||
{
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
|
@ -1,64 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Framework.Timing;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public abstract class DifficultyCalculator
|
||||
{
|
||||
protected double TimeRate = 1;
|
||||
|
||||
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||
}
|
||||
|
||||
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
|
||||
{
|
||||
protected readonly Beatmap<T> Beatmap;
|
||||
protected readonly Mod[] Mods;
|
||||
|
||||
protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null)
|
||||
{
|
||||
Mods = mods ?? new Mod[0];
|
||||
|
||||
var converter = CreateBeatmapConverter(beatmap);
|
||||
|
||||
foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<T>>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
Beatmap = converter.Convert(beatmap);
|
||||
|
||||
ApplyMods(Mods);
|
||||
|
||||
PreprocessHitObjects();
|
||||
}
|
||||
|
||||
protected virtual void ApplyMods(Mod[] mods)
|
||||
{
|
||||
var clock = new StopwatchClock();
|
||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
||||
TimeRate = clock.Rate;
|
||||
|
||||
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
foreach (var h in Beatmap.HitObjects)
|
||||
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject<T>>())
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
mod.ApplyToHitObject(obj);
|
||||
}
|
||||
|
||||
protected virtual void PreprocessHitObjects()
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract BeatmapConverter<T> CreateBeatmapConverter(Beatmap beatmap);
|
||||
}
|
||||
}
|
@ -6,7 +6,9 @@ using System.Collections.Generic;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@ -39,7 +41,7 @@ namespace osu.Game.Beatmaps
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
protected override Beatmap GetBeatmap() => new Beatmap();
|
||||
protected override IBeatmap GetBeatmap() => new Beatmap();
|
||||
|
||||
protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4");
|
||||
|
||||
@ -53,12 +55,14 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
|
||||
|
||||
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null;
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => null;
|
||||
|
||||
public override string Description => "dummy";
|
||||
|
||||
@ -68,6 +72,14 @@ namespace osu.Game.Beatmaps
|
||||
: base(rulesetInfo)
|
||||
{
|
||||
}
|
||||
|
||||
private class DummyBeatmapConverter : IBeatmapConverter
|
||||
{
|
||||
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||
public IBeatmap Beatmap { get; set; }
|
||||
public bool CanConvert => true;
|
||||
public IBeatmap Convert() => Beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
56
osu.Game/Beatmaps/IBeatmap.cs
Normal file
56
osu.Game/Beatmaps/IBeatmap.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public interface IBeatmap : IJsonSerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// This beatmap's info.
|
||||
/// </summary>
|
||||
BeatmapInfo BeatmapInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This beatmap's metadata.
|
||||
/// </summary>
|
||||
BeatmapMetadata Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The control points in this beatmap.
|
||||
/// </summary>
|
||||
ControlPointInfo ControlPointInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The breaks in this beatmap.
|
||||
/// </summary>
|
||||
List<BreakPeriod> Breaks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Total amount of break time in the beatmap.
|
||||
/// </summary>
|
||||
double TotalBreakTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The hitobjects contained by this beatmap.
|
||||
/// </summary>
|
||||
IEnumerable<HitObject> HitObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns statistics for the <see cref="HitObjects"/> contained in this beatmap.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerable<BeatmapStatistic> GetStatistics();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shallow-clone of this beatmap and returns it.
|
||||
/// </summary>
|
||||
/// <returns>The shallow-cloned beatmap.</returns>
|
||||
IBeatmap Clone();
|
||||
}
|
||||
}
|
@ -16,10 +16,16 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||
|
||||
IBeatmap Beatmap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Beatmap using this Beatmap Converter.
|
||||
/// Whether <see cref="Beatmap"/> can be converted by this <see cref="IBeatmapConverter"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
||||
void Convert(Beatmap beatmap);
|
||||
bool CanConvert { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="Beatmap"/>.
|
||||
/// </summary>
|
||||
IBeatmap Convert();
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Beatmaps.Legacy
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of Beatmap loaded from a legacy .osu beatmap file (version <=15).
|
||||
/// </summary>
|
||||
public class LegacyBeatmap : Beatmap
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new beatmap.
|
||||
/// </summary>
|
||||
/// <param name="original">The original beatmap to use the parameters of.</param>
|
||||
internal LegacyBeatmap(Beatmap original = null)
|
||||
: base(original)
|
||||
{
|
||||
HitObjects = original?.HitObjects;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ using osu.Framework.IO.File;
|
||||
using System.IO;
|
||||
using osu.Game.IO.Serialization;
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@ -36,7 +38,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
Mods.ValueChanged += mods => applyRateAdjustments();
|
||||
|
||||
beatmap = new AsyncLazy<Beatmap>(populateBeatmap);
|
||||
beatmap = new AsyncLazy<IBeatmap>(populateBeatmap);
|
||||
background = new AsyncLazy<Texture>(populateBackground, b => b == null || !b.IsDisposed);
|
||||
track = new AsyncLazy<Track>(populateTrack);
|
||||
waveform = new AsyncLazy<Waveform>(populateWaveform);
|
||||
@ -45,7 +47,7 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the <see cref="Beatmap"/>.
|
||||
/// Saves the <see cref="Beatmaps.Beatmap"/>.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
@ -55,7 +57,7 @@ namespace osu.Game.Beatmaps
|
||||
Process.Start(path);
|
||||
}
|
||||
|
||||
protected abstract Beatmap GetBeatmap();
|
||||
protected abstract IBeatmap GetBeatmap();
|
||||
protected abstract Texture GetBackground();
|
||||
protected abstract Track GetTrack();
|
||||
protected virtual Skin GetSkin() => new DefaultSkin();
|
||||
@ -63,12 +65,11 @@ namespace osu.Game.Beatmaps
|
||||
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
||||
|
||||
public bool BeatmapLoaded => beatmap.IsResultAvailable;
|
||||
public Beatmap Beatmap => beatmap.Value.Result;
|
||||
public async Task<Beatmap> GetBeatmapAsync() => await beatmap.Value;
|
||||
public IBeatmap Beatmap => beatmap.Value.Result;
|
||||
public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value;
|
||||
private readonly AsyncLazy<IBeatmap> beatmap;
|
||||
|
||||
private readonly AsyncLazy<Beatmap> beatmap;
|
||||
|
||||
private Beatmap populateBeatmap()
|
||||
private IBeatmap populateBeatmap()
|
||||
{
|
||||
var b = GetBeatmap() ?? new Beatmap();
|
||||
|
||||
@ -78,6 +79,51 @@ namespace osu.Game.Beatmaps
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
||||
/// <para>
|
||||
/// The returned <see cref="IBeatmap"/> is in a playable state - all <see cref="HitObject"/> and <see cref="BeatmapDifficulty"/> <see cref="Mod"/>s
|
||||
/// have been applied, and <see cref="HitObject"/>s have been fully constructed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
|
||||
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
|
||||
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
|
||||
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
IBeatmapConverter converter = rulesetInstance.CreateBeatmapConverter(Beatmap);
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (!converter.CanConvert)
|
||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
|
||||
|
||||
// Apply conversion mods
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToBeatmapConverter>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
// Convert
|
||||
IBeatmap converted = converter.Convert();
|
||||
|
||||
// Apply difficulty mods
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
// Post-process
|
||||
rulesetInstance.CreateBeatmapProcessor(converted)?.PostProcess();
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
foreach (var obj in converted.HitObjects)
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToHitObject>())
|
||||
foreach (var obj in converted.HitObjects)
|
||||
mod.ApplyToHitObject(obj);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
public bool BackgroundLoaded => background.IsResultAvailable;
|
||||
public Texture Background => background.Value.Result;
|
||||
public async Task<Texture> GetBackgroundAsync() => await background.Value;
|
||||
|
@ -44,19 +44,6 @@ namespace osu.Game.Graphics.Containers
|
||||
return base.OnClick(state);
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state)
|
||||
{
|
||||
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnDragStart(state);
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state) => State == Visibility.Hidden;
|
||||
|
||||
private void onStateChanged(Visibility visibility)
|
||||
{
|
||||
switch (visibility)
|
||||
|
@ -14,14 +14,18 @@ namespace osu.Game.Graphics.UserInterface
|
||||
public class BreadcrumbControl<T> : OsuTabControl<T>
|
||||
{
|
||||
private const float padding = 10;
|
||||
private const float item_chevron_size = 10;
|
||||
|
||||
protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value);
|
||||
protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value)
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
};
|
||||
|
||||
protected override float StripWidth() => base.StripWidth() - (padding + 8);
|
||||
protected override float StripWidth() => base.StripWidth() - (padding + item_chevron_size);
|
||||
|
||||
public BreadcrumbControl()
|
||||
{
|
||||
Height = 26;
|
||||
Height = 32;
|
||||
TabContainer.Spacing = new Vector2(padding, 0f);
|
||||
Current.ValueChanged += tab =>
|
||||
{
|
||||
@ -47,6 +51,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public override bool HandleKeyboardInput => State == Visibility.Visible;
|
||||
public override bool HandleMouseInput => State == Visibility.Visible;
|
||||
public override bool IsRemovable => true;
|
||||
|
||||
private Visibility state;
|
||||
|
||||
@ -77,13 +82,14 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public BreadcrumbTabItem(T value) : base(value)
|
||||
{
|
||||
Text.TextSize = 16;
|
||||
Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width
|
||||
Text.TextSize = 18;
|
||||
Text.Margin = new MarginPadding { Vertical = 8 };
|
||||
Padding = new MarginPadding { Right = padding + item_chevron_size };
|
||||
Add(Chevron = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(12),
|
||||
Size = new Vector2(item_chevron_size),
|
||||
Icon = FontAwesome.fa_chevron_right,
|
||||
Margin = new MarginPadding { Left = padding },
|
||||
Alpha = 0f,
|
||||
|
66
osu.Game/Overlays/HoldToConfirmOverlay.cs
Normal file
66
osu.Game/Overlays/HoldToConfirmOverlay.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
/// <summary>
|
||||
/// An overlay which will display a black screen that dims over a period before confirming an exit action.
|
||||
/// Action is BYO (derived class will need to call <see cref="BeginConfirm"/> and <see cref="AbortConfirm"/> from a user event).
|
||||
/// </summary>
|
||||
public abstract class HoldToConfirmOverlay : Container
|
||||
{
|
||||
public Action Action;
|
||||
|
||||
private Box overlay;
|
||||
|
||||
private const int activate_delay = 400;
|
||||
private const int fadeout_delay = 200;
|
||||
|
||||
private bool fired;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the overlay should be allowed to return from a fired state.
|
||||
/// </summary>
|
||||
protected virtual bool AllowMultipleFires => false;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
AlwaysPresent = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
overlay = new Box
|
||||
{
|
||||
Alpha = 0,
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void BeginConfirm()
|
||||
{
|
||||
if (!AllowMultipleFires && fired) return;
|
||||
overlay.FadeIn(activate_delay * (1 - overlay.Alpha), Easing.Out).OnComplete(_ =>
|
||||
{
|
||||
Action?.Invoke();
|
||||
fired = true;
|
||||
});
|
||||
}
|
||||
|
||||
protected void AbortConfirm()
|
||||
{
|
||||
if (!AllowMultipleFires && fired) return;
|
||||
overlay.FadeOut(fadeout_delay, Easing.Out);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Input;
|
||||
using OpenTK.Graphics;
|
||||
@ -43,7 +44,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
}
|
||||
|
||||
private OsuSpriteText text;
|
||||
private OsuSpriteText pressAKey;
|
||||
private OsuTextFlowContainer pressAKey;
|
||||
|
||||
private FillFlowContainer<KeyButton> buttons;
|
||||
|
||||
@ -95,10 +96,11 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight
|
||||
},
|
||||
pressAKey = new OsuSpriteText
|
||||
pressAKey = new OsuTextFlowContainer
|
||||
{
|
||||
Text = "Press a key to change binding, DEL to delete, ESC to cancel.",
|
||||
Y = height,
|
||||
Text = "Press a key to change binding, Shift+Delete to delete, Escape to cancel.",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding(padding),
|
||||
Alpha = 0,
|
||||
Colour = colours.YellowDark
|
||||
@ -204,9 +206,16 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
finalise();
|
||||
return true;
|
||||
case Key.Delete:
|
||||
bindTarget.UpdateKeyCombination(InputKey.None);
|
||||
finalise();
|
||||
return true;
|
||||
{
|
||||
if (state.Keyboard.ShiftPressed)
|
||||
{
|
||||
bindTarget.UpdateKeyCombination(InputKey.None);
|
||||
finalise();
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
|
||||
@ -223,6 +232,26 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnJoystickPress(InputState state, Framework.Input.JoystickEventArgs args)
|
||||
{
|
||||
if (!HasFocus)
|
||||
return false;
|
||||
|
||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
|
||||
finalise();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnJoystickRelease(InputState state, Framework.Input.JoystickEventArgs args)
|
||||
{
|
||||
if (!HasFocus)
|
||||
return base.OnJoystickRelease(state, args);
|
||||
|
||||
finalise();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void finalise()
|
||||
{
|
||||
if (bindTarget != null)
|
||||
@ -241,7 +270,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
GetContainingInputManager().ChangeFocus(null);
|
||||
|
||||
pressAKey.FadeOut(300, Easing.OutQuint);
|
||||
pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
|
||||
pressAKey.Padding = new MarginPadding { Top = height, Bottom = -pressAKey.DrawHeight };
|
||||
}
|
||||
|
||||
protected override void OnFocus(InputState state)
|
||||
@ -250,7 +279,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
|
||||
pressAKey.FadeIn(300, Easing.OutQuint);
|
||||
pressAKey.Padding = new MarginPadding();
|
||||
pressAKey.Padding = new MarginPadding { Top = height };
|
||||
|
||||
updateBindTarget();
|
||||
base.OnFocus(state);
|
||||
|
@ -116,6 +116,7 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
|
||||
private Mod mod;
|
||||
private readonly Container scaleContainer;
|
||||
|
||||
public Mod Mod
|
||||
{
|
||||
@ -149,14 +150,26 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
switch (args.Button)
|
||||
scaleContainer.ScaleTo(0.9f, 800, Easing.Out);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
scaleContainer.ScaleTo(1, 500, Easing.OutElastic);
|
||||
|
||||
// only trigger the event if we are inside the area of the button
|
||||
if (Contains(ToScreenSpace(state.Mouse.Position - Position)))
|
||||
{
|
||||
case MouseButton.Left:
|
||||
SelectNext(1);
|
||||
break;
|
||||
case MouseButton.Right:
|
||||
SelectNext(-1);
|
||||
break;
|
||||
switch (args.Button)
|
||||
{
|
||||
case MouseButton.Left:
|
||||
SelectNext(1);
|
||||
break;
|
||||
case MouseButton.Right:
|
||||
SelectNext(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -176,7 +189,8 @@ namespace osu.Game.Overlays.Mods
|
||||
start = Mods.Length - 1;
|
||||
|
||||
for (int i = start; i < Mods.Length && i >= 0; i += direction)
|
||||
if (SelectAt(i)) return;
|
||||
if (SelectAt(i))
|
||||
return;
|
||||
|
||||
Deselect();
|
||||
}
|
||||
@ -242,8 +256,14 @@ namespace osu.Game.Overlays.Mods
|
||||
Anchor = Anchor.TopCentre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
iconsContainer = new Container<ModIcon>
|
||||
scaleContainer = new Container
|
||||
{
|
||||
Child = iconsContainer = new Container<ModIcon>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -4,7 +4,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
@ -16,7 +17,8 @@ namespace osu.Game.Overlays.Music
|
||||
{
|
||||
public class PlaylistList : CompositeDrawable
|
||||
{
|
||||
public Action<BeatmapSetInfo> OnSelect;
|
||||
public Action<BeatmapSetInfo> Selected;
|
||||
public Action<BeatmapSetInfo, int> OrderChanged;
|
||||
|
||||
private readonly ItemsScrollContainer items;
|
||||
|
||||
@ -25,7 +27,8 @@ namespace osu.Game.Overlays.Music
|
||||
InternalChild = items = new ItemsScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OnSelect = set => OnSelect?.Invoke(set)
|
||||
Selected = set => Selected?.Invoke(set),
|
||||
OrderChanged = (s, i) => OrderChanged?.Invoke(s, i)
|
||||
};
|
||||
}
|
||||
|
||||
@ -35,34 +38,20 @@ namespace osu.Game.Overlays.Music
|
||||
set { base.Padding = value; }
|
||||
}
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
||||
{
|
||||
get { return items.Sets; }
|
||||
set { items.Sets = value; }
|
||||
}
|
||||
|
||||
public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet;
|
||||
public BeatmapSetInfo NextSet => items.NextSet;
|
||||
public BeatmapSetInfo PreviousSet => items.PreviousSet;
|
||||
|
||||
public BeatmapSetInfo SelectedSet
|
||||
{
|
||||
get { return items.SelectedSet; }
|
||||
set { items.SelectedSet = value; }
|
||||
}
|
||||
|
||||
public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet);
|
||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet);
|
||||
|
||||
public void Filter(string searchTerm) => items.SearchTerm = searchTerm;
|
||||
|
||||
private class ItemsScrollContainer : OsuScrollContainer
|
||||
{
|
||||
public Action<BeatmapSetInfo> OnSelect;
|
||||
public Action<BeatmapSetInfo> Selected;
|
||||
public Action<BeatmapSetInfo, int> OrderChanged;
|
||||
|
||||
private readonly SearchContainer search;
|
||||
private readonly FillFlowContainer<PlaylistItem> items;
|
||||
|
||||
private readonly IBindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public ItemsScrollContainer()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
@ -83,14 +72,36 @@ namespace osu.Game.Overlays.Music
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> Sets
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps, OsuGameBase osuGame)
|
||||
{
|
||||
get { return items.Select(x => x.BeatmapSetInfo).ToList(); }
|
||||
set
|
||||
{
|
||||
items.Clear();
|
||||
value.ForEach(AddBeatmapSet);
|
||||
}
|
||||
beatmaps.GetAllUsableBeatmapSets().ForEach(addBeatmapSet);
|
||||
beatmaps.ItemAdded += addBeatmapSet;
|
||||
beatmaps.ItemRemoved += removeBeatmapSet;
|
||||
|
||||
beatmapBacking.BindTo(osuGame.Beatmap);
|
||||
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
||||
}
|
||||
|
||||
private void addBeatmapSet(BeatmapSetInfo obj)
|
||||
{
|
||||
var newItem = new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) };
|
||||
|
||||
items.Add(newItem);
|
||||
items.SetLayoutPosition(newItem, items.Count - 1);
|
||||
}
|
||||
|
||||
private void removeBeatmapSet(BeatmapSetInfo obj)
|
||||
{
|
||||
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == obj.ID);
|
||||
if (itemToRemove != null)
|
||||
items.Remove(itemToRemove);
|
||||
}
|
||||
|
||||
private void updateSelectedSet()
|
||||
{
|
||||
foreach (PlaylistItem s in items.Children)
|
||||
s.Selected = s.BeatmapSetInfo.ID == beatmapBacking.Value.BeatmapSetInfo.ID;
|
||||
}
|
||||
|
||||
public string SearchTerm
|
||||
@ -99,34 +110,7 @@ namespace osu.Game.Overlays.Music
|
||||
set { search.SearchTerm = value; }
|
||||
}
|
||||
|
||||
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) };
|
||||
|
||||
items.Add(newItem);
|
||||
items.SetLayoutPosition(newItem, items.Count);
|
||||
}
|
||||
|
||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID);
|
||||
if (itemToRemove != null)
|
||||
items.Remove(itemToRemove);
|
||||
}
|
||||
|
||||
public BeatmapSetInfo SelectedSet
|
||||
{
|
||||
get { return items.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; }
|
||||
set
|
||||
{
|
||||
foreach (PlaylistItem s in items.Children)
|
||||
s.Selected = s.BeatmapSetInfo.ID == value?.ID;
|
||||
}
|
||||
}
|
||||
|
||||
public BeatmapSetInfo FirstVisibleSet => items.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo;
|
||||
public BeatmapSetInfo NextSet => (items.SkipWhile(i => !i.Selected).Skip(1).FirstOrDefault() ?? items.FirstOrDefault())?.BeatmapSetInfo;
|
||||
public BeatmapSetInfo PreviousSet => (items.TakeWhile(i => !i.Selected).LastOrDefault() ?? items.LastOrDefault())?.BeatmapSetInfo;
|
||||
|
||||
private Vector2 nativeDragPosition;
|
||||
private PlaylistItem draggedItem;
|
||||
@ -227,6 +211,7 @@ namespace osu.Game.Overlays.Music
|
||||
}
|
||||
|
||||
items.SetLayoutPosition(draggedItem, dstIndex);
|
||||
OrderChanged?.Invoke(draggedItem.BeatmapSetInfo, dstIndex);
|
||||
}
|
||||
|
||||
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
@ -19,18 +19,16 @@ namespace osu.Game.Overlays.Music
|
||||
public class PlaylistOverlay : OverlayContainer
|
||||
{
|
||||
private const float transition_duration = 600;
|
||||
|
||||
private const float playlist_height = 510;
|
||||
|
||||
public Action<BeatmapSetInfo, int> OrderChanged;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
private FilterControl filter;
|
||||
private PlaylistList list;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> BeatmapSets => list.BeatmapSets;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours)
|
||||
{
|
||||
@ -60,7 +58,8 @@ namespace osu.Game.Overlays.Music
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 },
|
||||
OnSelect = itemSelected,
|
||||
Selected = itemSelected,
|
||||
OrderChanged = (s, i) => OrderChanged?.Invoke(s, i)
|
||||
},
|
||||
filter = new FilterControl
|
||||
{
|
||||
@ -74,30 +73,16 @@ namespace osu.Game.Overlays.Music
|
||||
},
|
||||
};
|
||||
|
||||
beatmaps.ItemAdded += handleBeatmapAdded;
|
||||
beatmaps.ItemRemoved += handleBeatmapRemoved;
|
||||
|
||||
list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
|
||||
|
||||
beatmapBacking.BindTo(game.Beatmap);
|
||||
|
||||
filter.Search.OnCommit = (sender, newText) =>
|
||||
{
|
||||
var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
|
||||
if (beatmap != null) playSpecified(beatmap);
|
||||
BeatmapInfo beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
|
||||
if (beatmap != null)
|
||||
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(beatmap);
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo;
|
||||
beatmapBacking.TriggerChange();
|
||||
}
|
||||
|
||||
private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo));
|
||||
private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo));
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
filter.Search.HoldFocus = true;
|
||||
@ -123,49 +108,7 @@ namespace osu.Game.Overlays.Music
|
||||
return;
|
||||
}
|
||||
|
||||
playSpecified(set.Beatmaps.First());
|
||||
}
|
||||
|
||||
public void PlayPrevious()
|
||||
{
|
||||
var playable = list.PreviousSet;
|
||||
|
||||
if (playable != null)
|
||||
{
|
||||
playSpecified(playable.Beatmaps.First());
|
||||
list.SelectedSet = playable;
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayNext()
|
||||
{
|
||||
var playable = list.NextSet;
|
||||
|
||||
if (playable != null)
|
||||
{
|
||||
playSpecified(playable.Beatmaps.First());
|
||||
list.SelectedSet = playable;
|
||||
}
|
||||
}
|
||||
|
||||
private void playSpecified(BeatmapInfo info)
|
||||
{
|
||||
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking);
|
||||
|
||||
var track = beatmapBacking.Value.Track;
|
||||
|
||||
track.Restart();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (beatmaps != null)
|
||||
{
|
||||
beatmaps.ItemAdded -= handleBeatmapAdded;
|
||||
beatmaps.ItemRemoved -= handleBeatmapRemoved;
|
||||
}
|
||||
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(set.Beatmaps.First());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
@ -50,7 +51,10 @@ namespace osu.Game.Overlays
|
||||
|
||||
private LocalisationEngine localisation;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
|
||||
private List<BeatmapSetInfo> beatmapSets;
|
||||
private BeatmapSetInfo currentSet;
|
||||
|
||||
private Container dragContainer;
|
||||
private Container playerContainer;
|
||||
@ -93,8 +97,9 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, OsuColour colours, LocalisationEngine localisation)
|
||||
private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours, LocalisationEngine localisation)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
this.localisation = localisation;
|
||||
|
||||
Children = new Drawable[]
|
||||
@ -111,6 +116,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = player_height + 10,
|
||||
OrderChanged = playlistOrderChanged
|
||||
},
|
||||
playerContainer = new Container
|
||||
{
|
||||
@ -185,7 +191,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Action = next,
|
||||
Action = () => next(),
|
||||
Icon = FontAwesome.fa_step_forward,
|
||||
},
|
||||
}
|
||||
@ -214,11 +220,24 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
};
|
||||
|
||||
beatmapSets = beatmaps.GetAllUsableBeatmapSets();
|
||||
beatmaps.ItemAdded += handleBeatmapAdded;
|
||||
beatmaps.ItemRemoved += handleBeatmapRemoved;
|
||||
|
||||
beatmapBacking.BindTo(game.Beatmap);
|
||||
|
||||
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
|
||||
{
|
||||
beatmapSets.Remove(beatmapSetInfo);
|
||||
beatmapSets.Insert(index, beatmapSetInfo);
|
||||
}
|
||||
|
||||
private void handleBeatmapAdded(BeatmapSetInfo obj) => beatmapSets.Add(obj);
|
||||
private void handleBeatmapRemoved(BeatmapSetInfo obj) => beatmapSets.RemoveAll(s => s.ID == obj.ID);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
beatmapBacking.ValueChanged += beatmapChanged;
|
||||
@ -257,7 +276,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
|
||||
|
||||
if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && playlist.BeatmapSets.Any())
|
||||
if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && beatmapSets.Any())
|
||||
next();
|
||||
}
|
||||
else
|
||||
@ -271,7 +290,7 @@ namespace osu.Game.Overlays
|
||||
if (track == null)
|
||||
{
|
||||
if (!beatmapBacking.Disabled)
|
||||
playlist.PlayNext();
|
||||
next(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -284,13 +303,26 @@ namespace osu.Game.Overlays
|
||||
private void prev()
|
||||
{
|
||||
queuedDirection = TransformDirection.Prev;
|
||||
playlist.PlayPrevious();
|
||||
|
||||
var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault();
|
||||
if (playable != null)
|
||||
{
|
||||
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmapBacking);
|
||||
beatmapBacking.Value.Track.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
private void next()
|
||||
private void next(bool instant = false)
|
||||
{
|
||||
queuedDirection = TransformDirection.Next;
|
||||
playlist.PlayNext();
|
||||
if (!instant)
|
||||
queuedDirection = TransformDirection.Next;
|
||||
|
||||
var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault();
|
||||
if (playable != null)
|
||||
{
|
||||
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmapBacking);
|
||||
beatmapBacking.Value.Track.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
private WorkingBeatmap current;
|
||||
@ -314,8 +346,8 @@ namespace osu.Game.Overlays
|
||||
else
|
||||
{
|
||||
//figure out the best direction based on order in playlist.
|
||||
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
|
||||
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
|
||||
var last = beatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
|
||||
var next = beatmap == null ? -1 : beatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
|
||||
|
||||
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
|
||||
}
|
||||
|
@ -100,6 +100,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
public bool Adjust(GlobalAction action)
|
||||
{
|
||||
if (!IsLoaded) return false;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.DecreaseVolume:
|
||||
|
41
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
Normal file
41
osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public abstract class DifficultyCalculator
|
||||
{
|
||||
protected readonly IBeatmap Beatmap;
|
||||
protected readonly Mod[] Mods;
|
||||
|
||||
protected double TimeRate { get; private set; } = 1;
|
||||
|
||||
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
Mods = mods ?? new Mod[0];
|
||||
|
||||
ApplyMods(Mods);
|
||||
}
|
||||
|
||||
protected virtual void ApplyMods(Mod[] mods)
|
||||
{
|
||||
var clock = new StopwatchClock();
|
||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
||||
TimeRate = clock.Rate;
|
||||
}
|
||||
|
||||
protected virtual void PreprocessHitObjects()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||
}
|
||||
}
|
@ -3,41 +3,43 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public abstract class PerformanceCalculator
|
||||
{
|
||||
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||
}
|
||||
|
||||
public abstract class PerformanceCalculator<TObject> : PerformanceCalculator
|
||||
where TObject : HitObject
|
||||
{
|
||||
private readonly Dictionary<string, double> attributes = new Dictionary<string, double>();
|
||||
protected IDictionary<string, double> Attributes => attributes;
|
||||
|
||||
protected readonly Beatmap<TObject> Beatmap;
|
||||
protected readonly IBeatmap Beatmap;
|
||||
protected readonly Score Score;
|
||||
|
||||
protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
|
||||
protected double TimeRate { get; private set; } = 1;
|
||||
|
||||
protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
||||
{
|
||||
Score = score;
|
||||
|
||||
var converter = CreateBeatmapConverter();
|
||||
|
||||
foreach (var mod in score.Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
Beatmap = converter.Convert(beatmap);
|
||||
Beatmap = beatmap;
|
||||
|
||||
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
|
||||
diffCalc.Calculate(attributes);
|
||||
|
||||
ApplyMods(score.Mods);
|
||||
}
|
||||
|
||||
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
|
||||
protected virtual void ApplyMods(Mod[] mods)
|
||||
{
|
||||
var clock = new StopwatchClock();
|
||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
||||
TimeRate = clock.Rate;
|
||||
}
|
||||
|
||||
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||
}
|
||||
}
|
@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool;
|
||||
|
||||
protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true);
|
||||
protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap);
|
||||
|
||||
protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; }
|
||||
|
||||
|
@ -10,13 +10,12 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
|
||||
public interface IApplicableToBeatmapConverter<TObject> : IApplicableMod
|
||||
where TObject : HitObject
|
||||
public interface IApplicableToBeatmapConverter : IApplicableMod
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies this <see cref="Mod"/> to a <see cref="BeatmapConverter{TObject}"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmapConverter">The <see cref="BeatmapConverter{TObject}"/> to apply to.</param>
|
||||
void ApplyToBeatmapConverter(BeatmapConverter<TObject> beatmapConverter);
|
||||
void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter);
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,12 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// <summary>
|
||||
/// An interface for <see cref="Mod"/>s that can be applied to <see cref="HitObject"/>s.
|
||||
/// </summary>
|
||||
public interface IApplicableToHitObject<in TObject> : IApplicableMod
|
||||
where TObject : HitObject
|
||||
public interface IApplicableToHitObject : IApplicableMod
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param>
|
||||
void ApplyToHitObject(TObject hitObject);
|
||||
void ApplyToHitObject(HitObject hitObject);
|
||||
}
|
||||
}
|
||||
|
@ -51,16 +51,10 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY;
|
||||
|
||||
private HitWindows hitWindows;
|
||||
|
||||
/// <summary>
|
||||
/// The hit windows for this <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
public HitWindows HitWindows
|
||||
{
|
||||
get => hitWindows ?? (hitWindows = new HitWindows(overallDifficulty));
|
||||
protected set => hitWindows = value;
|
||||
}
|
||||
public HitWindows HitWindows { get; set; }
|
||||
|
||||
private readonly SortedList<HitObject> nestedHitObjects = new SortedList<HitObject>((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
||||
|
||||
@ -78,7 +72,11 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
nestedHitObjects.Clear();
|
||||
CreateNestedHitObjects();
|
||||
nestedHitObjects.ForEach(h => h.ApplyDefaults(controlPointInfo, difficulty));
|
||||
nestedHitObjects.ForEach(h =>
|
||||
{
|
||||
h.HitWindows = HitWindows;
|
||||
h.ApplyDefaults(controlPointInfo, difficulty);
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
@ -89,8 +87,9 @@ namespace osu.Game.Rulesets.Objects
|
||||
Kiai = effectPoint.KiaiMode;
|
||||
SampleControlPoint = samplePoint;
|
||||
|
||||
overallDifficulty = difficulty.OverallDifficulty;
|
||||
hitWindows = null;
|
||||
if (HitWindows == null)
|
||||
HitWindows = CreateHitWindows();
|
||||
HitWindows?.SetDifficulty(difficulty.OverallDifficulty);
|
||||
}
|
||||
|
||||
protected virtual void CreateNestedHitObjects()
|
||||
@ -98,5 +97,14 @@ namespace osu.Game.Rulesets.Objects
|
||||
}
|
||||
|
||||
protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="HitWindows"/> for this <see cref="HitObject"/>.
|
||||
/// This can be null to indicate that the <see cref="HitObject"/> has no <see cref="HitWindows"/>.
|
||||
/// <para>
|
||||
/// This will only be invoked if <see cref="HitWindows"/> hasn't been set externally (e.g. from a <see cref="BeatmapConverter"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
protected virtual HitWindows CreateHitWindows() => new HitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -63,10 +63,10 @@ namespace osu.Game.Rulesets.Objects
|
||||
public bool AllowsOk;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
|
||||
/// Sets hit windows with values that correspond to a difficulty parameter.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The parameter.</param>
|
||||
public HitWindows(double difficulty)
|
||||
public virtual void SetDifficulty(double difficulty)
|
||||
{
|
||||
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]);
|
||||
Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
|
||||
|
@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
public float X { get; set; }
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
32
osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitWindows.cs
Normal file
32
osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitWindows.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
{
|
||||
public class ConvertHitWindows : HitWindows
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<HitResult, (double od0, double od5, double od10)> base_ranges = new Dictionary<HitResult, (double, double, double)>
|
||||
{
|
||||
{ HitResult.Perfect, (44.8, 38.8, 27.8) },
|
||||
{ HitResult.Great, (128, 98, 68 ) },
|
||||
{ HitResult.Good, (194, 164, 134) },
|
||||
{ HitResult.Ok, (254, 224, 194) },
|
||||
{ HitResult.Meh, (302, 272, 242) },
|
||||
{ HitResult.Miss, (376, 346, 316) },
|
||||
};
|
||||
|
||||
public override void SetDifficulty(double difficulty)
|
||||
{
|
||||
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Perfect]);
|
||||
Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
|
||||
Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
|
||||
Ok = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Ok]);
|
||||
Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]);
|
||||
Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,5 +12,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
public double EndTime { get; set; }
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
public float X { get; set; }
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -15,5 +15,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public float X { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -18,5 +18,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
public float Y => Position.Y;
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
28
osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitWindows.cs
Normal file
28
osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitWindows.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
{
|
||||
public class ConvertHitWindows : HitWindows
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<HitResult, (double od0, double od5, double od10)> base_ranges = new Dictionary<HitResult, (double, double, double)>
|
||||
{
|
||||
{ HitResult.Great, (160, 100, 40) },
|
||||
{ HitResult.Good, (280, 200, 120) },
|
||||
{ HitResult.Meh, (400, 300, 200) },
|
||||
{ HitResult.Miss, (400, 400, 400) },
|
||||
};
|
||||
|
||||
public override void SetDifficulty(double difficulty)
|
||||
{
|
||||
Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
|
||||
Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
|
||||
Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]);
|
||||
Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,5 +18,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
public float Y => Position.Y;
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -20,5 +20,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
public float X => Position.X;
|
||||
|
||||
public float Y => Position.Y;
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
||||
internal sealed class ConvertHit : HitObject, IHasCombo
|
||||
{
|
||||
public bool NewCombo { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
28
osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitWindows.cs
Normal file
28
osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitWindows.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
||||
{
|
||||
public class ConvertHitWindows : HitWindows
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<HitResult, (double od0, double od5, double od10)> base_ranges = new Dictionary<HitResult, (double, double, double)>
|
||||
{
|
||||
{ HitResult.Great, (100, 70, 40) },
|
||||
{ HitResult.Good, (240, 160, 100) },
|
||||
{ HitResult.Meh, (270, 190, 140) },
|
||||
{ HitResult.Miss, (400, 400, 400) },
|
||||
};
|
||||
|
||||
public override void SetDifficulty(double difficulty)
|
||||
{
|
||||
Great = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Great]);
|
||||
Good = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Good]);
|
||||
Meh = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Meh]);
|
||||
Miss = BeatmapDifficulty.DifficultyRange(difficulty, base_ranges[HitResult.Miss]);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,5 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
||||
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasCombo
|
||||
{
|
||||
public bool NewCombo { get; set; }
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
||||
public double EndTime { get; set; }
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
protected override HitWindows CreateHitWindows() => new ConvertHitWindows();
|
||||
}
|
||||
}
|
||||
|
@ -99,11 +99,9 @@ namespace osu.Game.Rulesets.Objects
|
||||
cumulativeLength.Add(l);
|
||||
}
|
||||
|
||||
//TODO: Figure out if the following code is needed in some cases. Judging by the map
|
||||
// "Transform" http://osu.ppy.sh/s/484689 it seems like we should _not_ be doing this.
|
||||
// Lengthen slider curves that are too short compared to what's
|
||||
// in the .osu file.
|
||||
/*if (l < Length && calculatedPath.Count > 1)
|
||||
if (l < Distance && calculatedPath.Count > 1)
|
||||
{
|
||||
Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2];
|
||||
double d = diff.Length;
|
||||
@ -111,9 +109,9 @@ namespace osu.Game.Rulesets.Objects
|
||||
if (d <= 0)
|
||||
return;
|
||||
|
||||
calculatedPath[calculatedPath.Count - 1] += diff * (float)((Length - l) / d);
|
||||
cumulativeLength[calculatedPath.Count - 1] = Length;
|
||||
}*/
|
||||
calculatedPath[calculatedPath.Count - 1] += diff * (float)((Distance - l) / d);
|
||||
cumulativeLength[calculatedPath.Count - 1] = Distance;
|
||||
}
|
||||
}
|
||||
|
||||
public void Calculate()
|
||||
|
@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Replays.Types
|
||||
/// </summary>
|
||||
/// <param name="legacyFrame">The <see cref="LegacyReplayFrame"/> to extract values from.</param>
|
||||
/// <param name="beatmap">The beatmap.</param>
|
||||
void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap);
|
||||
void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
|
||||
namespace osu.Game.Rulesets
|
||||
{
|
||||
@ -22,8 +23,6 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
public readonly RulesetInfo RulesetInfo;
|
||||
|
||||
public virtual IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { };
|
||||
|
||||
public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>()
|
||||
// Confine all mods of each mod type into a single IEnumerable<Mod>
|
||||
.SelectMany(GetModsFor)
|
||||
@ -52,14 +51,17 @@ namespace osu.Game.Rulesets
|
||||
/// Attempt to create a hit renderer for a beatmap
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
|
||||
/// <param name="isForCurrentRuleset">Whether the hit renderer should assume the beatmap is for the current ruleset.</param>
|
||||
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
|
||||
/// <returns></returns>
|
||||
public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset);
|
||||
public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap);
|
||||
|
||||
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null);
|
||||
public abstract IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap);
|
||||
|
||||
public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null;
|
||||
public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null;
|
||||
|
||||
public abstract DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null);
|
||||
|
||||
public virtual PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => null;
|
||||
|
||||
public virtual HitObjectComposer CreateHitObjectComposer() => null;
|
||||
|
||||
@ -114,7 +116,8 @@ namespace osu.Game.Rulesets
|
||||
Name = Description,
|
||||
ShortName = ShortName,
|
||||
InstantiationInfo = GetType().AssemblyQualifiedName,
|
||||
ID = LegacyID
|
||||
ID = LegacyID,
|
||||
Available = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.LoadFrom(file);
|
||||
loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsSubclassOf(typeof(Ruleset)));
|
||||
loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset)));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring.Legacy
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="LegacyScoreParser"/> which retrieves the applicable <see cref="Beatmap"/> and <see cref="Ruleset"/>
|
||||
/// for the score from the database.
|
||||
/// </summary>
|
||||
public class DatabasedLegacyScoreParser : LegacyScoreParser
|
||||
{
|
||||
private readonly RulesetStore rulesets;
|
||||
private readonly BeatmapManager beatmaps;
|
||||
|
||||
public DatabasedLegacyScoreParser(RulesetStore rulesets, BeatmapManager beatmaps)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
this.beatmaps = beatmaps;
|
||||
}
|
||||
|
||||
protected override Ruleset GetRuleset(int rulesetId) => rulesets.GetRuleset(rulesetId).CreateInstance();
|
||||
protected override WorkingBeatmap GetBeatmap(string md5Hash) => beatmaps.GetWorkingBeatmap(beatmaps.QueryBeatmap(b => b.MD5Hash == md5Hash));
|
||||
}
|
||||
}
|
@ -14,18 +14,9 @@ using System.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring.Legacy
|
||||
{
|
||||
public class LegacyScoreParser
|
||||
public abstract class LegacyScoreParser
|
||||
{
|
||||
private readonly RulesetStore rulesets;
|
||||
private readonly BeatmapManager beatmaps;
|
||||
|
||||
public LegacyScoreParser(RulesetStore rulesets, BeatmapManager beatmaps)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
this.beatmaps = beatmaps;
|
||||
}
|
||||
|
||||
private Beatmap currentBeatmap;
|
||||
private IBeatmap currentBeatmap;
|
||||
private Ruleset currentRuleset;
|
||||
|
||||
public Score Parse(Stream stream)
|
||||
@ -34,33 +25,35 @@ namespace osu.Game.Rulesets.Scoring.Legacy
|
||||
|
||||
using (SerializationReader sr = new SerializationReader(stream))
|
||||
{
|
||||
score = new Score { Ruleset = rulesets.GetRuleset(sr.ReadByte()) };
|
||||
currentRuleset = score.Ruleset.CreateInstance();
|
||||
currentRuleset = GetRuleset(sr.ReadByte());
|
||||
score = new Score { Ruleset = currentRuleset.RulesetInfo };
|
||||
|
||||
/* score.Pass = true;*/
|
||||
var version = sr.ReadInt32();
|
||||
|
||||
/* score.FileChecksum = */
|
||||
var beatmapHash = sr.ReadString();
|
||||
score.Beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == beatmapHash);
|
||||
currentBeatmap = beatmaps.GetWorkingBeatmap(score.Beatmap).Beatmap;
|
||||
currentBeatmap = GetBeatmap(sr.ReadString()).Beatmap;
|
||||
score.Beatmap = currentBeatmap.BeatmapInfo;
|
||||
|
||||
/* score.PlayerName = */
|
||||
score.User = new User { Username = sr.ReadString() };
|
||||
/* var localScoreChecksum = */
|
||||
sr.ReadString();
|
||||
/* score.Count300 = */
|
||||
sr.ReadUInt16();
|
||||
/* score.Count100 = */
|
||||
sr.ReadUInt16();
|
||||
/* score.Count50 = */
|
||||
sr.ReadUInt16();
|
||||
/* score.CountGeki = */
|
||||
sr.ReadUInt16();
|
||||
/* score.CountKatu = */
|
||||
sr.ReadUInt16();
|
||||
/* score.CountMiss = */
|
||||
sr.ReadUInt16();
|
||||
|
||||
var count300 = sr.ReadUInt16();
|
||||
var count100 = sr.ReadUInt16();
|
||||
var count50 = sr.ReadUInt16();
|
||||
var countGeki = sr.ReadUInt16();
|
||||
var countKatu = sr.ReadUInt16();
|
||||
var countMiss = sr.ReadUInt16();
|
||||
|
||||
score.Statistics[HitResult.Great] = count300;
|
||||
score.Statistics[HitResult.Good] = count100;
|
||||
score.Statistics[HitResult.Meh] = count50;
|
||||
score.Statistics[HitResult.Perfect] = countGeki;
|
||||
score.Statistics[HitResult.Ok] = countKatu;
|
||||
score.Statistics[HitResult.Miss] = countMiss;
|
||||
|
||||
score.TotalScore = sr.ReadInt32();
|
||||
score.MaxCombo = sr.ReadUInt16();
|
||||
/* score.Perfect = */
|
||||
@ -81,6 +74,34 @@ namespace osu.Game.Rulesets.Scoring.Legacy
|
||||
/*OnlineId =*/
|
||||
sr.ReadInt32();
|
||||
|
||||
switch (score.Ruleset.ID)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
int totalHits = count50 + count100 + count300 + countMiss;
|
||||
score.Accuracy = totalHits > 0 ? (double)(count50 * 50 + count100 * 100 + count300 * 300) / (totalHits * 300) : 1;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
int totalHits = count50 + count100 + count300 + countMiss;
|
||||
score.Accuracy = totalHits > 0 ? (double)(count100 * 150 + count300 * 300) / (totalHits * 300) : 1;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
int totalHits = count50 + count100 + count300 + countMiss + countKatu;
|
||||
score.Accuracy = totalHits > 0 ? (double)(count50 + count100 + count300 ) / totalHits : 1;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
int totalHits = count50 + count100 + count300 + countMiss + countGeki + countKatu;
|
||||
score.Accuracy = totalHits > 0 ? (double)(count50 * 50 + count100 * 100 + countKatu * 200 + (count300 + countGeki) * 300) / (totalHits * 300) : 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using (var replayInStream = new MemoryStream(compressedReplay))
|
||||
{
|
||||
byte[] properties = new byte[5];
|
||||
@ -150,5 +171,19 @@ namespace osu.Game.Rulesets.Scoring.Legacy
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="Ruleset"/> for a specific id.
|
||||
/// </summary>
|
||||
/// <param name="rulesetId">The id.</param>
|
||||
/// <returns>The <see cref="Ruleset"/>.</returns>
|
||||
protected abstract Ruleset GetRuleset(int rulesetId);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="WorkingBeatmap"/> corresponding to an MD5 hash.
|
||||
/// </summary>
|
||||
/// <param name="md5Hash">The MD5 hash.</param>
|
||||
/// <returns>The <see cref="WorkingBeatmap"/>.</returns>
|
||||
protected abstract WorkingBeatmap GetBeatmap(string md5Hash);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
public Score ReadReplayFile(string replayFilename)
|
||||
{
|
||||
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
|
||||
return new LegacyScoreParser(rulesets, beatmaps).Parse(s);
|
||||
return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,11 +190,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
protected readonly WorkingBeatmap WorkingBeatmap;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
|
||||
/// </summary>
|
||||
public readonly bool IsForCurrentRuleset;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
@ -206,43 +201,18 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The ruleset being repesented.</param>
|
||||
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
|
||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
|
||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap)
|
||||
: base(ruleset)
|
||||
{
|
||||
Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
|
||||
|
||||
WorkingBeatmap = workingBeatmap;
|
||||
IsForCurrentRuleset = isForCurrentRuleset;
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
Mods = workingBeatmap.Mods.Value;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
BeatmapConverter<TObject> converter = CreateBeatmapConverter();
|
||||
BeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (!converter.CanConvert(workingBeatmap.Beatmap))
|
||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter}).");
|
||||
|
||||
// Apply conversion adjustments before converting
|
||||
foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
// Convert the beatmap
|
||||
Beatmap = converter.Convert(workingBeatmap.Beatmap);
|
||||
|
||||
// Apply difficulty adjustments from mods before using Difficulty.
|
||||
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
// Post-process the beatmap
|
||||
processor.PostProcess(Beatmap);
|
||||
|
||||
// Apply defaults
|
||||
foreach (var h in Beatmap.HitObjects)
|
||||
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
|
||||
|
||||
KeyBindingInputManager = CreateInputManager();
|
||||
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
||||
@ -277,10 +247,6 @@ namespace osu.Game.Rulesets.UI
|
||||
if (mods == null)
|
||||
return;
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject<TObject>>())
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
mod.ApplyToHitObject(obj);
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToRulesetContainer<TObject>>())
|
||||
mod.ApplyToRulesetContainer(this);
|
||||
}
|
||||
@ -324,13 +290,6 @@ namespace osu.Game.Rulesets.UI
|
||||
Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a processor to perform post-processing operations
|
||||
/// on HitObjects in converted Beatmaps.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap processor.</returns>
|
||||
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Computes the size of the <see cref="Playfield"/> in relative coordinate space after aspect adjustments.
|
||||
/// </summary>
|
||||
@ -344,12 +303,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default
|
||||
|
||||
/// <summary>
|
||||
/// Creates a converter to convert Beatmap to a specific mode.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap converter.</returns>
|
||||
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DrawableHitObject from a HitObject.
|
||||
/// </summary>
|
||||
@ -377,9 +330,8 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The ruleset being repesented.</param>
|
||||
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
|
||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// <returns></returns>
|
||||
protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
|
||||
|
||||
protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
|
34
osu.Game/Screens/Menu/ExitConfirmOverlay.cs
Normal file
34
osu.Game/Screens/Menu/ExitConfirmOverlay.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Overlays;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class ExitConfirmOverlay : HoldToConfirmOverlay
|
||||
{
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Key == Key.Escape && !args.Repeat)
|
||||
{
|
||||
BeginConfirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||
{
|
||||
if (args.Key == Key.Escape)
|
||||
{
|
||||
AbortConfirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyUp(state, args);
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Charts;
|
||||
using osu.Game.Screens.Direct;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Multiplayer;
|
||||
using osu.Game.Screens.Multi.Screens;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Tournament;
|
||||
|
||||
@ -39,6 +39,10 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new ExitConfirmOverlay
|
||||
{
|
||||
Action = Exit,
|
||||
},
|
||||
new ParallaxContainer
|
||||
{
|
||||
ParallaxAmount = 0.01f,
|
||||
|
@ -9,7 +9,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Components
|
||||
{
|
||||
public class DrawableGameType : CircularContainer, IHasTooltip
|
||||
{
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -17,8 +15,10 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Components
|
||||
{
|
||||
public class DrawableRoom : OsuClickableContainer
|
||||
{
|
@ -1,14 +1,14 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Components
|
||||
{
|
||||
public class ModeTypeInfo : Container
|
||||
{
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -11,8 +10,9 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Users;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Components
|
||||
{
|
||||
public class ParticipantInfo : Container
|
||||
{
|
@ -2,8 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -20,8 +18,10 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Components
|
||||
{
|
||||
public class RoomInspector : Container
|
||||
{
|
@ -4,7 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Screens
|
||||
{
|
||||
public class Lobby : ScreenWhiteBox
|
||||
{
|
@ -3,14 +3,14 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Screens
|
||||
{
|
||||
public class Match : ScreenWhiteBox
|
||||
{
|
@ -4,7 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
namespace osu.Game.Screens.Multi.Screens
|
||||
{
|
||||
public class MatchCreate : ScreenWhiteBox
|
||||
{
|
@ -1,50 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class HotkeyRetryOverlay : Container, IKeyBindingHandler<GlobalAction>
|
||||
public class HotkeyRetryOverlay : HoldToConfirmOverlay, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public Action Action;
|
||||
|
||||
private Box overlay;
|
||||
|
||||
private const int activate_delay = 400;
|
||||
private const int fadeout_delay = 200;
|
||||
|
||||
private bool fired;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
AlwaysPresent = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
overlay = new Box
|
||||
{
|
||||
Alpha = 0,
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (action != GlobalAction.QuickRetry) return false;
|
||||
|
||||
overlay.FadeIn(activate_delay, Easing.Out);
|
||||
BeginConfirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -52,18 +21,8 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (action != GlobalAction.QuickRetry) return false;
|
||||
|
||||
overlay.FadeOut(fadeout_delay, Easing.Out);
|
||||
AbortConfirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
if (!fired && overlay.Alpha == 1)
|
||||
{
|
||||
fired = true;
|
||||
Action?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Screens.Play
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
|
||||
|
||||
Beatmap beatmap;
|
||||
IBeatmap beatmap;
|
||||
|
||||
try
|
||||
{
|
||||
@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
try
|
||||
{
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working);
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play
|
||||
// let's try again forcing the beatmap's ruleset.
|
||||
ruleset = beatmap.BeatmapInfo.Ruleset;
|
||||
rulesetInstance = ruleset.CreateInstance();
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true);
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap);
|
||||
}
|
||||
|
||||
if (!RulesetContainer.Objects.Any())
|
||||
@ -162,7 +162,7 @@ namespace osu.Game.Screens.Play
|
||||
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
|
||||
},
|
||||
OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
|
||||
Children = new Drawable[]
|
||||
Children = new[]
|
||||
{
|
||||
storyboardContainer = new Container
|
||||
{
|
||||
@ -174,12 +174,12 @@ namespace osu.Game.Screens.Play
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = RulesetContainer
|
||||
},
|
||||
new SkipOverlay(firstObjectTime)
|
||||
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
|
||||
{
|
||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ProcessCustomClock = false,
|
||||
AdjustableClock = adjustableClock,
|
||||
FramedClock = offsetClock,
|
||||
Breaks = beatmap.Breaks
|
||||
},
|
||||
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
||||
{
|
||||
@ -188,13 +188,14 @@ namespace osu.Game.Screens.Play
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
|
||||
RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
|
||||
new SkipOverlay(firstObjectTime)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
Breaks = beatmap.Breaks
|
||||
}
|
||||
AdjustableClock = adjustableClock,
|
||||
FramedClock = offsetClock,
|
||||
},
|
||||
}
|
||||
},
|
||||
failOverlay = new FailOverlay
|
||||
|
@ -85,11 +85,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (currentSecond != previousSecond && songCurrentTime < songLength)
|
||||
{
|
||||
timeCurrent.Text = TimeSpan.FromSeconds(currentSecond).ToString(songCurrentTime < 0 ? @"\-m\:ss" : @"m\:ss");
|
||||
timeLeft.Text = TimeSpan.FromMilliseconds(endTime - AudioClock.CurrentTime).ToString(@"\-m\:ss");
|
||||
timeCurrent.Text = formatTime(TimeSpan.FromSeconds(currentSecond));
|
||||
timeLeft.Text = formatTime(TimeSpan.FromMilliseconds(endTime - AudioClock.CurrentTime));
|
||||
|
||||
previousSecond = currentSecond;
|
||||
}
|
||||
}
|
||||
|
||||
private string formatTime(TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : "")}{timeSpan.Duration().TotalMinutes:N0}:{timeSpan.Duration().Seconds:D2}";
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@ -21,6 +23,8 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
@ -28,6 +32,8 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
|
||||
|
||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
protected BufferedWedgeInfo Info;
|
||||
|
||||
public BeatmapInfoWedge()
|
||||
@ -46,6 +52,14 @@ namespace osu.Game.Screens.Select
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] OsuGame osuGame)
|
||||
{
|
||||
if (osuGame != null)
|
||||
ruleset.BindTo(osuGame.Ruleset);
|
||||
ruleset.ValueChanged += updateRuleset;
|
||||
}
|
||||
|
||||
protected override bool BlockPassThroughMouse => false;
|
||||
|
||||
protected override void PopIn()
|
||||
@ -62,19 +76,39 @@ namespace osu.Game.Screens.Select
|
||||
this.FadeOut(500, Easing.In);
|
||||
}
|
||||
|
||||
private WorkingBeatmap beatmap;
|
||||
|
||||
public void UpdateBeatmap(WorkingBeatmap beatmap)
|
||||
{
|
||||
LoadComponentAsync(new BufferedWedgeInfo(beatmap)
|
||||
{
|
||||
Shear = -Shear,
|
||||
Depth = Info?.Depth + 1 ?? 0,
|
||||
}, newInfo =>
|
||||
this.beatmap = beatmap;
|
||||
loadBeatmap();
|
||||
}
|
||||
|
||||
private void updateRuleset(RulesetInfo ruleset) => loadBeatmap();
|
||||
|
||||
private void loadBeatmap()
|
||||
{
|
||||
void updateState()
|
||||
{
|
||||
State = beatmap == null ? Visibility.Hidden : Visibility.Visible;
|
||||
|
||||
Info?.FadeOut(250);
|
||||
Info?.Expire();
|
||||
}
|
||||
|
||||
if (beatmap == null)
|
||||
{
|
||||
updateState();
|
||||
return;
|
||||
}
|
||||
|
||||
LoadComponentAsync(new BufferedWedgeInfo(beatmap, ruleset.Value)
|
||||
{
|
||||
Shear = -Shear,
|
||||
Depth = Info?.Depth + 1 ?? 0,
|
||||
}, newInfo =>
|
||||
{
|
||||
updateState();
|
||||
Add(Info = newInfo);
|
||||
});
|
||||
}
|
||||
@ -90,9 +124,13 @@ namespace osu.Game.Screens.Select
|
||||
private UnicodeBindableString titleBinding;
|
||||
private UnicodeBindableString artistBinding;
|
||||
|
||||
public BufferedWedgeInfo(WorkingBeatmap working)
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
public BufferedWedgeInfo(WorkingBeatmap working, RulesetInfo userRuleset)
|
||||
{
|
||||
this.working = working;
|
||||
|
||||
ruleset = userRuleset ?? working.BeatmapInfo.Ruleset;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -211,11 +249,10 @@ namespace osu.Game.Screens.Select
|
||||
private InfoLabel[] getInfoLabels()
|
||||
{
|
||||
var beatmap = working.Beatmap;
|
||||
var info = working.BeatmapInfo;
|
||||
|
||||
List<InfoLabel> labels = new List<InfoLabel>();
|
||||
|
||||
if (beatmap?.HitObjects?.Count > 0)
|
||||
if (beatmap?.HitObjects?.Any() == true)
|
||||
{
|
||||
HitObject lastObject = beatmap.HitObjects.LastOrDefault();
|
||||
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
|
||||
@ -224,7 +261,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
Name = "Length",
|
||||
Icon = FontAwesome.fa_clock_o,
|
||||
Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
|
||||
Content = TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
|
||||
}));
|
||||
|
||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
||||
@ -234,14 +271,26 @@ namespace osu.Game.Screens.Select
|
||||
Content = getBPMRange(beatmap),
|
||||
}));
|
||||
|
||||
//get statistics from the current ruleset.
|
||||
labels.AddRange(info.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s)));
|
||||
IBeatmap playableBeatmap;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to get the beatmap with the user's ruleset
|
||||
playableBeatmap = working.GetPlayableBeatmap(ruleset);
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
|
||||
playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset);
|
||||
}
|
||||
|
||||
labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)));
|
||||
}
|
||||
|
||||
return labels.ToArray();
|
||||
}
|
||||
|
||||
private string getBPMRange(Beatmap beatmap)
|
||||
private string getBPMRange(IBeatmap beatmap)
|
||||
{
|
||||
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
|
||||
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
|
||||
|
@ -10,12 +10,14 @@ using NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[TestFixture]
|
||||
public abstract class BeatmapConversionTest<TConvertValue>
|
||||
public abstract class BeatmapConversionTest<TRuleset, TConvertValue>
|
||||
where TRuleset : Ruleset, new()
|
||||
where TConvertValue : IEquatable<TConvertValue>
|
||||
{
|
||||
private const string resource_namespace = "Testing.Beatmaps";
|
||||
@ -79,6 +81,9 @@ namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
var beatmap = getBeatmap(name);
|
||||
|
||||
var rulesetInstance = new TRuleset();
|
||||
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
|
||||
|
||||
var result = new ConvertResult();
|
||||
|
||||
var converter = CreateConverter(beatmap);
|
||||
@ -92,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
result.Mappings.Add(mapping);
|
||||
};
|
||||
|
||||
converter.Convert(beatmap);
|
||||
converter.Convert();
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -107,7 +112,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private Beatmap getBeatmap(string name)
|
||||
private IBeatmap getBeatmap(string name)
|
||||
{
|
||||
using (var resStream = openResource($"{resource_namespace}.{name}.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
@ -125,7 +130,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<TConvertValue> CreateConvertValue(HitObject hitObject);
|
||||
protected abstract IBeatmapConverter CreateConverter(Beatmap beatmap);
|
||||
protected abstract IBeatmapConverter CreateConverter(IBeatmap beatmap);
|
||||
|
||||
private class ConvertMapping
|
||||
{
|
||||
|
@ -12,8 +12,14 @@ namespace osu.Game.Tests.Beatmaps
|
||||
public class TestBeatmap : Beatmap
|
||||
{
|
||||
public TestBeatmap(RulesetInfo ruleset)
|
||||
: base(createTestBeatmap())
|
||||
{
|
||||
var baseBeatmap = createTestBeatmap();
|
||||
|
||||
BeatmapInfo = baseBeatmap.BeatmapInfo;
|
||||
ControlPointInfo = baseBeatmap.ControlPointInfo;
|
||||
Breaks = baseBeatmap.Breaks;
|
||||
HitObjects = baseBeatmap.HitObjects;
|
||||
|
||||
BeatmapInfo.Ruleset = ruleset;
|
||||
}
|
||||
|
||||
|
@ -17,14 +17,14 @@ namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
}
|
||||
|
||||
public TestWorkingBeatmap(Beatmap beatmap)
|
||||
public TestWorkingBeatmap(IBeatmap beatmap)
|
||||
: base(beatmap.BeatmapInfo)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
private readonly Beatmap beatmap;
|
||||
protected override Beatmap GetBeatmap() => beatmap;
|
||||
private readonly IBeatmap beatmap;
|
||||
protected override IBeatmap GetBeatmap() => beatmap;
|
||||
protected override Texture GetBackground() => null;
|
||||
|
||||
protected override Track GetTrack()
|
||||
|
@ -259,9 +259,9 @@ namespace osu.Game.Tests.Visual
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
private readonly Score score;
|
||||
private readonly Beatmap beatmap;
|
||||
private readonly IBeatmap beatmap;
|
||||
|
||||
public PerformanceDisplay(Score score, Beatmap beatmap)
|
||||
public PerformanceDisplay(Score score, IBeatmap beatmap)
|
||||
{
|
||||
this.score = score;
|
||||
this.beatmap = beatmap;
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Beatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
|
||||
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);
|
||||
|
||||
private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance());
|
||||
|
||||
|
Reference in New Issue
Block a user