mirror of
https://github.com/ppy/osu
synced 2024-12-15 11:25:29 +00:00
Merge branch 'master' into fix-back-sample-playback
This commit is contained in:
commit
43fa89ebcd
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||
{
|
||||
host.Run(new OsuTestBrowser());
|
||||
return 0;
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||
{
|
||||
host.Run(new OsuTestBrowser());
|
||||
return 0;
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||
{
|
||||
host.Run(new OsuTestBrowser());
|
||||
return 0;
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||
{
|
||||
host.Run(new OsuTestBrowser());
|
||||
return 0;
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -102,7 +102,7 @@ namespace osu.Desktop
|
||||
}
|
||||
}
|
||||
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = !tournamentClient }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null }))
|
||||
{
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
|
@ -23,9 +23,9 @@
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Clowd.Squirrel" Version="2.10.2" />
|
||||
<PackageReference Include="Clowd.Squirrel" Version="2.11.1" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="8.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
|
||||
<PackageReference Include="nunit" Version="3.13.3" />
|
||||
<PackageReference Include="nunit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -1,10 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
@ -16,37 +14,35 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[Test]
|
||||
public void TestMinor()
|
||||
{
|
||||
AddStep("Create barlines", () => recreate());
|
||||
AddStep("Create barlines", recreate);
|
||||
}
|
||||
|
||||
private void recreate(Func<IEnumerable<BarLine>>? createBarLines = null)
|
||||
private void recreate()
|
||||
{
|
||||
var stageDefinitions = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition(4),
|
||||
};
|
||||
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions).With(s =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
if (createBarLines != null)
|
||||
var maniaPlayfield = new ManiaPlayfield(stageDefinitions);
|
||||
|
||||
// Must be scheduled so the pool is loaded before we try and retrieve from it.
|
||||
Schedule(() =>
|
||||
{
|
||||
var barLines = createBarLines();
|
||||
|
||||
foreach (var b in barLines)
|
||||
s.Add(b);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
s.Add(new BarLine
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
StartTime = Time.Current + i * 500,
|
||||
Major = i % 4 == 0,
|
||||
});
|
||||
}
|
||||
}));
|
||||
maniaPlayfield.Add(new BarLine
|
||||
{
|
||||
StartTime = Time.Current + i * 500,
|
||||
Major = i % 4 == 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return maniaPlayfield;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -25,16 +25,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
|
||||
private readonly List<DrawablePool<TestDrawableOsuJudgement>> pools;
|
||||
private readonly List<DrawablePool<TestDrawableOsuJudgement>> pools = new List<DrawablePool<TestDrawableOsuJudgement>>();
|
||||
|
||||
public TestSceneDrawableJudgement()
|
||||
[TestCaseSource(nameof(validResults))]
|
||||
public void Test(HitResult result)
|
||||
{
|
||||
pools = new List<DrawablePool<TestDrawableOsuJudgement>>();
|
||||
|
||||
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
||||
showResult(result);
|
||||
showResult(result);
|
||||
}
|
||||
|
||||
private static IEnumerable<HitResult> validResults => Enum.GetValues<HitResult>().Skip(1);
|
||||
|
||||
[Test]
|
||||
public void TestHitLightingDisabled()
|
||||
{
|
||||
@ -72,32 +72,33 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
pools.Add(pool = new DrawablePool<TestDrawableOsuJudgement>(1));
|
||||
else
|
||||
{
|
||||
pool = pools[poolIndex];
|
||||
|
||||
// We need to make sure neither the pool nor the judgement get disposed when new content is set, and they both share the same parent.
|
||||
pool = pools[poolIndex];
|
||||
((Container)pool.Parent!).Clear(false);
|
||||
}
|
||||
|
||||
var container = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
pool,
|
||||
pool.Get(j => j.Apply(new JudgementResult(new HitObject
|
||||
{
|
||||
StartTime = Time.Current
|
||||
}, new Judgement())
|
||||
{
|
||||
Type = result,
|
||||
}, null)).With(j =>
|
||||
{
|
||||
j.Anchor = Anchor.Centre;
|
||||
j.Origin = Anchor.Centre;
|
||||
})
|
||||
}
|
||||
Child = pool,
|
||||
};
|
||||
|
||||
// Must be scheduled so the pool is loaded before we try and retrieve from it.
|
||||
Schedule(() =>
|
||||
{
|
||||
container.Add(pool.Get(j => j.Apply(new JudgementResult(new HitObject
|
||||
{
|
||||
StartTime = Time.Current
|
||||
}, new Judgement())
|
||||
{
|
||||
Type = result,
|
||||
}, null)).With(j =>
|
||||
{
|
||||
j.Anchor = Anchor.Centre;
|
||||
j.Origin = Anchor.Centre;
|
||||
}));
|
||||
});
|
||||
|
||||
poolIndex++;
|
||||
return container;
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
DrawableSlider dho = null;
|
||||
|
||||
AddStep("create slider", () => Child = dho = new DrawableSlider(prepareObject(new Slider
|
||||
AddStep("create slider", () => Child = dho = new DrawableSlider(applyDefaults(new Slider
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
IndexInCurrentCombo = 0,
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
AddWaitStep("wait for progression", 1);
|
||||
|
||||
AddStep("apply new slider", () => dho.Apply(prepareObject(new Slider
|
||||
AddStep("apply new slider", () => dho.Apply(applyDefaults(new Slider
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
ComboIndex = 1,
|
||||
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Child = new SkinProvidingContainer(provider)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = dho = new DrawableSlider(prepareObject(new Slider
|
||||
Child = dho = new DrawableSlider(applyDefaults(new Slider
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
IndexInCurrentCombo = 0,
|
||||
@ -97,7 +97,38 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddAssert("ball is red", () => dho.ChildrenOfType<LegacySliderBall>().Single().BallColour == Color4.Red);
|
||||
}
|
||||
|
||||
private Slider prepareObject(Slider slider)
|
||||
[Test]
|
||||
public void TestIncreaseRepeatCount()
|
||||
{
|
||||
DrawableSlider dho = null;
|
||||
|
||||
AddStep("create slider", () =>
|
||||
{
|
||||
Child = dho = new DrawableSlider(applyDefaults(new Slider
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
IndexInCurrentCombo = 0,
|
||||
StartTime = Time.Current,
|
||||
Path = new SliderPath(PathType.LINEAR, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(150, 100),
|
||||
new Vector2(300, 0),
|
||||
})
|
||||
}));
|
||||
});
|
||||
|
||||
AddStep("increase repeat count", () =>
|
||||
{
|
||||
dho.HitObject.RepeatCount++;
|
||||
applyDefaults(dho.HitObject);
|
||||
});
|
||||
|
||||
AddAssert("repeat got custom anchor", () =>
|
||||
dho.ChildrenOfType<DrawableSliderRepeat>().Single().RelativeAnchorPosition == Vector2.Divide(dho.SliderBody!.PathOffset, dho.DrawSize));
|
||||
}
|
||||
|
||||
private Slider applyDefaults(Slider slider)
|
||||
{
|
||||
slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
return slider;
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private Container<DrawableSliderRepeat> repeatContainer;
|
||||
private PausableSkinnableSound slidingSample;
|
||||
|
||||
private readonly LayoutValue drawSizeLayout;
|
||||
private readonly LayoutValue relativeAnchorPositionLayout;
|
||||
|
||||
public DrawableSlider()
|
||||
: this(null)
|
||||
@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
};
|
||||
AddLayout(drawSizeLayout = new LayoutValue(Invalidation.DrawSize | Invalidation.MiscGeometry));
|
||||
AddLayout(relativeAnchorPositionLayout = new LayoutValue(Invalidation.DrawSize | Invalidation.MiscGeometry));
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -190,6 +190,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
repeatContainer.Add(repeat);
|
||||
break;
|
||||
}
|
||||
|
||||
relativeAnchorPositionLayout.Invalidate();
|
||||
}
|
||||
|
||||
protected override void ClearNestedHitObjects()
|
||||
@ -265,14 +267,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Size = SliderBody?.Size ?? Vector2.Zero;
|
||||
OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero;
|
||||
|
||||
if (!drawSizeLayout.IsValid)
|
||||
if (!relativeAnchorPositionLayout.IsValid)
|
||||
{
|
||||
Vector2 pos = Vector2.Divide(OriginPosition, DrawSize);
|
||||
foreach (var obj in NestedHitObjects)
|
||||
obj.RelativeAnchorPosition = pos;
|
||||
Ball.RelativeAnchorPosition = pos;
|
||||
|
||||
drawSizeLayout.Validate();
|
||||
relativeAnchorPositionLayout.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
{
|
||||
public partial class ArgonCursor : OsuCursorSprite
|
||||
public partial class ArgonCursor : SkinnableCursor
|
||||
{
|
||||
public ArgonCursor()
|
||||
{
|
||||
|
@ -65,14 +65,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
if (Result == HitResult.IgnoreMiss || Result == HitResult.LargeTickMiss)
|
||||
{
|
||||
this.RotateTo(-45);
|
||||
this.ScaleTo(1.8f);
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1.2f, 100, Easing.In);
|
||||
|
||||
this.MoveTo(Vector2.Zero);
|
||||
this.MoveToOffset(new Vector2(0, 10), 800, Easing.InQuint);
|
||||
this.FadeOutFromOne(400);
|
||||
}
|
||||
else if (Result.IsMiss())
|
||||
{
|
||||
this.FadeOutFromOne(800);
|
||||
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
|
||||
@ -84,14 +85,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
}
|
||||
else
|
||||
{
|
||||
this.FadeOutFromOne(800);
|
||||
|
||||
JudgementText
|
||||
.FadeInFromZero(300, Easing.OutQuint)
|
||||
.ScaleTo(Vector2.One)
|
||||
.ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint);
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(800);
|
||||
|
||||
ringExplosion?.PlayAnimation();
|
||||
}
|
||||
|
||||
|
@ -3,47 +3,39 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public partial class DefaultApproachCircle : SkinnableSprite
|
||||
public partial class DefaultApproachCircle : Sprite
|
||||
{
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; } = null!;
|
||||
|
||||
public DefaultApproachCircle()
|
||||
: base("Gameplay/osu/approachcircle")
|
||||
{
|
||||
}
|
||||
private IBindable<Color4> accentColour = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
Texture = textures.Get(@"Gameplay/osu/approachcircle").WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2);
|
||||
|
||||
// account for the sprite being used for the default approach circle being taken from stable,
|
||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
||||
Scale = new Vector2(128 / 118f);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
accentColour.BindValueChanged(colour => Colour = colour.NewValue, true);
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefault(ISkinComponentLookup lookup)
|
||||
{
|
||||
var drawable = base.CreateDefault(lookup);
|
||||
|
||||
// Although this is a non-legacy component, osu-resources currently stores approach circle as a legacy-like texture.
|
||||
// See LegacyApproachCircle for documentation as to why this is required.
|
||||
drawable.Scale = new Vector2(128 / 118f);
|
||||
|
||||
return drawable;
|
||||
accentColour = drawableObject.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
TriangleScale = 1.2f;
|
||||
HideAlphaDiscrepancies = false;
|
||||
ClampToDrawable = false;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -1,9 +1,10 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Skinning;
|
||||
@ -12,40 +13,31 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
// todo: this should probably not be a SkinnableSprite, as this is always created for legacy skins and is recreated on skin change.
|
||||
public partial class LegacyApproachCircle : SkinnableSprite
|
||||
public partial class LegacyApproachCircle : Sprite
|
||||
{
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; } = null!;
|
||||
|
||||
public LegacyApproachCircle()
|
||||
: base("Gameplay/osu/approachcircle", OsuHitObject.OBJECT_DIMENSIONS * 2)
|
||||
{
|
||||
}
|
||||
private IBindable<Color4> accentColour = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
var texture = skin.GetTexture(@"approachcircle");
|
||||
Debug.Assert(texture != null);
|
||||
Texture = texture.WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2);
|
||||
|
||||
// account for the sprite being used for the default approach circle being taken from stable,
|
||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
||||
Scale = new Vector2(128 / 118f);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
accentColour = drawableObject.AccentColour.GetBoundCopy();
|
||||
accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefault(ISkinComponentLookup lookup)
|
||||
{
|
||||
var drawable = base.CreateDefault(lookup);
|
||||
|
||||
// account for the sprite being used for the default approach circle being taken from stable,
|
||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
||||
drawable.Scale = new Vector2(128 / 118f);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,11 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public partial class LegacyCursor : OsuCursorSprite
|
||||
public partial class LegacyCursor : SkinnableCursor
|
||||
{
|
||||
private const float pressed_scale = 1.3f;
|
||||
private const float released_scale = 1f;
|
||||
|
||||
private readonly ISkin skin;
|
||||
private bool spin;
|
||||
|
||||
@ -51,5 +54,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
if (spin)
|
||||
ExpandTarget.Spin(10000, RotationDirection.Clockwise);
|
||||
}
|
||||
|
||||
public override void Expand()
|
||||
{
|
||||
ExpandTarget?.ScaleTo(released_scale)
|
||||
.ScaleTo(pressed_scale, 100, Easing.Out);
|
||||
}
|
||||
|
||||
public override void Contract()
|
||||
{
|
||||
ExpandTarget?.ScaleTo(released_scale, 100, Easing.Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,19 +34,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject drawableObject, ISkinSource skinSource)
|
||||
{
|
||||
const string lookup_name = @"reversearrow";
|
||||
|
||||
drawableRepeat = (DrawableSliderRepeat)drawableObject;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
string lookupName = new OsuSkinComponentLookup(OsuSkinComponents.ReverseArrow).LookupName;
|
||||
|
||||
var skin = skinSource.FindProvider(s => s.GetTexture(lookupName) != null);
|
||||
var skin = skinSource.FindProvider(s => s.GetTexture(lookup_name) != null);
|
||||
|
||||
InternalChild = arrow = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = skin?.GetTexture(lookupName)?.WithMaximumSize(maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2),
|
||||
Texture = skin?.GetTexture(lookup_name)?.WithMaximumSize(maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2),
|
||||
};
|
||||
|
||||
textureIsDefaultSkin = skin is ISkinTransformer transformer && transformer.Skin is DefaultLegacySkin;
|
||||
|
@ -163,7 +163,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ApproachCircle:
|
||||
return new LegacyApproachCircle();
|
||||
if (GetTexture(@"approachcircle") != null)
|
||||
return new LegacyApproachCircle();
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
|
@ -24,15 +24,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
public const float SIZE = 28;
|
||||
|
||||
private const float pressed_scale = 1.2f;
|
||||
private const float released_scale = 1f;
|
||||
|
||||
private bool cursorExpand;
|
||||
|
||||
private SkinnableDrawable cursorSprite;
|
||||
private Container cursorScaleContainer = null!;
|
||||
|
||||
private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite;
|
||||
private SkinnableCursor skinnableCursor => (SkinnableCursor)cursorSprite.Drawable;
|
||||
|
||||
public IBindable<float> CursorScale => cursorScale;
|
||||
|
||||
@ -108,10 +105,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
if (!cursorExpand) return;
|
||||
|
||||
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 400, Easing.OutElasticHalf);
|
||||
skinnableCursor.Expand();
|
||||
}
|
||||
|
||||
public void Contract() => expandTarget.ScaleTo(released_scale, 400, Easing.OutQuad);
|
||||
public void Contract() => skinnableCursor.Contract();
|
||||
|
||||
/// <summary>
|
||||
/// Get the scale applicable to the ActiveCursor based on a beatmap's circle size.
|
||||
@ -119,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
public static float GetScaleForCircleSize(float circleSize) =>
|
||||
1f - 0.7f * (1f + circleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY;
|
||||
|
||||
private partial class DefaultCursor : OsuCursorSprite
|
||||
private partial class DefaultCursor : SkinnableCursor
|
||||
{
|
||||
public DefaultCursor()
|
||||
{
|
||||
|
@ -1,19 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
public abstract partial class OsuCursorSprite : CompositeDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The an optional piece of the cursor to expand when in a clicked state.
|
||||
/// If null, the whole cursor will be affected by expansion.
|
||||
/// </summary>
|
||||
public Drawable ExpandTarget { get; protected set; }
|
||||
}
|
||||
}
|
31
osu.Game.Rulesets.Osu/UI/Cursor/SkinnableCursor.cs
Normal file
31
osu.Game.Rulesets.Osu/UI/Cursor/SkinnableCursor.cs
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
public abstract partial class SkinnableCursor : CompositeDrawable
|
||||
{
|
||||
private const float pressed_scale = 1.2f;
|
||||
private const float released_scale = 1f;
|
||||
|
||||
public virtual void Expand()
|
||||
{
|
||||
ExpandTarget?.ScaleTo(released_scale)
|
||||
.ScaleTo(pressed_scale, 400, Easing.OutElasticHalf);
|
||||
}
|
||||
|
||||
public virtual void Contract()
|
||||
{
|
||||
ExpandTarget?.ScaleTo(released_scale, 400, Easing.OutQuad);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The an optional piece of the cursor to expand when in a clicked state.
|
||||
/// If null, the whole cursor will be affected by expansion.
|
||||
/// </summary>
|
||||
public Drawable? ExpandTarget { get; protected set; }
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -56,24 +56,6 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
"Gameplay/osu/followpoint", 1
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new[] { "followpoint@2x", "followpoint" },
|
||||
"Gameplay/osu/followpoint",
|
||||
"followpoint@2x", 2
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new[] { "followpoint@2x" },
|
||||
"Gameplay/osu/followpoint",
|
||||
"followpoint@2x", 2
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new[] { "followpoint" },
|
||||
"Gameplay/osu/followpoint",
|
||||
"followpoint", 1
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
// Looking up a filename with extension specified should work.
|
||||
new[] { "followpoint.png" },
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
AddSliderStep("Triangle scale", 0f, 10f, 1f, s => triangles.TriangleScale = s);
|
||||
AddSliderStep("Seed", 0, 1000, 0, s => triangles.Reset(s));
|
||||
AddToggleStep("Masking", m => triangles.Masking = m);
|
||||
AddToggleStep("ClampToDrawable", c => triangles.ClampToDrawable = c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
AddStep("White colour", () => box.Colour = triangles.Colour = maskedTriangles.Colour = Color4.White);
|
||||
AddStep("Vertical gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red));
|
||||
AddStep("Horizontal gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Red));
|
||||
AddToggleStep("Masking", m => maskedTriangles.Masking = m);
|
||||
AddToggleStep("ClampToDrawable", c => maskedTriangles.ClampToDrawable = c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,10 +129,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
checkRate(1);
|
||||
}
|
||||
|
||||
private const int max_frames_catchup = 50;
|
||||
|
||||
private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () =>
|
||||
mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) { MaxCatchUpFrames = max_frames_catchup }
|
||||
mainContainer.Child = new FrameStabilityContainer(gameplayStartTime)
|
||||
.WithChild(consumer = new ClockConsumingChild()));
|
||||
|
||||
private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time);
|
||||
|
@ -93,15 +93,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
double currentTime = masterClock.CurrentTime;
|
||||
|
||||
bool goingForward = currentTime >= (masterClock.LastStopTime ?? lastStopTime);
|
||||
bool goingForward = currentTime >= lastStopTime;
|
||||
|
||||
alwaysGoingForward &= goingForward;
|
||||
|
||||
if (!goingForward)
|
||||
Logger.Log($"Went too far backwards (last stop: {lastStopTime:N1} current: {currentTime:N1})");
|
||||
|
||||
if (masterClock.LastStopTime != null)
|
||||
lastStopTime = masterClock.LastStopTime.Value;
|
||||
};
|
||||
});
|
||||
|
||||
@ -125,7 +122,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
resumeAndConfirm();
|
||||
|
||||
AddAssert("Resumed without seeking forward", () => Player.LastResumeTime, () => Is.LessThanOrEqualTo(Player.LastPauseTime));
|
||||
AddAssert("continued playing forward", () => Player.LastResumeTime, () => Is.GreaterThanOrEqualTo(Player.LastPauseTime));
|
||||
|
||||
AddUntilStep("player playing", () => Player.LocalUserPlaying.Value);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
@ -383,6 +384,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AllowImportCompletion = new SemaphoreSlim(1);
|
||||
}
|
||||
|
||||
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart)
|
||||
{
|
||||
ShouldValidatePlaybackRate = false,
|
||||
};
|
||||
|
||||
protected override async Task ImportScore(Score score)
|
||||
{
|
||||
ScoreImportStarted = true;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
@ -47,7 +48,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
Child = frameStabilityContainer = new FrameStabilityContainer
|
||||
{
|
||||
MaxCatchUpFrames = 1
|
||||
Child = new FakeLoad()
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -56,6 +57,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Dependencies.CacheAs<IFrameStableClock>(frameStabilityContainer);
|
||||
}
|
||||
|
||||
private partial class FakeLoad : Drawable
|
||||
{
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
});
|
||||
AddStep("create IPC sender channels", () =>
|
||||
{
|
||||
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true });
|
||||
ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { IPCPort = OsuGame.IPC_PORT });
|
||||
osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost);
|
||||
archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost);
|
||||
});
|
||||
|
@ -40,8 +40,15 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
AddStep("change value from default", () => textBox.Current.Value = "non-default");
|
||||
AddUntilStep("restore button shown", () => revertToDefaultButton.Alpha > 0);
|
||||
|
||||
AddStep("disable setting", () => textBox.Current.Disabled = true);
|
||||
AddUntilStep("restore button still shown", () => revertToDefaultButton.Alpha > 0);
|
||||
|
||||
AddStep("enable setting", () => textBox.Current.Disabled = false);
|
||||
AddStep("restore default", () => textBox.Current.SetDefault());
|
||||
AddUntilStep("restore button hidden", () => revertToDefaultButton.Alpha == 0);
|
||||
|
||||
AddStep("disable setting", () => textBox.Current.Disabled = true);
|
||||
AddUntilStep("restore button still hidden", () => revertToDefaultButton.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -4,7 +4,7 @@
|
||||
<PackageReference Include="DeepEqual" Version="4.2.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
</ItemGroup>
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Tournament.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development", new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development"))
|
||||
{
|
||||
host.Run(new TournamentTestBrowser());
|
||||
return 0;
|
||||
|
@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK;
|
||||
@ -101,11 +102,25 @@ namespace osu.Game.Tournament.Components
|
||||
|
||||
private void refreshContent()
|
||||
{
|
||||
if (beatmap == null)
|
||||
beatmap ??= new BeatmapInfo
|
||||
{
|
||||
flow.Clear();
|
||||
return;
|
||||
}
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = "unknown",
|
||||
Title = "no beatmap selected",
|
||||
Author = new RealmUser { Username = "unknown" },
|
||||
},
|
||||
DifficultyName = "unknown",
|
||||
BeatmapSet = new BeatmapSetInfo(),
|
||||
StarRating = 0,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 0,
|
||||
DrainRate = 0,
|
||||
OverallDifficulty = 0,
|
||||
ApproachRate = 0,
|
||||
},
|
||||
};
|
||||
|
||||
double bpm = beatmap.BPM;
|
||||
double length = beatmap.Length;
|
||||
|
@ -194,7 +194,7 @@ namespace osu.Game.Tournament.Components
|
||||
|
||||
// Use DelayedLoadWrapper to avoid content unloading when switching away to another screen.
|
||||
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
|
||||
=> new DelayedLoadWrapper(createContentFunc, timeBeforeLoad);
|
||||
=> new DelayedLoadWrapper(createContentFunc(), timeBeforeLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,6 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
private readonly bool applyOffsets;
|
||||
|
||||
/// <summary>
|
||||
/// The total frequency adjustment from pause transforms. Should eventually be handled in a better way.
|
||||
/// </summary>
|
||||
public readonly BindableDouble ExternalPauseFrequencyAdjust = new BindableDouble(1);
|
||||
|
||||
private readonly OffsetCorrectionClock? userGlobalOffsetClock;
|
||||
private readonly OffsetCorrectionClock? platformOffsetClock;
|
||||
private readonly OffsetCorrectionClock? userBeatmapOffsetClock;
|
||||
@ -69,13 +64,13 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
// Audio timings in general with newer BASS versions don't match stable.
|
||||
// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
|
||||
platformOffsetClock = new OffsetCorrectionClock(interpolatedTrack, ExternalPauseFrequencyAdjust) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 };
|
||||
platformOffsetClock = new OffsetCorrectionClock(interpolatedTrack) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 };
|
||||
|
||||
// User global offset (set in settings) should also be applied.
|
||||
userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock, ExternalPauseFrequencyAdjust);
|
||||
userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock);
|
||||
|
||||
// User per-beatmap offset will be applied to this final clock.
|
||||
finalClockSource = userBeatmapOffsetClock = new OffsetCorrectionClock(userGlobalOffsetClock, ExternalPauseFrequencyAdjust);
|
||||
finalClockSource = userBeatmapOffsetClock = new OffsetCorrectionClock(userGlobalOffsetClock);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -142,6 +142,7 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
|
||||
SetDefault(OsuSetting.KeyOverlay, false);
|
||||
SetDefault(OsuSetting.ReplaySettingsOverlay, true);
|
||||
SetDefault(OsuSetting.ReplayPlaybackControlsExpanded, true);
|
||||
SetDefault(OsuSetting.GameplayLeaderboard, true);
|
||||
SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true);
|
||||
|
||||
@ -421,6 +422,7 @@ namespace osu.Game.Configuration
|
||||
ProfileCoverExpanded,
|
||||
EditorLimitedDistanceSnap,
|
||||
ReplaySettingsOverlay,
|
||||
ReplayPlaybackControlsExpanded,
|
||||
AutomaticallyDownloadMissingBeatmaps,
|
||||
EditorShowSpeedChanges,
|
||||
TouchDisableGameplayTaps,
|
||||
|
@ -15,7 +15,6 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Rendering;
|
||||
using osu.Framework.Graphics.Rendering.Vertices;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
@ -79,9 +78,9 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, only the portion of triangles that falls within this <see cref="Drawable"/>'s
|
||||
/// shape is drawn to the screen.
|
||||
/// shape is drawn to the screen. Default is true.
|
||||
/// </summary>
|
||||
public bool Masking { get; set; }
|
||||
public bool ClampToDrawable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we should drop-off alpha values of triangles more quickly to improve
|
||||
@ -258,13 +257,12 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
private IShader shader;
|
||||
private Texture texture;
|
||||
private bool masking;
|
||||
private bool clamp;
|
||||
|
||||
private readonly List<TriangleParticle> parts = new List<TriangleParticle>();
|
||||
private readonly Vector2 triangleSize = new Vector2(1f, equilateral_triangle_ratio) * triangle_size;
|
||||
|
||||
private Vector2 size;
|
||||
private IVertexBatch<TexturedVertex2D> vertexBatch;
|
||||
|
||||
public TrianglesDrawNode(Triangles source)
|
||||
: base(source)
|
||||
@ -278,7 +276,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
shader = Source.shader;
|
||||
texture = Source.texture;
|
||||
size = Source.DrawSize;
|
||||
masking = Source.Masking;
|
||||
clamp = Source.ClampToDrawable;
|
||||
|
||||
parts.Clear();
|
||||
parts.AddRange(Source.parts);
|
||||
@ -290,12 +288,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
base.Draw(renderer);
|
||||
|
||||
if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount))
|
||||
{
|
||||
vertexBatch?.Dispose();
|
||||
vertexBatch = renderer.CreateQuadBatch<TexturedVertex2D>(Source.AimCount, 1);
|
||||
}
|
||||
|
||||
borderDataBuffer ??= renderer.CreateUniformBuffer<TriangleBorderData>();
|
||||
borderDataBuffer.Data = borderDataBuffer.Data with
|
||||
{
|
||||
@ -314,7 +306,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f);
|
||||
|
||||
Quad triangleQuad = masking ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y);
|
||||
Quad triangleQuad = clamp ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y);
|
||||
|
||||
var drawQuad = new Quad(
|
||||
Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix),
|
||||
@ -333,7 +325,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
triangleQuad.Height
|
||||
) / relativeSize;
|
||||
|
||||
renderer.DrawQuad(texture, drawQuad, colourInfo, new RectangleF(0, 0, 1, 1), vertexBatch.AddAction, textureCoords: textureCoords);
|
||||
renderer.DrawQuad(texture, drawQuad, colourInfo, new RectangleF(0, 0, 1, 1), textureCoords: textureCoords);
|
||||
}
|
||||
|
||||
shader.Unbind();
|
||||
@ -356,7 +348,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
vertexBatch?.Dispose();
|
||||
borderDataBuffer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Rendering;
|
||||
using osu.Framework.Graphics.Rendering.Vertices;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
@ -35,9 +34,9 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, only the portion of triangles that falls within this <see cref="Drawable"/>'s
|
||||
/// shape is drawn to the screen.
|
||||
/// shape is drawn to the screen. Default is true.
|
||||
/// </summary>
|
||||
public bool Masking { get; set; }
|
||||
public bool ClampToDrawable { get; set; } = true;
|
||||
|
||||
private readonly BindableFloat spawnRatio = new BindableFloat(1f);
|
||||
|
||||
@ -194,9 +193,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
private Vector2 size;
|
||||
private float thickness;
|
||||
private float texelSize;
|
||||
private bool masking;
|
||||
|
||||
private IVertexBatch<TexturedVertex2D>? vertexBatch;
|
||||
private bool clamp;
|
||||
|
||||
public TrianglesDrawNode(TrianglesV2 source)
|
||||
: base(source)
|
||||
@ -211,7 +208,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
texture = Source.texture;
|
||||
size = Source.DrawSize;
|
||||
thickness = Source.Thickness;
|
||||
masking = Source.Masking;
|
||||
clamp = Source.ClampToDrawable;
|
||||
|
||||
Quad triangleQuad = new Quad(
|
||||
Vector2Extensions.Transform(Vector2.Zero, DrawInfo.Matrix),
|
||||
@ -235,12 +232,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
if (Source.AimCount == 0 || thickness == 0)
|
||||
return;
|
||||
|
||||
if (vertexBatch == null || vertexBatch.Size != Source.AimCount)
|
||||
{
|
||||
vertexBatch?.Dispose();
|
||||
vertexBatch = renderer.CreateQuadBatch<TexturedVertex2D>(Source.AimCount, 1);
|
||||
}
|
||||
|
||||
borderDataBuffer ??= renderer.CreateUniformBuffer<TriangleBorderData>();
|
||||
borderDataBuffer.Data = borderDataBuffer.Data with
|
||||
{
|
||||
@ -257,7 +248,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f);
|
||||
|
||||
Quad triangleQuad = masking ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y);
|
||||
Quad triangleQuad = clamp ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y);
|
||||
|
||||
var drawQuad = new Quad(
|
||||
Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix),
|
||||
@ -273,7 +264,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
triangleQuad.Height
|
||||
) / relativeSize;
|
||||
|
||||
renderer.DrawQuad(texture, drawQuad, DrawColourInfo.Colour.Interpolate(triangleQuad), new RectangleF(0, 0, 1, 1), vertexBatch.AddAction, textureCoords: textureCoords);
|
||||
renderer.DrawQuad(texture, drawQuad, DrawColourInfo.Colour.Interpolate(triangleQuad), new RectangleF(0, 0, 1, 1), textureCoords: textureCoords);
|
||||
}
|
||||
|
||||
shader.Unbind();
|
||||
@ -296,7 +287,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
vertexBatch?.Dispose();
|
||||
borderDataBuffer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
TriangleScale = 4,
|
||||
ColourDark = OsuColour.Gray(0.88f),
|
||||
Shear = new Vector2(-0.2f, 0),
|
||||
ClampToDrawable = false
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -170,6 +170,8 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(InputKey.MouseMiddle, GlobalAction.TogglePauseReplay),
|
||||
new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward),
|
||||
new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward),
|
||||
new KeyBinding(InputKey.Comma, GlobalAction.StepReplayBackward),
|
||||
new KeyBinding(InputKey.Period, GlobalAction.StepReplayForward),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.ToggleReplaySettings),
|
||||
};
|
||||
|
||||
@ -411,7 +413,13 @@ namespace osu.Game.Input.Bindings
|
||||
IncreaseOffset,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseOffset))]
|
||||
DecreaseOffset
|
||||
DecreaseOffset,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.StepReplayForward))]
|
||||
StepReplayForward,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.StepReplayBackward))]
|
||||
StepReplayBackward,
|
||||
}
|
||||
|
||||
public enum GlobalActionCategory
|
||||
|
@ -10,9 +10,9 @@ namespace osu.Game.Localisation
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.BeatmapOffsetControl";
|
||||
|
||||
/// <summary>
|
||||
/// "Beatmap offset"
|
||||
/// "Audio offset (this beatmap)"
|
||||
/// </summary>
|
||||
public static LocalisableString BeatmapOffset => new TranslatableString(getKey(@"beatmap_offset"), @"Beatmap offset");
|
||||
public static LocalisableString AudioOffsetThisBeatmap => new TranslatableString(getKey(@"beatmap_offset"), @"Audio offset (this beatmap)");
|
||||
|
||||
/// <summary>
|
||||
/// "Previous play:"
|
||||
|
@ -324,6 +324,16 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString SeekReplayBackward => new TranslatableString(getKey(@"seek_replay_backward"), @"Seek replay backward");
|
||||
|
||||
/// <summary>
|
||||
/// "Seek replay forward one frame"
|
||||
/// </summary>
|
||||
public static LocalisableString StepReplayForward => new TranslatableString(getKey(@"step_replay_forward"), @"Seek replay forward one frame");
|
||||
|
||||
/// <summary>
|
||||
/// "Step replay backward one frame"
|
||||
/// </summary>
|
||||
public static LocalisableString StepReplayBackward => new TranslatableString(getKey(@"step_replay_backward"), @"Step replay backward one frame");
|
||||
|
||||
/// <summary>
|
||||
/// "Toggle chat focus"
|
||||
/// </summary>
|
||||
|
@ -9,6 +9,16 @@ namespace osu.Game.Localisation
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.PlaybackSettings";
|
||||
|
||||
/// <summary>
|
||||
/// "Step backward one frame"
|
||||
/// </summary>
|
||||
public static LocalisableString StepBackward => new TranslatableString(getKey(@"step_backward_frame"), @"Step backward one frame");
|
||||
|
||||
/// <summary>
|
||||
/// "Step forward one frame"
|
||||
/// </summary>
|
||||
public static LocalisableString StepForward => new TranslatableString(getKey(@"step_forward_frame"), @"Step forward one frame");
|
||||
|
||||
/// <summary>
|
||||
/// "Seek backward {0} seconds"
|
||||
/// </summary>
|
||||
|
@ -28,6 +28,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty("latest_build")]
|
||||
public APIChangelogBuild LatestBuild { get; set; }
|
||||
|
||||
[JsonProperty("user_count")]
|
||||
public int UserCount { get; set; }
|
||||
|
||||
public bool Equals(APIUpdateStream other) => Id == other?.Id;
|
||||
|
||||
internal static readonly Dictionary<string, Color4> KNOWN_STREAMS = new Dictionary<string, Color4>
|
||||
|
@ -80,6 +80,13 @@ namespace osu.Game
|
||||
[Cached(typeof(OsuGame))]
|
||||
public partial class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler
|
||||
{
|
||||
#if DEBUG
|
||||
// Different port allows runnning release and debug builds alongside each other.
|
||||
public const int IPC_PORT = 44824;
|
||||
#else
|
||||
public const int IPC_PORT = 44823;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications).
|
||||
/// </summary>
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
|
||||
protected override LocalisableString AdditionalText => Value.LatestBuild.DisplayVersion;
|
||||
|
||||
protected override LocalisableString InfoText => Value.LatestBuild.Users > 0 ? $"{"user".ToQuantity(Value.LatestBuild.Users, "N0")} online" : null;
|
||||
protected override LocalisableString InfoText => Value.UserCount > 0 ? $"{"user".ToQuantity(Value.UserCount, "N0")} online" : null;
|
||||
|
||||
protected override Color4 GetBarColour(OsuColour colours) => Value.Colour;
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Height = header_height,
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||
Velocity = 0.7f,
|
||||
ClampToDrawable = false
|
||||
},
|
||||
headerText = new OsuTextFlowContainer(t =>
|
||||
{
|
||||
|
@ -115,7 +115,12 @@ namespace osu.Game.Overlays
|
||||
|
||||
Enabled.Value = !current.Disabled;
|
||||
|
||||
this.FadeTo(current.Disabled ? 0.2f : (current.IsDefault ? 0 : 1), fade_duration, Easing.OutQuint);
|
||||
if (current.IsDefault)
|
||||
this.FadeTo(0, fade_duration, Easing.OutQuint);
|
||||
else if (current.Disabled)
|
||||
this.FadeTo(0.2f, fade_duration, Easing.OutQuint);
|
||||
else
|
||||
this.FadeTo(1, fade_duration, Easing.OutQuint);
|
||||
|
||||
if (IsHovered && Enabled.Value)
|
||||
{
|
||||
|
@ -12,12 +12,14 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
@ -67,7 +69,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TimeSlider
|
||||
new OffsetSliderBar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Current = { BindTarget = Current },
|
||||
@ -157,6 +159,11 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
||||
: $@"Based on the last {averageHitErrorHistory.Count} play(s), the suggested offset is {SuggestedOffset.Value:N0} ms.";
|
||||
applySuggestion.Enabled.Value = SuggestedOffset.Value != null;
|
||||
}
|
||||
|
||||
private partial class OffsetSliderBar : RoundedSliderBar<double>
|
||||
{
|
||||
public override LocalisableString TooltipText => BeatmapOffsetControl.GetOffsetExplanatoryText(Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,13 +45,14 @@ namespace osu.Game.Rulesets.Judgements
|
||||
if (Result == HitResult.IgnoreMiss || Result == HitResult.LargeTickMiss)
|
||||
{
|
||||
this.RotateTo(-45);
|
||||
this.ScaleTo(1.8f);
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1.2f, 100, Easing.In);
|
||||
|
||||
this.MoveTo(Vector2.Zero);
|
||||
this.MoveToOffset(new Vector2(0, 10), 800, Easing.InQuint);
|
||||
this.FadeOutFromOne(400);
|
||||
return;
|
||||
}
|
||||
else if (Result.IsMiss())
|
||||
|
||||
if (Result.IsMiss())
|
||||
{
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
|
@ -153,6 +153,9 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
|
||||
protected override bool CheckChildrenLife()
|
||||
{
|
||||
if (!IsPresent)
|
||||
return false;
|
||||
|
||||
bool aliveChanged = base.CheckChildrenLife();
|
||||
aliveChanged |= lifetimeManager.Update(Time.Current - PastLifetimeExtension, Time.Current + FutureLifetimeExtension);
|
||||
return aliveChanged;
|
||||
|
@ -25,17 +25,15 @@ namespace osu.Game.Rulesets.UI
|
||||
public ReplayInputHandler? ReplayInputHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of frames (per parent frame) which can be run in an attempt to catch-up to real-time.
|
||||
/// The number of CPU milliseconds to spend at most during seek catch-up.
|
||||
/// </summary>
|
||||
public int MaxCatchUpFrames { get; set; } = 5;
|
||||
private const double max_catchup_milliseconds = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to enable frame-stable playback.
|
||||
/// </summary>
|
||||
internal bool FrameStablePlayback { get; set; } = true;
|
||||
|
||||
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && state != PlaybackState.NotValid;
|
||||
|
||||
private readonly Bindable<bool> isCatchingUp = new Bindable<bool>();
|
||||
|
||||
private readonly Bindable<bool> waitingOnFrames = new Bindable<bool>();
|
||||
@ -61,6 +59,8 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
private readonly FramedClock framedClock;
|
||||
|
||||
private readonly Stopwatch stopwatch = new Stopwatch();
|
||||
|
||||
/// <summary>
|
||||
/// The current direction of playback to be exposed to frame stable children.
|
||||
/// </summary>
|
||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public override bool UpdateSubTree()
|
||||
{
|
||||
int loops = MaxCatchUpFrames;
|
||||
stopwatch.Restart();
|
||||
|
||||
do
|
||||
{
|
||||
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
base.UpdateSubTree();
|
||||
UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat);
|
||||
} while (state == PlaybackState.RequiresCatchUp && loops-- > 0);
|
||||
} while (state == PlaybackState.RequiresCatchUp && stopwatch.ElapsedMilliseconds < max_catchup_milliseconds);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.UI
|
||||
// if waiting on frames, run one update loop to determine if frames have arrived.
|
||||
state = PlaybackState.Valid;
|
||||
}
|
||||
else if (IsPaused.Value)
|
||||
else if (IsPaused.Value && !hasReplayAttached)
|
||||
{
|
||||
// time should not advance while paused, nor should anything run.
|
||||
state = PlaybackState.NotValid;
|
||||
|
@ -114,7 +114,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
bool selectionPerformed = performMouseDownActions(e);
|
||||
bool movementPossible = prepareSelectionMovement();
|
||||
bool movementPossible = prepareSelectionMovement(e);
|
||||
|
||||
// check if selection has occurred
|
||||
if (selectionPerformed)
|
||||
@ -536,9 +536,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <summary>
|
||||
/// Attempts to begin the movement of any selected blueprints.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="MouseDownEvent"/> defining the beginning of a movement.</param>
|
||||
/// <returns>Whether a movement is possible.</returns>
|
||||
private bool prepareSelectionMovement()
|
||||
private bool prepareSelectionMovement(MouseDownEvent e)
|
||||
{
|
||||
if (e.Button == MouseButton.Right)
|
||||
return false;
|
||||
|
||||
if (!SelectionHandler.SelectedBlueprints.Any())
|
||||
return false;
|
||||
|
||||
|
@ -78,8 +78,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
isPaused.Value = false;
|
||||
|
||||
PrepareStart();
|
||||
|
||||
// The case which caused this to be added is FrameStabilityContainer, which manages its own current and elapsed time.
|
||||
// Because we generally update our own current time quicker than children can query it (via Start/Seek/Update),
|
||||
// this means that the first frame ever exposed to children may have a non-zero current time.
|
||||
@ -99,14 +97,6 @@ namespace osu.Game.Screens.Play
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When <see cref="Start"/> is called, this will be run to give an opportunity to prepare the clock at the correct
|
||||
/// start location.
|
||||
/// </summary>
|
||||
protected virtual void PrepareStart()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek to a specific time in gameplay.
|
||||
/// </summary>
|
||||
|
@ -42,35 +42,30 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Origin = anchor;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
labelText = new OsuSpriteText
|
||||
{
|
||||
labelText = new OsuSpriteText
|
||||
Alpha = 0,
|
||||
Text = label.GetValueOrDefault(),
|
||||
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold),
|
||||
Margin = new MarginPadding { Left = 2.5f },
|
||||
},
|
||||
NumberContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
Alpha = 0,
|
||||
Text = label.GetValueOrDefault(),
|
||||
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold),
|
||||
Margin = new MarginPadding { Left = 2.5f },
|
||||
},
|
||||
NumberContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
wireframesPart = new ArgonCounterSpriteText(wireframesLookup)
|
||||
{
|
||||
wireframesPart = new ArgonCounterSpriteText(wireframesLookup)
|
||||
{
|
||||
Anchor = anchor,
|
||||
Origin = anchor,
|
||||
},
|
||||
textPart = new ArgonCounterSpriteText(textLookup)
|
||||
{
|
||||
Anchor = anchor,
|
||||
Origin = anchor,
|
||||
},
|
||||
}
|
||||
Anchor = anchor,
|
||||
Origin = anchor,
|
||||
},
|
||||
textPart = new ArgonCounterSpriteText(textLookup)
|
||||
{
|
||||
Anchor = anchor,
|
||||
Origin = anchor,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -110,7 +105,11 @@ namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
base.LoadComplete();
|
||||
WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true);
|
||||
ShowLabel.BindValueChanged(s => labelText.Alpha = s.NewValue ? 1 : 0, true);
|
||||
ShowLabel.BindValueChanged(s =>
|
||||
{
|
||||
labelText.Alpha = s.NewValue ? 1 : 0;
|
||||
NumberContainer.Y = s.NewValue ? 12 : 0;
|
||||
}, true);
|
||||
}
|
||||
|
||||
private partial class ArgonCounterSpriteText : OsuSpriteText
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -44,6 +45,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Direction = FillDirection.Horizontal;
|
||||
Spacing = new Vector2(20, 0);
|
||||
Margin = new MarginPadding(10);
|
||||
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
@ -66,9 +69,15 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Action = () => Action(),
|
||||
}
|
||||
};
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private SessionStatics sessionStatics { get; set; }
|
||||
|
||||
private Bindable<bool> touchActive;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
button.HoldActivationDelay.BindValueChanged(v =>
|
||||
@ -78,7 +87,20 @@ namespace osu.Game.Screens.Play.HUD
|
||||
: "press for menu";
|
||||
}, true);
|
||||
|
||||
text.FadeInFromZero(500, Easing.OutQuint).Delay(1500).FadeOut(500, Easing.OutQuint);
|
||||
touchActive = sessionStatics.GetBindable<bool>(Static.TouchInputActive);
|
||||
|
||||
if (touchActive.Value)
|
||||
{
|
||||
Alpha = 1f;
|
||||
text.FadeInFromZero(500, Easing.OutQuint)
|
||||
.Delay(1500)
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
Alpha = 0;
|
||||
text.Alpha = 0f;
|
||||
}
|
||||
|
||||
base.LoadComplete();
|
||||
}
|
||||
@ -99,9 +121,11 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Alpha = 1;
|
||||
else
|
||||
{
|
||||
float minAlpha = touchActive.Value ? .08f : 0;
|
||||
|
||||
Alpha = Interpolation.ValueAt(
|
||||
Math.Clamp(Clock.ElapsedFrameTime, 0, 200),
|
||||
Alpha, Math.Clamp(1 - positionalAdjust, 0.04f, 1), 0, 200, Easing.OutQuint);
|
||||
Alpha, Math.Clamp(1 - positionalAdjust, minAlpha, 1), 0, 200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private readonly SkinComponentsContainer mainComponents;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly SkinComponentsContainer rulesetComponents;
|
||||
|
||||
/// <summary>
|
||||
/// A flow which sits at the left side of the screen to house leaderboard (and related) components.
|
||||
/// Will automatically be positioned to avoid colliding with top scoring elements.
|
||||
@ -111,7 +114,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
|
||||
{
|
||||
Drawable rulesetComponents;
|
||||
this.drawableRuleset = drawableRuleset;
|
||||
this.mods = mods;
|
||||
|
||||
@ -125,8 +127,8 @@ namespace osu.Game.Screens.Play
|
||||
clicksPerSecondController = new ClicksPerSecondController(),
|
||||
InputCountController = new InputCountController(),
|
||||
mainComponents = new HUDComponentsContainer { AlwaysPresent = true, },
|
||||
rulesetComponents = drawableRuleset != null
|
||||
? new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, }
|
||||
drawableRuleset != null
|
||||
? (rulesetComponents = new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, })
|
||||
: Empty(),
|
||||
playfieldComponents = drawableRuleset != null
|
||||
? new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.Playfield, drawableRuleset.Ruleset.RulesetInfo)) { AlwaysPresent = true, }
|
||||
@ -170,7 +172,10 @@ namespace osu.Game.Screens.Play
|
||||
},
|
||||
};
|
||||
|
||||
hideTargets = new List<Drawable> { mainComponents, rulesetComponents, playfieldComponents, topRightElements };
|
||||
hideTargets = new List<Drawable> { mainComponents, playfieldComponents, topRightElements };
|
||||
|
||||
if (rulesetComponents != null)
|
||||
hideTargets.Add(rulesetComponents);
|
||||
|
||||
if (!alwaysShowLeaderboard)
|
||||
hideTargets.Add(LeaderboardFlow);
|
||||
@ -256,13 +261,37 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
// LINQ cast can be removed when IDrawable interface includes Anchor / RelativeSizeAxes.
|
||||
foreach (var element in mainComponents.Components.Cast<Drawable>())
|
||||
processDrawable(element);
|
||||
|
||||
if (rulesetComponents != null)
|
||||
{
|
||||
foreach (var element in rulesetComponents.Components.Cast<Drawable>())
|
||||
processDrawable(element);
|
||||
}
|
||||
|
||||
if (lowestTopScreenSpaceRight.HasValue)
|
||||
topRightElements.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceRight.Value)).Y, 0, DrawHeight - topRightElements.DrawHeight);
|
||||
else
|
||||
topRightElements.Y = 0;
|
||||
|
||||
if (lowestTopScreenSpaceLeft.HasValue)
|
||||
LeaderboardFlow.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceLeft.Value)).Y, 0, DrawHeight - LeaderboardFlow.DrawHeight);
|
||||
else
|
||||
LeaderboardFlow.Y = 0;
|
||||
|
||||
if (highestBottomScreenSpace.HasValue)
|
||||
bottomRightElements.Y = BottomScoringElementsHeight = -MathHelper.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight);
|
||||
else
|
||||
bottomRightElements.Y = 0;
|
||||
|
||||
void processDrawable(Drawable element)
|
||||
{
|
||||
// for now align some top components with the bottom-edge of the lowest top-anchored hud element.
|
||||
if (element.Anchor.HasFlagFast(Anchor.y0))
|
||||
{
|
||||
// health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area.
|
||||
if (element is LegacyHealthDisplay)
|
||||
continue;
|
||||
return;
|
||||
|
||||
float bottom = element.ScreenSpaceDrawQuad.BottomRight.Y;
|
||||
|
||||
@ -288,21 +317,6 @@ namespace osu.Game.Screens.Play
|
||||
highestBottomScreenSpace = topLeft;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowestTopScreenSpaceRight.HasValue)
|
||||
topRightElements.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceRight.Value)).Y, 0, DrawHeight - topRightElements.DrawHeight);
|
||||
else
|
||||
topRightElements.Y = 0;
|
||||
|
||||
if (lowestTopScreenSpaceLeft.HasValue)
|
||||
LeaderboardFlow.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceLeft.Value)).Y, 0, DrawHeight - LeaderboardFlow.DrawHeight);
|
||||
else
|
||||
LeaderboardFlow.Y = 0;
|
||||
|
||||
if (highestBottomScreenSpace.HasValue)
|
||||
bottomRightElements.Y = BottomScoringElementsHeight = -MathHelper.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight);
|
||||
else
|
||||
bottomRightElements.Y = 0;
|
||||
}
|
||||
|
||||
private void updateVisibility()
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -60,17 +59,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private readonly double skipTargetTime;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the time at which the last <see cref="StopGameplayClock"/> call was triggered.
|
||||
/// This is used to ensure we resume from that precise point in time, ignoring the proceeding frequency ramp.
|
||||
///
|
||||
/// Optimally, we'd have gameplay ramp down with the frequency, but I believe this was intentionally disabled
|
||||
/// to avoid fails occurring after the pause screen has been shown.
|
||||
///
|
||||
/// In the future I want to change this.
|
||||
/// </summary>
|
||||
internal double? LastStopTime;
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; } = null!;
|
||||
|
||||
@ -113,71 +101,17 @@ namespace osu.Game.Screens.Play
|
||||
return time;
|
||||
}
|
||||
|
||||
protected override void StopGameplayClock()
|
||||
{
|
||||
LastStopTime = GameplayClock.CurrentTime;
|
||||
|
||||
if (IsLoaded)
|
||||
{
|
||||
// During normal operation, the source is stopped after performing a frequency ramp.
|
||||
this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 0, 200, Easing.Out).OnComplete(_ =>
|
||||
{
|
||||
if (IsPaused.Value)
|
||||
base.StopGameplayClock();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
base.StopGameplayClock();
|
||||
|
||||
// If not yet loaded, we still want to ensure relevant state is correct, as it is used for offset calculations.
|
||||
GameplayClock.ExternalPauseFrequencyAdjust.Value = 0;
|
||||
|
||||
// We must also process underlying gameplay clocks to update rate-adjusted offsets with the new frequency adjustment.
|
||||
// Without doing this, an initial seek may be performed with the wrong offset.
|
||||
GameplayClock.ProcessFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Seek(double time)
|
||||
{
|
||||
// Safety in case the clock is seeked while stopped.
|
||||
LastStopTime = null;
|
||||
elapsedValidationTime = null;
|
||||
|
||||
base.Seek(time);
|
||||
}
|
||||
|
||||
protected override void PrepareStart()
|
||||
{
|
||||
if (LastStopTime != null)
|
||||
{
|
||||
Seek(LastStopTime.Value);
|
||||
LastStopTime = null;
|
||||
}
|
||||
else
|
||||
base.PrepareStart();
|
||||
}
|
||||
|
||||
protected override void StartGameplayClock()
|
||||
{
|
||||
addAdjustmentsToTrack();
|
||||
|
||||
base.StartGameplayClock();
|
||||
|
||||
if (IsLoaded)
|
||||
{
|
||||
this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 1, 200, Easing.In);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not yet loaded, we still want to ensure relevant state is correct, as it is used for offset calculations.
|
||||
GameplayClock.ExternalPauseFrequencyAdjust.Value = 1;
|
||||
|
||||
// We must also process underlying gameplay clocks to update rate-adjusted offsets with the new frequency adjustment.
|
||||
// Without doing this, an initial seek may be performed with the wrong offset.
|
||||
GameplayClock.ProcessFrame();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -273,7 +207,6 @@ namespace osu.Game.Screens.Play
|
||||
musicController.ResetTrackAdjustments();
|
||||
|
||||
track.BindAdjustments(AdjustmentsFromMods);
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = true;
|
||||
@ -285,7 +218,6 @@ namespace osu.Game.Screens.Play
|
||||
return;
|
||||
|
||||
track.UnbindAdjustments(AdjustmentsFromMods);
|
||||
track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
track.RemoveAdjustment(AdjustableProperty.Frequency, UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = false;
|
||||
|
@ -1,15 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class OffsetCorrectionClock : FramedOffsetClock
|
||||
{
|
||||
private readonly BindableDouble pauseRateAdjust;
|
||||
|
||||
private double offset;
|
||||
|
||||
public new double Offset
|
||||
@ -28,10 +25,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public double RateAdjustedOffset => base.Offset;
|
||||
|
||||
public OffsetCorrectionClock(IClock source, BindableDouble pauseRateAdjust)
|
||||
public OffsetCorrectionClock(IClock source)
|
||||
: base(source)
|
||||
{
|
||||
this.pauseRateAdjust = pauseRateAdjust;
|
||||
}
|
||||
|
||||
public override void ProcessFrame()
|
||||
@ -42,12 +38,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void updateOffset()
|
||||
{
|
||||
// changing this during the pause transform effect will cause a potentially large offset to be suddenly applied as we approach zero rate.
|
||||
if (pauseRateAdjust.Value == 1)
|
||||
{
|
||||
// we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this.
|
||||
base.Offset = Offset * Rate;
|
||||
}
|
||||
// we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this.
|
||||
base.Offset = Offset * Rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1240,9 +1240,14 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
b.IgnoreUserSettings.Value = true;
|
||||
|
||||
b.IsBreakTime.UnbindFrom(breakTracker.IsBreakTime);
|
||||
b.IsBreakTime.Value = false;
|
||||
// May be null if the load never completed.
|
||||
if (breakTracker != null)
|
||||
{
|
||||
b.IsBreakTime.UnbindFrom(breakTracker.IsBreakTime);
|
||||
b.IsBreakTime.Value = false;
|
||||
}
|
||||
});
|
||||
|
||||
storyboardReplacesBackground.Value = false;
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
new OffsetSliderBar
|
||||
{
|
||||
KeyboardStep = 5,
|
||||
LabelText = BeatmapOffsetControlStrings.BeatmapOffset,
|
||||
LabelText = BeatmapOffsetControlStrings.AudioOffsetThisBeatmap,
|
||||
Current = Current,
|
||||
},
|
||||
referenceScoreContainer = new FillFlowContainer
|
||||
@ -307,7 +307,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
}
|
||||
}
|
||||
|
||||
public partial class OffsetSliderBar : PlayerSliderBar<double>
|
||||
private partial class OffsetSliderBar : PlayerSliderBar<double>
|
||||
{
|
||||
protected override Drawable CreateControl() => new CustomSliderBar();
|
||||
|
||||
|
@ -1,19 +1,17 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Screens.Edit.Timing;
|
||||
using osuTK;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Screens.Play.PlayerSettings
|
||||
{
|
||||
@ -28,26 +26,28 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
private readonly PlayerSliderBar<double> rateSlider;
|
||||
private PlayerSliderBar<double> rateSlider = null!;
|
||||
|
||||
private readonly OsuSpriteText multiplierText;
|
||||
private OsuSpriteText multiplierText = null!;
|
||||
|
||||
private readonly BindableBool isPaused = new BindableBool();
|
||||
private readonly IBindable<bool> isPaused = new BindableBool();
|
||||
|
||||
[Resolved]
|
||||
private GameplayClockContainer? gameplayClock { get; set; }
|
||||
private ReplayPlayer replayPlayer { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private GameplayState? gameplayState { get; set; }
|
||||
private GameplayClockContainer gameplayClock { get; set; } = null!;
|
||||
|
||||
private IconButton pausePlay = null!;
|
||||
|
||||
public PlaybackSettings()
|
||||
: base("playback")
|
||||
{
|
||||
const double seek_amount = 5000;
|
||||
const double seek_fast_amount = 10000;
|
||||
|
||||
IconButton play;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
@ -71,50 +71,62 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.FastBackward,
|
||||
Action = () => seek(-1, seek_fast_amount),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_fast_amount / 1000),
|
||||
Action = () => replayPlayer.SeekInDirection(-10),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(10 * ReplayPlayer.BASE_SEEK_AMOUNT / 1000),
|
||||
},
|
||||
new SeekButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.Backward,
|
||||
Action = () => seek(-1, seek_amount),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_amount / 1000),
|
||||
Action = () => replayPlayer.SeekInDirection(-1),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(ReplayPlayer.BASE_SEEK_AMOUNT / 1000),
|
||||
},
|
||||
play = new IconButton
|
||||
new SeekButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.StepBackward,
|
||||
Action = () => replayPlayer.StepFrame(-1),
|
||||
TooltipText = PlayerSettingsOverlayStrings.StepBackward,
|
||||
},
|
||||
pausePlay = new IconButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1.4f),
|
||||
IconScale = new Vector2(1.4f),
|
||||
Icon = FontAwesome.Regular.PlayCircle,
|
||||
Action = () =>
|
||||
{
|
||||
if (gameplayClock != null)
|
||||
{
|
||||
if (gameplayClock.IsRunning)
|
||||
gameplayClock.Stop();
|
||||
else
|
||||
gameplayClock.Start();
|
||||
}
|
||||
if (gameplayClock.IsRunning)
|
||||
gameplayClock.Stop();
|
||||
else
|
||||
gameplayClock.Start();
|
||||
},
|
||||
},
|
||||
new SeekButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.StepForward,
|
||||
Action = () => replayPlayer.StepFrame(1),
|
||||
TooltipText = PlayerSettingsOverlayStrings.StepForward,
|
||||
},
|
||||
new SeekButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.Forward,
|
||||
Action = () => seek(1, seek_amount),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_amount / 1000),
|
||||
Action = () => replayPlayer.SeekInDirection(1),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(ReplayPlayer.BASE_SEEK_AMOUNT / 1000),
|
||||
},
|
||||
new SeekButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.FastForward,
|
||||
Action = () => seek(1, seek_fast_amount),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_fast_amount / 1000),
|
||||
Action = () => replayPlayer.SeekInDirection(10),
|
||||
TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(10 * ReplayPlayer.BASE_SEEK_AMOUNT / 1000),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -141,26 +153,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
isPaused.BindValueChanged(paused =>
|
||||
{
|
||||
if (!paused.NewValue)
|
||||
{
|
||||
play.TooltipText = ToastStrings.PauseTrack;
|
||||
play.Icon = FontAwesome.Regular.PauseCircle;
|
||||
}
|
||||
else
|
||||
{
|
||||
play.TooltipText = ToastStrings.PlayTrack;
|
||||
play.Icon = FontAwesome.Regular.PlayCircle;
|
||||
}
|
||||
}, true);
|
||||
|
||||
void seek(int direction, double amount)
|
||||
{
|
||||
double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState?.Beatmap.GetLastObjectTime() ?? 0);
|
||||
gameplayClock?.Seek(target);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -168,8 +160,20 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
base.LoadComplete();
|
||||
rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.00}x", true);
|
||||
|
||||
if (gameplayClock != null)
|
||||
isPaused.BindTarget = gameplayClock.IsPaused;
|
||||
isPaused.BindTo(gameplayClock.IsPaused);
|
||||
isPaused.BindValueChanged(paused =>
|
||||
{
|
||||
if (!paused.NewValue)
|
||||
{
|
||||
pausePlay.TooltipText = ToastStrings.PauseTrack;
|
||||
pausePlay.Icon = FontAwesome.Regular.PauseCircle;
|
||||
}
|
||||
else
|
||||
{
|
||||
pausePlay.TooltipText = ToastStrings.PlayTrack;
|
||||
pausePlay.Icon = FontAwesome.Regular.PlayCircle;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
private partial class SeekButton : IconButton
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -22,8 +23,11 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
[Cached]
|
||||
public partial class ReplayPlayer : Player, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public const double BASE_SEEK_AMOUNT = 1000;
|
||||
|
||||
private readonly Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore;
|
||||
|
||||
private readonly bool replayIsFailedScore;
|
||||
@ -52,7 +56,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
if (!LoadedBeatmapSuccessfully)
|
||||
return;
|
||||
@ -60,7 +64,7 @@ namespace osu.Game.Screens.Play
|
||||
var playbackSettings = new PlaybackSettings
|
||||
{
|
||||
Depth = float.MaxValue,
|
||||
Expanded = { Value = false }
|
||||
Expanded = { BindTarget = config.GetBindable<bool>(OsuSetting.ReplayPlaybackControlsExpanded) }
|
||||
};
|
||||
|
||||
if (GameplayClockContainer is MasterGameplayClockContainer master)
|
||||
@ -92,16 +96,22 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
const double keyboard_seek_amount = 5000;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.StepReplayBackward:
|
||||
StepFrame(-1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.StepReplayForward:
|
||||
StepFrame(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SeekReplayBackward:
|
||||
keyboardSeek(-1);
|
||||
SeekInDirection(-5);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SeekReplayForward:
|
||||
keyboardSeek(1);
|
||||
SeekInDirection(5);
|
||||
return true;
|
||||
|
||||
case GlobalAction.TogglePauseReplay:
|
||||
@ -113,13 +123,28 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void keyboardSeek(int direction)
|
||||
{
|
||||
double target = Math.Clamp(GameplayClockContainer.CurrentTime + direction * keyboard_seek_amount, 0, GameplayState.Beatmap.GetLastObjectTime());
|
||||
public void StepFrame(int direction)
|
||||
{
|
||||
GameplayClockContainer.Stop();
|
||||
|
||||
Seek(target);
|
||||
}
|
||||
var frames = GameplayState.Score.Replay.Frames;
|
||||
|
||||
if (frames.Count == 0)
|
||||
return;
|
||||
|
||||
GameplayClockContainer.Seek(direction < 0
|
||||
? (frames.LastOrDefault(f => f.Time < GameplayClockContainer.CurrentTime) ?? frames.First()).Time
|
||||
: (frames.FirstOrDefault(f => f.Time > GameplayClockContainer.CurrentTime) ?? frames.Last()).Time
|
||||
);
|
||||
}
|
||||
|
||||
public void SeekInDirection(float amount)
|
||||
{
|
||||
double target = Math.Clamp(GameplayClockContainer.CurrentTime + amount * BASE_SEEK_AMOUNT, 0, GameplayState.Beatmap.GetLastObjectTime());
|
||||
|
||||
Seek(target);
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
|
@ -868,7 +868,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
var toDisplay = visibleItems.GetRange(displayedRange.first, displayedRange.last - displayedRange.first + 1);
|
||||
|
||||
foreach (var panel in Scroll.Children)
|
||||
foreach (var panel in Scroll)
|
||||
{
|
||||
Debug.Assert(panel.Item != null);
|
||||
|
||||
@ -899,7 +899,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
// Update externally controlled state of currently visible items (e.g. x-offset and opacity).
|
||||
// This is a per-frame update on all drawable panels.
|
||||
foreach (DrawableCarouselItem item in Scroll.Children)
|
||||
foreach (DrawableCarouselItem item in Scroll)
|
||||
{
|
||||
updateItem(item);
|
||||
|
||||
@ -1094,7 +1094,7 @@ namespace osu.Game.Screens.Select
|
||||
// to enter clamp-special-case mode where it animates completely differently to normal.
|
||||
float scrollChange = scrollTarget.Value - Scroll.Current;
|
||||
Scroll.ScrollTo(scrollTarget.Value, false);
|
||||
foreach (var i in Scroll.Children)
|
||||
foreach (var i in Scroll)
|
||||
i.Y += scrollChange;
|
||||
break;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
@ -28,8 +27,5 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected virtual string RulesetPrefix => string.Empty;
|
||||
protected virtual string ComponentName => Component.ToString();
|
||||
|
||||
public string LookupName =>
|
||||
string.Join('/', new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s)));
|
||||
}
|
||||
}
|
||||
|
@ -63,14 +63,10 @@ namespace osu.Game.Skinning
|
||||
// missed ticks / slider end don't get the normal animation.
|
||||
if (isMissedTick())
|
||||
{
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
this.ScaleTo(1.2f);
|
||||
this.ScaleTo(1f, 100, Easing.In);
|
||||
|
||||
if (legacyVersion > 1.0m)
|
||||
{
|
||||
this.MoveTo(new Vector2(0, -2f));
|
||||
this.MoveToOffset(new Vector2(0, 10), fade_out_delay + fade_out_length, Easing.In);
|
||||
}
|
||||
this.FadeOutFromOne(400);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -491,39 +491,30 @@ namespace osu.Game.Skinning
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (string name in getFallbackNames(componentName))
|
||||
Texture? texture = null;
|
||||
float ratio = 1;
|
||||
|
||||
if (AllowHighResolutionSprites)
|
||||
{
|
||||
string lookupName = name;
|
||||
Texture? texture = null;
|
||||
float ratio = 1;
|
||||
// some component names (especially user-controlled ones, like `HitX` in mania)
|
||||
// may contain `@2x` scale specifications.
|
||||
// stable happens to check for that and strip them, so do the same to match stable behaviour.
|
||||
componentName = componentName.Replace(@"@2x", string.Empty);
|
||||
|
||||
if (AllowHighResolutionSprites)
|
||||
{
|
||||
// some component names (especially user-controlled ones, like `HitX` in mania)
|
||||
// may contain `@2x` scale specifications.
|
||||
// stable happens to check for that and strip them, so do the same to match stable behaviour.
|
||||
lookupName = name.Replace(@"@2x", string.Empty);
|
||||
string twoTimesFilename = $"{Path.ChangeExtension(componentName, null)}@2x{Path.GetExtension(componentName)}";
|
||||
|
||||
string twoTimesFilename = $"{Path.ChangeExtension(lookupName, null)}@2x{Path.GetExtension(lookupName)}";
|
||||
texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT);
|
||||
|
||||
texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT);
|
||||
if (texture != null)
|
||||
ratio = 2;
|
||||
}
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
ratio = 1;
|
||||
texture = Textures?.Get(lookupName, wrapModeS, wrapModeT);
|
||||
}
|
||||
|
||||
if (texture == null)
|
||||
continue;
|
||||
|
||||
texture.ScaleAdjust = ratio;
|
||||
return texture;
|
||||
}
|
||||
|
||||
return null;
|
||||
texture ??= Textures?.Get(componentName, wrapModeS, wrapModeT);
|
||||
|
||||
if (texture != null)
|
||||
texture.ScaleAdjust = ratio;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public override ISample? GetSample(ISampleInfo sampleInfo)
|
||||
@ -534,7 +525,7 @@ namespace osu.Game.Skinning
|
||||
lookupNames = getLegacyLookupNames(hitSample);
|
||||
else
|
||||
{
|
||||
lookupNames = sampleInfo.LookupNames.SelectMany(getFallbackNames);
|
||||
lookupNames = sampleInfo.LookupNames.SelectMany(getFallbackSampleNames);
|
||||
}
|
||||
|
||||
foreach (string lookup in lookupNames)
|
||||
@ -552,7 +543,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
private IEnumerable<string> getLegacyLookupNames(HitSampleInfo hitSample)
|
||||
{
|
||||
var lookupNames = hitSample.LookupNames.SelectMany(getFallbackNames);
|
||||
var lookupNames = hitSample.LookupNames.SelectMany(getFallbackSampleNames);
|
||||
|
||||
if (!UseCustomSampleBanks && !string.IsNullOrEmpty(hitSample.Suffix))
|
||||
{
|
||||
@ -571,13 +562,13 @@ namespace osu.Game.Skinning
|
||||
yield return hitSample.Name;
|
||||
}
|
||||
|
||||
private IEnumerable<string> getFallbackNames(string componentName)
|
||||
private IEnumerable<string> getFallbackSampleNames(string name)
|
||||
{
|
||||
// May be something like "Gameplay/osu/approachcircle" from lazer, or "Arrows/note1" from a user skin.
|
||||
yield return componentName;
|
||||
// May be something like "Gameplay/normal-hitnormal" from lazer.
|
||||
yield return name;
|
||||
|
||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
|
||||
yield return componentName.Split('/').Last();
|
||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/normal-hitnormal" -> "normal-hitnormal").
|
||||
yield return name.Split('/').Last();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
|
||||
// When reading from a skin, we match stables weird behaviour where `FrameCount` is ignored
|
||||
// and resources are retrieved until the end of the animation.
|
||||
var skinTextures = skin.GetTextures(Path.GetFileNameWithoutExtension(Animation.Path)!, default, default, true, string.Empty, null, out _);
|
||||
var skinTextures = skin.GetTextures(Path.ChangeExtension(Animation.Path, null), default, default, true, string.Empty, null, out _);
|
||||
|
||||
if (skinTextures.Length > 0)
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Tests
|
||||
[CallerMemberName] string callingMethodName = @"")
|
||||
: base($"{callingMethodName}-{Guid.NewGuid()}", new HostOptions
|
||||
{
|
||||
BindIPC = bindIPC,
|
||||
IPCPort = bindIPC ? OsuGame.IPC_PORT : null,
|
||||
}, bypassCleanup: bypassCleanupOnDispose, realtime: realtime)
|
||||
{
|
||||
this.bypassCleanupOnSetup = bypassCleanupOnSetup;
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development", new HostOptions { BindIPC = true, }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development"))
|
||||
{
|
||||
host.Run(new OsuTestBrowser());
|
||||
return 0;
|
||||
|
@ -21,12 +21,12 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="AutoMapper" Version="12.0.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.7.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.57" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
<PackageReference Include="MessagePack" Version="2.5.129" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.12" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.12" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="7.0.12" />
|
||||
<PackageReference Include="MessagePack" Version="2.5.140" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.15" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.15" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="7.0.15" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.12" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
||||
@ -36,13 +36,13 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2024.114.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2024.121.1" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.116.0" />
|
||||
<PackageReference Include="Sentry" Version="3.40.0" />
|
||||
<PackageReference Include="Sentry" Version="3.41.3" />
|
||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.6" />
|
||||
<PackageReference Include="SharpCompress" Version="0.36.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.7" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user