Merge remote-tracking branch 'origin/Liswiera-FL-changes' into Liswiera-FL-changes

This commit is contained in:
mk-56
2022-01-15 21:44:03 +01:00
653 changed files with 12915 additions and 5592 deletions

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Difficulty
protected const int ATTRIB_ID_OVERALL_DIFFICULTY = 5;
protected const int ATTRIB_ID_APPROACH_RATE = 7;
protected const int ATTRIB_ID_MAX_COMBO = 9;
protected const int ATTRIB_ID_STRAIN = 11;
protected const int ATTRIB_ID_DIFFICULTY = 11;
protected const int ATTRIB_ID_GREAT_HIT_WINDOW = 13;
protected const int ATTRIB_ID_SCORE_MULTIPLIER = 15;
protected const int ATTRIB_ID_FLASHLIGHT = 17;

View File

@ -120,14 +120,14 @@ namespace osu.Game.Rulesets.Difficulty
/// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap.
/// </summary>
/// <returns>A collection of structures describing the difficulty of the beatmap for each mod combination.</returns>
public IEnumerable<DifficultyAttributes> CalculateAll()
public IEnumerable<DifficultyAttributes> CalculateAll(CancellationToken cancellationToken = default)
{
foreach (var combination in CreateDifficultyAdjustmentModCombinations())
{
if (combination is MultiMod multi)
yield return Calculate(multi.Mods);
yield return Calculate(multi.Mods, cancellationToken);
else
yield return Calculate(combination.Yield());
yield return Calculate(combination.Yield(), cancellationToken);
}
}
@ -145,7 +145,11 @@ namespace osu.Game.Rulesets.Difficulty
{
playableMods = mods.Select(m => m.DeepClone()).ToArray();
Beatmap = beatmap.GetPlayableBeatmap(ruleset, playableMods, cancellationToken);
// Only pass through the cancellation token if it's non-default.
// This allows for the default timeout to be applied for playable beatmap construction.
Beatmap = cancellationToken == default
? beatmap.GetPlayableBeatmap(ruleset, playableMods)
: beatmap.GetPlayableBeatmap(ruleset, playableMods, cancellationToken);
var track = new TrackVirtual(10000);
playableMods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
namespace osu.Game.Rulesets.Difficulty
{
public class PerformanceAttributes
{
/// <summary>
/// Calculated score performance points.
/// </summary>
[JsonProperty("pp")]
public double Total { get; set; }
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
@ -37,6 +36,6 @@ namespace osu.Game.Rulesets.Difficulty
TimeRate = track.Rate;
}
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
public abstract PerformanceAttributes Calculate();
}
}

View File

