mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 01:42:55 +08:00
Merge branch 'master' into fix-storyboard-skin-textures-lookup
This commit is contained in:
commit
2ca1c684bb
39
README.md
39
README.md
@ -12,40 +12,43 @@
|
||||
|
||||
A free-to-win rhythm game. Rhythm is just a *click* away!
|
||||
|
||||
The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Currently known by and released under the release codename "*lazer*". As in sharper than cutting-edge.
|
||||
This is the future – and final – iteration of the [osu!](https://osu.ppy.sh) game client which marks the beginning of an open era! Currently known by and released under the release codename "*lazer*". As in sharper than cutting-edge.
|
||||
|
||||
## Status
|
||||
|
||||
This project is under constant development, but we aim to keep things in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||
This project is under constant development, but we do our best to keep things in a stable state. Players are encouraged to install from a release alongside their stable *osu!* client. This project will continue to evolve until we eventually reach the point where most users prefer it over the previous "osu!stable" release.
|
||||
|
||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passes come at the end of development, preceded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to a [stable release](https://osu.ppy.sh/home/download) of osu!. We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||
|
||||
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
||||
A few resources are available as starting points to getting involved and understanding the project:
|
||||
|
||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
||||
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
|
||||
- Track our current efforts [towards full "ranked play" support](https://github.com/orgs/ppy/projects/13?query=is%3Aopen+sort%3Aupdated-desc).
|
||||
|
||||
## Running osu!
|
||||
|
||||
If you are looking to install or test osu! without setting up a development environment, you can consume our [releases](https://github.com/ppy/osu/releases). You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download). Failing that, you may use the links below to download the latest version for your operating system of choice:
|
||||
If you are just looking to give the game a whirl, you can grab the latest release for your platform:
|
||||
|
||||
**Latest release:**
|
||||
### Latest release:
|
||||
|
||||
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
|
||||
| ------------- | ------------- | ------------- | ------------- | ------------- |
|
||||
|
||||
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
|
||||
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
|
||||
|
||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||
|
||||
**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores in early 2024.
|
||||
|
||||
## Developing a custom ruleset
|
||||
|
||||
osu! is designed to have extensible modular gameplay modes, called "rulesets". Building one of these allows a developer to harness the power of osu! for their own game style. To get started working on a ruleset, we have some templates available [here](https://github.com/ppy/osu/tree/master/Templates).
|
||||
osu! is designed to allow user-created gameplay variations, called "rulesets". Building one of these allows a developer to harness the power of the osu! beatmap library, game engine, and general UX for a new style of gameplay. To get started working on a ruleset, we have some templates available [here](https://github.com/ppy/osu/tree/master/Templates).
|
||||
|
||||
You can see some examples of custom rulesets by visiting the [custom ruleset directory](https://github.com/ppy/osu/discussions/13096).
|
||||
|
||||
## Developing osu!
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Please make sure you have the following prerequisites:
|
||||
|
||||
- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
|
||||
@ -69,9 +72,19 @@ git pull
|
||||
|
||||
### Building
|
||||
|
||||
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this is provided [below](#contributing).
|
||||
#### From an IDE
|
||||
|
||||
- Visual Studio / Rider users should load the project via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will allow access to template run configurations.
|
||||
You should load the solution via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will reduce dependencies and hide platforms that you don't care about. Valid `.slnf` files are:
|
||||
|
||||
- `osu.Desktop.slnf` (most common)
|
||||
- `osu.Android.slnf`
|
||||
- `osu.iOS.slnf`
|
||||
|
||||
Run configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `osu! (Tests)` project/configuration. More information on this is provided [below](#contributing).
|
||||
|
||||
To build for mobile platforms, you will likely need to run `sudo dotnet workload restore` if you haven't done so previously. This will install Android/iOS tooling required to complete the build.
|
||||
|
||||
#### From CLI
|
||||
|
||||
You can also build and run *osu!* from the command-line with a single command:
|
||||
|
||||
@ -79,12 +92,10 @@ You can also build and run *osu!* from the command-line with a single command:
|
||||
dotnet run --project osu.Desktop
|
||||
```
|
||||
|
||||
If you are not interested in debugging *osu!*, you can add `-c Release` to gain performance. In this case, you must replace `Debug` with `Release` in any commands mentioned in this document.
|
||||
When running locally to do any kind of performance testing, make sure to add `-c Release` to the build command, as the overhead of running with the default `Debug` configuration can be large (especially when testing with local framework modifications as below).
|
||||
|
||||
If the build fails, try to restore NuGet packages with `dotnet restore`.
|
||||
|
||||
_Due to a historical feature gap between .NET Core and Xamarin, running `dotnet` CLI from the root directory will not work for most commands. This can be resolved by specifying a target `.csproj` or the helper project at `build/Desktop.proj`. Configurations have been provided to work around this issue for all supported IDEs mentioned above._
|
||||
|
||||
### Testing with resource/framework modifications
|
||||
|
||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be quickly achieved using included commands:
|
||||
|
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.914.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.922.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -25,6 +25,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
// we're also a ScrollingHitObjectComposer candidate, but can't be everything can we?
|
||||
public partial class CatchHitObjectComposer : DistancedHitObjectComposer<CatchHitObject>
|
||||
{
|
||||
private const float distance_snap_radius = 50;
|
||||
@ -140,7 +141,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
return base.OnPressed(e);
|
||||
}
|
||||
|
||||
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null) =>
|
||||
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods) =>
|
||||
new DrawableCatchEditorRuleset(ruleset, beatmap, mods)
|
||||
{
|
||||
TimeRangeMultiplier = { BindTarget = timeRangeMultiplier, }
|
||||
|
@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public partial class DrawableCatchRuleset : DrawableScrollingRuleset<CatchHitObject>
|
||||
{
|
||||
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Constant;
|
||||
|
||||
protected override bool UserScrollSpeedAdjustment => false;
|
||||
|
||||
public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
|
||||
@ -30,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Down;
|
||||
TimeRange.Value = GetTimeRange(beatmap.Difficulty.ApproachRate);
|
||||
VisualisationMethod = ScrollVisualisationMethod.Constant;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||
PassCondition = () =>
|
||||
{
|
||||
var hitObject = Player.ChildrenOfType<DrawableManiaHitObject>().FirstOrDefault();
|
||||
return hitObject?.Dependencies.Get<IScrollingInfo>().Algorithm is ConstantScrollAlgorithm;
|
||||
return hitObject?.Dependencies.Get<IScrollingInfo>().Algorithm.Value is ConstantScrollAlgorithm;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(5000);
|
||||
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm();
|
||||
IBindable<IScrollAlgorithm> IScrollingInfo.Algorithm { get; } = new Bindable<IScrollAlgorithm>(new ConstantScrollAlgorithm());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,22 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osuTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public partial class DrawableManiaEditorRuleset : DrawableManiaRuleset
|
||||
public partial class DrawableManiaEditorRuleset : DrawableManiaRuleset, ISupportConstantAlgorithmToggle
|
||||
{
|
||||
public BindableBool ShowSpeedChanges { get; set; } = new BindableBool();
|
||||
|
||||
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
||||
|
||||
public DrawableManiaEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods)
|
||||
@ -21,6 +25,13 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ShowSpeedChanges.BindValueChanged(showChanges => VisualisationMethod = showChanges.NewValue ? ScrollVisualisationMethod.Sequential : ScrollVisualisationMethod.Constant, true);
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new ManiaEditorPlayfield(Beatmap.Stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -21,7 +21,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public partial class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>
|
||||
public partial class ManiaHitObjectComposer : ScrollingHitObjectComposer<ManiaHitObject>
|
||||
{
|
||||
private DrawableManiaEditorRuleset drawableRuleset;
|
||||
private ManiaBeatSnapGrid beatSnapGrid;
|
||||
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) =>
|
||||
Playfield.GetColumnByPosition(screenSpacePosition);
|
||||
|
||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
{
|
||||
drawableRuleset = new DrawableManiaEditorRuleset(ruleset, beatmap, mods);
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||
{
|
||||
var maniaRuleset = (DrawableManiaRuleset)drawableRuleset;
|
||||
maniaRuleset.ScrollMethod = ScrollVisualisationMethod.Constant;
|
||||
maniaRuleset.VisualisationMethod = ScrollVisualisationMethod.Constant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
@ -14,7 +13,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
@ -52,22 +50,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||
|
||||
public ScrollVisualisationMethod ScrollMethod
|
||||
{
|
||||
get => scrollMethod;
|
||||
set
|
||||
{
|
||||
if (IsLoaded)
|
||||
throw new InvalidOperationException($"Can't alter {nameof(ScrollMethod)} after ruleset is already loaded");
|
||||
|
||||
scrollMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
private ScrollVisualisationMethod scrollMethod = ScrollVisualisationMethod.Sequential;
|
||||
|
||||
protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod;
|
||||
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
private readonly BindableInt configScrollSpeed = new BindableInt();
|
||||
private double smoothTimeRange;
|
||||
|
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase("colinear-perfect-curve")]
|
||||
[TestCase("slider-ticks")]
|
||||
[TestCase("slider-ticks-edge-case")]
|
||||
[TestCase("slider-paths-edge-case")]
|
||||
[TestCase("repeat-slider")]
|
||||
[TestCase("uneven-repeat-slider")]
|
||||
[TestCase("old-stacking")]
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
}
|
||||
|
||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
=> new DrawableOsuEditorRuleset(ruleset, beatmap, mods);
|
||||
|
||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||
|
@ -61,10 +61,12 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
OsuHitObject firstObject = drawableRuleset.Beatmap.HitObjects.First();
|
||||
|
||||
// Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
|
||||
// Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
|
||||
bubbleSize = (float)(drawableRuleset.Beatmap.HitObjects.OfType<HitCircle>().First().Radius * 1.90f);
|
||||
bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType<HitCircle>().First().TimePreempt * 2;
|
||||
bubbleSize = (float)firstObject.Radius * 1.90f;
|
||||
bubbleFade = firstObject.TimePreempt * 2;
|
||||
|
||||
// We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
|
||||
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
||||
|
@ -0,0 +1,94 @@
|
||||
{
|
||||
"Mappings": [
|
||||
{
|
||||
"StartTime": 46060.0,
|
||||
"Objects": [
|
||||
{
|
||||
"StartTime": 46060.0,
|
||||
"EndTime": 46060.0,
|
||||
"X": 160.0,
|
||||
"Y": 208.0,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"StartTime": 46398.0,
|
||||
"EndTime": 46398.0,
|
||||
"X": 160.980164,
|
||||
"Y": 317.779083,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"StartTime": 46737.0,
|
||||
"EndTime": 46737.0,
|
||||
"X": 268.887268,
|
||||
"Y": 320.0,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"StartTime": 47040.0,
|
||||
"EndTime": 47040.0,
|
||||
"X": 378.995544,
|
||||
"Y": 320.0,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StartTime": 123348.0,
|
||||
"Objects": [
|
||||
{
|
||||
"StartTime": 123348.0,
|
||||
"EndTime": 123348.0,
|
||||
"X": 352.0,
|
||||
"Y": 160.0,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"StartTime": 123686.0,
|
||||
"EndTime": 123686.0,
|
||||
"X": 351.019836,
|
||||
"Y": 50.2209129,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"StartTime": 124025.0,
|
||||
"EndTime": 124025.0,
|
||||
"X": 243.112747,
|
||||
"Y": 48.0,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"StartTime": 124328.0,
|
||||
"EndTime": 124328.0,
|
||||
"X": 133.004471,
|
||||
"Y": 48.0,
|
||||
"StackOffset": {
|
||||
"X": 0.0,
|
||||
"Y": 0.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
osu file format v6
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.7
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:1
|
||||
CircleSize:3
|
||||
OverallDifficulty:1
|
||||
SliderMultiplier:1.1
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
-41,338.983050847458,4,1,0,70,1,0
|
||||
93648,-100,4,1,0,70,0,0
|
||||
|
||||
[HitObjects]
|
||||
160,208,46060,6,0,B|161:320|161:320|271:320|271:320,1,330,8|0
|
||||
352,160,123348,6,0,B|351:48|351:48|241:48|241:48,1,330,8|0
|
37
osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs
Normal file
37
osu.Game.Rulesets.Taiko/Edit/DrawableTaikoEditorRuleset.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Edit
|
||||
{
|
||||
public partial class DrawableTaikoEditorRuleset : DrawableTaikoRuleset, ISupportConstantAlgorithmToggle
|
||||
{
|
||||
public BindableBool ShowSpeedChanges { get; set; } = new BindableBool();
|
||||
|
||||
public DrawableTaikoEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ShowSpeedChanges.BindValueChanged(showChanges => VisualisationMethod = showChanges.NewValue ? ScrollVisualisationMethod.Overlapping : ScrollVisualisationMethod.Constant, true);
|
||||
}
|
||||
|
||||
protected override double ComputeTimeRange()
|
||||
{
|
||||
// Adjust when we're using constant algorithm to not be sluggish.
|
||||
double multiplier = ShowSpeedChanges.Value ? 1 : 4;
|
||||
return base.ComputeTimeRange() / multiplier;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,14 +2,17 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Edit
|
||||
{
|
||||
public partial class TaikoHitObjectComposer : HitObjectComposer<TaikoHitObject>
|
||||
public partial class TaikoHitObjectComposer : ScrollingHitObjectComposer<TaikoHitObject>
|
||||
{
|
||||
protected override bool ApplyHorizontalCentering => false;
|
||||
|
||||
@ -25,6 +28,9 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
||||
new SwellCompositionTool()
|
||||
};
|
||||
|
||||
protected override DrawableRuleset<TaikoHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods) =>
|
||||
new DrawableTaikoEditorRuleset(ruleset, beatmap, mods);
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
||||
=> new TaikoBlueprintContainer(this);
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager;
|
||||
|
||||
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
||||
|
||||
protected override bool UserScrollSpeedAdjustment => false;
|
||||
|
||||
private SkinnableDrawable scroller;
|
||||
@ -45,6 +43,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Left;
|
||||
VisualisationMethod = ScrollVisualisationMethod.Overlapping;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -65,6 +64,11 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
base.Update();
|
||||
|
||||
TimeRange.Value = ComputeTimeRange();
|
||||
}
|
||||
|
||||
protected virtual double ComputeTimeRange()
|
||||
{
|
||||
// Taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened.
|
||||
const float scroll_rate = 10;
|
||||
|
||||
@ -73,7 +77,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
// We clamp the ratio to the maximum aspect ratio to keep scroll speed consistent on widths lower than the default.
|
||||
float ratio = Math.Max(DrawSize.X / 768f, TaikoPlayfieldAdjustmentContainer.MAXIMUM_ASPECT);
|
||||
|
||||
TimeRange.Value = (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate;
|
||||
return (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
|
@ -621,6 +621,38 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidBankDefaultsToNormal()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||
|
||||
using (var resStream = TestResources.OpenResource("invalid-bank.osu"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
{
|
||||
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||
|
||||
assertObjectHasBanks(hitObjects[0], HitSampleInfo.BANK_DRUM);
|
||||
assertObjectHasBanks(hitObjects[1], HitSampleInfo.BANK_NORMAL);
|
||||
assertObjectHasBanks(hitObjects[2], HitSampleInfo.BANK_SOFT);
|
||||
assertObjectHasBanks(hitObjects[3], HitSampleInfo.BANK_DRUM);
|
||||
assertObjectHasBanks(hitObjects[4], HitSampleInfo.BANK_NORMAL);
|
||||
|
||||
assertObjectHasBanks(hitObjects[5], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_DRUM);
|
||||
assertObjectHasBanks(hitObjects[6], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_NORMAL);
|
||||
assertObjectHasBanks(hitObjects[7], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_SOFT);
|
||||
assertObjectHasBanks(hitObjects[8], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_DRUM);
|
||||
assertObjectHasBanks(hitObjects[9], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_NORMAL);
|
||||
}
|
||||
|
||||
void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null)
|
||||
{
|
||||
Assert.AreEqual(normalBank, hitObject.Samples[0].Bank);
|
||||
|
||||
if (additionsBank != null)
|
||||
Assert.AreEqual(additionsBank, hitObject.Samples[1].Bank);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFallbackDecoderForCorruptedHeader()
|
||||
{
|
||||
|
19
osu.Game.Tests/Resources/invalid-bank.osu
Normal file
19
osu.Game.Tests/Resources/invalid-bank.osu
Normal file
@ -0,0 +1,19 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
SampleSet: Normal
|
||||
|
||||
[TimingPoints]
|
||||
0,500,4,3,0,100,1,0
|
||||
|
||||
[HitObjects]
|
||||
256,192,1000,5,0,0:0:0:0:
|
||||
256,192,2000,1,0,1:0:0:0:
|
||||
256,192,3000,1,0,2:0:0:0:
|
||||
256,192,4000,1,0,3:0:0:0:
|
||||
256,192,5000,1,0,42:0:0:0:
|
||||
256,192,6000,5,4,0:0:0:0:
|
||||
256,192,7000,1,4,0:1:0:0:
|
||||
256,192,8000,1,4,0:2:0:0:
|
||||
256,192,9000,1,4,0:3:0:0:
|
||||
256,192,10000,1,4,0:42:0:0:
|
@ -311,14 +311,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected override bool RelativeScaleBeatLengths => RelativeScaleBeatLengthsOverride;
|
||||
|
||||
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
||||
|
||||
public new Bindable<double> TimeRange => base.TimeRange;
|
||||
|
||||
public TestDrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
TimeRange.Value = time_range;
|
||||
VisualisationMethod = ScrollVisualisationMethod.Overlapping;
|
||||
}
|
||||
|
||||
public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h)
|
||||
|
@ -3,14 +3,17 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.PolygonExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osuTK;
|
||||
@ -139,6 +142,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
=> AddAssert($"leaderboard height is {panelCount} panels high", () => leaderboard.DrawHeight == (GameplayLeaderboardScore.PANEL_HEIGHT + leaderboard.Spacing) * panelCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFriendScore()
|
||||
{
|
||||
APIUser friend = new APIUser { Username = "my friend", Id = 10000 };
|
||||
|
||||
createLeaderboard();
|
||||
addLocalPlayer();
|
||||
|
||||
AddStep("Add friend to API", () =>
|
||||
{
|
||||
var api = (DummyAPIAccess)API;
|
||||
|
||||
api.Friends.Clear();
|
||||
api.Friends.Add(friend);
|
||||
});
|
||||
|
||||
int playerNumber = 1;
|
||||
|
||||
AddRepeatStep("add 3 other players", () => createRandomScore(new APIUser { Username = $"Player {playerNumber++}" }), 3);
|
||||
AddUntilStep("no pink color scores",
|
||||
() => leaderboard.ChildrenOfType<Box>().Select(b => ((Colour4)b.Colour).ToHex()),
|
||||
() => Does.Not.Contain("#FF549A"));
|
||||
|
||||
AddRepeatStep("add 3 friend score", () => createRandomScore(friend), 3);
|
||||
AddUntilStep("at least one friend score is pink",
|
||||
() => leaderboard.GetAllScoresForUsername("my friend")
|
||||
.SelectMany(score => score.ChildrenOfType<Box>())
|
||||
.Select(b => ((Colour4)b.Colour).ToHex()),
|
||||
() => Does.Contain("#FF549A"));
|
||||
}
|
||||
|
||||
private void addLocalPlayer()
|
||||
{
|
||||
AddStep("add local player", () =>
|
||||
@ -179,6 +213,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
return scoreItem != null && scoreItem.ScorePosition == expectedPosition;
|
||||
}
|
||||
|
||||
public IEnumerable<GameplayLeaderboardScore> GetAllScoresForUsername(string username)
|
||||
=> Flow.Where(i => i.User?.Username == username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
@ -1087,6 +1088,25 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert("0 matching shown", () => songSelect.ChildrenOfType<FilterControl>().Single().InformationalText == "0 matches");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeleteHotkey()
|
||||
{
|
||||
createSongSelect();
|
||||
|
||||
addRulesetImportStep(0);
|
||||
AddAssert("3 matching shown", () => songSelect.ChildrenOfType<FilterControl>().Single().InformationalText == "3 matches");
|
||||
|
||||
AddStep("press shift-delete", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ShiftLeft);
|
||||
InputManager.Key(Key.Delete);
|
||||
InputManager.ReleaseKey(Key.ShiftLeft);
|
||||
});
|
||||
AddUntilStep("delete dialog shown", () => DialogOverlay.CurrentDialog, Is.InstanceOf<BeatmapDeleteDialog>);
|
||||
AddStep("confirm deletion", () => DialogOverlay.CurrentDialog!.PerformAction<PopupDialogDangerousButton>());
|
||||
AddAssert("0 matching shown", () => songSelect.ChildrenOfType<FilterControl>().Single().InformationalText == "0 matches");
|
||||
}
|
||||
|
||||
private void waitForInitialSelection()
|
||||
{
|
||||
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
|
||||
|
@ -216,8 +216,6 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public double FramesPerSecond => finalClockSource.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => finalClockSource.TimeInfo;
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -64,6 +64,12 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.Username, string.Empty);
|
||||
SetDefault(OsuSetting.Token, string.Empty);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// this default set MUST remain despite the setting being deprecated, because `SetDefault()` calls are implicitly used to declare the type returned for the lookup.
|
||||
// if this is removed, the setting will be interpreted as a string, and `Migrate()` will fail due to cast failure.
|
||||
// can be removed 20240618
|
||||
SetDefault(OsuSetting.AutomaticallyDownloadWhenSpectating, false);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
SetDefault(OsuSetting.AutomaticallyDownloadMissingBeatmaps, false);
|
||||
|
||||
SetDefault(OsuSetting.SavePassword, false).ValueChanged += enabled =>
|
||||
@ -218,7 +224,7 @@ namespace osu.Game.Configuration
|
||||
if (combined < 20230918)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
SetValue(OsuSetting.AutomaticallyDownloadMissingBeatmaps, Get<OsuSetting>(OsuSetting.AutomaticallyDownloadWhenSpectating)); // can be removed 20240618
|
||||
SetValue(OsuSetting.AutomaticallyDownloadMissingBeatmaps, Get<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating)); // can be removed 20240618
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
}
|
||||
|
@ -99,9 +99,14 @@ namespace osu.Game.Online.API
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
// Network failure.
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
//todo: potentially only kill the refresh token on certain exception types.
|
||||
// Force a full re-reauthentication.
|
||||
Token.Value = null;
|
||||
return false;
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
private void load()
|
||||
{
|
||||
RightToolbox.Add(new EditorToolboxGroup("snapping")
|
||||
{
|
||||
|
@ -87,6 +87,8 @@ namespace osu.Game.Rulesets.Edit
|
||||
private IBindable<bool> hasTiming;
|
||||
private Bindable<bool> autoSeekOnPlacement;
|
||||
|
||||
protected DrawableRuleset<TObject> DrawableRuleset { get; private set; }
|
||||
|
||||
protected HitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
@ -104,7 +106,8 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
try
|
||||
{
|
||||
drawableRulesetWrapper = new DrawableEditorRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap, new[] { Ruleset.GetAutoplayMod() }))
|
||||
DrawableRuleset = CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap, new[] { Ruleset.GetAutoplayMod() });
|
||||
drawableRulesetWrapper = new DrawableEditorRulesetWrapper<TObject>(DrawableRuleset)
|
||||
{
|
||||
Clock = EditorClock,
|
||||
ProcessCustomClock = false
|
||||
@ -304,7 +307,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// <param name="beatmap">The loaded beatmap.</param>
|
||||
/// <param name="mods">The mods to be applied.</param>
|
||||
/// <returns>An editor-relevant <see cref="DrawableRuleset{TObject}"/>.</returns>
|
||||
protected virtual DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
protected virtual DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
=> (DrawableRuleset<TObject>)ruleset.CreateDrawableRulesetWith(beatmap, mods);
|
||||
|
||||
#region Tool selection logic
|
||||
|
51
osu.Game/Rulesets/Edit/ScrollingHitObjectComposer.cs
Normal file
51
osu.Game/Rulesets/Edit/ScrollingHitObjectComposer.cs
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
public abstract partial class ScrollingHitObjectComposer<TObject> : HitObjectComposer<TObject>
|
||||
where TObject : HitObject
|
||||
{
|
||||
private readonly Bindable<TernaryState> showSpeedChanges = new Bindable<TernaryState>();
|
||||
|
||||
protected ScrollingHitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
if (DrawableRuleset is ISupportConstantAlgorithmToggle toggleRuleset)
|
||||
{
|
||||
LeftToolbox.Add(new EditorToolboxGroup("playfield")
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableTernaryButton(new TernaryButton(showSpeedChanges, "Show speed changes", () => new SpriteIcon { Icon = FontAwesome.Solid.TachometerAlt }))
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
showSpeedChanges.BindValueChanged(state => toggleRuleset.ShowSpeedChanges.Value = state.NewValue == TernaryState.True, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -190,7 +190,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
string[] split = str.Split(':');
|
||||
|
||||
var bank = (LegacySampleBank)Parsing.ParseInt(split[0]);
|
||||
if (!Enum.IsDefined(bank))
|
||||
bank = LegacySampleBank.Normal;
|
||||
|
||||
var addBank = (LegacySampleBank)Parsing.ParseInt(split[1]);
|
||||
if (!Enum.IsDefined(addBank))
|
||||
addBank = LegacySampleBank.Normal;
|
||||
|
||||
string stringBank = bank.ToString().ToLowerInvariant();
|
||||
if (stringBank == @"none")
|
||||
|
@ -262,9 +262,16 @@ namespace osu.Game.Rulesets.Objects
|
||||
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
|
||||
var segmentType = ControlPoints[start].Type ?? PathType.Linear;
|
||||
|
||||
foreach (Vector2 t in calculateSubPath(segmentVertices, segmentType))
|
||||
// No need to calculate path when there is only 1 vertex
|
||||
if (segmentVertices.Length == 1)
|
||||
calculatedPath.Add(segmentVertices[0]);
|
||||
else if (segmentVertices.Length > 1)
|
||||
{
|
||||
if (calculatedPath.Count == 0 || calculatedPath.Last() != t)
|
||||
List<Vector2> subPath = calculateSubPath(segmentVertices, segmentType);
|
||||
// Skip the first vertex if it is the same as the last vertex from the previous segment
|
||||
int skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0] ? 1 : 0;
|
||||
|
||||
foreach (Vector2 t in subPath.Skip(skipFirst))
|
||||
calculatedPath.Add(t);
|
||||
}
|
||||
|
||||
@ -328,8 +335,8 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
if (ExpectedDistance.Value is double expectedDistance && calculatedLength != expectedDistance)
|
||||
{
|
||||
// In osu-stable, if the last two control points of a slider are equal, extension is not performed.
|
||||
if (ControlPoints.Count >= 2 && ControlPoints[^1].Position == ControlPoints[^2].Position && expectedDistance > calculatedLength)
|
||||
// In osu-stable, if the last two path points of a slider are equal, extension is not performed.
|
||||
if (calculatedPath.Count >= 2 && calculatedPath[^1] == calculatedPath[^2] && expectedDistance > calculatedLength)
|
||||
{
|
||||
cumulativeLength.Add(calculatedLength);
|
||||
return;
|
||||
|
@ -264,8 +264,6 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public double FramesPerSecond => framedClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => framedClock.TimeInfo;
|
||||
|
||||
public double StartTime => parentGameplayClock?.StartTime ?? 0;
|
||||
|
||||
private readonly AudioAdjustments gameplayAdjustments = new AudioAdjustments();
|
||||
|
@ -64,8 +64,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
protected virtual ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Sequential;
|
||||
|
||||
ScrollVisualisationMethod IDrawableScrollingRuleset.VisualisationMethod => VisualisationMethod;
|
||||
|
||||
/// <summary>
|
||||
@ -99,20 +97,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
switch (VisualisationMethod)
|
||||
{
|
||||
case ScrollVisualisationMethod.Sequential:
|
||||
scrollingInfo.Algorithm = new SequentialScrollAlgorithm(ControlPoints);
|
||||
break;
|
||||
|
||||
case ScrollVisualisationMethod.Overlapping:
|
||||
scrollingInfo.Algorithm = new OverlappingScrollAlgorithm(ControlPoints);
|
||||
break;
|
||||
|
||||
case ScrollVisualisationMethod.Constant:
|
||||
scrollingInfo.Algorithm = new ConstantScrollAlgorithm();
|
||||
break;
|
||||
}
|
||||
updateScrollAlgorithm();
|
||||
|
||||
double lastObjectTime = Beatmap.HitObjects.Any() ? Beatmap.GetLastObjectTime() : double.MaxValue;
|
||||
double baseBeatLength = TimingControlPoint.DEFAULT_BEAT_LENGTH;
|
||||
@ -178,6 +163,36 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
throw new ArgumentException($"{nameof(Playfield)} must be a {nameof(ScrollingPlayfield)} when using {nameof(DrawableScrollingRuleset<TObject>)}.");
|
||||
}
|
||||
|
||||
private ScrollVisualisationMethod visualisationMethod = ScrollVisualisationMethod.Sequential;
|
||||
|
||||
public ScrollVisualisationMethod VisualisationMethod
|
||||
{
|
||||
get => visualisationMethod;
|
||||
set
|
||||
{
|
||||
visualisationMethod = value;
|
||||
updateScrollAlgorithm();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateScrollAlgorithm()
|
||||
{
|
||||
switch (VisualisationMethod)
|
||||
{
|
||||
case ScrollVisualisationMethod.Sequential:
|
||||
scrollingInfo.Algorithm.Value = new SequentialScrollAlgorithm(ControlPoints);
|
||||
break;
|
||||
|
||||
case ScrollVisualisationMethod.Overlapping:
|
||||
scrollingInfo.Algorithm.Value = new OverlappingScrollAlgorithm(ControlPoints);
|
||||
break;
|
||||
|
||||
case ScrollVisualisationMethod.Constant:
|
||||
scrollingInfo.Algorithm.Value = new ConstantScrollAlgorithm();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the scroll speed of <see cref="HitObject"/>s.
|
||||
/// </summary>
|
||||
@ -217,7 +232,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
||||
public IBindable<double> TimeRange { get; } = new BindableDouble();
|
||||
|
||||
public IScrollAlgorithm Algorithm { get; set; }
|
||||
public readonly Bindable<IScrollAlgorithm> Algorithm = new Bindable<IScrollAlgorithm>(new ConstantScrollAlgorithm());
|
||||
|
||||
IBindable<IScrollAlgorithm> IScrollingInfo.Algorithm => Algorithm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
IBindable<ScrollingDirection> Direction { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// The span of time that is visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
IBindable<double> TimeRange { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The algorithm which controls <see cref="HitObject"/> positions and sizes.
|
||||
/// </summary>
|
||||
IScrollAlgorithm Algorithm { get; }
|
||||
IBindable<IScrollAlgorithm> Algorithm { get; }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// Denotes a <see cref="IDrawableScrollingRuleset"/> which supports toggling constant algorithm for better display in the editor.
|
||||
/// </summary>
|
||||
public interface ISupportConstantAlgorithmToggle : IDrawableScrollingRuleset
|
||||
{
|
||||
public BindableBool ShowSpeedChanges { get; }
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ using osu.Framework.Layout;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
@ -21,6 +22,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
private readonly IBindable<double> timeRange = new BindableDouble();
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
private readonly IBindable<IScrollAlgorithm> algorithm = new Bindable<IScrollAlgorithm>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the scrolling direction is horizontal or vertical.
|
||||
@ -59,9 +61,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
timeRange.BindTo(scrollingInfo.TimeRange);
|
||||
algorithm.BindTo(scrollingInfo.Algorithm);
|
||||
|
||||
direction.ValueChanged += _ => layoutCache.Invalidate();
|
||||
timeRange.ValueChanged += _ => layoutCache.Invalidate();
|
||||
algorithm.ValueChanged += _ => layoutCache.Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -73,7 +77,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
public double TimeAtPosition(float localPosition, double currentTime)
|
||||
{
|
||||
float scrollPosition = axisInverted ? -localPosition : localPosition;
|
||||
return scrollingInfo.Algorithm.TimeAt(scrollPosition, currentTime, timeRange.Value, scrollLength);
|
||||
return algorithm.Value.TimeAt(scrollPosition, currentTime, timeRange.Value, scrollLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -95,7 +99,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// </summary>
|
||||
public float PositionAtTime(double time, double currentTime, double? originTime = null)
|
||||
{
|
||||
float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength, originTime);
|
||||
float scrollPosition = algorithm.Value.PositionAt(time, currentTime, timeRange.Value, scrollLength, originTime);
|
||||
return axisInverted ? -scrollPosition : scrollPosition;
|
||||
}
|
||||
|
||||
@ -122,7 +126,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// </summary>
|
||||
public float LengthAtTime(double startTime, double endTime)
|
||||
{
|
||||
return scrollingInfo.Algorithm.GetLength(startTime, endTime, timeRange.Value, scrollLength);
|
||||
return algorithm.Value.GetLength(startTime, endTime, timeRange.Value, scrollLength);
|
||||
}
|
||||
|
||||
private float scrollLength => scrollingAxis == Direction.Horizontal ? DrawWidth : DrawHeight;
|
||||
@ -169,7 +173,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
foreach (var entry in Entries)
|
||||
setComputedLifetimeStart(entry);
|
||||
|
||||
scrollingInfo.Algorithm.Reset();
|
||||
algorithm.Value.Reset();
|
||||
|
||||
layoutCache.Validate();
|
||||
}
|
||||
@ -224,7 +228,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
break;
|
||||
}
|
||||
|
||||
return scrollingInfo.Algorithm.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength);
|
||||
return algorithm.Value.GetDisplayStartTime(entry.HitObject.StartTime, startOffset, timeRange.Value, scrollLength);
|
||||
}
|
||||
|
||||
private void setComputedLifetimeStart(HitObjectLifetimeEntry entry)
|
||||
|
@ -14,7 +14,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
{
|
||||
internal partial class DrawableTernaryButton : OsuButton
|
||||
public partial class DrawableTernaryButton : OsuButton
|
||||
{
|
||||
private Color4 defaultBackgroundColour;
|
||||
private Color4 defaultIconColour;
|
||||
|
@ -231,8 +231,6 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
public double FramesPerSecond => underlyingClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||
|
||||
public void ChangeSource(IClock source)
|
||||
{
|
||||
track.Value = source as Track;
|
||||
|
@ -234,7 +234,5 @@ namespace osu.Game.Screens.Play
|
||||
public double ElapsedFrameTime => GameplayClock.ElapsedFrameTime;
|
||||
|
||||
public double FramesPerSecond => GameplayClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => GameplayClock.TimeInfo;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -11,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
@ -107,6 +109,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
private IBindable<ScoringMode> scoreDisplayMode = null!;
|
||||
|
||||
private bool isFriend;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="GameplayLeaderboardScore"/>.
|
||||
/// </summary>
|
||||
@ -124,7 +128,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, OsuConfigManager osuConfigManager)
|
||||
private void load(OsuColour colours, OsuConfigManager osuConfigManager, IAPIProvider api)
|
||||
{
|
||||
Container avatarContainer;
|
||||
|
||||
@ -311,6 +315,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}, true);
|
||||
|
||||
HasQuit.BindValueChanged(_ => updateState());
|
||||
|
||||
isFriend = User != null && api.Friends.Any(u => User.OnlineID == u.Id);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -389,6 +395,11 @@ namespace osu.Game.Screens.Play.HUD
|
||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("ffd966");
|
||||
textColour = TextColour ?? Color4Extensions.FromHex("2e576b");
|
||||
}
|
||||
else if (isFriend)
|
||||
{
|
||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("ff549a");
|
||||
textColour = TextColour ?? Color4.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("3399cc");
|
||||
|
@ -254,6 +254,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public OsuSpriteText FilterText { get; private set; }
|
||||
|
||||
// clipboard is disabled because one of the "cut" platform key bindings (shift-delete) conflicts with the beatmap deletion action.
|
||||
protected override bool AllowClipboardExport => false;
|
||||
|
||||
public FilterControlTextBox()
|
||||
{
|
||||
Height += filter_text_size;
|
||||
|
@ -142,14 +142,32 @@ namespace osu.Game.Storyboards.Drawables
|
||||
public void Dispose() =>
|
||||
realmFileStore.Dispose();
|
||||
|
||||
public byte[] Get(string name) =>
|
||||
realmFileStore.Get(storyboard.GetStoragePathFromStoryboardPath(name));
|
||||
public byte[] Get(string name)
|
||||
{
|
||||
string? storagePath = storyboard.GetStoragePathFromStoryboardPath(name);
|
||||
|
||||
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) =>
|
||||
realmFileStore.GetAsync(storyboard.GetStoragePathFromStoryboardPath(name), cancellationToken);
|
||||
return string.IsNullOrEmpty(storagePath)
|
||||
? null!
|
||||
: realmFileStore.Get(storagePath);
|
||||
}
|
||||
|
||||
public Stream GetStream(string name) =>
|
||||
realmFileStore.GetStream(storyboard.GetStoragePathFromStoryboardPath(name));
|
||||
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
string? storagePath = storyboard.GetStoragePathFromStoryboardPath(name);
|
||||
|
||||
return string.IsNullOrEmpty(storagePath)
|
||||
? Task.FromResult<byte[]>(null!)
|
||||
: realmFileStore.GetAsync(storagePath, cancellationToken);
|
||||
}
|
||||
|
||||
public Stream? GetStream(string name)
|
||||
{
|
||||
string? storagePath = storyboard.GetStoragePathFromStoryboardPath(name);
|
||||
|
||||
return string.IsNullOrEmpty(storagePath)
|
||||
? null
|
||||
: realmFileStore.GetStream(storagePath);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetAvailableResources() =>
|
||||
realmFileStore.GetAvailableResources();
|
||||
|
@ -29,12 +29,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(IBindable<WorkingBeatmap> beatmap, TextureStore textureStore)
|
||||
{
|
||||
string? path = beatmap.Value.BeatmapSetInfo?.GetPathForFile(Video.Path);
|
||||
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
var stream = textureStore.GetStream(path);
|
||||
var stream = textureStore.GetStream(Video.Path);
|
||||
|
||||
if (stream == null)
|
||||
return;
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual
|
||||
IBindable<double> IScrollingInfo.TimeRange => TimeRange;
|
||||
|
||||
public readonly TestScrollAlgorithm Algorithm = new TestScrollAlgorithm();
|
||||
IScrollAlgorithm IScrollingInfo.Algorithm => Algorithm;
|
||||
IBindable<IScrollAlgorithm> IScrollingInfo.Algorithm => new Bindable<IScrollAlgorithm>(Algorithm);
|
||||
}
|
||||
|
||||
public class TestScrollAlgorithm : IScrollAlgorithm
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.914.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.922.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.914.0" />
|
||||
<PackageReference Include="Sentry" Version="3.39.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
||||
|
@ -23,6 +23,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.914.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.922.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -34,9 +34,9 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We don't really use the camera.</string>
|
||||
<string>We don't really use the camera.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>We don't really use the microphone.</string>
|
||||
<string>We don't really use the microphone.</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
@ -130,5 +130,7 @@
|
||||
<string>Editor</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.music-games</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
Loading…
Reference in New Issue
Block a user