mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 09:32:55 +08:00
Merge branch 'master' into argon-spinner-sides
This commit is contained in:
commit
c0709a59f5
@ -15,6 +15,8 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen
|
||||
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
|
||||
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
|
||||
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
|
||||
M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
|
||||
M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
|
||||
M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
||||
M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
|
||||
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
|
||||
|
@ -51,8 +51,8 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1008.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1011.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1022.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -0,0 +1,49 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class CatchEditorPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
|
||||
{
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
public CatchEditorPlayfieldAdjustmentContainer()
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
Size = new Vector2(0.8f, 0.9f);
|
||||
|
||||
InternalChild = new ScalingContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Child = content = new Container { RelativeSizeAxes = Axes.Both },
|
||||
};
|
||||
}
|
||||
|
||||
private class ScalingContainer : Container
|
||||
{
|
||||
public ScalingContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = CatchPlayfield.WIDTH;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Scale = new Vector2(Math.Min(Parent.ChildSize.X / CatchPlayfield.WIDTH, Parent.ChildSize.Y / CatchPlayfield.HEIGHT));
|
||||
Height = 1 / Scale.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,8 +12,10 @@ using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -37,6 +39,12 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
private readonly BindableDouble timeRangeMultiplier = new BindableDouble(1)
|
||||
{
|
||||
MinValue = 1,
|
||||
MaxValue = 10,
|
||||
};
|
||||
|
||||
public CatchHitObjectComposer(CatchRuleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
@ -51,7 +59,10 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
|
||||
LayerBelowRuleset.Add(new PlayfieldBorder
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = CatchPlayfield.HEIGHT,
|
||||
PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners }
|
||||
});
|
||||
|
||||
@ -77,8 +88,30 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
updateDistanceSnapGrid();
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
// Note that right now these are hard to use as the default key bindings conflict with existing editor key bindings.
|
||||
// In the future we will want to expose this via UI and potentially change the key bindings to be editor-specific.
|
||||
// May be worth considering standardising "zoom" behaviour with what the timeline uses (ie. alt-wheel) but that may cause new conflicts.
|
||||
case GlobalAction.IncreaseScrollSpeed:
|
||||
this.TransformBindableTo(timeRangeMultiplier, timeRangeMultiplier.Value - 1, 200, Easing.OutQuint);
|
||||
break;
|
||||
|
||||
case GlobalAction.DecreaseScrollSpeed:
|
||||
this.TransformBindableTo(timeRangeMultiplier, timeRangeMultiplier.Value + 1, 200, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
|
||||
return base.OnPressed(e);
|
||||
}
|
||||
|
||||
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) =>
|
||||
new DrawableCatchEditorRuleset(ruleset, beatmap, mods);
|
||||
new DrawableCatchEditorRuleset(ruleset, beatmap, mods)
|
||||
{
|
||||
TimeRangeMultiplier = { BindTarget = timeRangeMultiplier, }
|
||||
};
|
||||
|
||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -13,11 +14,24 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
public class DrawableCatchEditorRuleset : DrawableCatchRuleset
|
||||
{
|
||||
public readonly BindableDouble TimeRangeMultiplier = new BindableDouble(1);
|
||||
|
||||
public DrawableCatchEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
double gamePlayTimeRange = GetTimeRange(Beatmap.Difficulty.ApproachRate);
|
||||
float playfieldStretch = Playfield.DrawHeight / CatchPlayfield.HEIGHT;
|
||||
TimeRange.Value = gamePlayTimeRange * TimeRangeMultiplier.Value * playfieldStretch;
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.Difficulty);
|
||||
|
||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchEditorPlayfieldAdjustmentContainer();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// </summary>
|
||||
public const float WIDTH = 512;
|
||||
|
||||
/// <summary>
|
||||
/// The height of the playfield.
|
||||
/// This doesn't include the catcher area.
|
||||
/// </summary>
|
||||
public const float HEIGHT = 384;
|
||||
|
||||
/// <summary>
|
||||
/// The center position of the playfield.
|
||||
/// </summary>
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Down;
|
||||
TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeRange.Value = GetTimeRange(beatmap.Difficulty.ApproachRate);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -42,6 +42,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
KeyBindingInputManager.Add(new CatchTouchInputMapper());
|
||||
}
|
||||
|
||||
protected double GetTimeRange(float approachRate) => IBeatmapDifficultyInfo.DifficultyRange(approachRate, 1800, 1200, 450);
|
||||
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
|
||||
protected override ReplayRecorder CreateReplayRecorder(Score score) => new CatchReplayRecorder(score, (CatchPlayfield)Playfield);
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
@ -146,6 +147,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
|
||||
private bool isBreak() => Player.IsBreakTime.Value;
|
||||
|
||||
private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.AsNonNull().Alpha, alpha, 0.1f);
|
||||
private OsuPlayfield playfield => (OsuPlayfield)Player.DrawableRuleset.Playfield;
|
||||
|
||||
private bool cursorAlphaAlmostEquals(float alpha) =>
|
||||
Precision.AlmostEquals(playfield.Cursor.AsNonNull().Alpha, alpha, 0.1f) &&
|
||||
Precision.AlmostEquals(playfield.Smoke.Alpha, alpha, 0.1f);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
@ -68,10 +69,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
AddStep("create slider", () =>
|
||||
{
|
||||
var tintingSkin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo());
|
||||
tintingSkin.Configuration.ConfigDictionary["AllowSliderBallTint"] = "1";
|
||||
|
||||
var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(tintingSkin, Beatmap.Value.Beatmap);
|
||||
var skin = skinManager.GetSkin(DefaultLegacySkin.CreateInfo());
|
||||
var provider = Ruleset.Value.CreateInstance().CreateSkinTransformer(skin, Beatmap.Value.Beatmap);
|
||||
|
||||
Child = new SkinProvidingContainer(provider)
|
||||
{
|
||||
@ -92,10 +91,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
});
|
||||
|
||||
AddStep("set accent white", () => dho.AccentColour.Value = Color4.White);
|
||||
AddAssert("ball is white", () => dho.ChildrenOfType<DrawableSliderBall>().Single().AccentColour == Color4.White);
|
||||
AddAssert("ball is white", () => dho.ChildrenOfType<LegacySliderBall>().Single().BallColour == Color4.White);
|
||||
|
||||
AddStep("set accent red", () => dho.AccentColour.Value = Color4.Red);
|
||||
AddAssert("ball is red", () => dho.ChildrenOfType<DrawableSliderBall>().Single().AccentColour == Color4.Red);
|
||||
AddAssert("ball is red", () => dho.ChildrenOfType<LegacySliderBall>().Single().BallColour == Color4.Red);
|
||||
}
|
||||
|
||||
private Slider prepareObject(Slider slider)
|
||||
|
@ -60,6 +60,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Give a bit of breathing room around the playfield content.
|
||||
PlayfieldContentContainer.Padding = new MarginPadding(10);
|
||||
|
||||
LayerBelowRuleset.AddRange(new Drawable[]
|
||||
{
|
||||
distanceSnapGridContainer = new Container
|
||||
|
@ -2,14 +2,15 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Utils;
|
||||
|
||||
@ -34,11 +35,15 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
bool shouldAlwaysShowCursor = IsBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime);
|
||||
float targetAlpha = shouldAlwaysShowCursor ? 1 : ComboBasedAlpha;
|
||||
var osuPlayfield = (OsuPlayfield)playfield;
|
||||
Debug.Assert(osuPlayfield.Cursor != null);
|
||||
|
||||
var cursor = playfield.Cursor.AsNonNull();
|
||||
cursor.Alpha = (float)Interpolation.Lerp(cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / TRANSITION_DURATION, 0, 1));
|
||||
bool shouldAlwaysShowCursor = IsBreakTime.Value || spinnerPeriods.IsInAny(osuPlayfield.Clock.CurrentTime);
|
||||
float targetAlpha = shouldAlwaysShowCursor ? 1 : ComboBasedAlpha;
|
||||
float currentAlpha = (float)Interpolation.Lerp(osuPlayfield.Cursor.Alpha, targetAlpha, Math.Clamp(osuPlayfield.Time.Elapsed / TRANSITION_DURATION, 0, 1));
|
||||
|
||||
osuPlayfield.Cursor.Alpha = currentAlpha;
|
||||
osuPlayfield.Smoke.Alpha = currentAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,10 @@ using osu.Game.Audio;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -106,7 +104,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
foreach (var drawableHitObject in NestedHitObjects)
|
||||
drawableHitObject.AccentColour.Value = colour.NewValue;
|
||||
updateBallTint();
|
||||
}, true);
|
||||
|
||||
Tracking.BindValueChanged(updateSlidingSample);
|
||||
@ -257,22 +254,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
SliderBody?.RecyclePath();
|
||||
}
|
||||
|
||||
protected override void ApplySkin(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.ApplySkin(skin, allowFallback);
|
||||
|
||||
updateBallTint();
|
||||
}
|
||||
|
||||
private void updateBallTint()
|
||||
{
|
||||
if (CurrentSkin == null)
|
||||
return;
|
||||
|
||||
bool allowBallTint = CurrentSkin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
|
||||
Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White;
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (userTriggered || Time.Current < HitObject.EndTime)
|
||||
@ -331,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
|
||||
const float fade_out_time = 450;
|
||||
const float fade_out_time = 240;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
@ -341,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
break;
|
||||
}
|
||||
|
||||
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
||||
this.FadeOut(fade_out_time).Expire();
|
||||
}
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => SliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
@ -11,28 +11,20 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition, IHasAccentColour
|
||||
public class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
public const float FOLLOW_AREA = 2.4f;
|
||||
|
||||
public Func<OsuAction?> GetInitialHitAction;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => ball.Colour;
|
||||
set => ball.Colour = value;
|
||||
}
|
||||
|
||||
private Drawable followCircleReceptor;
|
||||
private DrawableSlider drawableSlider;
|
||||
private Drawable ball;
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
/// </summary>
|
||||
public readonly IBindable<double> SpinsPerMinute = new BindableDouble();
|
||||
|
||||
private const double fade_out_duration = 160;
|
||||
private const double fade_out_duration = 240;
|
||||
|
||||
public DrawableSpinner()
|
||||
: this(null)
|
||||
|
@ -108,18 +108,23 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
|
||||
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
|
||||
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
|
||||
flash.Colour = colour.NewValue;
|
||||
}, true);
|
||||
|
||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
// A colour transform is applied.
|
||||
// Without removing transforms first, when it is rewound it may apply an old colour.
|
||||
outerGradient.ClearTransforms(targetMember: nameof(Colour));
|
||||
outerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.1f));
|
||||
|
||||
outerFill.Colour = innerFill.Colour = colour.NewValue.Darken(4);
|
||||
innerGradient.Colour = ColourInfo.GradientVertical(colour.NewValue.Darken(0.5f), colour.NewValue.Darken(0.6f));
|
||||
flash.Colour = colour.NewValue;
|
||||
|
||||
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||
}, true);
|
||||
|
||||
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||
}
|
||||
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
@ -173,11 +178,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
.FadeOut(flash_in_duration);
|
||||
}
|
||||
|
||||
// The flash layer starts white to give the wanted brightness, but is almost immediately
|
||||
// recoloured to the accent colour. This would more correctly be done with two layers (one for the initial flash)
|
||||
// but works well enough with the colour fade.
|
||||
flash.FadeTo(1, flash_in_duration, Easing.OutQuint);
|
||||
flash.FlashColour(accentColour.Value, fade_out_time, Easing.OutQuint);
|
||||
|
||||
this.FadeOut(fade_out_time, Easing.OutQuad);
|
||||
break;
|
||||
|
@ -134,10 +134,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
CircleSprite.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
CircleSprite.FadeOut(legacy_fade_duration);
|
||||
CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
|
||||
OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
OverlaySprite.FadeOut(legacy_fade_duration);
|
||||
OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
|
||||
if (hasNumber)
|
||||
@ -146,11 +146,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
if (legacyVersion >= 2.0m)
|
||||
// legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
|
||||
hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out);
|
||||
hitCircleText.FadeOut(legacy_fade_duration / 4);
|
||||
else
|
||||
{
|
||||
// old skins scale and fade it normally along other pieces.
|
||||
hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
hitCircleText.FadeOut(legacy_fade_duration);
|
||||
hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
}
|
||||
}
|
||||
|
@ -107,8 +107,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
||||
this.FadeOut();
|
||||
|
||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2))
|
||||
this.FadeInFromZero(spinner.TimeFadeIn / 2);
|
||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn))
|
||||
this.FadeInFromZero(spinner.TimeFadeIn);
|
||||
|
||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableHitObject? parentObject { get; set; }
|
||||
|
||||
public Color4 BallColour => animationContent.Colour;
|
||||
|
||||
private Sprite layerNd = null!;
|
||||
private Sprite layerSpec = null!;
|
||||
|
||||
@ -61,6 +64,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
};
|
||||
}
|
||||
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -69,6 +74,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
parentObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(parentObject, parentObject.State.Value);
|
||||
|
||||
if (skin.GetConfig<SkinConfiguration.LegacySetting, bool>(SkinConfiguration.LegacySetting.AllowSliderBallTint)?.Value == true)
|
||||
{
|
||||
accentColour.BindTo(parentObject.AccentColour);
|
||||
accentColour.BindValueChanged(a => animationContent.Colour = a.NewValue, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
spin = new Sprite
|
||||
{
|
||||
Alpha = 0,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = source.GetTexture("spinner-spin"),
|
||||
@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
},
|
||||
bonusCounter = new LegacySpriteText(LegacyFont.Score)
|
||||
{
|
||||
Alpha = 0f,
|
||||
Alpha = 0,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(SPRITE_SCALE),
|
||||
@ -179,6 +180,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
spmCounter.MoveToOffset(new Vector2(0, -spm_hide_offset), d.HitObject.TimeFadeIn, Easing.Out);
|
||||
}
|
||||
|
||||
using (BeginAbsoluteSequence(d.HitObject.StartTime - d.HitObject.TimeFadeIn / 2))
|
||||
spin.FadeInFromZero(d.HitObject.TimeFadeIn / 2);
|
||||
|
||||
using (BeginAbsoluteSequence(d.HitObject.StartTime))
|
||||
ApproachCircle?.ScaleTo(SPRITE_SCALE * 0.1f, d.HitObject.Duration);
|
||||
|
||||
|
@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
SliderBorderSize,
|
||||
SliderPathRadius,
|
||||
AllowSliderBallTint,
|
||||
CursorCentre,
|
||||
CursorExpand,
|
||||
CursorRotate,
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -22,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public abstract class SmokeSegment : Drawable, ITexturedShaderDrawable
|
||||
{
|
||||
private const int max_point_count = 18_000;
|
||||
|
||||
// fade anim values
|
||||
private const double initial_fade_out_duration = 4000;
|
||||
|
||||
@ -85,12 +84,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
totalDistance = pointInterval;
|
||||
}
|
||||
|
||||
private Vector2 nextPointDirection()
|
||||
{
|
||||
float angle = RNG.NextSingle(0, 2 * MathF.PI);
|
||||
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
|
||||
}
|
||||
|
||||
public void AddPosition(Vector2 position, double time)
|
||||
{
|
||||
lastPosition ??= position;
|
||||
@ -107,33 +100,27 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
Vector2 pointPos = (pointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition;
|
||||
increment *= pointInterval;
|
||||
|
||||
if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time)
|
||||
{
|
||||
int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer());
|
||||
SmokePoints.RemoveRange(index, SmokePoints.Count - index);
|
||||
}
|
||||
|
||||
totalDistance %= pointInterval;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
if (SmokePoints.Count == 0 || SmokePoints[^1].Time <= time)
|
||||
{
|
||||
SmokePoints.Add(new SmokePoint
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Position = pointPos,
|
||||
Time = time,
|
||||
Direction = nextPointDirection(),
|
||||
});
|
||||
SmokePoints.Add(new SmokePoint
|
||||
{
|
||||
Position = pointPos,
|
||||
Time = time,
|
||||
Angle = RNG.NextSingle(0, 2 * MathF.PI),
|
||||
});
|
||||
|
||||
pointPos += increment;
|
||||
pointPos += increment;
|
||||
}
|
||||
}
|
||||
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
|
||||
lastPosition = position;
|
||||
|
||||
if (SmokePoints.Count >= max_point_count)
|
||||
FinishDrawing(time);
|
||||
}
|
||||
|
||||
public void FinishDrawing(double time)
|
||||
@ -157,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public Vector2 Position;
|
||||
public double Time;
|
||||
public Vector2 Direction;
|
||||
public float Angle;
|
||||
|
||||
public struct UpperBoundComparer : IComparer<SmokePoint>
|
||||
{
|
||||
@ -171,6 +158,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
return x.Time > target.Time ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public struct LowerBoundComparer : IComparer<SmokePoint>
|
||||
{
|
||||
public int Compare(SmokePoint x, SmokePoint target)
|
||||
{
|
||||
// Similar logic as UpperBoundComparer, except returned index will always be
|
||||
// the first element larger or equal
|
||||
|
||||
return x.Time < target.Time ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class SmokeDrawNode : TexturedShaderDrawNode
|
||||
@ -187,11 +185,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
private Vector2 drawSize;
|
||||
private Texture? texture;
|
||||
private int rotationSeed;
|
||||
private int rotationIndex;
|
||||
private int firstVisiblePointIndex;
|
||||
|
||||
// anim calculation vars (color, scale, direction)
|
||||
private double initialFadeOutDurationTrunc;
|
||||
private double firstVisiblePointTime;
|
||||
private double firstVisiblePointTimeAfterSmokeEnded;
|
||||
|
||||
private double initialFadeOutTime;
|
||||
private double reFadeInTime;
|
||||
@ -206,9 +204,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
base.ApplyState();
|
||||
|
||||
points.Clear();
|
||||
points.AddRange(Source.SmokePoints);
|
||||
|
||||
radius = Source.radius;
|
||||
drawSize = Source.DrawSize;
|
||||
texture = Source.Texture;
|
||||
@ -220,11 +215,18 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
rotationSeed = Source.rotationSeed;
|
||||
|
||||
initialFadeOutDurationTrunc = Math.Min(initial_fade_out_duration, SmokeEndTime - SmokeStartTime);
|
||||
firstVisiblePointTime = SmokeEndTime - initialFadeOutDurationTrunc;
|
||||
firstVisiblePointTimeAfterSmokeEnded = SmokeEndTime - initialFadeOutDurationTrunc;
|
||||
|
||||
initialFadeOutTime = CurrentTime;
|
||||
reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / re_fade_in_speed);
|
||||
finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTime * (1 - 1 / final_fade_out_speed);
|
||||
initialFadeOutTime = Math.Min(CurrentTime, SmokeEndTime);
|
||||
reFadeInTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / re_fade_in_speed);
|
||||
finalFadeOutTime = CurrentTime - initialFadeOutDurationTrunc - firstVisiblePointTimeAfterSmokeEnded * (1 - 1 / final_fade_out_speed);
|
||||
|
||||
double firstVisiblePointTime = Math.Min(SmokeEndTime, CurrentTime) - initialFadeOutDurationTrunc;
|
||||
firstVisiblePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = firstVisiblePointTime }, new SmokePoint.LowerBoundComparer());
|
||||
int futurePointIndex = ~Source.SmokePoints.BinarySearch(new SmokePoint { Time = CurrentTime }, new SmokePoint.UpperBoundComparer());
|
||||
|
||||
points.Clear();
|
||||
points.AddRange(Source.SmokePoints.Skip(firstVisiblePointIndex).Take(futurePointIndex - firstVisiblePointIndex));
|
||||
}
|
||||
|
||||
public sealed override void Draw(IRenderer renderer)
|
||||
@ -234,9 +236,14 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
if (points.Count == 0)
|
||||
return;
|
||||
|
||||
rotationIndex = 0;
|
||||
quadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(200, 4);
|
||||
|
||||
if (points.Count > quadBatch.Size && quadBatch.Size != IRenderer.MAX_QUADS)
|
||||
{
|
||||
int batchSize = Math.Min(quadBatch.Size * 2, IRenderer.MAX_QUADS);
|
||||
quadBatch = renderer.CreateQuadBatch<TexturedVertex2D>(batchSize, 4);
|
||||
}
|
||||
|
||||
quadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(max_point_count / 10, 10);
|
||||
texture ??= renderer.WhitePixel;
|
||||
RectangleF textureRect = texture.GetTextureRect();
|
||||
|
||||
@ -248,8 +255,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
shader.Bind();
|
||||
texture.Bind();
|
||||
|
||||
foreach (var point in points)
|
||||
drawPointQuad(point, textureRect);
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex);
|
||||
|
||||
shader.Unbind();
|
||||
renderer.PopLocalMatrix();
|
||||
@ -263,30 +270,34 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
var color = Color4.White;
|
||||
|
||||
double timeDoingInitialFadeOut = Math.Min(initialFadeOutTime, SmokeEndTime) - point.Time;
|
||||
double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed;
|
||||
|
||||
if (timeDoingInitialFadeOut > 0)
|
||||
if (timeDoingFinalFadeOut > 0 && point.Time >= firstVisiblePointTimeAfterSmokeEnded)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1);
|
||||
color.A = (1 - fraction) * initial_alpha;
|
||||
float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1);
|
||||
fraction = MathF.Pow(fraction, 5);
|
||||
color.A = (1 - fraction) * re_fade_in_alpha;
|
||||
}
|
||||
|
||||
if (color.A > 0)
|
||||
else
|
||||
{
|
||||
double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed;
|
||||
double timeDoingFinalFadeOut = finalFadeOutTime - point.Time / final_fade_out_speed;
|
||||
double timeDoingInitialFadeOut = initialFadeOutTime - point.Time;
|
||||
|
||||
if (timeDoingFinalFadeOut > 0)
|
||||
if (timeDoingInitialFadeOut > 0)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingFinalFadeOut / final_fade_out_duration), 0, 1);
|
||||
fraction = MathF.Pow(fraction, 5);
|
||||
color.A = (1 - fraction) * re_fade_in_alpha;
|
||||
float fraction = Math.Clamp((float)(timeDoingInitialFadeOut / initial_fade_out_duration), 0, 1);
|
||||
color.A = (1 - fraction) * initial_alpha;
|
||||
}
|
||||
else if (timeDoingReFadeIn > 0)
|
||||
|
||||
if (point.Time > firstVisiblePointTimeAfterSmokeEnded)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1);
|
||||
fraction = 1 - MathF.Pow(1 - fraction, 5);
|
||||
color.A = fraction * (re_fade_in_alpha - color.A) + color.A;
|
||||
double timeDoingReFadeIn = reFadeInTime - point.Time / re_fade_in_speed;
|
||||
|
||||
if (timeDoingReFadeIn > 0)
|
||||
{
|
||||
float fraction = Math.Clamp((float)(timeDoingReFadeIn / re_fade_in_duration), 0, 1);
|
||||
fraction = 1 - MathF.Pow(1 - fraction, 5);
|
||||
color.A = fraction * (re_fade_in_alpha - color.A) + color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,33 +312,33 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
return fraction * (final_scale - initial_scale) + initial_scale;
|
||||
}
|
||||
|
||||
protected virtual Vector2 PointDirection(SmokePoint point)
|
||||
protected virtual Vector2 PointDirection(SmokePoint point, int index)
|
||||
{
|
||||
float initialAngle = MathF.Atan2(point.Direction.Y, point.Direction.X);
|
||||
float finalAngle = initialAngle + nextRotation();
|
||||
|
||||
double timeDoingRotation = CurrentTime - point.Time;
|
||||
float fraction = Math.Clamp((float)(timeDoingRotation / rotation_duration), 0, 1);
|
||||
fraction = 1 - MathF.Pow(1 - fraction, 5);
|
||||
float angle = fraction * (finalAngle - initialAngle) + initialAngle;
|
||||
float angle = fraction * getRotation(index) + point.Angle;
|
||||
|
||||
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
|
||||
}
|
||||
|
||||
private float nextRotation() => max_rotation * (StatelessRNG.NextSingle(rotationSeed, rotationIndex++) * 2 - 1);
|
||||
private float getRotation(int index) => max_rotation * (StatelessRNG.NextSingle(rotationSeed, index) * 2 - 1);
|
||||
|
||||
private void drawPointQuad(SmokePoint point, RectangleF textureRect)
|
||||
private void drawPointQuad(SmokePoint point, RectangleF textureRect, int index)
|
||||
{
|
||||
Debug.Assert(quadBatch != null);
|
||||
|
||||
var colour = PointColour(point);
|
||||
float scale = PointScale(point);
|
||||
var dir = PointDirection(point);
|
||||
var ortho = dir.PerpendicularLeft;
|
||||
|
||||
if (colour.A == 0 || scale == 0)
|
||||
if (colour.A == 0)
|
||||
return;
|
||||
|
||||
float scale = PointScale(point);
|
||||
if (scale == 0)
|
||||
return;
|
||||
|
||||
var dir = PointDirection(point, index);
|
||||
var ortho = dir.PerpendicularLeft;
|
||||
|
||||
var localTopLeft = point.Position + (radius * scale * (-ortho - dir));
|
||||
var localTopRight = point.Position + (radius * scale * (-ortho + dir));
|
||||
var localBotLeft = point.Position + (radius * scale * (ortho - dir));
|
||||
|
@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
private readonly ProxyContainer spinnerProxies;
|
||||
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
|
||||
|
||||
public SmokeContainer Smoke { get; }
|
||||
public FollowPointRenderer FollowPoints { get; }
|
||||
|
||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||
@ -54,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both },
|
||||
new SmokeContainer { RelativeSizeAxes = Axes.Both },
|
||||
Smoke = new SmokeContainer { RelativeSizeAxes = Axes.Both },
|
||||
spinnerProxies = new ProxyContainer { RelativeSizeAxes = Axes.Both },
|
||||
FollowPoints = new FollowPointRenderer { RelativeSizeAxes = Axes.Both },
|
||||
judgementLayer = new JudgementContainer<DrawableOsuJudgement> { RelativeSizeAxes = Axes.Both },
|
||||
|
@ -244,7 +244,10 @@ namespace osu.Game.Tests.Visual.Background
|
||||
public void TestResumeFromPlayer()
|
||||
{
|
||||
performFullSetup();
|
||||
AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos));
|
||||
AddStep("Move mouse to Visual Settings location", () => InputManager.MoveMouseTo(playerLoader.ScreenSpaceDrawQuad.TopRight
|
||||
+ new Vector2(-playerLoader.VisualSettingsPos.ScreenSpaceDrawQuad.Width,
|
||||
playerLoader.VisualSettingsPos.ScreenSpaceDrawQuad.Height / 2
|
||||
)));
|
||||
AddStep("Resume PlayerLoader", () => player.Restart());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||
|
@ -214,7 +214,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void addControlPoints(IList<MultiplierControlPoint> controlPoints, double sequenceStartTime)
|
||||
{
|
||||
controlPoints.ForEach(point => point.StartTime += sequenceStartTime);
|
||||
controlPoints.ForEach(point => point.Time += sequenceStartTime);
|
||||
|
||||
scrollContainers.ForEach(container =>
|
||||
{
|
||||
@ -224,7 +224,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
foreach (var playfield in playfields)
|
||||
{
|
||||
foreach (var controlPoint in controlPoints)
|
||||
playfield.Add(createDrawablePoint(playfield, controlPoint.StartTime));
|
||||
playfield.Add(createDrawablePoint(playfield, controlPoint.Time));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,14 +97,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCurrentItemDoesNotHaveDeleteButton()
|
||||
public void TestSingleItemDoesNotHaveDeleteButton()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
|
||||
assertDeleteButtonVisibility(0, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCurrentItemHasDeleteButtonIfNotSingle()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
|
||||
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
|
||||
|
||||
assertDeleteButtonVisibility(0, false);
|
||||
assertDeleteButtonVisibility(0, true);
|
||||
assertDeleteButtonVisibility(1, true);
|
||||
|
||||
AddStep("finish current item", () => MultiplayerClient.FinishCurrentItem().WaitSafely());
|
||||
|
@ -178,6 +178,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Test]
|
||||
public void TestKeyboardLocalCursor([Values] bool clickToShow)
|
||||
{
|
||||
AddStep("Enable cursor hiding", () => globalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = true);
|
||||
AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3].ScreenSpaceDrawQuad.Centre + new Vector2(10, 0)));
|
||||
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
|
||||
AddAssert("Check global cursor alpha is 1", () => globalCursorDisplay.MenuCursor.Alpha == 1);
|
||||
@ -201,6 +202,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Test]
|
||||
public void TestKeyboardUserCursor([Values] bool clickToShow)
|
||||
{
|
||||
AddStep("Enable cursor hiding", () => globalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = true);
|
||||
AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0]));
|
||||
AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor));
|
||||
AddAssert("Check global cursor alpha is 0", () => !checkVisible(globalCursorDisplay.MenuCursor) && globalCursorDisplay.MenuCursor.ActiveCursor.Alpha == 0);
|
||||
|
@ -9,11 +9,8 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.ControlPoints
|
||||
{
|
||||
public abstract class ControlPoint : IComparable<ControlPoint>, IDeepCloneable<ControlPoint>, IEquatable<ControlPoint>
|
||||
public abstract class ControlPoint : IComparable<ControlPoint>, IDeepCloneable<ControlPoint>, IEquatable<ControlPoint>, IControlPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The time at which the control point takes effect.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public double Time { get; set; }
|
||||
|
||||
|
@ -196,8 +196,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
/// <param name="time">The time to find the control point at.</param>
|
||||
/// <param name="fallback">The control point to use when <paramref name="time"/> is before any control points.</param>
|
||||
/// <returns>The active control point at <paramref name="time"/>, or a fallback <see cref="ControlPoint"/> if none found.</returns>
|
||||
protected T BinarySearchWithFallback<T>(IReadOnlyList<T> list, double time, T fallback)
|
||||
where T : ControlPoint
|
||||
public static T BinarySearchWithFallback<T>(IReadOnlyList<T> list, double time, T fallback)
|
||||
where T : class, IControlPoint
|
||||
{
|
||||
return BinarySearch(list, time) ?? fallback;
|
||||
}
|
||||
@ -207,9 +207,9 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
/// </summary>
|
||||
/// <param name="list">The list to search.</param>
|
||||
/// <param name="time">The time to find the control point at.</param>
|
||||
/// <returns>The active control point at <paramref name="time"/>.</returns>
|
||||
protected virtual T BinarySearch<T>(IReadOnlyList<T> list, double time)
|
||||
where T : ControlPoint
|
||||
/// <returns>The active control point at <paramref name="time"/>. Will return <c>null</c> if there are no control points, or if the time is before the first control point.</returns>
|
||||
public static T BinarySearch<T>(IReadOnlyList<T> list, double time)
|
||||
where T : class, IControlPoint
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
13
osu.Game/Beatmaps/ControlPoints/IControlPoint.cs
Normal file
13
osu.Game/Beatmaps/ControlPoints/IControlPoint.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Beatmaps.ControlPoints
|
||||
{
|
||||
public interface IControlPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The time at which the control point takes effect.
|
||||
/// </summary>
|
||||
double Time { get; }
|
||||
}
|
||||
}
|
@ -355,6 +355,14 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LegacyEventType.Sprite:
|
||||
// Generally, the background is the first thing defined in a beatmap file.
|
||||
// In some older beatmaps, it is not present and replaced by a storyboard-level background instead.
|
||||
// Allow the first sprite (by file order) to act as the background in such cases.
|
||||
if (string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile))
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[3]);
|
||||
break;
|
||||
|
||||
case LegacyEventType.Background:
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
|
||||
break;
|
||||
|
@ -23,6 +23,21 @@ namespace osu.Game.Graphics.Cursor
|
||||
private readonly IBindable<bool> screenshotCursorVisibility = new Bindable<bool>(true);
|
||||
public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent;
|
||||
|
||||
private bool hideCursorOnNonMouseInput;
|
||||
|
||||
public bool HideCursorOnNonMouseInput
|
||||
{
|
||||
get => hideCursorOnNonMouseInput;
|
||||
set
|
||||
{
|
||||
if (hideCursorOnNonMouseInput == value)
|
||||
return;
|
||||
|
||||
hideCursorOnNonMouseInput = value;
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Drawable CreateCursor() => activeCursor = new Cursor();
|
||||
|
||||
private Cursor activeCursor = null!;
|
||||
@ -75,7 +90,7 @@ namespace osu.Game.Graphics.Cursor
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
bool combinedVisibility = State.Value == Visibility.Visible && lastInputWasMouse.Value && !isIdle.Value;
|
||||
bool combinedVisibility = State.Value == Visibility.Visible && (lastInputWasMouse.Value || !hideCursorOnNonMouseInput) && !isIdle.Value;
|
||||
|
||||
if (visible == combinedVisibility)
|
||||
return;
|
||||
@ -262,14 +277,19 @@ namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case MouseEvent:
|
||||
case MouseDownEvent:
|
||||
case MouseMoveEvent:
|
||||
lastInputWasMouseSource.Value = true;
|
||||
return false;
|
||||
|
||||
default:
|
||||
case KeyDownEvent keyDown when !keyDown.Repeat:
|
||||
case JoystickPressEvent:
|
||||
case MidiDownEvent:
|
||||
lastInputWasMouseSource.Value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
[Description("button")]
|
||||
Button,
|
||||
|
||||
[Description("button-sidebar")]
|
||||
ButtonSidebar,
|
||||
|
||||
[Description("toolbar")]
|
||||
Toolbar,
|
||||
|
||||
|
@ -44,6 +44,8 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public virtual LocalisableString TooltipText { get; private set; }
|
||||
|
||||
public bool PlaySamplesOnAdjust { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to format the tooltip as a percentage or the actual value.
|
||||
/// </summary>
|
||||
@ -187,6 +189,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private void playSample(T value)
|
||||
{
|
||||
if (!PlaySamplesOnAdjust)
|
||||
return;
|
||||
|
||||
if (Clock == null || Clock.CurrentTime - lastSampleTime <= 30)
|
||||
return;
|
||||
|
||||
|
@ -114,6 +114,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty("has_replay")]
|
||||
public bool HasReplay { get; set; }
|
||||
|
||||
// These properties are calculated or not relevant to any external usage.
|
||||
public bool ShouldSerializeID() => false;
|
||||
public bool ShouldSerializeUser() => false;
|
||||
public bool ShouldSerializeBeatmap() => false;
|
||||
@ -122,6 +123,18 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
public bool ShouldSerializeOnlineID() => false;
|
||||
public bool ShouldSerializeHasReplay() => false;
|
||||
|
||||
// These fields only need to be serialised if they hold values.
|
||||
// Generally this is required because this model may be used by server-side components, but
|
||||
// we don't want to bother sending these fields in score submission requests, for instance.
|
||||
public bool ShouldSerializeEndedAt() => EndedAt != default;
|
||||
public bool ShouldSerializeStartedAt() => StartedAt != default;
|
||||
public bool ShouldSerializeLegacyScoreId() => LegacyScoreId != null;
|
||||
public bool ShouldSerializeLegacyTotalScore() => LegacyTotalScore != null;
|
||||
public bool ShouldSerializeMods() => Mods.Length > 0;
|
||||
public bool ShouldSerializeUserID() => UserID > 0;
|
||||
public bool ShouldSerializeBeatmapID() => BeatmapID > 0;
|
||||
public bool ShouldSerializeBuildID() => BuildID != null;
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString() => $"score_id: {ID} user_id: {UserID}";
|
||||
|
@ -1333,6 +1333,8 @@ namespace osu.Game
|
||||
OverlayActivationMode.BindTo(newOsuScreen.OverlayActivationMode);
|
||||
API.Activity.BindTo(newOsuScreen.Activity);
|
||||
|
||||
GlobalCursorDisplay.MenuCursor.HideCursorOnNonMouseInput = newOsuScreen.HideMenuCursorOnNonMouseInput;
|
||||
|
||||
if (newOsuScreen.HideOverlaysOnEnter)
|
||||
CloseAllOverlays();
|
||||
else
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
@ -21,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsSlider<double>
|
||||
new VolumeAdjustSlider
|
||||
{
|
||||
LabelText = AudioSettingsStrings.MasterVolume,
|
||||
Current = audio.Volume,
|
||||
@ -35,14 +36,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true
|
||||
},
|
||||
new SettingsSlider<double>
|
||||
new VolumeAdjustSlider
|
||||
{
|
||||
LabelText = AudioSettingsStrings.EffectVolume,
|
||||
Current = audio.VolumeSample,
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true
|
||||
},
|
||||
new SettingsSlider<double>
|
||||
|
||||
new VolumeAdjustSlider
|
||||
{
|
||||
LabelText = AudioSettingsStrings.MusicVolume,
|
||||
Current = audio.VolumeTrack,
|
||||
@ -51,5 +53,15 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class VolumeAdjustSlider : SettingsSlider<double>
|
||||
{
|
||||
protected override Drawable CreateControl()
|
||||
{
|
||||
var sliderBar = (OsuSliderBar<double>)base.CreateControl();
|
||||
sliderBar.PlaySamplesOnAdjust = false;
|
||||
return sliderBar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,11 @@ namespace osu.Game.Overlays.Settings
|
||||
[Resolved]
|
||||
protected OverlayColourProvider ColourProvider { get; private set; }
|
||||
|
||||
protected SidebarButton()
|
||||
: base(HoverSampleSet.ButtonSidebar)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
|
@ -40,8 +40,6 @@ namespace osu.Game.Overlays
|
||||
Anchor = Anchor.TopCentre,
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeDuration = transition_duration,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 10, Top = 5, Bottom = 10 },
|
||||
Spacing = new Vector2(0, 15),
|
||||
|
@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
public virtual void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,8 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
FillFlow.Spacing = new Vector2(10);
|
||||
FillFlow.Spacing = new Vector2(5);
|
||||
Padding = new MarginPadding { Vertical = 5 };
|
||||
}
|
||||
|
||||
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => base.ReceivePositionalInputAtSubTree(screenSpacePos) && anyToolboxHovered(screenSpacePos);
|
||||
|
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
PlayfieldContentContainer = new Container
|
||||
{
|
||||
Name = "Content",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -166,6 +166,15 @@ namespace osu.Game.Rulesets.Edit
|
||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Houses all content relevant to the playfield.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Generally implementations should not be adding to this directly.
|
||||
/// Use <see cref="LayerBelowRuleset"/> or <see cref="BlueprintContainer"/> instead.
|
||||
/// </remarks>
|
||||
protected Container PlayfieldContentContainer { get; private set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
@ -11,12 +11,12 @@ namespace osu.Game.Rulesets.Timing
|
||||
/// <summary>
|
||||
/// A control point which adds an aggregated multiplier based on the provided <see cref="TimingPoint"/>'s BeatLength and <see cref="EffectPoint"/>'s SpeedMultiplier.
|
||||
/// </summary>
|
||||
public class MultiplierControlPoint : IComparable<MultiplierControlPoint>
|
||||
public class MultiplierControlPoint : IComparable<MultiplierControlPoint>, IControlPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The time in milliseconds at which this <see cref="MultiplierControlPoint"/> starts.
|
||||
/// </summary>
|
||||
public double StartTime;
|
||||
public double Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The aggregate multiplier which this <see cref="MultiplierControlPoint"/> provides.
|
||||
@ -54,13 +54,13 @@ namespace osu.Game.Rulesets.Timing
|
||||
/// <summary>
|
||||
/// Creates a <see cref="MultiplierControlPoint"/>.
|
||||
/// </summary>
|
||||
/// <param name="startTime">The start time of this <see cref="MultiplierControlPoint"/>.</param>
|
||||
public MultiplierControlPoint(double startTime)
|
||||
/// <param name="time">The start time of this <see cref="MultiplierControlPoint"/>.</param>
|
||||
public MultiplierControlPoint(double time)
|
||||
{
|
||||
StartTime = startTime;
|
||||
Time = time;
|
||||
}
|
||||
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime);
|
||||
public int CompareTo(MultiplierControlPoint other) => Time.CompareTo(other?.Time);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
return -PositionAt(startTime, endTime, timeRange, scrollLength);
|
||||
}
|
||||
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null)
|
||||
=> (float)((time - currentTime) / timeRange * scrollLength);
|
||||
|
||||
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
|
||||
|
@ -53,8 +53,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
/// <param name="currentTime">The current time.</param>
|
||||
/// <param name="timeRange">The amount of visible time.</param>
|
||||
/// <param name="scrollLength">The absolute spatial length through <paramref name="timeRange"/>.</param>
|
||||
/// <param name="originTime">The time to be used for control point lookups (ie. the parent's start time for nested hit objects).</param>
|
||||
/// <returns>The absolute spatial position.</returns>
|
||||
float PositionAt(double time, double currentTime, double timeRange, float scrollLength);
|
||||
float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the time which brings a point to a provided spatial position given the current time.
|
||||
@ -63,7 +64,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
/// <param name="currentTime">The current time.</param>
|
||||
/// <param name="timeRange">The amount of visible time.</param>
|
||||
/// <param name="scrollLength">The absolute spatial length through <paramref name="timeRange"/>.</param>
|
||||
/// <returns>The time at which <see cref="PositionAt(double,double,double,float)"/> == <paramref name="position"/>.</returns>
|
||||
/// <returns>The time at which <see cref="PositionAt(double,double,double,float, double?)"/> == <paramref name="position"/>.</returns>
|
||||
double TimeAt(float position, double currentTime, double timeRange, float scrollLength);
|
||||
|
||||
/// <summary>
|
||||
|
@ -4,22 +4,20 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
{
|
||||
public class OverlappingScrollAlgorithm : IScrollAlgorithm
|
||||
{
|
||||
private readonly MultiplierControlPoint searchPoint;
|
||||
|
||||
private readonly SortedList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public OverlappingScrollAlgorithm(SortedList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
|
||||
searchPoint = new MultiplierControlPoint();
|
||||
}
|
||||
|
||||
public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength)
|
||||
@ -37,8 +35,8 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
return -PositionAt(startTime, endTime, timeRange, scrollLength);
|
||||
}
|
||||
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
||||
=> (float)((time - currentTime) / timeRange * controlPointAt(time).Multiplier * scrollLength);
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null)
|
||||
=> (float)((time - currentTime) / timeRange * controlPointAt(originTime ?? time).Multiplier * scrollLength);
|
||||
|
||||
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
|
||||
{
|
||||
@ -52,7 +50,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
for (; i < controlPoints.Count; i++)
|
||||
{
|
||||
float lastPos = pos;
|
||||
pos = PositionAt(controlPoints[i].StartTime, currentTime, timeRange, scrollLength);
|
||||
pos = PositionAt(controlPoints[i].Time, currentTime, timeRange, scrollLength);
|
||||
|
||||
if (pos > position)
|
||||
{
|
||||
@ -64,7 +62,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
|
||||
i = Math.Clamp(i, 0, controlPoints.Count - 1);
|
||||
|
||||
return controlPoints[i].StartTime + (position - pos) * timeRange / controlPoints[i].Multiplier / scrollLength;
|
||||
return controlPoints[i].Time + (position - pos) * timeRange / controlPoints[i].Multiplier / scrollLength;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@ -78,19 +76,11 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
/// <returns>The <see cref="MultiplierControlPoint"/>.</returns>
|
||||
private MultiplierControlPoint controlPointAt(double time)
|
||||
{
|
||||
if (controlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(double.NegativeInfinity);
|
||||
|
||||
if (time < controlPoints[0].StartTime)
|
||||
return controlPoints[0];
|
||||
|
||||
searchPoint.StartTime = time;
|
||||
int index = controlPoints.BinarySearch(searchPoint);
|
||||
|
||||
if (index < 0)
|
||||
index = ~index - 1;
|
||||
|
||||
return controlPoints[index];
|
||||
return ControlPointInfo.BinarySearch(controlPoints, time)
|
||||
// The standard binary search will fail if there's no control points, or if the time is before the first.
|
||||
// For this method, we want to use the first control point in the latter case.
|
||||
?? controlPoints.FirstOrDefault()
|
||||
?? new MultiplierControlPoint(double.NegativeInfinity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
return (float)(objectLength * scrollLength);
|
||||
}
|
||||
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null)
|
||||
{
|
||||
double timelineLength = relativePositionAt(time, timeRange) - relativePositionAt(currentTime, timeRange);
|
||||
return (float)(timelineLength * scrollLength);
|
||||
@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
if (controlPoints.Count == 0)
|
||||
return;
|
||||
|
||||
positionMappings.Add(new PositionMapping(controlPoints[0].StartTime, controlPoints[0]));
|
||||
positionMappings.Add(new PositionMapping(controlPoints[0].Time, controlPoints[0]));
|
||||
|
||||
for (int i = 0; i < controlPoints.Count - 1; i++)
|
||||
{
|
||||
@ -129,9 +129,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
|
||||
var next = controlPoints[i + 1];
|
||||
|
||||
// Figure out how much of the time range the duration represents, and adjust it by the speed multiplier
|
||||
float length = (float)((next.StartTime - current.StartTime) / timeRange * current.Multiplier);
|
||||
float length = (float)((next.Time - current.Time) / timeRange * current.Multiplier);
|
||||
|
||||
positionMappings.Add(new PositionMapping(next.StartTime, next, positionMappings[^1].Position + length));
|
||||
positionMappings.Add(new PositionMapping(next.Time, next, positionMappings[^1].Position + length));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,9 +158,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
// Trim unwanted sequences of timing changes
|
||||
timingChanges = timingChanges
|
||||
// Collapse sections after the last hit object
|
||||
.Where(s => s.StartTime <= lastObjectTime)
|
||||
.Where(s => s.Time <= lastObjectTime)
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);
|
||||
.GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time);
|
||||
|
||||
ControlPoints.AddRange(timingChanges);
|
||||
|
||||
|
@ -93,9 +93,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// <summary>
|
||||
/// Given a time, return the position along the scrolling axis within this <see cref="HitObjectContainer"/> at time <paramref name="currentTime"/>.
|
||||
/// </summary>
|
||||
public float PositionAtTime(double time, double currentTime)
|
||||
public float PositionAtTime(double time, double currentTime, double? originTime = null)
|
||||
{
|
||||
float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength);
|
||||
float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength, originTime);
|
||||
return axisInverted ? -scrollPosition : scrollPosition;
|
||||
}
|
||||
|
||||
@ -236,8 +236,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - judgementOffset, computedStartTime);
|
||||
}
|
||||
|
||||
private void updateLayoutRecursive(DrawableHitObject hitObject)
|
||||
private void updateLayoutRecursive(DrawableHitObject hitObject, double? parentHitObjectStartTime = null)
|
||||
{
|
||||
parentHitObjectStartTime ??= hitObject.HitObject.StartTime;
|
||||
|
||||
if (hitObject.HitObject is IHasDuration e)
|
||||
{
|
||||
float length = LengthAtTime(hitObject.HitObject.StartTime, e.EndTime);
|
||||
@ -249,17 +251,17 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
||||
foreach (var obj in hitObject.NestedHitObjects)
|
||||
{
|
||||
updateLayoutRecursive(obj);
|
||||
updateLayoutRecursive(obj, parentHitObjectStartTime);
|
||||
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions and start lifetime
|
||||
updatePosition(obj, hitObject.HitObject.StartTime);
|
||||
updatePosition(obj, hitObject.HitObject.StartTime, parentHitObjectStartTime);
|
||||
setComputedLifetimeStart(obj.Entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePosition(DrawableHitObject hitObject, double currentTime)
|
||||
private void updatePosition(DrawableHitObject hitObject, double currentTime, double? parentHitObjectStartTime = null)
|
||||
{
|
||||
float position = PositionAtTime(hitObject.HitObject.StartTime, currentTime);
|
||||
float position = PositionAtTime(hitObject.HitObject.StartTime, currentTime, parentHitObjectStartTime);
|
||||
|
||||
if (scrollingAxis == Direction.Horizontal)
|
||||
hitObject.X = position;
|
||||
|
@ -106,7 +106,6 @@ namespace osu.Game.Screens.Edit
|
||||
Name = "Main content",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = float.MaxValue,
|
||||
Padding = new MarginPadding(10),
|
||||
Child = spinner = new LoadingSpinner(true)
|
||||
{
|
||||
State = { Value = Visibility.Visible },
|
||||
|
@ -41,6 +41,11 @@ namespace osu.Game.Screens
|
||||
/// </summary>
|
||||
bool HideOverlaysOnEnter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the menu cursor should be hidden when non-mouse input is received.
|
||||
/// </summary>
|
||||
bool HideMenuCursorOnNonMouseInput { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether overlays should be able to be opened when this screen is current.
|
||||
/// </summary>
|
||||
|
@ -78,9 +78,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
return;
|
||||
|
||||
bool isItemOwner = Item.OwnerID == api.LocalUser.Value.OnlineID || multiplayerClient.IsHost;
|
||||
bool isValidItem = isItemOwner && !Item.Expired;
|
||||
|
||||
AllowDeletion = isItemOwner && !Item.Expired && Item.ID != multiplayerClient.Room.Settings.PlaylistItemId;
|
||||
AllowEditing = isItemOwner && !Item.Expired;
|
||||
AllowDeletion = isValidItem
|
||||
&& (Item.ID != multiplayerClient.Room.Settings.PlaylistItemId // This is an optimisation for the following check.
|
||||
|| multiplayerClient.Room.Playlist.Count(i => !i.Expired) > 1);
|
||||
|
||||
AllowEditing = isValidItem;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -40,11 +40,10 @@ namespace osu.Game.Screens
|
||||
|
||||
public virtual bool AllowExternalScreenChange => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether all overlays should be hidden when this screen is entered or resumed.
|
||||
/// </summary>
|
||||
public virtual bool HideOverlaysOnEnter => false;
|
||||
|
||||
public virtual bool HideMenuCursorOnNonMouseInput => false;
|
||||
|
||||
/// <summary>
|
||||
/// The initial overlay activation mode to use when this screen is entered for the first time.
|
||||
/// </summary>
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -44,8 +45,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
public Bindable<LabelStyles> LabelStyle { get; } = new Bindable<LabelStyles>(LabelStyles.Icons);
|
||||
|
||||
private SpriteIcon arrow;
|
||||
private Drawable labelEarly;
|
||||
private Drawable labelLate;
|
||||
private UprightAspectMaintainingContainer labelEarly;
|
||||
private UprightAspectMaintainingContainer labelLate;
|
||||
|
||||
private Container colourBarsEarly;
|
||||
private Container colourBarsLate;
|
||||
@ -122,6 +123,20 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = judgement_line_width,
|
||||
},
|
||||
labelEarly = new UprightAspectMaintainingContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Y = -10,
|
||||
},
|
||||
labelLate = new UprightAspectMaintainingContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
Y = 10,
|
||||
},
|
||||
}
|
||||
},
|
||||
arrowContainer = new Container
|
||||
@ -261,57 +276,39 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
{
|
||||
const float icon_size = 14;
|
||||
|
||||
labelEarly?.Expire();
|
||||
labelEarly = null;
|
||||
|
||||
labelLate?.Expire();
|
||||
labelLate = null;
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case LabelStyles.None:
|
||||
break;
|
||||
|
||||
case LabelStyles.Icons:
|
||||
labelEarly = new SpriteIcon
|
||||
labelEarly.Child = new SpriteIcon
|
||||
{
|
||||
Y = -10,
|
||||
Size = new Vector2(icon_size),
|
||||
Icon = FontAwesome.Solid.ShippingFast,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
labelLate = new SpriteIcon
|
||||
labelLate.Child = new SpriteIcon
|
||||
{
|
||||
Y = 10,
|
||||
Size = new Vector2(icon_size),
|
||||
Icon = FontAwesome.Solid.Bicycle,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
case LabelStyles.Text:
|
||||
labelEarly = new OsuSpriteText
|
||||
labelEarly.Child = new OsuSpriteText
|
||||
{
|
||||
Y = -10,
|
||||
Text = "Early",
|
||||
Font = OsuFont.Default.With(size: 10),
|
||||
Height = 12,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
labelLate = new OsuSpriteText
|
||||
labelLate.Child = new OsuSpriteText
|
||||
{
|
||||
Y = 10,
|
||||
Text = "Late",
|
||||
Font = OsuFont.Default.With(size: 10),
|
||||
Height = 12,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
break;
|
||||
@ -320,26 +317,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
throw new ArgumentOutOfRangeException(nameof(style), style, null);
|
||||
}
|
||||
|
||||
if (labelEarly != null)
|
||||
{
|
||||
colourBars.Add(labelEarly);
|
||||
labelEarly.FadeInFromZero(500);
|
||||
}
|
||||
|
||||
if (labelLate != null)
|
||||
{
|
||||
colourBars.Add(labelLate);
|
||||
labelLate.FadeInFromZero(500);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// undo any layout rotation to display icons in the correct orientation
|
||||
if (labelEarly != null) labelEarly.Rotation = -Rotation;
|
||||
if (labelLate != null) labelLate.Rotation = -Rotation;
|
||||
labelEarly.FadeInFromZero(500);
|
||||
labelLate.FadeInFromZero(500);
|
||||
}
|
||||
|
||||
private void createColourBars((HitResult result, double length)[] windows)
|
||||
|
@ -66,6 +66,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
|
||||
public override bool HideMenuCursorOnNonMouseInput => true;
|
||||
|
||||
protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered;
|
||||
|
||||
// We are managing our own adjustments (see OnEntering/OnExiting).
|
||||
|
@ -64,6 +64,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected Task? DisposalTask { get; private set; }
|
||||
|
||||
private OsuScrollContainer settingsScroll = null!;
|
||||
|
||||
private bool backgroundBrightnessReduction;
|
||||
|
||||
private readonly BindableDouble volumeAdjustment = new BindableDouble(1);
|
||||
@ -168,30 +170,30 @@ namespace osu.Game.Screens.Play
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new OsuScrollContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2,
|
||||
Padding = new MarginPadding { Vertical = padding },
|
||||
Masking = false,
|
||||
Child = PlayerSettings = new FillFlowContainer<PlayerSettingsGroup>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 20),
|
||||
Padding = new MarginPadding { Horizontal = padding },
|
||||
Children = new PlayerSettingsGroup[]
|
||||
{
|
||||
VisualSettings = new VisualSettings(),
|
||||
AudioSettings = new AudioSettings(),
|
||||
new InputSettings()
|
||||
}
|
||||
},
|
||||
},
|
||||
idleTracker = new IdleTracker(750),
|
||||
}),
|
||||
settingsScroll = new OsuScrollContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2,
|
||||
Padding = new MarginPadding { Vertical = padding },
|
||||
Masking = false,
|
||||
Child = PlayerSettings = new FillFlowContainer<PlayerSettingsGroup>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 20),
|
||||
Padding = new MarginPadding { Horizontal = padding },
|
||||
Children = new PlayerSettingsGroup[]
|
||||
{
|
||||
VisualSettings = new VisualSettings(),
|
||||
AudioSettings = new AudioSettings(),
|
||||
new InputSettings()
|
||||
}
|
||||
},
|
||||
},
|
||||
idleTracker = new IdleTracker(750),
|
||||
lowPassFilter = new AudioFilter(audio.TrackMixer),
|
||||
highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass)
|
||||
};
|
||||
@ -227,6 +229,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Beatmap.Value.Track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment);
|
||||
|
||||
// Start off-screen.
|
||||
settingsScroll.MoveToX(settingsScroll.DrawWidth);
|
||||
|
||||
content.ScaleTo(0.7f);
|
||||
|
||||
contentIn();
|
||||
@ -316,6 +321,16 @@ namespace osu.Game.Screens.Play
|
||||
content.StopTracking();
|
||||
}
|
||||
|
||||
protected override void LogoSuspending(OsuLogo logo)
|
||||
{
|
||||
base.LogoSuspending(logo);
|
||||
content.StopTracking();
|
||||
|
||||
logo
|
||||
.FadeOut(CONTENT_OUT_DURATION / 2, Easing.OutQuint)
|
||||
.ScaleTo(logo.Scale * 0.8f, CONTENT_OUT_DURATION * 2, Easing.OutQuint);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void Update()
|
||||
@ -394,6 +409,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
content.FadeInFromZero(400);
|
||||
content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer);
|
||||
|
||||
settingsScroll.FadeInFromZero(500, Easing.Out)
|
||||
.MoveToX(0, 500, Easing.OutQuint);
|
||||
|
||||
lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint);
|
||||
highPassFilter.CutoffTo(300).Then().CutoffTo(0, 1250); // 1250 is to line up with the appearance of MetadataInfo (750 delay + 500 fade-in)
|
||||
|
||||
@ -407,6 +426,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
content.ScaleTo(0.7f, CONTENT_OUT_DURATION * 2, Easing.OutQuint);
|
||||
content.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint);
|
||||
|
||||
settingsScroll.FadeOut(CONTENT_OUT_DURATION, Easing.OutQuint)
|
||||
.MoveToX(settingsScroll.DrawWidth, CONTENT_OUT_DURATION * 2, Easing.OutQuint);
|
||||
|
||||
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, CONTENT_OUT_DURATION);
|
||||
highPassFilter.CutoffTo(0, CONTENT_OUT_DURATION);
|
||||
}
|
||||
@ -435,7 +458,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
ContentOut();
|
||||
|
||||
TransformSequence<PlayerLoader> pushSequence = this.Delay(CONTENT_OUT_DURATION);
|
||||
TransformSequence<PlayerLoader> pushSequence = this.Delay(0);
|
||||
|
||||
// only show if the warning was created (i.e. the beatmap needs it)
|
||||
// and this is not a restart of the map (the warning expires after first load).
|
||||
@ -444,6 +467,7 @@ namespace osu.Game.Screens.Play
|
||||
const double epilepsy_display_length = 3000;
|
||||
|
||||
pushSequence
|
||||
.Delay(CONTENT_OUT_DURATION)
|
||||
.Schedule(() => epilepsyWarning.State.Value = Visibility.Visible)
|
||||
.TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint)
|
||||
.Delay(epilepsy_display_length)
|
||||
|
@ -86,16 +86,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
// Generally a timeout would not happen here as APIAccess will timeout first.
|
||||
if (!tcs.Task.Wait(60000))
|
||||
handleTokenFailure(new InvalidOperationException("Token retrieval timed out (request never run)"));
|
||||
req.TriggerFailure(new InvalidOperationException("Token retrieval timed out (request never run)"));
|
||||
|
||||
return true;
|
||||
|
||||
void handleTokenFailure(Exception exception)
|
||||
{
|
||||
// This method may be invoked multiple times due to the Task.Wait call above.
|
||||
// We only really care about the first error.
|
||||
if (!tcs.TrySetResult(false))
|
||||
return;
|
||||
tcs.SetResult(false);
|
||||
|
||||
if (HandleTokenRetrievalFailure(exception))
|
||||
{
|
||||
|
@ -46,6 +46,8 @@ namespace osu.Game.Skinning
|
||||
new Color4(242, 24, 57, 255)
|
||||
};
|
||||
|
||||
Configuration.ConfigDictionary[nameof(SkinConfiguration.LegacySetting.AllowSliderBallTint)] = @"true";
|
||||
|
||||
Configuration.LegacyVersion = 2.7m;
|
||||
}
|
||||
}
|
||||
|
@ -85,10 +85,6 @@ namespace osu.Game.Skinning.Editor
|
||||
{
|
||||
public Action<Type>? RequestPlacement;
|
||||
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child) => false;
|
||||
|
||||
public override bool PropagateNonPositionalInputSubTree => false;
|
||||
|
||||
private readonly Drawable component;
|
||||
private readonly CompositeDrawable? dependencySource;
|
||||
|
||||
@ -177,6 +173,10 @@ namespace osu.Game.Skinning.Editor
|
||||
|
||||
public class DependencyBorrowingContainer : Container
|
||||
{
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child) => false;
|
||||
|
||||
public override bool PropagateNonPositionalInputSubTree => false;
|
||||
|
||||
private readonly CompositeDrawable? donor;
|
||||
|
||||
public DependencyBorrowingContainer(CompositeDrawable? donor)
|
||||
|
@ -38,7 +38,8 @@ namespace osu.Game.Skinning
|
||||
HitCirclePrefix,
|
||||
HitCircleOverlap,
|
||||
AnimationFramerate,
|
||||
LayeredHitSounds
|
||||
LayeredHitSounds,
|
||||
AllowSliderBallTint,
|
||||
}
|
||||
|
||||
public static List<Color4> DefaultComboColours { get; } = new List<Color4>
|
||||
|
@ -99,8 +99,8 @@ namespace osu.Game.Tests.Visual
|
||||
public float GetLength(double startTime, double endTime, double timeRange, float scrollLength)
|
||||
=> implementation.GetLength(startTime, endTime, timeRange, scrollLength);
|
||||
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
||||
=> implementation.PositionAt(time, currentTime, timeRange, scrollLength);
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength, double? originTime = null)
|
||||
=> implementation.PositionAt(time, currentTime, timeRange, scrollLength, originTime);
|
||||
|
||||
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
|
||||
=> implementation.TimeAt(position, currentTime, timeRange, scrollLength);
|
||||
|
@ -18,7 +18,7 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="AutoMapper" Version="12.0.0" />
|
||||
<PackageReference Include="AutoMapper" Version="11.0.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.7.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
@ -35,8 +35,8 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.17.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1011.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1008.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1022.1" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
|
||||
<PackageReference Include="Sentry" Version="3.22.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
|
@ -61,8 +61,8 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1008.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1011.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1022.1" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||
<PropertyGroup>
|
||||
@ -82,7 +82,7 @@
|
||||
<PackageReference Include="DiffPlex" Version="1.7.1" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1011.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1022.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user