Merge branch 'master' into apply-sv-to-taiko-hr-ez

This commit is contained in:
Dean Herbert
2021-01-25 14:28:06 +09:00
committed by GitHub
277 changed files with 3891 additions and 2113 deletions

View File

@ -332,7 +332,7 @@ namespace osu.Game.Rulesets.Edit
EditorBeatmap.Add(hitObject);
if (EditorClock.CurrentTime < hitObject.StartTime)
EditorClock.SeekTo(hitObject.StartTime);
EditorClock.SeekSmoothlyTo(hitObject.StartTime);
}
}

View File

@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Judgements
{
JudgementText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = Result.GetDescription().ToUpperInvariant(),
Colour = colours.ForHitResult(Result),
Font = OsuFont.Numeric.With(size: 20),

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
@ -84,12 +83,10 @@ namespace osu.Game.Rulesets.Mods
foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties())
{
object bindableObj = property.GetValue(this);
var bindable = (IBindable)property.GetValue(this);
if ((bindableObj as IHasDefaultValue)?.IsDefault == true)
continue;
tooltipTexts.Add($"{attr.Label} {bindableObj}");
if (!bindable.IsDefault)
tooltipTexts.Add($"{attr.Label} {bindable}");
}
return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s)));
@ -131,22 +128,50 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public virtual Mod CreateCopy()
{
var copy = (Mod)Activator.CreateInstance(GetType());
var result = (Mod)Activator.CreateInstance(GetType());
result.CopyFrom(this);
return result;
}
/// <summary>
/// Copies mod setting values from <paramref name="source"/> into this instance.
/// </summary>
/// <param name="source">The mod to copy properties from.</param>
public void CopyFrom(Mod source)
{
if (source.GetType() != GetType())
throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source));
// Copy bindable values across
foreach (var (_, prop) in this.GetSettingsSourceProperties())
{
var origBindable = prop.GetValue(this);
var copyBindable = prop.GetValue(copy);
var targetBindable = (IBindable)prop.GetValue(this);
var sourceBindable = (IBindable)prop.GetValue(source);
// The bindables themselves are readonly, so the value must be transferred through the Bindable<T>.Value property.
var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable<object>.Value), BindingFlags.Public | BindingFlags.Instance);
Debug.Assert(valueProperty != null);
valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable));
// we only care about changes that have been made away from defaults.
if (!sourceBindable.IsDefault)
CopyAdjustedSetting(targetBindable, sourceBindable);
}
}
return copy;
/// <summary>
/// When creating copies or clones of a Mod, this method will be called
/// to copy explicitly adjusted user settings from <paramref name="target"/>.
/// The base implementation will transfer the value via <see cref="Bindable{T}.Parse"/>
/// or by binding and unbinding (if <paramref name="source"/> is an <see cref="IBindable"/>)
/// and should be called unless replaced with custom logic.
/// </summary>
/// <param name="target">The target bindable to apply the adjustment to.</param>
/// <param name="source">The adjustment to apply.</param>
internal virtual void CopyAdjustedSetting(IBindable target, object source)
{
if (source is IBindable sourceBindable)
{
// copy including transfer of default values.
target.BindTo(sourceBindable);
target.UnbindFrom(sourceBindable);
}
else
target.Parse(source);
}
public bool Equals(IMod other) => GetType() == other?.GetType();

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods
public void ApplyToPlayer(Player player)
{
player.Background.EnableUserDim.Value = false;
player.ApplyToBackground(b => b.EnableUserDim.Value = false);
player.DimmableStoryboard.IgnoreUserSettings.Value = true;

View File

@ -114,14 +114,32 @@ namespace osu.Game.Rulesets.Mods
bindable.ValueChanged += _ => userChangedSettings[bindable] = !bindable.IsDefault;
}
internal override void CopyAdjustedSetting(IBindable target, object source)
{
// if the value is non-bindable, it's presumably coming from an external source (like the API) - therefore presume it is not default.
// if the value is bindable, defer to the source's IsDefault to be able to tell.
userChangedSettings[target] = !(source is IBindable bindableSource) || !bindableSource.IsDefault;
base.CopyAdjustedSetting(target, source);
}
/// <summary>
/// Applies a setting from a configuration bindable using <paramref name="applyFunc"/>, if it has been changed by the user.
/// </summary>
protected void ApplySetting<T>(BindableNumber<T> setting, Action<T> applyFunc)
where T : struct, IComparable<T>, IConvertible, IEquatable<T>
{
if (userChangedSettings.TryGetValue(setting, out bool userChangedSetting) && userChangedSetting)
applyFunc.Invoke(setting.Value);
}
/// <summary>
/// Apply all custom settings to the provided beatmap.
/// </summary>
/// <param name="difficulty">The beatmap to have settings applied.</param>
protected virtual void ApplySettings(BeatmapDifficulty difficulty)
{
difficulty.DrainRate = DrainRate.Value;
difficulty.OverallDifficulty = OverallDifficulty.Value;
ApplySetting(DrainRate, dr => difficulty.DrainRate = dr);
ApplySetting(OverallDifficulty, od => difficulty.OverallDifficulty = od);
}
}
}

