1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-16 04:52:35 +08:00

Compare commits

...

828 Commits

387 changed files with 8721 additions and 3152 deletions
View File
-2
View File
@@ -1,2 +0,0 @@
language: csharp
solution: osu.sln
+24 -24
View File
@@ -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)
+3 -2
View File
@@ -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.
+2 -2
View File
@@ -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>
+13 -16
View File
@@ -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
+15 -15
View File
@@ -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
```
----
+3 -5
View File
@@ -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>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -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;
};
+2 -2
View File
@@ -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>();
+2 -3
View File
@@ -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:
+2 -2
View File
@@ -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[]
+2 -3
View File
@@ -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
};
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

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();
}
}
}
}
}
+86 -74
View File
@@ -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[]
+42 -1
View File
@@ -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);

Some files were not shown because too many files have changed in this diff Show More