Compare commits
828 Commits
2019.730.0
...
2019.830.0
@@ -1,2 +0,0 @@
|
||||
language: csharp
|
||||
solution: osu.sln
|
||||
@@ -6,7 +6,7 @@ GEM
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
claide (1.0.3)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
@@ -14,11 +14,11 @@ GEM
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.4.1)
|
||||
domain_name (0.5.20180417)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.1)
|
||||
dotenv (2.7.5)
|
||||
emoji_regex (1.0.1)
|
||||
excon (0.62.0)
|
||||
excon (0.66.0)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
@@ -27,7 +27,7 @@ GEM
|
||||
faraday_middleware (0.13.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.5)
|
||||
fastlane (2.117.0)
|
||||
fastlane (2.129.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
@@ -46,8 +46,8 @@ GEM
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
multi_json
|
||||
jwt (~> 2.1.0)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
@@ -56,15 +56,15 @@ GEM
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcodeproj (>= 1.8.1, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-clean_testflight_testers (0.2.0)
|
||||
fastlane-plugin-clean_testflight_testers (0.3.0)
|
||||
fastlane-plugin-souyuz (0.8.1)
|
||||
souyuz (>= 0.8.1)
|
||||
fastlane-plugin-xamarin (0.6.3)
|
||||
@@ -79,7 +79,7 @@ GEM
|
||||
signet (~> 0.9)
|
||||
google-cloud-core (1.3.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-env (1.0.5)
|
||||
google-cloud-env (1.2.0)
|
||||
faraday (~> 0.11)
|
||||
google-cloud-storage (1.16.0)
|
||||
digest-crc (~> 0.4)
|
||||
@@ -102,17 +102,17 @@ GEM
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2018.0812)
|
||||
mini_magick (4.5.1)
|
||||
mime-types-data (3.2019.0331)
|
||||
mini_magick (4.9.5)
|
||||
mini_portile2 (2.4.0)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.6)
|
||||
naturally (2.2.0)
|
||||
nokogiri (1.10.1)
|
||||
nokogiri (1.10.4)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
os (1.0.0)
|
||||
os (1.0.1)
|
||||
plist (3.5.0)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
@@ -121,7 +121,7 @@ GEM
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
rubyzip (1.2.2)
|
||||
rubyzip (1.2.3)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
@@ -136,20 +136,20 @@ GEM
|
||||
fastlane (>= 2.29.0)
|
||||
highline (~> 1.7)
|
||||
nokogiri (~> 1.7)
|
||||
terminal-notifier (1.8.0)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
tty-cursor (0.6.1)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.9.0)
|
||||
tty-cursor (~> 0.6.0)
|
||||
tty-cursor (0.7.0)
|
||||
tty-screen (0.7.0)
|
||||
tty-spinner (0.9.1)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.1)
|
||||
unf_ext (0.0.7.6)
|
||||
unicode-display_width (1.6.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.8.1)
|
||||
xcodeproj (1.12.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
|
||||
@@ -19,8 +19,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
|
||||
## Requirements
|
||||
|
||||
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
|
||||
- When running on linux, please have a system-wide ffmpeg installation available to support video decoding.
|
||||
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
|
||||
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
|
||||
|
||||
## Running osu!
|
||||
|
||||
@@ -34,7 +35,7 @@ If you are not interested in developing the game, you can still consume our [bin
|
||||
| ------------- | ------------- |
|
||||
|
||||
- **Linux** users are recommended to self-compile until we have official deployment in place.
|
||||
- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (note that due to high demand this is regularly full).
|
||||
- **iOS** users can join the [TestFlight beta program](https://testflight.apple.com/join/2tLcjWlF) (note that due to high demand this is regularly full).
|
||||
- **Android** users can self-compile, and expect a public beta soon.
|
||||
|
||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cake" Version="0.30.0" />
|
||||
<PackageReference Include="Cake.CoreCLR" Version="0.30.0" />
|
||||
<PackageReference Include="Cake" Version="0.34.1" />
|
||||
<PackageReference Include="Cake.CoreCLR" Version="0.34.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,22 +1,6 @@
|
||||
update_fastlane
|
||||
|
||||
default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
lane :testflight_prune_dry do
|
||||
clean_testflight_testers(days_of_inactivity:45, dry_run: true)
|
||||
end
|
||||
|
||||
# Specify a custom number for what's "inactive"
|
||||
lane :testflight_prune do
|
||||
clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months
|
||||
end
|
||||
|
||||
lane :update_version do |options|
|
||||
options[:plist_path] = '../osu.iOS/Info.plist'
|
||||
app_version(options)
|
||||
end
|
||||
|
||||
desc 'Deploy to testflight'
|
||||
lane :beta do |options|
|
||||
update_version(options)
|
||||
@@ -62,4 +46,17 @@ platform :ios do
|
||||
|
||||
match(options)
|
||||
end
|
||||
|
||||
lane :update_version do |options|
|
||||
options[:plist_path] = '../osu.iOS/Info.plist'
|
||||
app_version(options)
|
||||
end
|
||||
|
||||
lane :testflight_prune_dry do
|
||||
clean_testflight_testers(days_of_inactivity:45, dry_run: true)
|
||||
end
|
||||
|
||||
lane :testflight_prune do
|
||||
clean_testflight_testers(days_of_inactivity: 45)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,21 +16,6 @@ or alternatively using `brew cask install fastlane`
|
||||
|
||||
# Available Actions
|
||||
## iOS
|
||||
### ios testflight_prune_dry
|
||||
```
|
||||
fastlane ios testflight_prune_dry
|
||||
```
|
||||
|
||||
### ios testflight_prune
|
||||
```
|
||||
fastlane ios testflight_prune
|
||||
```
|
||||
|
||||
### ios update_version
|
||||
```
|
||||
fastlane ios update_version
|
||||
```
|
||||
|
||||
### ios beta
|
||||
```
|
||||
fastlane ios beta
|
||||
@@ -46,6 +31,21 @@ Compile the project
|
||||
fastlane ios provision
|
||||
```
|
||||
Install provisioning profiles using match
|
||||
### ios update_version
|
||||
```
|
||||
fastlane ios update_version
|
||||
```
|
||||
|
||||
### ios testflight_prune_dry
|
||||
```
|
||||
fastlane ios testflight_prune_dry
|
||||
```
|
||||
|
||||
### ios testflight_prune
|
||||
```
|
||||
fastlane ios testflight_prune
|
||||
```
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
@@ -49,9 +49,7 @@
|
||||
<None Include="$(MSBuildThisFileDirectory)\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<AndroidNativeLibrary Include="$(MSBuildThisFileDirectory)\osu.Android\lib\**\*.so">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</AndroidNativeLibrary>
|
||||
<AndroidNativeLibrary Include="$(OutputPath)\**\*.so" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@@ -62,7 +60,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.730.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.830.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.830.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -113,13 +113,14 @@ namespace osu.Desktop.Overlays
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, ChangelogOverlay changelog)
|
||||
private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay)
|
||||
{
|
||||
Icon = FontAwesome.Solid.CheckSquare;
|
||||
IconBackgound.Colour = colours.BlueDark;
|
||||
|
||||
Activated = delegate
|
||||
{
|
||||
notificationOverlay.Hide();
|
||||
changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="lazer.ico" />
|
||||
|
||||
@@ -13,14 +13,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="Info.plist" />
|
||||
<None Include="Entitlements.plist" />
|
||||
<None Include="..\osu.iOS\libbass.a">
|
||||
<Link>libbass.a</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\osu.iOS\libbass_fx.a">
|
||||
<Link>libbass_fx.a</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<LinkDescription Include="..\osu.iOS\Linker.xml">
|
||||
<Link>Linker.xml</Link>
|
||||
</LinkDescription>
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -22,10 +23,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[TestCase("spinner")]
|
||||
[TestCase("spinner-and-circles")]
|
||||
[TestCase("slider")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
[TestCase("hardrock-stream", new[] { typeof(CatchModHardRock) })]
|
||||
[TestCase("hardrock-repeat-slider", new[] { typeof(CatchModHardRock) })]
|
||||
[TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })]
|
||||
public new void Test(string name, params Type[] mods) => base.Test(name, mods);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
@@ -92,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
return null;
|
||||
}
|
||||
|
||||
public SampleChannel GetSample(string sampleName) =>
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public Texture GetTexture(string componentName) =>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Catch.MathUtils;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
@@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
base.PostProcess();
|
||||
|
||||
applyPositionOffsets();
|
||||
ApplyPositionOffsets(Beatmap);
|
||||
|
||||
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
|
||||
|
||||
@@ -40,19 +41,29 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPositionOffsets()
|
||||
public static void ApplyPositionOffsets(IBeatmap beatmap, params Mod[] mods)
|
||||
{
|
||||
var rng = new FastRandom(RNG_SEED);
|
||||
// todo: HardRock displacement should be applied here
|
||||
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
bool shouldApplyHardRockOffset = mods.Any(m => m is ModHardRock);
|
||||
float? lastPosition = null;
|
||||
double lastStartTime = 0;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects.OfType<CatchHitObject>())
|
||||
{
|
||||
obj.XOffset = 0;
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case Fruit fruit:
|
||||
if (shouldApplyHardRockOffset)
|
||||
applyHardRockOffset(fruit, ref lastPosition, ref lastStartTime, rng);
|
||||
break;
|
||||
|
||||
case BananaShower bananaShower:
|
||||
foreach (var banana in bananaShower.NestedHitObjects.OfType<Banana>())
|
||||
{
|
||||
banana.X = (float)rng.NextDouble();
|
||||
banana.XOffset = (float)rng.NextDouble();
|
||||
rng.Next(); // osu!stable retrieved a random banana type
|
||||
rng.Next(); // osu!stable retrieved a random banana rotation
|
||||
rng.Next(); // osu!stable retrieved a random banana colour
|
||||
@@ -63,12 +74,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
case JuiceStream juiceStream:
|
||||
foreach (var nested in juiceStream.NestedHitObjects)
|
||||
{
|
||||
var hitObject = (CatchHitObject)nested;
|
||||
if (hitObject is TinyDroplet)
|
||||
hitObject.X += rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH;
|
||||
else if (hitObject is Droplet)
|
||||
var catchObject = (CatchHitObject)nested;
|
||||
catchObject.XOffset = 0;
|
||||
|
||||
if (catchObject is TinyDroplet)
|
||||
catchObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -catchObject.X, 1 - catchObject.X);
|
||||
else if (catchObject is Droplet)
|
||||
rng.Next(); // osu!stable retrieved a random droplet rotation
|
||||
hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -76,6 +88,105 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
lastPosition = stream.EndX;
|
||||
lastStartTime = stream.EndTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hitObject is Fruit))
|
||||
return;
|
||||
|
||||
float offsetPosition = hitObject.X;
|
||||
double startTime = hitObject.StartTime;
|
||||
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = offsetPosition;
|
||||
lastStartTime = startTime;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float positionDiff = offsetPosition - lastPosition.Value;
|
||||
double timeDiff = startTime - lastStartTime;
|
||||
|
||||
if (timeDiff > 1000)
|
||||
{
|
||||
lastPosition = offsetPosition;
|
||||
lastStartTime = startTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (positionDiff == 0)
|
||||
{
|
||||
applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng);
|
||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
|
||||
applyOffset(ref offsetPosition, positionDiff);
|
||||
|
||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
||||
|
||||
lastPosition = offsetPosition;
|
||||
lastStartTime = startTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="maxOffset">The maximum offset, cannot exceed 20px.</param>
|
||||
/// <param name="rng">The random number generator.</param>
|
||||
private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng)
|
||||
{
|
||||
bool right = rng.NextBool();
|
||||
float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
if (right)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + rand <= 1)
|
||||
position += rand;
|
||||
else
|
||||
position -= rand;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position - rand >= 0)
|
||||
position -= rand;
|
||||
else
|
||||
position += rand;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="amount">The amount to offset by.</param>
|
||||
private static void applyOffset(ref float position, float amount)
|
||||
{
|
||||
if (amount > 0)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + amount < 1)
|
||||
position += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position + amount > 0)
|
||||
position += amount;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
{
|
||||
List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
|
||||
|
||||
@@ -11,7 +11,6 @@ using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
public class CatchRuleset : Ruleset
|
||||
{
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
|
||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
||||
|
||||
@@ -108,7 +107,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
case ModType.Fun:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ModWindUp<CatchHitObject>(), new ModWindDown<CatchHitObject>())
|
||||
new MultiMod(new ModWindUp(), new ModWindDown())
|
||||
};
|
||||
|
||||
default:
|
||||
|
||||
@@ -61,6 +61,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils
|
||||
/// <returns>The random value.</returns>
|
||||
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random integer value within the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||
/// </summary>
|
||||
/// <param name="lowerBound">The lower bound of the range.</param>
|
||||
/// <param name="upperBound">The upper bound of the range.</param>
|
||||
/// <returns>The random value.</returns>
|
||||
public int Next(double lowerBound, double upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double value within the range [0, 1).
|
||||
/// </summary>
|
||||
|
||||
@@ -1,121 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModHardRock : ModHardRock, IApplicableToHitObject
|
||||
public class CatchModHardRock : ModHardRock, IApplicableToBeatmap
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
public override bool Ranked => true;
|
||||
|
||||
private float? lastPosition;
|
||||
private double lastStartTime;
|
||||
|
||||
public void ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
lastPosition = stream.EndX;
|
||||
lastStartTime = stream.EndTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hitObject is Fruit))
|
||||
return;
|
||||
|
||||
var catchObject = (CatchHitObject)hitObject;
|
||||
|
||||
float position = catchObject.X;
|
||||
double startTime = hitObject.StartTime;
|
||||
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = position;
|
||||
lastStartTime = startTime;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float positionDiff = position - lastPosition.Value;
|
||||
double timeDiff = startTime - lastStartTime;
|
||||
|
||||
if (timeDiff > 1000)
|
||||
{
|
||||
lastPosition = position;
|
||||
lastStartTime = startTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (positionDiff == 0)
|
||||
{
|
||||
applyRandomOffset(ref position, timeDiff / 4d);
|
||||
catchObject.X = position;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
|
||||
applyOffset(ref position, positionDiff);
|
||||
|
||||
catchObject.X = position;
|
||||
|
||||
lastPosition = position;
|
||||
lastStartTime = startTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="maxOffset">The maximum offset, cannot exceed 20px.</param>
|
||||
private void applyRandomOffset(ref float position, double maxOffset)
|
||||
{
|
||||
bool right = RNG.NextBool();
|
||||
float rand = Math.Min(20, (float)RNG.NextDouble(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
if (right)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + rand <= 1)
|
||||
position += rand;
|
||||
else
|
||||
position -= rand;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position - rand >= 0)
|
||||
position -= rand;
|
||||
else
|
||||
position += rand;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="amount">The amount to offset by.</param>
|
||||
private void applyOffset(ref float position, float amount)
|
||||
{
|
||||
if (amount > 0)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + amount < 1)
|
||||
position += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position + amount > 0)
|
||||
position += amount;
|
||||
}
|
||||
}
|
||||
public void ApplyToBeatmap(IBeatmap beatmap) => CatchBeatmapProcessor.ApplyPositionOffsets(beatmap, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
@@ -12,7 +13,18 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public const double OBJECT_RADIUS = 44;
|
||||
|
||||
public float X { get; set; }
|
||||
private float x;
|
||||
|
||||
public float X
|
||||
{
|
||||
get => x + XOffset;
|
||||
set => x = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A random offset applied to <see cref="X"/>, set by the <see cref="CatchBeatmapProcessor"/>.
|
||||
/// </summary>
|
||||
internal float XOffset { get; set; }
|
||||
|
||||
public double TimePreempt = 1000;
|
||||
|
||||
|
||||
@@ -58,14 +58,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
|
||||
}
|
||||
|
||||
protected override bool UseTransformStateManagement => false;
|
||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
protected override void UpdateInitialTransforms() => this.FadeInFromZero(200);
|
||||
|
||||
protected override void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
// TODO: update to use new state management.
|
||||
using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt))
|
||||
this.FadeIn(200);
|
||||
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
|
||||
using (BeginAbsoluteSequence(endTime, true))
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AccentColour = Color4.Red,
|
||||
Blending = BlendingMode.Additive,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Alpha = 0.5f,
|
||||
Scale = new Vector2(1.333f)
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Blending = BlendingParameters.Additive;
|
||||
Colour = Color4.White.Opacity(0.9f);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"Position": 216.539276
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"Position": 256.5667
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"Position": 296.594116
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"Position": 336.621521
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"Position": 376.99762
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"Position": 337.318878
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"Position": 297.291443
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"Position": 257.264038
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"Position": 217.2366
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"Position": 216.818192
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"Position": 256.8456
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"Position": 296.873047
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"Position": 336.900452
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"Position": 376.99762
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"Position": 337.039948
|
||||
},
|
||||
{
|
||||
"StartTime": 1762,
|
||||
"Position": 297.0125
|
||||
},
|
||||
{
|
||||
"StartTime": 1844,
|
||||
"Position": 256.9851
|
||||
},
|
||||
{
|
||||
"StartTime": 1926,
|
||||
"Position": 216.957672
|
||||
},
|
||||
{
|
||||
"StartTime": 2008,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 2090,
|
||||
"Position": 217.097137
|
||||
},
|
||||
{
|
||||
"StartTime": 2172,
|
||||
"Position": 257.124573
|
||||
},
|
||||
{
|
||||
"StartTime": 2254,
|
||||
"Position": 297.152
|
||||
},
|
||||
{
|
||||
"StartTime": 2336,
|
||||
"Position": 337.179443
|
||||
},
|
||||
{
|
||||
"StartTime": 2418,
|
||||
"Position": 376.99762
|
||||
},
|
||||
{
|
||||
"StartTime": 2500,
|
||||
"Position": 336.760956
|
||||
},
|
||||
{
|
||||
"StartTime": 2582,
|
||||
"Position": 296.733643
|
||||
},
|
||||
{
|
||||
"StartTime": 2664,
|
||||
"Position": 256.7062
|
||||
},
|
||||
{
|
||||
"StartTime": 2746,
|
||||
"Position": 216.678772
|
||||
},
|
||||
{
|
||||
"StartTime": 2828,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 2909,
|
||||
"Position": 216.887909
|
||||
},
|
||||
{
|
||||
"StartTime": 2991,
|
||||
"Position": 256.915344
|
||||
},
|
||||
{
|
||||
"StartTime": 3073,
|
||||
"Position": 296.942749
|
||||
},
|
||||
{
|
||||
"StartTime": 3155,
|
||||
"Position": 336.970184
|
||||
},
|
||||
{
|
||||
"StartTime": 3237,
|
||||
"Position": 376.99762
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
177,191,369,6,0,L|382:192,7,200
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"Position": 65
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"Position": 482
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"Position": 164
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"Position": 315
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"Position": 145
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"Position": 159
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"Position": 310
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"Position": 441
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"Position": 428
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"Position": 243
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"Position": 422
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"Position": 481
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"Position": 104
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"Position": 473
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"Position": 135
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"Position": 360
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"Position": 123
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
256,192,369,12,0,1680,0:0:0:0:
|
||||
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"Position": 258
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"Objects": [{
|
||||
"StartTime": 450,
|
||||
"Position": 254
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"Objects": [{
|
||||
"StartTime": 532,
|
||||
"Position": 241
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"Objects": [{
|
||||
"StartTime": 614,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"Objects": [{
|
||||
"StartTime": 696,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"Objects": [{
|
||||
"StartTime": 778,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"Objects": [{
|
||||
"StartTime": 860,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"Objects": [{
|
||||
"StartTime": 942,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"Objects": [{
|
||||
"StartTime": 1024,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"Objects": [{
|
||||
"StartTime": 1106,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"Objects": [{
|
||||
"StartTime": 1188,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"Objects": [{
|
||||
"StartTime": 1270,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"Objects": [{
|
||||
"StartTime": 1352,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"Objects": [{
|
||||
"StartTime": 1434,
|
||||
"Position": 258
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"Objects": [{
|
||||
"StartTime": 1516,
|
||||
"Position": 253
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"Objects": [{
|
||||
"StartTime": 1598,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"Objects": [{
|
||||
"StartTime": 1680,
|
||||
"Position": 260
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1762,
|
||||
"Objects": [{
|
||||
"StartTime": 1762,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1844,
|
||||
"Objects": [{
|
||||
"StartTime": 1844,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1926,
|
||||
"Objects": [{
|
||||
"StartTime": 1926,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2008,
|
||||
"Objects": [{
|
||||
"StartTime": 2008,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2090,
|
||||
"Objects": [{
|
||||
"StartTime": 2090,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2172,
|
||||
"Objects": [{
|
||||
"StartTime": 2172,
|
||||
"Position": 243
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2254,
|
||||
"Objects": [{
|
||||
"StartTime": 2254,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2336,
|
||||
"Objects": [{
|
||||
"StartTime": 2336,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2418,
|
||||
"Objects": [{
|
||||
"StartTime": 2418,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2500,
|
||||
"Objects": [{
|
||||
"StartTime": 2500,
|
||||
"Position": 258
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2582,
|
||||
"Objects": [{
|
||||
"StartTime": 2582,
|
||||
"Position": 256
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2664,
|
||||
"Objects": [{
|
||||
"StartTime": 2664,
|
||||
"Position": 242
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2746,
|
||||
"Objects": [{
|
||||
"StartTime": 2746,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2828,
|
||||
"Objects": [{
|
||||
"StartTime": 2828,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2909,
|
||||
"Objects": [{
|
||||
"StartTime": 2909,
|
||||
"Position": 271
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2991,
|
||||
"Objects": [{
|
||||
"StartTime": 2991,
|
||||
"Position": 254
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
258,189,369,1,0,0:0:0:0:
|
||||
258,189,450,1,0,0:0:0:0:
|
||||
258,189,532,1,0,0:0:0:0:
|
||||
258,189,614,1,0,0:0:0:0:
|
||||
258,189,696,1,0,0:0:0:0:
|
||||
258,189,778,1,0,0:0:0:0:
|
||||
258,189,860,1,0,0:0:0:0:
|
||||
258,189,942,1,0,0:0:0:0:
|
||||
258,189,1024,1,0,0:0:0:0:
|
||||
258,189,1106,1,0,0:0:0:0:
|
||||
258,189,1188,1,0,0:0:0:0:
|
||||
258,189,1270,1,0,0:0:0:0:
|
||||
258,189,1352,1,0,0:0:0:0:
|
||||
258,189,1434,1,0,0:0:0:0:
|
||||
258,189,1516,1,0,0:0:0:0:
|
||||
258,189,1598,1,0,0:0:0:0:
|
||||
258,189,1680,1,0,0:0:0:0:
|
||||
258,189,1762,1,0,0:0:0:0:
|
||||
258,189,1844,1,0,0:0:0:0:
|
||||
258,189,1926,1,0,0:0:0:0:
|
||||
258,189,2008,1,0,0:0:0:0:
|
||||
258,189,2090,1,0,0:0:0:0:
|
||||
258,189,2172,1,0,0:0:0:0:
|
||||
258,189,2254,1,0,0:0:0:0:
|
||||
258,189,2336,1,0,0:0:0:0:
|
||||
258,189,2418,1,0,0:0:0:0:
|
||||
258,189,2500,1,0,0:0:0:0:
|
||||
258,189,2582,1,0,0:0:0:0:
|
||||
258,189,2664,1,0,0:0:0:0:
|
||||
258,189,2746,1,0,0:0:0:0:
|
||||
258,189,2828,1,0,0:0:0:0:
|
||||
258,189,2909,1,0,0:0:0:0:
|
||||
258,189,2991,1,0,0:0:0:0:
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
if (lastPlateableFruit == null)
|
||||
return;
|
||||
|
||||
// this is required to make this run after the last caught fruit runs UpdateState at least once.
|
||||
// this is required to make this run after the last caught fruit runs updateState() at least once.
|
||||
// TODO: find a better alternative
|
||||
if (lastPlateableFruit.IsLoaded)
|
||||
action();
|
||||
@@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
additive.Scale = Scale;
|
||||
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
||||
additive.RelativePositionAxes = RelativePositionAxes;
|
||||
additive.Blending = BlendingMode.Additive;
|
||||
additive.Blending = BlendingParameters.Additive;
|
||||
|
||||
AdditiveTarget.Add(additive);
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
protected override bool UserScrollSpeedAdjustment => false;
|
||||
|
||||
public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
public DrawableCatchRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Down;
|
||||
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||
}
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||
|
||||
@@ -13,14 +13,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="Info.plist" />
|
||||
<None Include="Entitlements.plist" />
|
||||
<None Include="..\osu.iOS\libbass.a">
|
||||
<Link>libbass.a</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\osu.iOS\libbass_fx.a">
|
||||
<Link>libbass_fx.a</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<LinkDescription Include="..\osu.iOS\Linker.xml">
|
||||
<Link>Linker.xml</Link>
|
||||
</LinkDescription>
|
||||
|
||||
@@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
[TestCase("basic")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
@@ -35,11 +32,37 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
}
|
||||
|
||||
protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter);
|
||||
private readonly Dictionary<HitObject, RngSnapshot> rngSnapshots = new Dictionary<HitObject, RngSnapshot>();
|
||||
|
||||
protected override void OnConversionGenerated(HitObject original, IEnumerable<HitObject> result, IBeatmapConverter beatmapConverter)
|
||||
{
|
||||
base.OnConversionGenerated(original, result, beatmapConverter);
|
||||
|
||||
rngSnapshots[original] = new RngSnapshot(beatmapConverter);
|
||||
}
|
||||
|
||||
protected override ManiaConvertMapping CreateConvertMapping(HitObject source) => new ManiaConvertMapping(rngSnapshots[source]);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
}
|
||||
|
||||
public class RngSnapshot
|
||||
{
|
||||
public readonly uint RandomW;
|
||||
public readonly uint RandomX;
|
||||
public readonly uint RandomY;
|
||||
public readonly uint RandomZ;
|
||||
|
||||
public RngSnapshot(IBeatmapConverter converter)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
}
|
||||
}
|
||||
|
||||
public class ManiaConvertMapping : ConvertMapping<ConvertValue>, IEquatable<ManiaConvertMapping>
|
||||
{
|
||||
public uint RandomW;
|
||||
@@ -51,13 +74,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaConvertMapping(IBeatmapConverter converter)
|
||||
public ManiaConvertMapping(RngSnapshot snapshot)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
RandomW = snapshot.RandomW;
|
||||
RandomX = snapshot.RandomX;
|
||||
RandomY = snapshot.RandomY;
|
||||
RandomZ = snapshot.RandomZ;
|
||||
}
|
||||
|
||||
public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
/// <summary>
|
||||
/// Keep the same as last row.
|
||||
/// </summary>
|
||||
ForceStack = 1 << 0,
|
||||
ForceStack = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Keep different from last row.
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
||||
|
||||
public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
public DrawableManiaEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
||||
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
|
||||
{
|
||||
protected new DrawableManiaEditRuleset DrawableRuleset { get; private set; }
|
||||
private DrawableManiaEditRuleset drawableRuleset;
|
||||
|
||||
public ManiaHitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
@@ -33,23 +33,23 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
/// </summary>
|
||||
/// <param name="screenSpacePosition">The screen-space position.</param>
|
||||
/// <returns>The column which intersects with <paramref name="screenSpacePosition"/>.</returns>
|
||||
public Column ColumnAt(Vector2 screenSpacePosition) => DrawableRuleset.GetColumnByPosition(screenSpacePosition);
|
||||
public Column ColumnAt(Vector2 screenSpacePosition) => drawableRuleset.GetColumnByPosition(screenSpacePosition);
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns;
|
||||
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
|
||||
|
||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
{
|
||||
DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||
|
||||
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
|
||||
dependencies.CacheAs(DrawableRuleset.ScrollingInfo);
|
||||
dependencies.CacheAs(drawableRuleset.ScrollingInfo);
|
||||
|
||||
return DrawableRuleset;
|
||||
return drawableRuleset;
|
||||
}
|
||||
|
||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||
|
||||
@@ -13,7 +13,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
public class ManiaRuleset : Ruleset
|
||||
{
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
@@ -154,7 +153,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
case ModType.Fun:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ModWindUp<ManiaHitObject>(), new ModWindDown<ManiaHitObject>())
|
||||
new MultiMod(new ModWindUp(), new ModWindDown())
|
||||
};
|
||||
|
||||
default:
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModMirror : Mod, IApplicableToBeatmap<ManiaHitObject>
|
||||
public class ManiaModMirror : Mod, IApplicableToBeatmap
|
||||
{
|
||||
public override string Name => "Mirror";
|
||||
public override string Acronym => "MR";
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModRandom : Mod, IApplicableToBeatmap<ManiaHitObject>
|
||||
public class ManiaModRandom : Mod, IApplicableToBeatmap
|
||||
{
|
||||
public override string Name => "Random";
|
||||
public override string Acronym => "RD";
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override string Description => @"Shuffle around the keys!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Alpha = 0.2f;
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
protected override void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
@@ -104,6 +105,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
||||
}
|
||||
|
||||
protected override void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
using (BeginDelayedSequence(HitObject.Duration, true))
|
||||
base.UpdateStateTransforms(state);
|
||||
}
|
||||
|
||||
protected void BeginHold()
|
||||
{
|
||||
holdStartTime = Time.Current;
|
||||
|
||||
@@ -45,24 +45,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||
where TObject : ManiaHitObject
|
||||
{
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
protected DrawableManiaHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
protected override void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
|
||||
protected override bool UseTransformStateManagement => false;
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
// TODO: update to use new state management.
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Miss:
|
||||
@@ -75,4 +60,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||
where TObject : ManiaHitObject
|
||||
{
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
protected DrawableManiaHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
|
||||
public BodyPiece()
|
||||
{
|
||||
Blending = BlendingMode.Additive;
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
Background = new Box { RelativeSizeAxes = Axes.Both },
|
||||
Foreground = new BufferedContainer
|
||||
{
|
||||
Blending = BlendingMode.Additive,
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Children = new Drawable[]
|
||||
@@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
private Cached subtractionCache = new Cached();
|
||||
private readonly Cached subtractionCache = new Cached();
|
||||
|
||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
Name = "Top",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.5f,
|
||||
Blending = BlendingMode.Additive,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha))
|
||||
},
|
||||
new Box
|
||||
@@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.5f,
|
||||
Blending = BlendingMode.Additive,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
@@ -77,13 +78,37 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
|
||||
private IEnumerable<IActionPoint> generateActionPoints()
|
||||
{
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
||||
{
|
||||
yield return new HitPoint { Time = obj.StartTime, Column = obj.Column };
|
||||
yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column };
|
||||
var currentObject = Beatmap.HitObjects[i];
|
||||
var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button
|
||||
|
||||
double endTime = (currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime;
|
||||
|
||||
bool canDelayKeyUp = nextObjectInColumn == null ||
|
||||
nextObjectInColumn.StartTime > endTime + RELEASE_DELAY;
|
||||
|
||||
double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9;
|
||||
|
||||
yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column };
|
||||
|
||||
yield return new ReleasePoint { Time = endTime + calculatedDelay, Column = currentObject.Column };
|
||||
}
|
||||
}
|
||||
|
||||
protected override HitObject GetNextObject(int currentIndex)
|
||||
{
|
||||
int desiredColumn = Beatmap.HitObjects[currentIndex].Column;
|
||||
|
||||
for (int i = currentIndex + 1; i < Beatmap.HitObjects.Count; i++)
|
||||
{
|
||||
if (Beatmap.HitObjects[i].Column == desiredColumn)
|
||||
return Beatmap.HitObjects[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private interface IActionPoint
|
||||
{
|
||||
double Time { get; set; }
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
Name = "Background Gradient Overlay",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.5f,
|
||||
Blending = BlendingMode.Additive,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Alpha = 0
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,11 +36,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public IEnumerable<BarLine> BarLines;
|
||||
|
||||
protected override bool RelativeScaleBeatLengths => true;
|
||||
|
||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
|
||||
public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
// Generate the bar lines
|
||||
|
||||
@@ -13,14 +13,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="Info.plist" />
|
||||
<None Include="Entitlements.plist" />
|
||||
<None Include="..\osu.iOS\libbass.a">
|
||||
<Link>libbass.a</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\osu.iOS\libbass_fx.a">
|
||||
<Link>libbass_fx.a</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<LinkDescription Include="..\osu.iOS\Linker.xml">
|
||||
<Link>Linker.xml</Link>
|
||||
</LinkDescription>
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
@@ -21,10 +20,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase("basic")]
|
||||
[TestCase("colinear-perfect-curve")]
|
||||
[TestCase("slider-ticks")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
[TestCase("repeat-slider")]
|
||||
[TestCase("uneven-repeat-slider")]
|
||||
[TestCase("old-stacking")]
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
@@ -32,22 +31,22 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
case Slider slider:
|
||||
foreach (var nested in slider.NestedHitObjects)
|
||||
yield return createConvertValue(nested);
|
||||
yield return createConvertValue((OsuHitObject)nested);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
yield return createConvertValue(hitObject);
|
||||
yield return createConvertValue((OsuHitObject)hitObject);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ConvertValue createConvertValue(HitObject obj) => new ConvertValue
|
||||
ConvertValue createConvertValue(OsuHitObject obj) => new ConvertValue
|
||||
{
|
||||
StartTime = obj.StartTime,
|
||||
EndTime = (obj as IHasEndTime)?.EndTime ?? obj.StartTime,
|
||||
X = (obj as IHasPosition)?.X ?? OsuPlayfield.BASE_SIZE.X / 2,
|
||||
Y = (obj as IHasPosition)?.Y ?? OsuPlayfield.BASE_SIZE.Y / 2,
|
||||
X = obj.StackedPosition.X,
|
||||
Y = obj.StackedPosition.Y
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
@@ -2,12 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
@@ -28,38 +28,54 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
var skins = new SkinManager(LocalStorage, ContextFactory, null, audio);
|
||||
var dllStore = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll");
|
||||
|
||||
metricsSkin = getSkinFromResources(skins, "metrics_skin");
|
||||
defaultSkin = getSkinFromResources(skins, "default_skin");
|
||||
specialSkin = getSkinFromResources(skins, "special_skin");
|
||||
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||
defaultSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/default_skin"), audio, false);
|
||||
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
||||
}
|
||||
|
||||
public void SetContents(Func<Drawable> creationFunction)
|
||||
{
|
||||
Cell(0).Child = new LocalSkinOverrideContainer(null) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(1).Child = new LocalSkinOverrideContainer(metricsSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(2).Child = new LocalSkinOverrideContainer(defaultSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(0).Child = createProvider(null, creationFunction);
|
||||
Cell(1).Child = createProvider(metricsSkin, creationFunction);
|
||||
Cell(2).Child = createProvider(defaultSkin, creationFunction);
|
||||
Cell(3).Child = createProvider(specialSkin, creationFunction);
|
||||
}
|
||||
|
||||
private static Skin getSkinFromResources(SkinManager skins, string name)
|
||||
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
|
||||
{
|
||||
using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll"))
|
||||
var mainProvider = new SkinProvidingContainer(skin);
|
||||
|
||||
return mainProvider
|
||||
.WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
|
||||
{
|
||||
Child = creationFunction()
|
||||
});
|
||||
}
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
private readonly bool extrapolateAnimations;
|
||||
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, bool extrapolateAnimations)
|
||||
: base(skin, storage, audioManager, "skin.ini")
|
||||
{
|
||||
var tempName = Path.GetTempFileName();
|
||||
this.extrapolateAnimations = extrapolateAnimations;
|
||||
}
|
||||
|
||||
File.Delete(tempName);
|
||||
Directory.CreateDirectory(tempName);
|
||||
public override Texture GetTexture(string componentName)
|
||||
{
|
||||
// extrapolate frames to test longer animations
|
||||
if (extrapolateAnimations)
|
||||
{
|
||||
var match = Regex.Match(componentName, "-([0-9]*)");
|
||||
|
||||
var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}"));
|
||||
if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60)
|
||||
return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}"));
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
using (var stream = storage.GetStream(file))
|
||||
using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file))))
|
||||
stream.CopyTo(newFile);
|
||||
|
||||
return skins.GetSkin(skins.Import(tempName).Result);
|
||||
return base.GetTexture(componentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneDrawableJudgement : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DrawableJudgement),
|
||||
typeof(DrawableOsuJudgement)
|
||||
};
|
||||
|
||||
public TestSceneDrawableJudgement()
|
||||
{
|
||||
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
|
||||
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
|
||||
new DrawableOsuJudgement(new JudgementResult(null) { Type = result }, null)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,23 @@ using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneGameplayCursor : OsuTestScene, IProvideCursor
|
||||
public class TestSceneGameplayCursor : SkinnableTestScene
|
||||
{
|
||||
private GameplayCursorContainer cursorContainer;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(CursorTrail) };
|
||||
|
||||
public CursorContainer Cursor => cursorContainer;
|
||||
|
||||
public bool ProvidingUserCursor => true;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(cursorContainer = new OsuCursorContainer { RelativeSizeAxes = Axes.Both });
|
||||
SetContents(() => new OsuCursorContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneSkinFallbacks : PlayerTestScene
|
||||
{
|
||||
private readonly TestSource testUserSkin;
|
||||
private readonly TestSource testBeatmapSkin;
|
||||
|
||||
public TestSceneSkinFallbacks()
|
||||
: base(new OsuRuleset())
|
||||
{
|
||||
testUserSkin = new TestSource("user");
|
||||
testBeatmapSkin = new TestSource("beatmap");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapSkinDefault()
|
||||
{
|
||||
AddStep("enable user provider", () => testUserSkin.Enabled = true);
|
||||
|
||||
AddStep("enable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, true));
|
||||
checkNextHitObject("beatmap");
|
||||
|
||||
AddStep("disable beatmap skin", () => LocalConfig.Set<bool>(OsuSetting.BeatmapSkins, false));
|
||||
checkNextHitObject("user");
|
||||
|
||||
AddStep("disable user provider", () => testUserSkin.Enabled = false);
|
||||
checkNextHitObject(null);
|
||||
}
|
||||
|
||||
private void checkNextHitObject(string skin) =>
|
||||
AddUntilStep($"check skin from {skin}", () =>
|
||||
{
|
||||
var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault();
|
||||
|
||||
if (firstObject == null)
|
||||
return false;
|
||||
|
||||
var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable;
|
||||
|
||||
if (skin == null && skinnable?.Drawable is Sprite)
|
||||
// check for default skin provider
|
||||
return true;
|
||||
|
||||
var text = skinnable?.Drawable as SpriteText;
|
||||
|
||||
return text?.Text == skin;
|
||||
});
|
||||
|
||||
[Resolved]
|
||||
private AudioManager audio { get; set; }
|
||||
|
||||
protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap) => new CustomSkinWorkingBeatmap(beatmap, Clock, audio, testBeatmapSkin);
|
||||
|
||||
public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
||||
{
|
||||
private readonly ISkinSource skin;
|
||||
|
||||
public CustomSkinWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin)
|
||||
: base(beatmap, frameBasedClock, audio)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
protected override ISkin GetSkin() => skin;
|
||||
}
|
||||
|
||||
public class SkinProvidingPlayer : TestPlayer
|
||||
{
|
||||
private readonly TestSource userSkin;
|
||||
|
||||
public SkinProvidingPlayer(TestSource userSkin)
|
||||
{
|
||||
this.userSkin = userSkin;
|
||||
}
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
dependencies.CacheAs<ISkinSource>(userSkin);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestSource : ISkinSource
|
||||
{
|
||||
private readonly string identifier;
|
||||
|
||||
public TestSource(string identifier)
|
||||
{
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(string componentName)
|
||||
{
|
||||
if (!enabled) return null;
|
||||
|
||||
return new SpriteText
|
||||
{
|
||||
Text = identifier,
|
||||
Font = OsuFont.Default.With(size: 30),
|
||||
};
|
||||
}
|
||||
|
||||
public Texture GetTexture(string componentName) => null;
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => null;
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
private bool enabled = true;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => enabled;
|
||||
set
|
||||
{
|
||||
if (value == enabled)
|
||||
return;
|
||||
|
||||
enabled = value;
|
||||
SourceChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -27,83 +26,96 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneSlider : OsuTestScene
|
||||
public class TestSceneSlider : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(Slider),
|
||||
typeof(SliderTick),
|
||||
typeof(SliderTailCircle),
|
||||
typeof(SliderBall),
|
||||
typeof(SliderBody),
|
||||
typeof(SliderTick),
|
||||
typeof(SnakingSliderBody),
|
||||
typeof(DrawableSlider),
|
||||
typeof(DrawableSliderTick),
|
||||
typeof(DrawableSliderTail),
|
||||
typeof(DrawableSliderHead),
|
||||
typeof(DrawableRepeatPoint),
|
||||
typeof(DrawableOsuHitObject)
|
||||
};
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
private Container content;
|
||||
|
||||
protected override Container<Drawable> Content
|
||||
{
|
||||
get
|
||||
{
|
||||
if (content == null)
|
||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
private int depthIndex;
|
||||
|
||||
public TestSceneSlider()
|
||||
{
|
||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
||||
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
||||
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
|
||||
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
|
||||
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
|
||||
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
|
||||
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
|
||||
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
|
||||
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
|
||||
|
||||
AddStep("Big Single", () => testSimpleBig());
|
||||
AddStep("Medium Single", () => testSimpleMedium());
|
||||
AddStep("Small Single", () => testSimpleSmall());
|
||||
AddStep("Big 1 Repeat", () => testSimpleBig(1));
|
||||
AddStep("Medium 1 Repeat", () => testSimpleMedium(1));
|
||||
AddStep("Small 1 Repeat", () => testSimpleSmall(1));
|
||||
AddStep("Big 2 Repeats", () => testSimpleBig(2));
|
||||
AddStep("Medium 2 Repeats", () => testSimpleMedium(2));
|
||||
AddStep("Small 2 Repeats", () => testSimpleSmall(2));
|
||||
AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps
|
||||
AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed()));
|
||||
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
|
||||
|
||||
AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps
|
||||
AddStep("Slow Short Slider", () => testShortSlowSpeed());
|
||||
AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2));
|
||||
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
|
||||
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
|
||||
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
|
||||
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
|
||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
|
||||
|
||||
AddStep("Fast Slider", () => testHighSpeed());
|
||||
AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1));
|
||||
AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2));
|
||||
AddStep("Fast Short Slider", () => testShortHighSpeed());
|
||||
AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6));
|
||||
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
|
||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
|
||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
|
||||
|
||||
AddStep("Perfect Curve", () => testPerfect());
|
||||
AddStep("Perfect Curve 1 Repeat", () => testPerfect(1));
|
||||
AddStep("Perfect Curve 2 Repeats", () => testPerfect(2));
|
||||
AddStep("Linear Slider", () => SetContents(() => testLinear()));
|
||||
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
|
||||
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
|
||||
|
||||
AddStep("Linear Slider", () => testLinear());
|
||||
AddStep("Linear Slider 1 Repeat", () => testLinear(1));
|
||||
AddStep("Linear Slider 2 Repeats", () => testLinear(2));
|
||||
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
|
||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
|
||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
|
||||
|
||||
AddStep("Bezier Slider", () => testBezier());
|
||||
AddStep("Bezier Slider 1 Repeat", () => testBezier(1));
|
||||
AddStep("Bezier Slider 2 Repeats", () => testBezier(2));
|
||||
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
|
||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
|
||||
|
||||
AddStep("Linear Overlapping", () => testLinearOverlapping());
|
||||
AddStep("Linear Overlapping 1 Repeat", () => testLinearOverlapping(1));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => testLinearOverlapping(2));
|
||||
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
|
||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
|
||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
|
||||
|
||||
AddStep("Catmull Slider", () => testCatmull());
|
||||
AddStep("Catmull Slider 1 Repeat", () => testCatmull(1));
|
||||
AddStep("Catmull Slider 2 Repeats", () => testCatmull(2));
|
||||
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
|
||||
|
||||
AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset());
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1));
|
||||
|
||||
AddStep("Distance Overflow", () => testDistanceOverflow());
|
||||
AddStep("Distance Overflow 1 Repeat", () => testDistanceOverflow(1));
|
||||
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
|
||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
|
||||
}
|
||||
|
||||
private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
|
||||
private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
||||
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
||||
|
||||
private void testDistanceOverflow(int repeats = 0)
|
||||
private Drawable testDistanceOverflow(int repeats = 0)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -120,22 +132,22 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
StackHeight = 10
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 2);
|
||||
return createDrawable(slider, 2, 2);
|
||||
}
|
||||
|
||||
private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
|
||||
private Drawable testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
|
||||
|
||||
private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
|
||||
private Drawable testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
|
||||
|
||||
private void testSlowSpeed() => createSlider(speedMultiplier: 0.5);
|
||||
private Drawable testSlowSpeed() => createSlider(speedMultiplier: 0.5);
|
||||
|
||||
private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5);
|
||||
private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5);
|
||||
|
||||
private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
|
||||
private Drawable testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
|
||||
|
||||
private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
|
||||
private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
|
||||
|
||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0)
|
||||
private Drawable createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -151,10 +163,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
StackHeight = stackHeight
|
||||
};
|
||||
|
||||
addSlider(slider, circleSize, speedMultiplier);
|
||||
return createDrawable(slider, circleSize, speedMultiplier);
|
||||
}
|
||||
|
||||
private void testPerfect(int repeats = 0)
|
||||
private Drawable testPerfect(int repeats = 0)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -170,12 +182,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
return createDrawable(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testLinear(int repeats = 0) => createLinear(repeats);
|
||||
private Drawable testLinear(int repeats = 0) => createLinear(repeats);
|
||||
|
||||
private void createLinear(int repeats)
|
||||
private Drawable createLinear(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -194,12 +206,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
return createDrawable(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testBezier(int repeats = 0) => createBezier(repeats);
|
||||
private Drawable testBezier(int repeats = 0) => createBezier(repeats);
|
||||
|
||||
private void createBezier(int repeats)
|
||||
private Drawable createBezier(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -217,12 +229,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
return createDrawable(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testLinearOverlapping(int repeats = 0) => createOverlapping(repeats);
|
||||
private Drawable testLinearOverlapping(int repeats = 0) => createOverlapping(repeats);
|
||||
|
||||
private void createOverlapping(int repeats)
|
||||
private Drawable createOverlapping(int repeats)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
@@ -241,12 +253,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
return createDrawable(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void testCatmull(int repeats = 0) => createCatmull(repeats);
|
||||
private Drawable testCatmull(int repeats = 0) => createCatmull(repeats);
|
||||
|
||||
private void createCatmull(int repeats = 0)
|
||||
private Drawable createCatmull(int repeats = 0)
|
||||
{
|
||||
var repeatSamples = new List<List<HitSampleInfo>>();
|
||||
for (int i = 0; i < repeats; i++)
|
||||
@@ -267,7 +279,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
NodeSamples = repeatSamples
|
||||
};
|
||||
|
||||
addSlider(slider, 3, 1);
|
||||
return createDrawable(slider, 3, 1);
|
||||
}
|
||||
|
||||
private List<List<HitSampleInfo>> createEmptySamples(int repeats)
|
||||
@@ -278,7 +290,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
return repeatSamples;
|
||||
}
|
||||
|
||||
private void addSlider(Slider slider, float circleSize, double speedMultiplier)
|
||||
private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier)
|
||||
{
|
||||
var cpi = new ControlPointInfo();
|
||||
cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
|
||||
@@ -296,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
drawable.OnNewResult += onNewResult;
|
||||
|
||||
Add(drawable);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private float judgementOffsetDirection = 1;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
@@ -208,17 +209,22 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime)
|
||||
break;
|
||||
|
||||
// The start position of the hitobject, or the position at the end of the path if the hitobject is a slider
|
||||
Vector2 position2 = currHitObject is Slider currSlider
|
||||
? currSlider.Position + currSlider.Path.PositionAt(1)
|
||||
: currHitObject.Position;
|
||||
|
||||
if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance)
|
||||
{
|
||||
currHitObject.StackHeight++;
|
||||
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
|
||||
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime;
|
||||
}
|
||||
else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance)
|
||||
else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, position2) < stack_distance)
|
||||
{
|
||||
//Case for sliders - bump notes down and right, rather than up and left.
|
||||
sliderStack++;
|
||||
beatmap.HitObjects[j].StackHeight -= sliderStack;
|
||||
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
|
||||
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[j].StartTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class DrawableOsuEditRuleset : DrawableOsuRuleset
|
||||
{
|
||||
public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
}
|
||||
|
||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
|
||||
|
||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModAutopilot : Mod
|
||||
public class OsuModAutopilot : Mod, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>
|
||||
{
|
||||
public override string Name => "Autopilot";
|
||||
public override string Acronym => "AP";
|
||||
@@ -17,5 +23,40 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
|
||||
|
||||
public bool AllowFail => false;
|
||||
|
||||
private OsuInputManager inputManager;
|
||||
|
||||
private List<OsuReplayFrame> replayFrames;
|
||||
|
||||
private int currentFrame;
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
if (currentFrame == replayFrames.Count - 1) return;
|
||||
|
||||
double time = playfield.Time.Current;
|
||||
|
||||
// Very naive implementation of autopilot based on proximity to replay frames.
|
||||
// TODO: this needs to be based on user interactions to better match stable (pausing until judgement is registered).
|
||||
if (Math.Abs(replayFrames[currentFrame + 1].Time - time) <= Math.Abs(replayFrames[currentFrame].Time - time))
|
||||
{
|
||||
currentFrame++;
|
||||
new MousePositionAbsoluteInput { Position = playfield.ToScreenSpace(replayFrames[currentFrame].Position) }.Apply(inputManager.CurrentState, inputManager);
|
||||
}
|
||||
|
||||
// TODO: Implement the functionality to automatically spin spinners
|
||||
}
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
// Grab the input manager to disable the user's cursor, and for future use
|
||||
inputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager;
|
||||
inputManager.AllowUserCursorMovement = false;
|
||||
|
||||
// Generate the replay frames the cursor should follow
|
||||
replayFrames = new OsuAutoGenerator(drawableRuleset.Beatmap).Generate().Frames.Cast<OsuReplayFrame>().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
Child = new Box
|
||||
{
|
||||
Size = new Vector2(width),
|
||||
Blending = BlendingMode.Additive,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0.5f,
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private readonly HitArea hitArea;
|
||||
|
||||
private readonly SkinnableDrawable mainContent;
|
||||
|
||||
public DrawableHitCircle(HitCircle h)
|
||||
: base(h)
|
||||
{
|
||||
@@ -56,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
return true;
|
||||
},
|
||||
},
|
||||
new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
|
||||
mainContent = new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
@@ -108,6 +110,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
mainContent.FadeInFromZero(HitObject.TimeFadeIn);
|
||||
|
||||
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
|
||||
ApproachCircle.ScaleTo(1f, HitObject.TimePreempt);
|
||||
ApproachCircle.Expire(true);
|
||||
|
||||