View File

@ -750,7 +750,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (Result.Type != originalType)
{
Logger.Log($"{GetType().ReadableName()} applied an invalid hit result ({originalType}) when {nameof(HitResult.IgnoreMiss)} or {nameof(HitResult.IgnoreHit)} is expected.\n"
+ $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2020-03-28 onwards.", level: LogLevel.Important);
+ $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2021-03-28 onwards.", level: LogLevel.Important);
}
}

View File

@ -10,10 +10,7 @@ using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Objects.Legacy
{
internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset,
#pragma warning disable 618
IHasCurve
#pragma warning restore 618
internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset
{
/// <summary>
/// Scoring distance with a speed-adjusted beat length of 1 second.

View File

@ -1,55 +0,0 @@
// 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 osuTK;
namespace osu.Game.Rulesets.Objects.Types
{
[Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126
public interface IHasCurve : IHasDistance, IHasRepeats
{
/// <summary>
/// The curve.
/// </summary>
SliderPath Path { get; }
}
#pragma warning disable 618
[Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126
public static class HasCurveExtensions
{
/// <summary>
/// Computes the position on the curve relative to how much of the <see cref="HitObject"/> has been completed.
/// </summary>
/// <param name="obj">The curve.</param>
/// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param>
/// <returns>The position on the curve.</returns>
public static Vector2 CurvePositionAt(this IHasCurve obj, double progress)
=> obj.Path.PositionAt(obj.ProgressAt(progress));
/// <summary>
/// Computes the progress along the curve relative to how much of the <see cref="HitObject"/> has been completed.
/// </summary>
/// <param name="obj">The curve.</param>
/// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param>
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
public static double ProgressAt(this IHasCurve obj, double progress)
{
double p = progress * obj.SpanCount() % 1;
if (obj.SpanAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
/// <summary>
/// Determines which span of the curve the progress point is on.
/// </summary>
/// <param name="obj">The curve.</param>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
/// <returns>[0, SpanCount) where 0 is the first run.</returns>
public static int SpanAt(this IHasCurve obj, double progress)
=> (int)(progress * obj.SpanCount());
}
#pragma warning restore 618
}

View File

@ -6,26 +6,16 @@ namespace osu.Game.Rulesets.Objects.Types
/// <summary>
/// A HitObject that ends at a different time than its start time.
/// </summary>
#pragma warning disable 618
public interface IHasDuration : IHasEndTime
#pragma warning restore 618
public interface IHasDuration
{
double IHasEndTime.EndTime
{
get => EndTime;
set => Duration = (Duration - EndTime) + value;
}
double IHasEndTime.Duration => Duration;
/// <summary>
/// The time at which the HitObject ends.
/// </summary>
new double EndTime { get; }
double EndTime { get; }
/// <summary>
/// The duration of the HitObject.
/// </summary>
new double Duration { get; set; }
double Duration { get; set; }
}
}

View File

@ -1,26 +0,0 @@
// 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 Newtonsoft.Json;
namespace osu.Game.Rulesets.Objects.Types
{
/// <summary>
/// A HitObject that ends at a different time than its start time.
/// </summary>
[Obsolete("Use IHasDuration instead.")] // can be removed 20201126
public interface IHasEndTime
{
/// <summary>
/// The time at which the HitObject ends.
/// </summary>
[JsonIgnore]
double EndTime { get; set; }
/// <summary>
/// The duration of the HitObject.
/// </summary>
double Duration { get; }
}
}

View File

@ -100,9 +100,7 @@ namespace osu.Game.Rulesets
foreach (var r in instances.Where(r => !(r is ILegacyRuleset)))
{
// todo: StartsWith can be changed to Equals on 2020-11-08
// This is to give users enough time to have their database use new abbreviated info).
if (existingRulesets.FirstOrDefault(ri => ri.InstantiationInfo.StartsWith(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null)
if (existingRulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null)
context.RulesetInfo.Add(r.RulesetInfo);
}

View File

@ -8,7 +8,6 @@ using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Textures;

View File

@ -124,9 +124,11 @@ namespace osu.Game.Rulesets.UI
Debug.Assert(drawableMap.ContainsKey(entry));
var drawable = drawableMap[entry];
// OnKilled can potentially change the hitobject's result, so it needs to run first before unbinding.
drawable.OnKilled();
drawable.OnNewResult -= onNewResult;
drawable.OnRevertResult -= onRevertResult;
drawable.OnKilled();
drawableMap.Remove(entry);

View File

@ -8,7 +8,6 @@ using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Containers;
@ -20,6 +19,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Skinning;
using osuTK;
using System.Diagnostics;
using osu.Framework.Audio.Sample;
namespace osu.Game.Rulesets.UI
{