@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Overlays;
namespace osu.Game.Rulesets.Edit
{
public class ToolboxGroup : PlayerSettingsGroup
public class EditorToolboxGroup : SettingsToolboxGroup
{
public ToolboxGroup(string title)
public EditorToolboxGroup(string title)
: base(title)
{
RelativeSizeAxes = Axes.X;

View File

@ -13,6 +13,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
@ -80,7 +81,7 @@ namespace osu.Game.Rulesets.Edit
[BackgroundDependencyLoader]
private void load()
{
Config = Dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
Config = Dependencies.Get<IRulesetConfigCache>().GetConfigFor(Ruleset);
try
{
@ -98,14 +99,11 @@ namespace osu.Game.Rulesets.Edit
dependencies.CacheAs(Playfield);
const float toolbar_width = 200;
InternalChildren = new Drawable[]
{
new Container
{
Name = "Content",
Padding = new MarginPadding { Left = toolbar_width },
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
@ -117,20 +115,15 @@ namespace osu.Game.Rulesets.Edit
.WithChild(BlueprintContainer = CreateBlueprintContainer())
}
},
new FillFlowContainer
new LeftToolboxFlow
{
Name = "Sidebar",
RelativeSizeAxes = Axes.Y,
Width = toolbar_width,
Padding = new MarginPadding { Right = 10 },
Spacing = new Vector2(10),
Children = new Drawable[]
{
new ToolboxGroup("toolbox (1-9)")
new EditorToolboxGroup("toolbox (1-9)")
{
Child = toolboxCollection = new EditorRadioButtonCollection { RelativeSizeAxes = Axes.X }
},
new ToolboxGroup("toggles (Q~P)")
new EditorToolboxGroup("toggles (Q~P)")
{
Child = togglesCollection = new FillFlowContainer
{
@ -427,6 +420,18 @@ namespace osu.Game.Rulesets.Edit
}
#endregion
private class LeftToolboxFlow : ExpandingButtonContainer
{
public LeftToolboxFlow()
: base(80, 200)
{
RelativeSizeAxes = Axes.Y;
Padding = new MarginPadding { Right = 10 };
FillFlow.Spacing = new Vector2(10);
}
}
}
/// <summary>

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics.Containers;
namespace osu.Game.Rulesets.Edit
{
public class ScrollingToolboxGroup : ToolboxGroup
public class ScrollingToolboxGroup : EditorToolboxGroup
{
protected readonly OsuScrollContainer Scroll;

View File

@ -0,0 +1,23 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable enable
using osu.Game.Rulesets.Configuration;
namespace osu.Game.Rulesets
{
/// <summary>
/// A cache that provides a single <see cref="IRulesetConfigManager"/> per-ruleset.
/// This is done to support referring to and updating ruleset configs from multiple locations in the absence of inter-config bindings.
/// </summary>
public interface IRulesetConfigCache
{
/// <summary>
/// Retrieves the <see cref="IRulesetConfigManager"/> for a <see cref="Ruleset"/>.
/// </summary>
/// <param name="ruleset">The <see cref="Ruleset"/> to retrieve the <see cref="IRulesetConfigManager"/> for.</param>
/// <returns>The <see cref="IRulesetConfigManager"/> defined by <paramref name="ruleset"/>, null if <paramref name="ruleset"/> doesn't define one.</returns>
public IRulesetConfigManager? GetConfigFor(Ruleset ruleset);
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Database;
#nullable enable
@ -10,7 +11,7 @@ namespace osu.Game.Rulesets
/// <summary>
/// A representation of a ruleset's metadata.
/// </summary>
public interface IRulesetInfo : IHasOnlineID<int>
public interface IRulesetInfo : IHasOnlineID<int>, IEquatable<IRulesetInfo>
{
/// <summary>
/// The user-exposed name of this ruleset.

View File

@ -0,0 +1,31 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
#nullable enable
namespace osu.Game.Rulesets
{
public interface IRulesetStore
{
/// <summary>
/// Retrieve a ruleset using a known ID.
/// </summary>
/// <param name="id">The ruleset's internal ID.</param>
/// <returns>A ruleset, if available, else null.</returns>
IRulesetInfo? GetRuleset(int id);
/// <summary>
/// Retrieve a ruleset using a known short name.
/// </summary>
/// <param name="shortName">The ruleset's short name.</param>
/// <returns>A ruleset, if available, else null.</returns>
IRulesetInfo? GetRuleset(string shortName);
/// <summary>
/// All available rulesets.
/// </summary>
IEnumerable<IRulesetInfo> AvailableRulesets { get; }
}
}

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mods
public void ApplyToHUD(HUDOverlay overlay)
{
overlay.ShowHealthbar.BindTo(showHealthBar);
overlay.ShowHealthBar.BindTo(showHealthBar);
}
}
}

View File

@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Mods
drawableRuleset.SetReplayScore(CreateReplayScore(drawableRuleset.Beatmap, drawableRuleset.Mods));
// AlwaysPresent required for hitsounds
drawableRuleset.Playfield.AlwaysPresent = true;
drawableRuleset.Playfield.Hide();
drawableRuleset.AlwaysPresent = true;
drawableRuleset.Hide();
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Double Time";
public override string Acronym => "DT";
public override IconUsage? Icon => OsuIcon.ModDoubletime;
public override IconUsage? Icon => OsuIcon.ModDoubleTime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom...";

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Hard Rock";
public override string Acronym => "HR";
public override IconUsage? Icon => OsuIcon.ModHardrock;
public override IconUsage? Icon => OsuIcon.ModHardRock;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder...";
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) };

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "No Fail";
public override string Acronym => "NF";
public override IconUsage? Icon => OsuIcon.ModNofail;
public override IconUsage? Icon => OsuIcon.ModNoFail;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "You can't fail, no matter what.";
public override double ScoreMultiplier => 0.5;

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Sudden Death";
public override string Acronym => "SD";
public override IconUsage? Icon => OsuIcon.ModSuddendeath;
public override IconUsage? Icon => OsuIcon.ModSuddenDeath;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Miss and fail.";
public override double ScoreMultiplier => 1;

View File

@ -123,9 +123,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
public readonly Bindable<double> StartTimeBindable = new Bindable<double>();
private readonly BindableList<HitSampleInfo> samplesBindable = new BindableList<HitSampleInfo>();
private readonly Bindable<bool> userPositionalHitSounds = new Bindable<bool>();
private readonly Bindable<int> comboIndexBindable = new Bindable<int>();
private readonly Bindable<float> positionalHitsoundsLevel = new Bindable<float>();
private readonly Bindable<int> comboIndexWithOffsetsBindable = new Bindable<int>();
protected override bool RequiresChildrenUpdate => true;
@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, ISkinSource skinSource)
{
config.BindWith(OsuSetting.PositionalHitSounds, userPositionalHitSounds);
config.BindWith(OsuSetting.PositionalHitsoundsLevel, positionalHitsoundsLevel);
// Explicit non-virtual function call.
base.AddInternal(Samples = new PausableSkinnableSound());
@ -532,9 +532,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <param name="position">The lookup X position. Generally should be <see cref="SamplePlaybackPosition"/>.</param>
protected double CalculateSamplePlaybackBalance(double position)
{
const float balance_adjust_amount = 0.4f;
float balanceAdjustAmount = positionalHitsoundsLevel.Value * 2;
double returnedValue = balanceAdjustAmount * (position - 0.5f);
return balance_adjust_amount * (userPositionalHitSounds.Value ? position - 0.5f : 0);
return returnedValue;
}
/// <summary>

View File

@ -87,23 +87,6 @@ namespace osu.Game.Rulesets.Objects
[JsonIgnore]
public SlimReadOnlyListWrapper<HitObject> NestedHitObjects => nestedHitObjects.AsSlimReadOnly();
public HitObject()
{
StartTimeBindable.ValueChanged += time =>
{
double offset = time.NewValue - time.OldValue;
foreach (var nested in nestedHitObjects)
nested.StartTime += offset;
if (DifficultyControlPoint != DifficultyControlPoint.DEFAULT)
DifficultyControlPoint.Time = time.NewValue;
if (SampleControlPoint != SampleControlPoint.DEFAULT)
SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
};
}
/// <summary>
/// Applies default values to this HitObject.
/// </summary>
@ -115,24 +98,22 @@ namespace osu.Game.Rulesets.Objects
var legacyInfo = controlPointInfo as LegacyControlPointInfo;
if (legacyInfo != null)
{
DifficultyControlPoint = (DifficultyControlPoint)legacyInfo.DifficultyPointAt(StartTime).DeepClone();
DifficultyControlPoint.Time = StartTime;
}
else if (DifficultyControlPoint == DifficultyControlPoint.DEFAULT)
DifficultyControlPoint = new DifficultyControlPoint();
DifficultyControlPoint.Time = StartTime;
ApplyDefaultsToSelf(controlPointInfo, difficulty);
// This is done here after ApplyDefaultsToSelf as we may require custom defaults to be applied to have an accurate end time.
if (legacyInfo != null)
{
SampleControlPoint = (SampleControlPoint)legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency).DeepClone();
SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
}
else if (SampleControlPoint == SampleControlPoint.DEFAULT)
SampleControlPoint = new SampleControlPoint();
SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
nestedHitObjects.Clear();
CreateNestedHitObjects(cancellationToken);
@ -155,7 +136,28 @@ namespace osu.Game.Rulesets.Objects
foreach (var h in nestedHitObjects)
h.ApplyDefaults(controlPointInfo, difficulty, cancellationToken);
// `ApplyDefaults()` may be called multiple times on a single hitobject.
// to prevent subscribing to `StartTimeBindable.ValueChanged` multiple times with the same callback,
// remove the previous subscription (if present) before (re-)registering.
StartTimeBindable.ValueChanged -= onStartTimeChanged;
// this callback must be (re-)registered after default application
// to ensure that the read of `this.GetEndTime()` within `onStartTimeChanged` doesn't return an invalid value
// if `StartTimeBindable` is changed prior to default application.
StartTimeBindable.ValueChanged += onStartTimeChanged;
DefaultsApplied?.Invoke(this);
void onStartTimeChanged(ValueChangedEvent<double> time)
{
double offset = time.NewValue - time.OldValue;
foreach (var nested in nestedHitObjects)
nested.StartTime += offset;
DifficultyControlPoint.Time = time.NewValue;
SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
}
}
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)

View File

@ -188,12 +188,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
string[] split = str.Split(':');
var bank = (LegacySampleBank)Parsing.ParseInt(split[0]);
var addbank = (LegacySampleBank)Parsing.ParseInt(split[1]);
var addBank = (LegacySampleBank)Parsing.ParseInt(split[1]);
string stringBank = bank.ToString().ToLowerInvariant();
if (stringBank == @"none")
stringBank = null;
string stringAddBank = addbank.ToString().ToLowerInvariant();
string stringAddBank = addBank.ToString().ToLowerInvariant();
if (stringAddBank == @"none")
stringAddBank = null;

View File

@ -250,13 +250,13 @@ namespace osu.Game.Rulesets.Objects
if (subControlPoints.Length != 3)
break;
List<Vector2> subpath = PathApproximator.ApproximateCircularArc(subControlPoints);
List<Vector2> subPath = PathApproximator.ApproximateCircularArc(subControlPoints);
// If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation.
if (subpath.Count == 0)
if (subPath.Count == 0)
break;
return subpath;
return subPath;
case PathType.Catmull:
return PathApproximator.ApproximateCatmull(subControlPoints);

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types;
using osuTK;
@ -11,6 +12,15 @@ namespace osu.Game.Rulesets.Objects
{
public static class SliderPathExtensions
{
/// <summary>
/// Snaps the provided <paramref name="hitObject"/>'s duration using the <paramref name="snapProvider"/>.
/// </summary>
public static void SnapTo<THitObject>(this THitObject hitObject, IPositionSnapProvider? snapProvider)
where THitObject : HitObject, IHasPath
{
hitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(hitObject, (float)hitObject.Path.CalculatedDistance) ?? hitObject.Path.CalculatedDistance;
}
/// <summary>
/// Reverse the direction of this path.
/// </summary>

View File

@ -6,15 +6,12 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.Rulesets.Configuration;
namespace osu.Game.Rulesets
{
/// <summary>
/// A cache that provides a single <see cref="IRulesetConfigManager"/> per-ruleset.
/// This is done to support referring to and updating ruleset configs from multiple locations in the absence of inter-config bindings.
/// </summary>
public class RulesetConfigCache : Component
public class RulesetConfigCache : Component, IRulesetConfigCache
{
private readonly RealmContextFactory realmFactory;
private readonly RulesetStore rulesets;
@ -43,18 +40,13 @@ namespace osu.Game.Rulesets
}
}
/// <summary>
/// Retrieves the <see cref="IRulesetConfigManager"/> for a <see cref="Ruleset"/>.
/// </summary>
/// <param name="ruleset">The <see cref="Ruleset"/> to retrieve the <see cref="IRulesetConfigManager"/> for.</param>
/// <returns>The <see cref="IRulesetConfigManager"/> defined by <paramref name="ruleset"/>, null if <paramref name="ruleset"/> doesn't define one.</returns>
/// <exception cref="InvalidOperationException">If <paramref name="ruleset"/> doesn't have a valid <see cref="RulesetInfo.ID"/>.</exception>
public IRulesetConfigManager GetConfigFor(Ruleset ruleset)
{
if (!IsLoaded)
throw new InvalidOperationException($@"Cannot retrieve {nameof(IRulesetConfigManager)} before {nameof(RulesetConfigCache)} has loaded");
if (!configCache.TryGetValue(ruleset.RulesetInfo.ShortName, out var config))
// any ruleset request which wasn't initialised on startup should not be stored to realm.
// this should only be used by tests.
return ruleset.CreateConfig(null);
throw new InvalidOperationException($@"Attempted to retrieve {nameof(IRulesetConfigManager)} for an unavailable ruleset {ruleset.GetDisplayString()}");
return config;
}

View File

@ -49,6 +49,8 @@ namespace osu.Game.Rulesets
public override bool Equals(object obj) => obj is RulesetInfo rulesetInfo && Equals(rulesetInfo);
public bool Equals(IRulesetInfo other) => other is RulesetInfo b && Equals(b);
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode()
{

View File

@ -13,7 +13,7 @@ using osu.Game.Database;
namespace osu.Game.Rulesets
{
public class RulesetStore : DatabaseBackedStore, IDisposable
public class RulesetStore : DatabaseBackedStore, IRulesetStore, IDisposable
{
private const string ruleset_library_prefix = "osu.Game.Rulesets";
@ -236,5 +236,13 @@ namespace osu.Game.Rulesets
{
AppDomain.CurrentDomain.AssemblyResolve -= resolveRulesetDependencyAssembly;
}
#region Implementation of IRulesetStore
IRulesetInfo IRulesetStore.GetRuleset(int id) => GetRuleset(id);
IRulesetInfo IRulesetStore.GetRuleset(string shortName) => GetRuleset(shortName);
IEnumerable<IRulesetInfo> IRulesetStore.AvailableRulesets => AvailableRulesets;
#endregion
}
}

View File

@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Scoring
if (result == null)
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
result.Type = judgement.MaxResult;
result.Type = GetSimulatedHitResult(judgement);
ApplyResult(result);
}
}
@ -145,5 +145,12 @@ namespace osu.Game.Rulesets.Scoring
base.Update();
hasCompleted.Value = JudgedHits == MaxHits && (JudgedHits == 0 || lastAppliedResult.TimeAbsolute < Clock.CurrentTime);
}
/// <summary>
/// Gets a simulated <see cref="HitResult"/> for a judgement. Used during <see cref="SimulateAutoplay"/> to simulate a "perfect" play.
/// </summary>
/// <param name="judgement">The judgement to simulate a <see cref="HitResult"/> for.</param>
/// <returns>The simulated <see cref="HitResult"/> for the judgement.</returns>
protected virtual HitResult GetSimulatedHitResult(Judgement judgement) => judgement.MaxResult;
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@ -63,7 +64,7 @@ namespace osu.Game.Rulesets.UI
CacheAs(ShaderManager = new FallbackShaderManager(ShaderManager, parent.Get<ShaderManager>()));
}
RulesetConfigManager = parent.Get<RulesetConfigCache>().GetConfigFor(ruleset);
RulesetConfigManager = parent.Get<IRulesetConfigCache>().GetConfigFor(ruleset);
if (RulesetConfigManager != null)
Cache(RulesetConfigManager);
}
@ -115,7 +116,7 @@ namespace osu.Game.Rulesets.UI
public Sample Get(string name) => primary.Get(name) ?? fallback.Get(name);
public Task<Sample> GetAsync(string name) => primary.GetAsync(name) ?? fallback.GetAsync(name);
public Task<Sample> GetAsync(string name, CancellationToken cancellationToken = default) => primary.GetAsync(name, cancellationToken) ?? fallback.GetAsync(name, cancellationToken);
public Stream GetStream(string name) => primary.GetStream(name) ?? fallback.GetStream(name);

View File

@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.UI
private int direction = 1;
[BackgroundDependencyLoader(true)]
private void load(GameplayClock clock, ISamplePlaybackDisabler sampleDisabler)
private void load(GameplayClock clock)
{
if (clock != null)
{

View File

@ -19,7 +19,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Skinning;
using osuTK;
using System.Diagnostics;
using osu.Framework.Audio.Sample;
namespace osu.Game.Rulesets.UI
{
@ -88,9 +87,6 @@ namespace osu.Game.Rulesets.UI
[Resolved(CanBeNull = true)]
private IReadOnlyList<Mod> mods { get; set; }
[Resolved]
private ISampleStore sampleStore { get; set; }
/// <summary>
/// Creates a new <see cref="Playfield"/>.
/// </summary>

View File

@ -127,6 +127,17 @@ namespace osu.Game.Rulesets.UI
return base.Handle(e);
}
protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
{
if (mouseDisabled.Value)
{
// Only propagate positional data when mouse buttons are disabled.
e = new TouchStateChangeEvent(e.State, e.Input, e.Touch, false, e.LastPosition);
}
return base.HandleMouseTouchStateChange(e);
}
#endregion
#region Key Counter Attachment

View File

@ -116,25 +116,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
if (RelativeScaleBeatLengths)
{
IReadOnlyList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
double maxDuration = 0;
baseBeatLength = Beatmap.GetMostCommonBeatLength();
for (int i = 0; i < timingPoints.Count; i++)
{
if (timingPoints[i].Time > lastObjectTime)
break;
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time : lastObjectTime;
double duration = endTime - timingPoints[i].Time;
if (duration > maxDuration)
{
maxDuration = duration;
// The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths
// the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here
baseBeatLength = timingPoints[i].BeatLength / Beatmap.Difficulty.SliderMultiplier;
}
}
// The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths
// the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here
baseBeatLength /= Beatmap.Difficulty.SliderMultiplier;
}
// Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point