mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Merge branch 'master' into stamina-available-fingers
This commit is contained in:
commit
9065cb0526
66
Gemfile.lock
66
Gemfile.lock
@ -3,25 +3,25 @@ GEM
|
||||
specs:
|
||||
CFPropertyList (3.0.5)
|
||||
rexml
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.601.0)
|
||||
aws-sdk-core (3.131.2)
|
||||
aws-partitions (1.653.0)
|
||||
aws-sdk-core (3.166.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.525.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.57.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sdk-kms (1.59.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.114.0)
|
||||
aws-sdk-core (~> 3, >= 3.127.0)
|
||||
aws-sdk-s3 (1.117.1)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.0)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
@ -34,10 +34,10 @@ GEM
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.92.3)
|
||||
faraday (1.10.0)
|
||||
excon (0.93.1)
|
||||
faraday (1.10.2)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
@ -66,7 +66,7 @@ GEM
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.206.2)
|
||||
fastlane (2.210.1)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
@ -110,9 +110,9 @@ GEM
|
||||
souyuz (= 0.11.1)
|
||||
fastlane-plugin-xamarin (0.6.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.23.0)
|
||||
google-apis-core (>= 0.6, < 2.a)
|
||||
google-apis-core (0.6.0)
|
||||
google-apis-androidpublisher_v3 (0.29.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-apis-core (0.9.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
@ -121,27 +121,27 @@ GEM
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.12.0)
|
||||
google-apis-core (>= 0.6, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.9.0)
|
||||
google-apis-core (>= 0.6, < 2.a)
|
||||
google-apis-storage_v1 (0.16.0)
|
||||
google-apis-core (>= 0.6, < 2.a)
|
||||
google-apis-iamcredentials_v1 (0.15.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.12.0)
|
||||
google-apis-core (>= 0.9.1, < 2.a)
|
||||
google-apis-storage_v1 (0.19.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.2.0)
|
||||
google-cloud-storage (1.36.2)
|
||||
google-cloud-errors (1.3.0)
|
||||
google-cloud-storage (1.43.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.19.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.2.0)
|
||||
googleauth (1.3.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
@ -154,22 +154,22 @@ GEM
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.1)
|
||||
json (2.6.2)
|
||||
jwt (2.4.1)
|
||||
jwt (2.5.0)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.7.1)
|
||||
mini_portile2 (2.8.0)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
nokogiri (1.13.1)
|
||||
mini_portile2 (~> 2.7.0)
|
||||
nokogiri (1.13.9)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.6.0)
|
||||
public_suffix (4.0.7)
|
||||
public_suffix (5.0.0)
|
||||
racc (1.6.0)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
|
@ -138,10 +138,10 @@ platform :ios do
|
||||
end
|
||||
|
||||
lane :testflight_prune_dry do
|
||||
clean_testflight_testers(days_of_inactivity:45, dry_run: true)
|
||||
clean_testflight_testers(days_of_inactivity:30, dry_run: true)
|
||||
end
|
||||
|
||||
lane :testflight_prune do
|
||||
clean_testflight_testers(days_of_inactivity: 45)
|
||||
clean_testflight_testers(days_of_inactivity: 30)
|
||||
end
|
||||
end
|
||||
|
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1022.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1028.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -18,17 +18,9 @@ using osu.Framework;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Updater;
|
||||
using osu.Desktop.Windows;
|
||||
using osu.Framework.Input.Handlers;
|
||||
using osu.Framework.Input.Handlers.Joystick;
|
||||
using osu.Framework.Input.Handlers.Mouse;
|
||||
using osu.Framework.Input.Handlers.Tablet;
|
||||
using osu.Framework.Input.Handlers.Touch;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections;
|
||||
using osu.Game.Overlays.Settings.Sections.Input;
|
||||
using osu.Game.Utils;
|
||||
using SDL2;
|
||||
|
||||
@ -148,27 +140,6 @@ namespace osu.Desktop
|
||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
||||
}
|
||||
|
||||
public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
|
||||
{
|
||||
switch (handler)
|
||||
{
|
||||
case ITabletHandler th:
|
||||
return new TabletSettings(th);
|
||||
|
||||
case MouseHandler mh:
|
||||
return new MouseSettings(mh);
|
||||
|
||||
case JoystickHandler jh:
|
||||
return new JoystickSettings(jh);
|
||||
|
||||
case TouchHandler th:
|
||||
return new InputSection.HandlerSection(th);
|
||||
|
||||
default:
|
||||
return base.CreateSettingsSubsectionFor(handler);
|
||||
}
|
||||
}
|
||||
|
||||
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
|
||||
|
||||
private readonly List<string> importableFiles = new List<string>();
|
||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.Catch.Edit;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Catch.Scoring;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Argon;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
@ -188,6 +189,9 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
case LegacySkin:
|
||||
return new CatchLegacySkinTransformer(skin);
|
||||
|
||||
case ArgonSkin:
|
||||
return new CatchArgonSkinTransformer(skin);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
|
||||
public float DisplayRotation => Rotation;
|
||||
|
||||
public double DisplayStartTime => HitObject.StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
|
||||
/// </summary>
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject;
|
||||
|
||||
public double DisplayStartTime => LifetimeStart;
|
||||
|
||||
Bindable<Color4> IHasCatchObjectState.AccentColour => AccentColour;
|
||||
|
||||
public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
|
||||
|
@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
PalpableCatchHitObject HitObject { get; }
|
||||
|
||||
double DisplayStartTime { get; }
|
||||
|
||||
Bindable<Color4> AccentColour { get; }
|
||||
|
||||
Bindable<bool> HyperDash { get; }
|
||||
|
122
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs
Normal file
122
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonBananaPiece.cs
Normal file
@ -0,0 +1,122 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
internal class ArgonBananaPiece : ArgonFruitPiece
|
||||
{
|
||||
private Container stabilisedPieceContainer = null!;
|
||||
|
||||
private Drawable fadeContent = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(fadeContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
stabilisedPieceContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Circle
|
||||
{
|
||||
Colour = Color4.White.Opacity(0.4f),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Size = new Vector2(8),
|
||||
Scale = new Vector2(25, 1),
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White.Opacity(0.8f)),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.CentreRight,
|
||||
Width = 1.6f,
|
||||
Height = 2,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White.Opacity(0)),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Width = 1.6f,
|
||||
Height = 2,
|
||||
},
|
||||
}
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(1.2f),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Hollow = false,
|
||||
Colour = Color4.White.Opacity(0.1f),
|
||||
Radius = 50,
|
||||
},
|
||||
Child =
|
||||
{
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
BorderColour = Color4.White.Opacity(0.1f),
|
||||
BorderThickness = 3,
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
const float parent_scale_application = 0.4f;
|
||||
|
||||
// relative to time on screen
|
||||
const float lens_flare_start = 0.3f;
|
||||
const float lens_flare_end = 0.8f;
|
||||
|
||||
// Undo some of the parent scale being applied to make the lens flare feel a bit better..
|
||||
float scale = parent_scale_application + (1 - parent_scale_application) * (1 / (ObjectState.DisplaySize.X / (CatchHitObject.OBJECT_RADIUS * 2)));
|
||||
|
||||
stabilisedPieceContainer.Rotation = -ObjectState.DisplayRotation;
|
||||
stabilisedPieceContainer.Scale = new Vector2(scale, 1);
|
||||
|
||||
double duration = ObjectState.HitObject.StartTime - ObjectState.DisplayStartTime;
|
||||
|
||||
fadeContent.Alpha = MathHelper.Clamp(
|
||||
Interpolation.ValueAt(
|
||||
Time.Current, 1f, 0f,
|
||||
ObjectState.DisplayStartTime + duration * lens_flare_start,
|
||||
ObjectState.DisplayStartTime + duration * lens_flare_end,
|
||||
Easing.OutQuint
|
||||
), 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
85
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs
Normal file
85
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonCatcher.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
public class ArgonCatcher : CompositeDrawable
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 10,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.White,
|
||||
Width = Catcher.ALLOWED_CATCH_RANGE,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Name = "long line left",
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreRight,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0.25f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 20,
|
||||
Height = 1.8f,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
Name = "bumper left",
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2,
|
||||
Height = 4,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Name = "long line right",
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0.25f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 20,
|
||||
Height = 1.8f,
|
||||
},
|
||||
new Circle
|
||||
{
|
||||
Name = "bumper right",
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2,
|
||||
Height = 4,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
121
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs
Normal file
121
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonDropletPiece.cs
Normal file
@ -0,0 +1,121 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
internal class ArgonDropletPiece : CatchHitObjectPiece
|
||||
{
|
||||
protected override Drawable HyperBorderPiece => hyperBorderPiece;
|
||||
|
||||
private Drawable hyperBorderPiece = null!;
|
||||
|
||||
private Container layers = null!;
|
||||
|
||||
private float rotationRandomness;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
const float droplet_scale_down = 0.7f;
|
||||
|
||||
int largeBlobSeed = RNG.Next();
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Circle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(20),
|
||||
},
|
||||
layers = new Container
|
||||
{
|
||||
Scale = new Vector2(droplet_scale_down),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CircularBlob
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
InnerRadius = 0.5f,
|
||||
Alpha = 0.15f,
|
||||
Seed = largeBlobSeed
|
||||
},
|
||||
new CircularBlob
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
InnerRadius = 0.4f,
|
||||
Alpha = 0.5f,
|
||||
Scale = new Vector2(0.7f),
|
||||
Seed = RNG.Next()
|
||||
},
|
||||
}
|
||||
},
|
||||
hyperBorderPiece = new CircularBlob
|
||||
{
|
||||
Scale = new Vector2(droplet_scale_down),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
InnerRadius = 0.5f,
|
||||
Alpha = 0.15f,
|
||||
Seed = largeBlobSeed
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AccentColour.BindValueChanged(colour =>
|
||||
{
|
||||
foreach (var sprite in layers)
|
||||
sprite.Colour = colour.NewValue;
|
||||
}, true);
|
||||
|
||||
rotationRandomness = RNG.NextSingle(0.2f, 1);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// Note that droplets are rotated at a higher level, so this is mostly just to create more
|
||||
// random arrangements of the multiple layers than actually rotate.
|
||||
//
|
||||
// Because underlying rotation is always clockwise, we apply anti-clockwise resistance to avoid
|
||||
// making things spin too fast.
|
||||
for (int i = 0; i < layers.Count; i++)
|
||||
{
|
||||
layers[i].Rotation -=
|
||||
(float)Clock.ElapsedFrameTime
|
||||
* 0.4f * rotationRandomness
|
||||
// Each layer should alternate rotation speed.
|
||||
* (i % 2 == 1 ? 0.5f : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs
Normal file
121
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonFruitPiece.cs
Normal file
@ -0,0 +1,121 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
internal class ArgonFruitPiece : CatchHitObjectPiece
|
||||
{
|
||||
protected override Drawable HyperBorderPiece => hyperBorderPiece;
|
||||
|
||||
private Drawable hyperBorderPiece = null!;
|
||||
|
||||
private Container layers = null!;
|
||||
|
||||
private float rotationRandomness;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
int largeBlobSeed = RNG.Next();
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Circle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(20),
|
||||
},
|
||||
layers = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CircularBlob
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Alpha = 0.15f,
|
||||
InnerRadius = 0.5f,
|
||||
Size = new Vector2(1.1f),
|
||||
Seed = largeBlobSeed,
|
||||
},
|
||||
new CircularBlob
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
InnerRadius = 0.2f,
|
||||
Alpha = 0.5f,
|
||||
Seed = RNG.Next(),
|
||||
},
|
||||
new CircularBlob
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
InnerRadius = 0.05f,
|
||||
Seed = RNG.Next(),
|
||||
},
|
||||
}
|
||||
},
|
||||
hyperBorderPiece = new CircularBlob
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingParameters.Additive,
|
||||
InnerRadius = 0.08f,
|
||||
Size = new Vector2(1.15f),
|
||||
Seed = largeBlobSeed
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AccentColour.BindValueChanged(colour =>
|
||||
{
|
||||
foreach (var sprite in layers)
|
||||
sprite.Colour = colour.NewValue;
|
||||
}, true);
|
||||
|
||||
rotationRandomness = RNG.NextSingle(0.2f, 1) * (RNG.NextBool() ? -1 : 1);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
for (int i = 0; i < layers.Count; i++)
|
||||
{
|
||||
layers[i].Rotation +=
|
||||
// Layers are ordered from largest to smallest. Smaller layers should rotate more.
|
||||
(i * 2)
|
||||
* (float)Clock.ElapsedFrameTime
|
||||
* 0.02f * rotationRandomness
|
||||
// Each layer should alternate rotation direction.
|
||||
* (i % 2 == 1 ? 1 : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
112
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonHitExplosion.cs
Normal file
112
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonHitExplosion.cs
Normal file
@ -0,0 +1,112 @@
|
||||
// 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.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
public class ArgonHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
private Container tallExplosion = null!;
|
||||
private Container largeFaint = null!;
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
public ArgonHitExplosion()
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
tallExplosion = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Width = 0.1f,
|
||||
Child = new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
largeFaint = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
tallExplosion.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = colour.NewValue,
|
||||
Hollow = false,
|
||||
Roundness = 15,
|
||||
Radius = 15,
|
||||
};
|
||||
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.2f, colour.NewValue, Color4.White, 0, 1),
|
||||
Hollow = false,
|
||||
Radius = 50,
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void Animate(HitExplosionEntry entry)
|
||||
{
|
||||
X = entry.Position;
|
||||
Scale = new Vector2(entry.HitObject.Scale);
|
||||
accentColour.Value = entry.ObjectColour;
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
{
|
||||
this.FadeOutFromOne(400);
|
||||
|
||||
if (!(entry.HitObject is Droplet))
|
||||
{
|
||||
float scale = Math.Clamp(entry.JudgementResult.ComboAtJudgement / 200f, 0.35f, 1.125f);
|
||||
|
||||
tallExplosion
|
||||
.ScaleTo(new Vector2(1.1f, 20 * scale), 200, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(new Vector2(1.1f, 1), 600, Easing.In);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
193
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonJudgementPiece.cs
Normal file
193
osu.Game.Rulesets.Catch/Skinning/Argon/ArgonJudgementPiece.cs
Normal file
@ -0,0 +1,193 @@
|
||||
// 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.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
public class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
private RingExplosion? ringExplosion;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public ArgonJudgementPiece(HitResult result)
|
||||
{
|
||||
Result = result;
|
||||
Origin = Anchor.Centre;
|
||||
Y = 160;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
|
||||
},
|
||||
};
|
||||
|
||||
if (Result.IsHit())
|
||||
{
|
||||
AddInternal(ringExplosion = new RingExplosion(Result)
|
||||
{
|
||||
Colour = colours.ForHitResult(Result),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The base implementation only handles fade (for all result types) and misses.
|
||||
/// Individual rulesets are recommended to implement their appropriate hit animations.
|
||||
/// </remarks>
|
||||
public virtual void PlayAnimation()
|
||||
{
|
||||
switch (Result)
|
||||
{
|
||||
default:
|
||||
JudgementText
|
||||
.ScaleTo(Vector2.One)
|
||||
.ScaleTo(new Vector2(1.4f), 1800, Easing.OutQuint);
|
||||
break;
|
||||
|
||||
case HitResult.Miss:
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
|
||||
this.MoveTo(Vector2.Zero);
|
||||
this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
|
||||
|
||||
this.RotateTo(0);
|
||||
this.RotateTo(40, 800, Easing.InQuint);
|
||||
break;
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(800);
|
||||
|
||||
ringExplosion?.PlayAnimation();
|
||||
}
|
||||
|
||||
public Drawable? GetAboveHitObjectsProxiedContent() => null;
|
||||
|
||||
private class RingExplosion : CompositeDrawable
|
||||
{
|
||||
private readonly float travel = 52;
|
||||
|
||||
public RingExplosion(HitResult result)
|
||||
{
|
||||
const float thickness = 4;
|
||||
|
||||
const float small_size = 9;
|
||||
const float large_size = 14;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
int countSmall = 0;
|
||||
int countLarge = 0;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.Meh:
|
||||
countSmall = 3;
|
||||
travel *= 0.3f;
|
||||
break;
|
||||
|
||||
case HitResult.Ok:
|
||||
case HitResult.Good:
|
||||
countSmall = 4;
|
||||
travel *= 0.6f;
|
||||
break;
|
||||
|
||||
case HitResult.Great:
|
||||
case HitResult.Perfect:
|
||||
countSmall = 4;
|
||||
countLarge = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < countSmall; i++)
|
||||
AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) });
|
||||
|
||||
for (int i = 0; i < countLarge; i++)
|
||||
AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) });
|
||||
}
|
||||
|
||||
public void PlayAnimation()
|
||||
{
|
||||
foreach (var c in InternalChildren)
|
||||
{
|
||||
const float start_position_ratio = 0.3f;
|
||||
|
||||
float direction = RNG.NextSingle(0, 360);
|
||||
float distance = RNG.NextSingle(travel / 2, travel);
|
||||
|
||||
c.MoveTo(new Vector2(
|
||||
MathF.Cos(direction) * distance * start_position_ratio,
|
||||
MathF.Sin(direction) * distance * start_position_ratio
|
||||
));
|
||||
|
||||
c.MoveTo(new Vector2(
|
||||
MathF.Cos(direction) * distance,
|
||||
MathF.Sin(direction) * distance
|
||||
), 600, Easing.OutQuint);
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(1000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public class RingPiece : CircularContainer
|
||||
{
|
||||
public RingPiece(float thickness = 9)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Masking = true;
|
||||
BorderThickness = thickness;
|
||||
BorderColour = Color4.White;
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// 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.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
public class CatchArgonSkinTransformer : SkinTransformer
|
||||
{
|
||||
public CatchArgonSkinTransformer(ISkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
}
|
||||
|
||||
public override Drawable? GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case CatchSkinComponent catchComponent:
|
||||
// TODO: Once everything is finalised, consider throwing UnsupportedSkinComponentException on missing entries.
|
||||
switch (catchComponent.Component)
|
||||
{
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
return new ArgonHitExplosion();
|
||||
|
||||
case CatchSkinComponents.Catcher:
|
||||
return new ArgonCatcher();
|
||||
|
||||
case CatchSkinComponents.Fruit:
|
||||
return new ArgonFruitPiece();
|
||||
|
||||
case CatchSkinComponents.Banana:
|
||||
return new ArgonBananaPiece();
|
||||
|
||||
case CatchSkinComponents.Droplet:
|
||||
return new ArgonDropletPiece();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(component);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public class BananaPiece : CatchHitObjectPiece
|
||||
{
|
||||
protected override BorderPiece BorderPiece { get; }
|
||||
protected override Drawable BorderPiece { get; }
|
||||
|
||||
public BananaPiece()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new BananaPulpFormation
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osuTK.Graphics;
|
||||
@ -26,13 +27,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
/// A part of this piece that will be faded out while falling in the playfield.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
protected virtual BorderPiece BorderPiece => null;
|
||||
protected virtual Drawable BorderPiece => null;
|
||||
|
||||
/// <summary>
|
||||
/// A part of this piece that will be only visible when <see cref="HyperDash"/> is true.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
protected virtual HyperBorderPiece HyperBorderPiece => null;
|
||||
protected virtual Drawable HyperBorderPiece => null;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
|
@ -11,13 +11,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public class DropletPiece : CatchHitObjectPiece
|
||||
{
|
||||
protected override HyperBorderPiece HyperBorderPiece { get; }
|
||||
protected override Drawable HyperBorderPiece { get; }
|
||||
|
||||
public DropletPiece()
|
||||
{
|
||||
Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Pulp
|
||||
{
|
||||
|
@ -18,14 +18,14 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
|
||||
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
protected override BorderPiece BorderPiece { get; }
|
||||
protected override HyperBorderPiece HyperBorderPiece { get; }
|
||||
protected override Drawable BorderPiece { get; }
|
||||
protected override Drawable HyperBorderPiece { get; }
|
||||
|
||||
public FruitPiece()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new FruitPulpFormation
|
||||
{
|
||||
|
@ -59,6 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
|
||||
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||
private readonly BindableList<HitObject> selectedObjects = new BindableList<HitObject>();
|
||||
|
||||
public SliderSelectionBlueprint(Slider slider)
|
||||
: base(slider)
|
||||
@ -86,6 +87,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
pathVersion.BindValueChanged(_ => editorBeatmap?.Update(HitObject));
|
||||
|
||||
BodyPiece.UpdateFrom(HitObject);
|
||||
|
||||
if (editorBeatmap != null)
|
||||
selectedObjects.BindTo(editorBeatmap.SelectedHitObjects);
|
||||
selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition(), true);
|
||||
}
|
||||
|
||||
public override bool HandleQuickDeletion()
|
||||
@ -100,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool hasSingleObjectSelected => selectedObjects.Count == 1;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -108,14 +115,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
BodyPiece.UpdateFrom(HitObject);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateVisualDefinition();
|
||||
|
||||
// In the case more than a single object is selected, block hover from arriving at sliders behind this one.
|
||||
// Without doing this, the path visualisers of potentially hundreds of sliders will render, which is not only
|
||||
// visually noisy but also functionally useless.
|
||||
return !hasSingleObjectSelected;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateVisualDefinition();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override void OnSelected()
|
||||
{
|
||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
|
||||
{
|
||||
RemoveControlPointsRequested = removeControlPoints,
|
||||
SplitControlPointsRequested = splitControlPoints
|
||||
});
|
||||
|
||||
updateVisualDefinition();
|
||||
base.OnSelected();
|
||||
}
|
||||
|
||||
@ -123,13 +141,31 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
base.OnDeselected();
|
||||
|
||||
// throw away frame buffers on deselection.
|
||||
ControlPointVisualiser?.Expire();
|
||||
ControlPointVisualiser = null;
|
||||
|
||||
updateVisualDefinition();
|
||||
BodyPiece.RecyclePath();
|
||||
}
|
||||
|
||||
private void updateVisualDefinition()
|
||||
{
|
||||
// To reduce overhead of drawing these blueprints, only add extra detail when hovered or when only this slider is selected.
|
||||
if (IsSelected && (hasSingleObjectSelected || IsHovered))
|
||||
{
|
||||
if (ControlPointVisualiser == null)
|
||||
{
|
||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
|
||||
{
|
||||
RemoveControlPointsRequested = removeControlPoints,
|
||||
SplitControlPointsRequested = splitControlPoints
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlPointVisualiser?.Expire();
|
||||
ControlPointVisualiser = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 rightClickPosition;
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
|
@ -79,7 +79,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public override void ApplyTransformsAt(double time, bool propagateChildren = false)
|
||||
{
|
||||
// For the same reasons as above w.r.t rewinding, we shouldn't propagate to children here either.
|
||||
// ReSharper disable once RedundantArgumentDefaultValue - removing the "redundant" default value triggers BaseMethodCallWithDefaultParameter
|
||||
|
||||
// ReSharper disable once RedundantArgumentDefaultValue
|
||||
base.ApplyTransformsAt(time, false);
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,28 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, cancellationToken);
|
||||
|
||||
if (original.BeatmapInfo.Ruleset.OnlineID == 0)
|
||||
{
|
||||
// Post processing step to transform standard slider velocity changes into scroll speed changes
|
||||
double lastScrollSpeed = 1;
|
||||
|
||||
foreach (HitObject hitObject in original.HitObjects)
|
||||
{
|
||||
double nextScrollSpeed = hitObject.DifficultyControlPoint.SliderVelocity;
|
||||
EffectControlPoint currentEffectPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime);
|
||||
|
||||
if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed, acceptableDifference: currentEffectPoint.ScrollSpeedBindable.Precision))
|
||||
{
|
||||
converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint
|
||||
{
|
||||
KiaiMode = currentEffectPoint.KiaiMode,
|
||||
OmitFirstBarLine = currentEffectPoint.OmitFirstBarLine,
|
||||
ScrollSpeed = lastScrollSpeed = nextScrollSpeed,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (original.BeatmapInfo.Ruleset.OnlineID == 3)
|
||||
{
|
||||
// Post processing step to transform mania hit objects with the same start time into strong hits
|
||||
|
@ -188,7 +188,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
}
|
||||
|
||||
// Name matches the automatically chosen name from `CleanRunHeadlessGameHost` above, so we end up using the same storage location.
|
||||
using (HeadlessGameHost host = new TestRunHeadlessGameHost(firstRunName, null))
|
||||
using (HeadlessGameHost host = new TestRunHeadlessGameHost(firstRunName))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -1,17 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -26,9 +26,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
|
||||
{
|
||||
private OsuConfigManager localConfig;
|
||||
private OsuConfigManager localConfig = null!;
|
||||
|
||||
private HUDOverlay hudOverlay;
|
||||
private HUDOverlay hudOverlay = null!;
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||
@ -149,6 +149,41 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldForMenuDoesWorkWhenHidden()
|
||||
{
|
||||
bool activated = false;
|
||||
|
||||
HoldForMenuButton getHoldForMenu() => hudOverlay.ChildrenOfType<HoldForMenuButton>().Single();
|
||||
|
||||
createNew();
|
||||
|
||||
AddStep("bind action", () =>
|
||||
{
|
||||
activated = false;
|
||||
|
||||
var holdForMenu = getHoldForMenu();
|
||||
|
||||
holdForMenu.Action += () => activated = true;
|
||||
});
|
||||
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("attempt activate", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(getHoldForMenu().OfType<HoldToConfirmContainer>().Single());
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("activated", () => activated);
|
||||
|
||||
AddStep("release mouse button", () =>
|
||||
{
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInputDoesntWorkWhenHUDHidden()
|
||||
{
|
||||
@ -220,7 +255,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().ComponentsLoaded);
|
||||
}
|
||||
|
||||
private void createNew(Action<HUDOverlay> action = null)
|
||||
private void createNew(Action<HUDOverlay>? action = null)
|
||||
{
|
||||
AddStep("create overlay", () =>
|
||||
{
|
||||
@ -239,7 +274,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
localConfig?.Dispose();
|
||||
if (localConfig.IsNotNull())
|
||||
localConfig.Dispose();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -24,6 +25,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
private readonly Bindable<float> safeAreaPaddingLeft = new BindableFloat { MinValue = 0, MaxValue = 200 };
|
||||
private readonly Bindable<float> safeAreaPaddingRight = new BindableFloat { MinValue = 0, MaxValue = 200 };
|
||||
|
||||
private readonly Bindable<bool> applySafeAreaConsiderations = new Bindable<bool>(true);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -84,6 +87,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Current = safeAreaPaddingRight,
|
||||
LabelText = "Right"
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Apply",
|
||||
Current = applySafeAreaConsiderations,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,6 +101,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
safeAreaPaddingBottom.BindValueChanged(_ => updateSafeArea());
|
||||
safeAreaPaddingLeft.BindValueChanged(_ => updateSafeArea());
|
||||
safeAreaPaddingRight.BindValueChanged(_ => updateSafeArea());
|
||||
applySafeAreaConsiderations.BindValueChanged(_ => updateSafeArea());
|
||||
});
|
||||
|
||||
base.SetUpSteps();
|
||||
@ -107,6 +116,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Left = safeAreaPaddingLeft.Value,
|
||||
Right = safeAreaPaddingRight.Value,
|
||||
};
|
||||
|
||||
Game.LocalConfig.SetValue(OsuSetting.SafeAreaConsiderations, applySafeAreaConsiderations.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
[Test]
|
||||
public void TestCustomDirectory()
|
||||
{
|
||||
using (HeadlessGameHost host = new TestRunHeadlessGameHost(nameof(TestCustomDirectory), null)) // don't use clean run as we are writing a config file.
|
||||
using (HeadlessGameHost host = new TestRunHeadlessGameHost(nameof(TestCustomDirectory))) // don't use clean run as we are writing a config file.
|
||||
{
|
||||
string osuDesktopStorage = Path.Combine(host.UserStoragePaths.First(), nameof(TestCustomDirectory));
|
||||
const string custom_tournament = "custom";
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
public void CheckIPCLocation()
|
||||
{
|
||||
// don't use clean run because files are being written before osu! launches.
|
||||
using (var host = new TestRunHeadlessGameHost(nameof(CheckIPCLocation), null))
|
||||
using (var host = new TestRunHeadlessGameHost(nameof(CheckIPCLocation)))
|
||||
{
|
||||
string basePath = Path.Combine(host.UserStoragePaths.First(), nameof(CheckIPCLocation));
|
||||
|
||||
|
@ -340,7 +340,7 @@ namespace osu.Game.Beatmaps
|
||||
static string createBeatmapFilenameFromMetadata(BeatmapInfo beatmapInfo)
|
||||
{
|
||||
var metadata = beatmapInfo.Metadata;
|
||||
return $"{metadata.Artist} - {metadata.Title} ({metadata.Author.Username}) [{beatmapInfo.DifficultyName}].osu".GetValidArchiveContentFilename();
|
||||
return $"{metadata.Artist} - {metadata.Title} ({metadata.Author.Username}) [{beatmapInfo.DifficultyName}].osu".GetValidFilename();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +152,7 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.SongSelectRightMouseScroll, false);
|
||||
|
||||
SetDefault(OsuSetting.Scaling, ScalingMode.Off);
|
||||
SetDefault(OsuSetting.SafeAreaConsiderations, true);
|
||||
|
||||
SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
|
||||
SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
|
||||
@ -362,6 +363,7 @@ namespace osu.Game.Configuration
|
||||
DiscordRichPresence,
|
||||
AutomaticallyDownloadWhenSpectating,
|
||||
ShowOnlineExplicitContent,
|
||||
LastProcessedMetadataId
|
||||
LastProcessedMetadataId,
|
||||
SafeAreaConsiderations,
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Database
|
||||
/// <param name="item">The item to export.</param>
|
||||
public void Export(TModel item)
|
||||
{
|
||||
string filename = $"{item.GetDisplayString().GetValidArchiveContentFilename()}{FileExtension}";
|
||||
string filename = $"{item.GetDisplayString().GetValidFilename()}{FileExtension}";
|
||||
|
||||
using (var stream = exportStorage.CreateFileSafely(filename))
|
||||
ExportModelTo(item, stream);
|
||||
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
@ -15,6 +15,8 @@ namespace osu.Game.Extensions
|
||||
{
|
||||
public static class ModelExtensions
|
||||
{
|
||||
private static readonly Regex invalid_filename_chars = new Regex(@"(?!$)[^A-Za-z0-9_()[\]. \-]", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Get the relative path in osu! storage for this file.
|
||||
/// </summary>
|
||||
@ -137,20 +139,14 @@ namespace osu.Game.Extensions
|
||||
return instance.OnlineID.Equals(other.OnlineID);
|
||||
}
|
||||
|
||||
private static readonly char[] invalid_filename_characters = Path.GetInvalidFileNameChars()
|
||||
// Backslash is added to avoid issues when exporting to zip.
|
||||
// See SharpCompress filename normalisation https://github.com/adamhathcock/sharpcompress/blob/a1e7c0068db814c9aa78d86a94ccd1c761af74bd/src/SharpCompress/Writers/Zip/ZipWriter.cs#L143.
|
||||
.Append('\\')
|
||||
.ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Get a valid filename for use inside a zip file. Avoids backslashes being incorrectly converted to directories.
|
||||
/// Create a valid filename which should work across all platforms.
|
||||
/// </summary>
|
||||
public static string GetValidArchiveContentFilename(this string filename)
|
||||
{
|
||||
foreach (char c in invalid_filename_characters)
|
||||
filename = filename.Replace(c, '_');
|
||||
return filename;
|
||||
}
|
||||
/// <remarks>
|
||||
/// This function replaces all characters not included in a very pessimistic list which should be compatible
|
||||
/// across all operating systems. We are using this in place of <see cref="Path.GetInvalidFileNameChars"/> as
|
||||
/// that function does not have per-platform considerations (and is only made to work on windows).
|
||||
/// </remarks>
|
||||
public static string GetValidFilename(this string filename) => invalid_filename_chars.Replace(filename, "_");
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ namespace osu.Game.Graphics.Containers
|
||||
private Bindable<float> sizeY;
|
||||
private Bindable<float> posX;
|
||||
private Bindable<float> posY;
|
||||
private Bindable<bool> applySafeAreaPadding;
|
||||
|
||||
private Bindable<MarginPadding> safeAreaPadding;
|
||||
|
||||
@ -132,6 +133,9 @@ namespace osu.Game.Graphics.Containers
|
||||
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
|
||||
posY.ValueChanged += _ => Scheduler.AddOnce(updateSize);
|
||||
|
||||
applySafeAreaPadding = config.GetBindable<bool>(OsuSetting.SafeAreaConsiderations);
|
||||
applySafeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize));
|
||||
|
||||
safeAreaPadding = safeArea.SafeAreaPadding.GetBoundCopy();
|
||||
safeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize));
|
||||
}
|
||||
@ -192,7 +196,7 @@ namespace osu.Game.Graphics.Containers
|
||||
bool requiresMasking = targetRect.Size != Vector2.One
|
||||
// For the top level scaling container, for now we apply masking if safe areas are in use.
|
||||
// In the future this can likely be removed as more of the actual UI supports overflowing into the safe areas.
|
||||
|| (targetMode == ScalingMode.Everything && safeAreaPadding.Value.Total != Vector2.Zero);
|
||||
|| (targetMode == ScalingMode.Everything && (applySafeAreaPadding.Value && safeAreaPadding.Value.Total != Vector2.Zero));
|
||||
|
||||
if (requiresMasking)
|
||||
sizableContainer.Masking = true;
|
||||
@ -225,6 +229,9 @@ namespace osu.Game.Graphics.Containers
|
||||
[Resolved]
|
||||
private ISafeArea safeArea { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
private readonly bool confineHostCursor;
|
||||
private readonly LayoutValue cursorRectCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||
|
||||
@ -259,7 +266,7 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
if (host.Window == null) return;
|
||||
|
||||
bool coversWholeScreen = Size == Vector2.One && safeArea.SafeAreaPadding.Value.Total == Vector2.Zero;
|
||||
bool coversWholeScreen = Size == Vector2.One && (!config.Get<bool>(OsuSetting.SafeAreaConsiderations) || safeArea.SafeAreaPadding.Value.Total == Vector2.Zero);
|
||||
host.Window.CursorConfineRect = coversWholeScreen ? null : ToScreenSpace(DrawRectangle).AABBFloat;
|
||||
}
|
||||
}
|
||||
|
@ -154,10 +154,8 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
|
||||
var mods = Mods.Select(apiMod => apiMod.ToMod(rulesetInstance)).ToArray();
|
||||
|
||||
var scoreInfo = ToScoreInfo(mods);
|
||||
|
||||
var scoreInfo = ToScoreInfo(mods, beatmap);
|
||||
scoreInfo.Ruleset = ruleset;
|
||||
if (beatmap != null) scoreInfo.BeatmapInfo = beatmap;
|
||||
|
||||
return scoreInfo;
|
||||
}
|
||||
@ -166,25 +164,47 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
/// Create a <see cref="ScoreInfo"/> from an API score instance.
|
||||
/// </summary>
|
||||
/// <param name="mods">The mod instances, resolved from a ruleset.</param>
|
||||
/// <returns></returns>
|
||||
public ScoreInfo ToScoreInfo(Mod[] mods) => new ScoreInfo
|
||||
/// <param name="beatmap">The object to populate the scores' beatmap with.
|
||||
///<list type="bullet">
|
||||
/// <item>If this is a <see cref="BeatmapInfo"/> type, then the score will be fully populated with the given object.</item>
|
||||
/// <item>Otherwise, if this is an <see cref="IBeatmapInfo"/> type (e.g. <see cref="APIBeatmap"/>), then only the beatmap ruleset will be populated.</item>
|
||||
/// <item>Otherwise, if this is <c>null</c>, then the beatmap ruleset will not be populated.</item>
|
||||
/// <item>The online beatmap ID is populated in all cases.</item>
|
||||
/// </list>
|
||||
/// </param>
|
||||
/// <returns>The populated <see cref="ScoreInfo"/>.</returns>
|
||||
public ScoreInfo ToScoreInfo(Mod[] mods, IBeatmapInfo? beatmap = null)
|
||||
{
|
||||
OnlineID = OnlineID,
|
||||
User = User ?? new APIUser { Id = UserID },
|
||||
BeatmapInfo = new BeatmapInfo { OnlineID = BeatmapID },
|
||||
Ruleset = new RulesetInfo { OnlineID = RulesetID },
|
||||
Passed = Passed,
|
||||
TotalScore = TotalScore,
|
||||
Accuracy = Accuracy,
|
||||
MaxCombo = MaxCombo,
|
||||
Rank = Rank,
|
||||
Statistics = Statistics,
|
||||
MaximumStatistics = MaximumStatistics,
|
||||
Date = EndedAt,
|
||||
Hash = HasReplay ? "online" : string.Empty, // TODO: temporary?
|
||||
Mods = mods,
|
||||
PP = PP,
|
||||
};
|
||||
var score = new ScoreInfo
|
||||
{
|
||||
OnlineID = OnlineID,
|
||||
User = User ?? new APIUser { Id = UserID },
|
||||
BeatmapInfo = new BeatmapInfo { OnlineID = BeatmapID },
|
||||
Ruleset = new RulesetInfo { OnlineID = RulesetID },
|
||||
Passed = Passed,
|
||||
TotalScore = TotalScore,
|
||||
Accuracy = Accuracy,
|
||||
MaxCombo = MaxCombo,
|
||||
Rank = Rank,
|
||||
Statistics = Statistics,
|
||||
MaximumStatistics = MaximumStatistics,
|
||||
Date = EndedAt,
|
||||
Hash = HasReplay ? "online" : string.Empty, // TODO: temporary?
|
||||
Mods = mods,
|
||||
PP = PP,
|
||||
};
|
||||
|
||||
if (beatmap is BeatmapInfo realmBeatmap)
|
||||
score.BeatmapInfo = realmBeatmap;
|
||||
else if (beatmap != null)
|
||||
{
|
||||
score.BeatmapInfo.Ruleset.OnlineID = beatmap.Ruleset.OnlineID;
|
||||
score.BeatmapInfo.Ruleset.Name = beatmap.Ruleset.Name;
|
||||
score.BeatmapInfo.Ruleset.ShortName = beatmap.Ruleset.ShortName;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SoloScoreInfo"/> from a local score for score submission.
|
||||
|
@ -179,6 +179,8 @@ namespace osu.Game
|
||||
|
||||
private Bindable<string> configRuleset;
|
||||
|
||||
private Bindable<bool> applySafeAreaConsiderations;
|
||||
|
||||
private Bindable<float> uiScale;
|
||||
|
||||
private Bindable<string> configSkin;
|
||||
@ -309,6 +311,9 @@ namespace osu.Game
|
||||
|
||||
SelectedMods.BindValueChanged(modsChanged);
|
||||
Beatmap.BindValueChanged(beatmapChanged, true);
|
||||
|
||||
applySafeAreaConsiderations = LocalConfig.GetBindable<bool>(OsuSetting.SafeAreaConsiderations);
|
||||
applySafeAreaConsiderations.BindValueChanged(apply => SafeAreaContainer.SafeAreaOverrideEdges = apply.NewValue ? SafeAreaOverrideEdges : Edges.All);
|
||||
}
|
||||
|
||||
private ExternalLinkOpener externalLinkOpener;
|
||||
|
@ -21,7 +21,11 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Handlers;
|
||||
using osu.Framework.Input.Handlers.Joystick;
|
||||
using osu.Framework.Input.Handlers.Midi;
|
||||
using osu.Framework.Input.Handlers.Mouse;
|
||||
using osu.Framework.Input.Handlers.Tablet;
|
||||
using osu.Framework.Input.Handlers.Touch;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
@ -46,6 +50,7 @@ using osu.Game.Online.Spectator;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections;
|
||||
using osu.Game.Overlays.Settings.Sections.Input;
|
||||
using osu.Game.Resources;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -189,6 +194,8 @@ namespace osu.Game
|
||||
|
||||
private RealmAccess realm;
|
||||
|
||||
protected SafeAreaContainer SafeAreaContainer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For now, this is used as a source specifically for beat synced components.
|
||||
/// Going forward, it could potentially be used as the single source-of-truth for beatmap timing.
|
||||
@ -341,7 +348,7 @@ namespace osu.Game
|
||||
|
||||
GlobalActionContainer globalBindings;
|
||||
|
||||
base.Content.Add(new SafeAreaContainer
|
||||
base.Content.Add(SafeAreaContainer = new SafeAreaContainer
|
||||
{
|
||||
SafeAreaOverrideEdges = SafeAreaOverrideEdges,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -521,6 +528,29 @@ namespace osu.Game
|
||||
/// <remarks>Should be overriden per-platform to provide settings for platform-specific handlers.</remarks>
|
||||
public virtual SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
|
||||
{
|
||||
// One would think that this could be moved to the `OsuGameDesktop` class, but doing so means that
|
||||
// OsuGameTestScenes will not show any input options (as they are based on OsuGame not OsuGameDesktop).
|
||||
//
|
||||
// This in turn makes it hard for ruleset creators to adjust input settings while testing their ruleset
|
||||
// within the test browser interface.
|
||||
if (RuntimeInfo.IsDesktop)
|
||||
{
|
||||
switch (handler)
|
||||
{
|
||||
case ITabletHandler th:
|
||||
return new TabletSettings(th);
|
||||
|
||||
case MouseHandler mh:
|
||||
return new MouseSettings(mh);
|
||||
|
||||
case JoystickHandler jh:
|
||||
return new JoystickSettings(jh);
|
||||
|
||||
case TouchHandler:
|
||||
return new InputSection.HandlerSection(handler);
|
||||
}
|
||||
}
|
||||
|
||||
switch (handler)
|
||||
{
|
||||
case MidiHandler:
|
||||
|
@ -20,6 +20,7 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
@ -50,6 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
private SettingsDropdown<Size> resolutionDropdown = null!;
|
||||
private SettingsDropdown<Display> displayDropdown = null!;
|
||||
private SettingsDropdown<WindowMode> windowModeDropdown = null!;
|
||||
private SettingsCheckbox safeAreaConsiderationsCheckbox = null!;
|
||||
|
||||
private Bindable<float> scalingPositionX = null!;
|
||||
private Bindable<float> scalingPositionY = null!;
|
||||
@ -101,6 +103,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
ItemSource = resolutions,
|
||||
Current = sizeFullscreen
|
||||
},
|
||||
safeAreaConsiderationsCheckbox = new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Shrink game to avoid cameras and notches",
|
||||
Current = osuConfig.GetBindable<bool>(OsuSetting.SafeAreaConsiderations),
|
||||
},
|
||||
new SettingsSlider<float, UIScaleSlider>
|
||||
{
|
||||
LabelText = GraphicsSettingsStrings.UIScaling,
|
||||
@ -166,7 +173,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
|
||||
windowModeDropdown.Current.BindValueChanged(_ =>
|
||||
{
|
||||
updateDisplayModeDropdowns();
|
||||
updateDisplaySettingsVisibility();
|
||||
updateScreenModeWarning();
|
||||
}, true);
|
||||
|
||||
@ -191,7 +198,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
.Distinct());
|
||||
}
|
||||
|
||||
updateDisplayModeDropdowns();
|
||||
updateDisplaySettingsVisibility();
|
||||
}), true);
|
||||
|
||||
scalingMode.BindValueChanged(_ =>
|
||||
@ -221,11 +228,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
Scheduler.AddOnce(d =>
|
||||
{
|
||||
displayDropdown.Items = d;
|
||||
updateDisplayModeDropdowns();
|
||||
updateDisplaySettingsVisibility();
|
||||
}, displays);
|
||||
}
|
||||
|
||||
private void updateDisplayModeDropdowns()
|
||||
private void updateDisplaySettingsVisibility()
|
||||
{
|
||||
if (resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen)
|
||||
resolutionDropdown.Show();
|
||||
@ -236,6 +243,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
displayDropdown.Show();
|
||||
else
|
||||
displayDropdown.Hide();
|
||||
|
||||
if (host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero)
|
||||
safeAreaConsiderationsCheckbox.Show();
|
||||
else
|
||||
safeAreaConsiderationsCheckbox.Hide();
|
||||
}
|
||||
|
||||
private void updateScreenModeWarning()
|
||||
|
@ -141,6 +141,8 @@ namespace osu.Game.Overlays.Toolbar
|
||||
Name = "Right buttons",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
|
@ -1,28 +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.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
public class CircularButton : OsuButton
|
||||
{
|
||||
private const float width = 125;
|
||||
private const float height = 30;
|
||||
|
||||
public CircularButton()
|
||||
{
|
||||
Size = new Vector2(width, height);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
Content.CornerRadius = DrawHeight / 2f;
|
||||
Content.CornerExponent = 2;
|
||||
}
|
||||
}
|
||||
}
|
@ -250,7 +250,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
private void seekTrackToCurrent()
|
||||
{
|
||||
double target = Current / Content.DrawWidth * editorClock.TrackLength;
|
||||
double target = TimeAtPosition(Current);
|
||||
editorClock.Seek(Math.Min(editorClock.TrackLength, target));
|
||||
}
|
||||
|
||||
@ -264,7 +264,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
if (handlingDragInput)
|
||||
editorClock.Stop();
|
||||
|
||||
ScrollTo((float)(editorClock.CurrentTime / editorClock.TrackLength) * Content.DrawWidth, false);
|
||||
float position = PositionAtTime(editorClock.CurrentTime);
|
||||
ScrollTo(position, false);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
|
@ -279,6 +279,8 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
switch (style)
|
||||
{
|
||||
case LabelStyles.None:
|
||||
labelEarly.Clear();
|
||||
labelLate.Clear();
|
||||
break;
|
||||
|
||||
case LabelStyles.Icons:
|
||||
|
@ -39,9 +39,16 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
public float BottomScoringElementsHeight { get; private set; }
|
||||
|
||||
// HUD uses AlwaysVisible on child components so they can be in an updated state for next display.
|
||||
// Without blocking input, this would also allow them to be interacted with in such a state.
|
||||
public override bool PropagatePositionalInputSubTree => ShowHud.Value;
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child)
|
||||
{
|
||||
// HUD uses AlwaysVisible on child components so they can be in an updated state for next display.
|
||||
// Without blocking input, this would also allow them to be interacted with in such a state.
|
||||
if (ShowHud.Value)
|
||||
return base.ShouldBeConsideredForInput(child);
|
||||
|
||||
// hold to quit button should always be interactive.
|
||||
return child == bottomRightElements;
|
||||
}
|
||||
|
||||
public readonly KeyCounterDisplay KeyCounter;
|
||||
public readonly ModDisplay ModDisplay;
|
||||
|
@ -35,7 +35,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.17.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1022.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1028.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
|
||||
<PackageReference Include="Sentry" Version="3.22.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -62,7 +62,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1021.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1022.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1028.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||
<PropertyGroup>
|
||||
@ -82,7 +82,7 @@
|
||||
<PackageReference Include="DiffPlex" Version="1.7.1" />
|
||||
<PackageReference Include="Humanizer" Version="2.14.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1022.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1028.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user