1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 07:29:55 +08:00

Compare commits

..

3 Commits

909 changed files with 10238 additions and 20295 deletions
-1
View File
@@ -1 +0,0 @@
custom: https://osu.ppy.sh/home/support
+11
View File
@@ -0,0 +1,11 @@
osu!lazer is currently still under heavy development!
Please ensure that you are making an issue for one of the following:
- A bug with currently implemented features (not features that don't exist)
- A feature you are considering adding, so we can collaborate on feedback and design.
- Discussions about technical design decisions
If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide.
Screenshots and log files are highly welcomed.
+7 -4
View File
@@ -1,11 +1,14 @@
---
name: Bug Report
about: Issues regarding encountered bugs.
about: For issues regarding encountered game bugs
---
**Describe the bug:**
<!-- After you fill in all information, delete all comments in the issue -->
**Describe your problem:** <!-- Provide any information you believe could be useful -->
**Screenshots or videos showing encountered issue:**
**osu!lazer version:**
**osu!lazer version:** <!-- Provide the version of your osu!lazer, you can find it at the bottom of the screen -->
**Logs:**
**Logs:** <!-- Attach your osu!lazer logs, you can find them under %appdata%\osu\logs in Windows, or under ~/.local/share/osu/ in Linux and macOS -->
+8 -5
View File
@@ -1,13 +1,16 @@
---
name: Crash Report
about: Issues regarding crashes or permanent freezes.
about: For issues regarding game crashes or permanent freezes
---
**Describe the crash:**
<!-- After you fill in all information, delete all comments in the issue -->
**Describe your problem:** <!-- Provide any information you believe could be useful -->
**Screenshots or videos showing encountered issue:**
**osu!lazer version:**
**osu!lazer version:** <!-- Provide the version of your osu!lazer, you can find it at the bottom of the screen -->
**Logs:**
**Logs:** <!-- Attach your osu!lazer logs, you can find them under %appdata%\osu\logs in Windows, or under ~/.local/share/osu/ in Linux and macOS -->
**Computer Specifications:**
**Computer Specifications:** <!-- Attach your computer specifications, you can find them by using System Information in Windows, System Monitor in Linux, or About This Mac in macOS -->
@@ -1,7 +1,10 @@
---
name: Feature Request
about: Features you would like to see in the game!
about: Let us know what you would like to see in the game!
---
**Describe the new feature:**
**Proposal designs of the feature:**
<!-- After you fill in all information, delete all comments in the issue -->
**Describe the feature:** <!-- Describe the feature you would like to see in the game -->
**Proposal designs of the feature:** <!-- Attach screenshots of how the feature should look like according to you -->
@@ -1,7 +1,10 @@
---
name: Missing for Live
about: Features which are available in osu!stable but not yet in osu!lazer.
about: Let us know the features you need which are available in osu-stable but not lazer
---
**Describe the missing feature:**
**Proposal designs of the feature:**
<!-- After you fill in all information, delete all comments in the issue -->
**Describe the feature:** <!-- Describe the missing game feature -->
**Designs:** <!-- Attach screenshots of how the feature is supposed to look like. For illustrative purpose only; final designs are usually re-imagined from scratch. -->
+6 -15
View File
@@ -10,8 +10,6 @@ This project is still heavily under development, but is in a state where users a
We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
@@ -22,24 +20,17 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
### Releases
![](https://puu.sh/DCmvA/f6a74f5fbb.png)
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled.
If you are not interested in developing the game, you can consume our [binary releases](https://github.com/ppy/osu/releases).
**Latest build:***
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) |
| ------------- | ------------- |
- **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).
- **Android** users can self-compile, and expect a public beta soon.
- Windows (x64) users should download and run `install.exe`.
- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`.
- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18).
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
### Downloading the source code
Clone the repository:
Clone the repository **including submodules**:
```shell
git clone https://github.com/ppy/osu
@@ -54,7 +45,7 @@ git pull
### Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided [below](#contributing).
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below.
> Visual Studio Code users must run the `Restore` task before any build attempt.
+1 -1
View File
@@ -1,6 +1,6 @@
clone_depth: 1
version: '{branch}-{build}'
image: Previous Visual Studio 2017
image: Visual Studio 2017
test: off
install:
- cmd: git submodule update --init --recursive --depth=5
+2 -4
View File
@@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.1.1"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
@@ -46,9 +46,7 @@ Task("InspectCode")
OutputFile = "inspectcodereport.xml",
});
int returnCode = StartProcess(nVikaToolPath, $@"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
if (returnCode != 0)
throw new Exception($"inspectcode failed with return code {returnCode}");
StartProcess(nVikaToolPath, @"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
});
Task("CodeFileSanity")
+1 -1
View File
@@ -35,7 +35,7 @@ platform :ios do
changelog.gsub!('$BUILD_ID', options[:build])
pilot(
wait_processing_interval: 1800,
wait_processing_interval: 900,
changelog: changelog,
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
)
+11 -14
View File
@@ -7,13 +7,13 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using osu.Desktop.Overlays;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform;
using osu.Game;
using osuTK.Input;
using Microsoft.Win32;
using osu.Desktop.Updater;
using osu.Framework;
using osu.Framework.Logging;
using osu.Framework.Platform.Windows;
using osu.Framework.Screens;
using osu.Game.Screens.Menu;
@@ -35,15 +35,12 @@ namespace osu.Desktop
{
try
{
if (Host is DesktopGameHost desktopHost)
return new StableStorage(desktopHost);
return new StableStorage();
}
catch (Exception e)
catch
{
Logger.Error(e, "Error while searching for stable install");
return null;
}
return null;
}
protected override void LoadComplete()
@@ -55,7 +52,7 @@ namespace osu.Desktop
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.Show();
v.State = Visibility.Visible;
});
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
@@ -73,11 +70,12 @@ namespace osu.Desktop
{
case Intro _:
case MainMenu _:
versionManager?.Show();
if (versionManager != null)
versionManager.State = Visibility.Visible;
break;
default:
versionManager?.Hide();
if (versionManager != null)
versionManager.State = Visibility.Hidden;
break;
}
}
@@ -85,7 +83,6 @@ namespace osu.Desktop
public override void SetHost(GameHost host)
{
base.SetHost(host);
if (host.Window is DesktopGameWindow desktopWindow)
{
desktopWindow.CursorState |= CursorState.Hidden;
@@ -142,8 +139,8 @@ namespace osu.Desktop
return null;
}
public StableStorage(DesktopGameHost host)
: base(string.Empty, host)
public StableStorage()
: base(string.Empty, null)
{
}
}
+14 -15
View File
@@ -1,11 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
using osu.Game;
using osu.Game.Configuration;
using osu.Game.Graphics;
@@ -23,13 +25,15 @@ namespace osu.Desktop.Overlays
private OsuConfigManager config;
private OsuGameBase game;
private NotificationOverlay notificationOverlay;
private GameHost host;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
this.host = host;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
@@ -91,38 +95,33 @@ namespace osu.Desktop.Overlays
var version = game.Version;
var lastVersion = config.Get<string>(OsuSetting.Version);
if (game.IsDeployedBuild && version != lastVersion)
{
config.Set(OsuSetting.Version, version);
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
notificationOverlay.Post(new UpdateCompleteNotification(version));
notificationOverlay.Post(new UpdateCompleteNotification(version, host.OpenUrlExternally));
}
}
private class UpdateCompleteNotification : SimpleNotification
{
private readonly string version;
public UpdateCompleteNotification(string version)
public UpdateCompleteNotification(string version, Action<string> openUrl = null)
{
this.version = version;
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
openUrl?.Invoke($"https://osu.ppy.sh/home/changelog/lazer/{version}");
return true;
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, ChangelogOverlay changelog)
private void load(OsuColour colours)
{
Icon = FontAwesome.Solid.CheckSquare;
IconBackgound.Colour = colours.BlueDark;
Activated = delegate
{
changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
return true;
};
}
}
-1
View File
@@ -31,7 +31,6 @@ namespace osu.Desktop
var importer = new ArchiveImportIPCChannel(host);
// Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd);
foreach (var file in args)
{
Console.WriteLine(@"Importing {0}", file);
+2 -3
View File
@@ -7,10 +7,10 @@ using Newtonsoft.Json;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.IO.Network;
using osu.Framework.Platform;
using osu.Game;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
@@ -54,7 +54,7 @@ namespace osu.Desktop.Updater
{
Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n"
+ "Click here to download the new version, which can be installed over the top of your existing installation",
Icon = FontAwesome.Solid.Upload,
Icon = FontAwesome.fa_upload,
Activated = () =>
{
host.OpenUrlExternally(getBestUrl(latest));
@@ -78,7 +78,6 @@ namespace osu.Desktop.Updater
case RuntimeInfo.Platform.Windows:
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe"));
break;
case RuntimeInfo.Platform.MacOsx:
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip"));
break;
+12 -7
View File
@@ -2,12 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Logging;
using osu.Game;
using osu.Game.Graphics;
@@ -157,7 +158,7 @@ namespace osu.Desktop.Updater
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.Solid.Upload,
Icon = FontAwesome.fa_upload,
Colour = Color4.White,
Size = new Vector2(20),
}
@@ -167,19 +168,23 @@ namespace osu.Desktop.Updater
private class SquirrelLogger : Splat.ILogger, IDisposable
{
private readonly string path;
private readonly object locker = new object();
public LogLevel Level { get; set; } = LogLevel.Info;
private Logger logger;
public SquirrelLogger()
{
var file = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "SquirrelSetupUpdater.log");
if (File.Exists(file)) File.Delete(file);
path = file;
}
public void Write(string message, LogLevel logLevel)
{
if (logLevel < Level)
return;
if (logger == null)
logger = Logger.GetLogger("updater");
logger.Add(message);
lock (locker) File.AppendAllText(path, message + "\r\n");
}
public void Dispose()
+3 -3
View File
@@ -26,9 +26,9 @@
</ItemGroup>
<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="ppy.squirrel.windows" Version="1.9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
@@ -36,13 +36,11 @@ namespace osu.Game.Rulesets.Catch.Tests
yield return new ConvertValue((CatchHitObject)nested);
break;
case BananaShower shower:
foreach (var nested in shower.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested);
break;
default:
yield return new ConvertValue((CatchHitObject)hitObject);
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
[TestCase(4.2058561036909863d, "diffcalc-test")]
[TestCase(4.2038001515546597d, "diffcalc-test")]
public void Test(double expected, string name)
=> base.Test(expected, name);
@@ -13,21 +13,21 @@ using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestSceneAutoJuiceStream : PlayerTestScene
public class TestCaseAutoJuiceStream : TestCasePlayer
{
public TestSceneAutoJuiceStream()
public TestCaseAutoJuiceStream()
: base(new CatchRuleset())
{
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
Ruleset = ruleset
Ruleset = ruleset.RulesetInfo
}
};
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(ruleset);
}
}
@@ -8,12 +8,11 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneBananaShower : PlayerTestScene
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@@ -21,22 +20,22 @@ namespace osu.Game.Rulesets.Catch.Tests
typeof(DrawableBananaShower),
typeof(CatchRuleset),
typeof(DrawableCatchRuleset),
typeof(CatchRulesetContainer),
};
public TestSceneBananaShower()
public TestCaseBananaShower()
: base(new CatchRuleset())
{
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset
Ruleset = ruleset.RulesetInfo
}
};
@@ -2,14 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneCatchPlayer : PlayerTestScene
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
{
public TestSceneCatchPlayer()
public TestCaseCatchPlayer()
: base(new CatchRuleset())
{
}
@@ -4,26 +4,25 @@
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneCatchStacker : PlayerTestScene
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
{
public TestSceneCatchStacker()
public TestCaseCatchStacker()
: base(new CatchRuleset())
{
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset
Ruleset = ruleset.RulesetInfo
}
};
@@ -13,7 +13,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneCatcherArea : OsuTestScene
public class TestCaseCatcherArea : OsuTestCase
{
private RulesetInfo catchRuleset;
private TestCatcherArea catcherArea;
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Tests
typeof(CatcherArea),
};
public TestSceneCatcherArea()
public TestCaseCatcherArea()
{
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
@@ -15,7 +15,7 @@ using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneFruitObjects : OsuTestScene
public class TestCaseFruitObjects : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests
typeof(Pulp),
};
public TestSceneFruitObjects()
public TestCaseFruitObjects()
{
Add(new GridContainer
{
@@ -0,0 +1,29 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseHyperDash : Game.Tests.Visual.TestCasePlayer
{
public TestCaseHyperDash()
: base(new CatchRuleset())
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
return beatmap;
}
}
}
@@ -1,48 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestSceneHyperDash : PlayerTestScene
{
public TestSceneHyperDash()
: base(new CatchRuleset())
{
}
[BackgroundDependencyLoader]
private void load()
{
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo =
{
Ruleset = ruleset,
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
}
};
// Should produce a hyper-dash
beatmap.HitObjects.Add(new Fruit { StartTime = 816, X = 308 / 512f, NewCombo = true });
beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, });
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 });
return beatmap;
}
}
}
@@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -3,8 +3,8 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
@@ -23,19 +23,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
Name = @"Fruit Count",
Content = fruits.ToString(),
Icon = FontAwesome.Regular.Circle
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Juice Stream Count",
Content = juiceStreams.ToString(),
Icon = FontAwesome.Regular.Circle
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Banana Shower Count",
Content = bananaShowers.ToString(),
Icon = FontAwesome.Regular.Circle
Icon = FontAwesome.fa_circle
}
};
}
@@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
int index = 0;
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
{
obj.IndexInBeatmap = index++;
@@ -59,7 +58,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
}
break;
case JuiceStream juiceStream:
foreach (var nested in juiceStream.NestedHitObjects)
{
@@ -102,10 +100,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
CatchHitObject nextObject = objectWithDroplets[i + 1];
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
double timeToNext = nextObject.StartTime - currentObject.StartTime;
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
if (distanceToHyper < 0)
{
currentObject.HyperDashTarget = nextObject;
+2 -16
View File
@@ -9,22 +9,19 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
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;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Scoring;
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 RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new CatchRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
@@ -87,7 +84,6 @@ namespace osu.Game.Rulesets.Catch
new CatchModNoFail(),
new MultiMod(new CatchModHalfTime(), new CatchModDaycore())
};
case ModType.DifficultyIncrease:
return new Mod[]
{
@@ -97,20 +93,12 @@ namespace osu.Game.Rulesets.Catch
new CatchModHidden(),
new CatchModFlashlight(),
};
case ModType.Automation:
return new Mod[]
{
new MultiMod(new CatchModAutoplay(), new ModCinema()),
new CatchModRelax(),
};
case ModType.Fun:
return new Mod[]
{
new MultiMod(new ModWindUp<CatchHitObject>(), new ModWindDown<CatchHitObject>())
};
default:
return new Mod[] { };
}
@@ -120,12 +108,10 @@ namespace osu.Game.Rulesets.Catch
public override string ShortName => "fruits";
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch };
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
public override int? LegacyID => 2;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
@@ -7,7 +7,6 @@ using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
using osu.Game.Rulesets.Catch.Difficulty.Skills;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Difficulty;
@@ -23,15 +22,22 @@ namespace osu.Game.Rulesets.Catch.Difficulty
protected override int SectionLength => 750;
private readonly float halfCatchWidth;
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
halfCatchWidth = catcher.CatchWidth * 0.5f;
// We're only using 80% of the catcher's width to simulate imperfect gameplay.
halfCatchWidth *= 0.8f;
}
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
return new CatchDifficultyAttributes { Mods = mods };
// this is the same as osu!, so there's potential to share the implementation... maybe
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
@@ -41,37 +47,40 @@ namespace osu.Game.Rulesets.Catch.Difficulty
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
Mods = mods,
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)),
Skills = skills
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet))
};
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
{
float halfCatchWidth;
using (var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty))
{
halfCatchWidth = catcher.CatchWidth * 0.5f;
halfCatchWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
}
CatchHitObject lastObject = null;
// In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream.
foreach (var hitObject in beatmap.HitObjects
.SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects : new[] { obj })
.Cast<CatchHitObject>()
.OrderBy(x => x.StartTime))
foreach (var hitObject in beatmap.HitObjects.OfType<CatchHitObject>())
{
// We want to only consider fruits that contribute to the combo.
if (hitObject is BananaShower || hitObject is TinyDroplet)
if (lastObject == null)
{
lastObject = hitObject;
continue;
}
if (lastObject != null)
yield return new CatchDifficultyHitObject(hitObject, lastObject, clockRate, halfCatchWidth);
switch (hitObject)
{
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
case Fruit fruit:
yield return new CatchDifficultyHitObject(fruit, lastObject, clockRate, halfCatchWidth);
lastObject = hitObject;
lastObject = hitObject;
break;
case JuiceStream _:
foreach (var nested in hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)))
{
yield return new CatchDifficultyHitObject(nested, lastObject, clockRate, halfCatchWidth);
lastObject = nested;
}
break;
}
}
}
@@ -79,13 +88,5 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
new Movement(),
};
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
{
new CatchModDoubleTime(),
new CatchModHalfTime(),
new CatchModHardRock(),
new CatchModEasy(),
};
}
}
@@ -1,104 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osuTK;
namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchPerformanceCalculator : PerformanceCalculator
{
protected new CatchDifficultyAttributes Attributes => (CatchDifficultyAttributes)base.Attributes;
private Mod[] mods;
private int fruitsHit;
private int ticksHit;
private int tinyTicksHit;
private int tinyTicksMissed;
private int misses;
public CatchPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score)
: base(ruleset, beatmap, score)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
mods = Score.Mods;
var legacyScore = Score as LegacyScoreInfo;
fruitsHit = legacyScore?.Count300 ?? Score.Statistics[HitResult.Perfect];
ticksHit = legacyScore?.Count100 ?? 0;
tinyTicksHit = legacyScore?.Count50 ?? 0;
tinyTicksMissed = legacyScore?.CountKatu ?? 0;
misses = Score.Statistics[HitResult.Miss];
// Don't count scores made with supposedly unranked mods
if (mods.Any(m => !m.Ranked))
return 0;
// We are heavily relying on aim in catch the beat
double value = Math.Pow(5.0f * Math.Max(1.0f, Attributes.StarRating / 0.0049f) - 4.0f, 2.0f) / 100000.0f;
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
int numTotalHits = totalComboHits();
// Longer maps are worth more
float lengthBonus =
0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) +
(numTotalHits > 3000 ? (float)Math.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f);
// Longer maps are worth more
value *= lengthBonus;
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
value *= Math.Pow(0.97f, misses);
// Combo scaling
float beatmapMaxCombo = Attributes.MaxCombo;
if (beatmapMaxCombo > 0)
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
float approachRate = (float)Attributes.ApproachRate;
float approachRateFactor = 1.0f;
if (approachRate > 9.0f)
approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9
else if (approachRate < 8.0f)
approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8
value *= approachRateFactor;
if (mods.Any(m => m is ModHidden))
// Hiddens gives nothing on max approach rate, and more the lower it is
value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10
if (mods.Any(m => m is ModFlashlight))
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
value *= 1.35f * lengthBonus;
// Scale the aim value with accuracy _slightly_
value *= Math.Pow(accuracy(), 5.5f);
// Custom multipliers for NoFail. SpunOut is not applicable.
if (mods.Any(m => m is ModNoFail))
value *= 0.90f;
return value;
}
private float accuracy() => totalHits() == 0 ? 0 : MathHelper.Clamp((float)totalSuccessfulHits() / totalHits(), 0f, 1f);
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
private int totalComboHits() => misses + ticksHit + fruitsHit;
}
}
@@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
default:
return 0;
case HitResult.Perfect:
return 1100;
}
@@ -28,9 +27,8 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
default:
return 0;
case HitResult.Perfect:
return 0.008;
return 8;
}
}
@@ -13,7 +13,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
default:
return 0;
case HitResult.Perfect:
return 30;
}
@@ -24,10 +23,9 @@ namespace osu.Game.Rulesets.Catch.Judgements
switch (result)
{
default:
return base.HealthIncreaseFor(result);
return 0;
case HitResult.Perfect:
return 0.007;
return 7;
}
}
}
@@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
default:
return 0;
case HitResult.Perfect:
return 300;
}
@@ -28,10 +27,9 @@ namespace osu.Game.Rulesets.Catch.Judgements
switch (result)
{
default:
return -0.02;
return 0;
case HitResult.Perfect:
return 0.01;
return 10.2;
}
}
@@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
default:
return 0;
case HitResult.Perfect:
return 10;
}
@@ -27,9 +26,8 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
default:
return 0;
case HitResult.Perfect:
return 0.004;
return 4;
}
}
}
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
protected override Score CreateReplayScore(Beatmap<CatchHitObject> beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
@@ -21,10 +21,10 @@ namespace osu.Game.Rulesets.Catch.Mods
private CatchPlayfield playfield;
public override void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
public override void ApplyToRulesetContainer(RulesetContainer<CatchHitObject> rulesetContainer)
{
playfield = (CatchPlayfield)drawableRuleset.Playfield;
base.ApplyToDrawableRuleset(drawableRuleset);
playfield = (CatchPlayfield)rulesetContainer.Playfield;
base.ApplyToRulesetContainer(rulesetContainer);
}
private class CatchFlashlight : Flashlight
@@ -15,107 +15,77 @@ namespace osu.Game.Rulesets.Catch.Mods
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
private float? lastPosition;
private double lastStartTime;
private float lastStartX;
private int 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;
int startTime = (int)hitObject.StartTime;
if (lastPosition == null)
if (lastStartX == 0)
{
lastPosition = position;
lastStartX = position;
lastStartTime = startTime;
return;
}
float positionDiff = position - lastPosition.Value;
double timeDiff = startTime - lastStartTime;
float diff = lastStartX - position;
int timeDiff = startTime - lastStartTime;
if (timeDiff > 1000)
{
lastPosition = position;
lastStartX = position;
lastStartTime = startTime;
return;
}
if (positionDiff == 0)
if (diff == 0)
{
applyRandomOffset(ref position, timeDiff / 4d);
bool right = RNG.NextBool();
float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH;
if (right)
{
if (position + rand <= 1)
position += rand;
else
position -= rand;
}
else
{
if (position - rand >= 0)
position -= rand;
else
position += rand;
}
catchObject.X = position;
return;
}
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
applyOffset(ref position, positionDiff);
if (Math.Abs(diff) < timeDiff / 3d)
{
if (diff > 0)
{
if (position - diff > 0)
position -= diff;
}
else
{
if (position - diff < 1)
position -= diff;
}
}
catchObject.X = position;
lastPosition = position;
lastStartX = 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;
}
}
}
}
@@ -13,17 +13,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
private readonly Container bananaContainer;
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
: base(s)
{
RelativeSizeAxes = Axes.X;
Origin = Anchor.BottomLeft;
X = 0;
AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both });
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
foreach (var b in s.NestedHitObjects.Cast<Banana>())
AddNested(createDrawableRepresentation?.Invoke(b));
AddNested(getVisualRepresentation?.Invoke(b));
}
protected override void AddNested(DrawableHitObject h)
@@ -84,7 +84,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
case ArmedState.Miss:
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
break;
case ArmedState.Hit:
this.FadeOut().Expire();
break;
@@ -26,7 +26,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader]
private void load()
{
AddInternal(pulp = new Pulp { Size = Size });
InternalChild = pulp = new Pulp
{
Size = Size
};
}
public override Color4 AccentColour
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
@@ -43,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
// todo: this should come from the skin.
AccentColour = colourForRepresentation(HitObject.VisualRepresentation);
AddRangeInternal(new[]
InternalChildren = new[]
{
createPulp(HitObject.VisualRepresentation),
border = new Circle
@@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}
}
},
});
};
if (HitObject.HyperDash)
{
@@ -106,7 +105,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
default:
return new Container();
case FruitVisualRepresentation.Raspberry:
return new Container
{
@@ -145,7 +143,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
},
}
};
case FruitVisualRepresentation.Pineapple:
return new Container
{
@@ -184,7 +181,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
},
}
};
case FruitVisualRepresentation.Pear:
return new Container
{
@@ -217,7 +213,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
},
}
};
case FruitVisualRepresentation.Grape:
return new Container
{
@@ -250,7 +245,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
},
}
};
case FruitVisualRepresentation.Banana:
return new Container
{
@@ -288,25 +282,19 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
default:
case FruitVisualRepresentation.Pear:
return new Color4(17, 136, 170, 255);
case FruitVisualRepresentation.Grape:
return new Color4(204, 102, 0, 255);
case FruitVisualRepresentation.Raspberry:
return new Color4(121, 9, 13, 255);
case FruitVisualRepresentation.Pineapple:
return new Color4(102, 136, 0, 255);
case FruitVisualRepresentation.Banana:
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
@@ -13,17 +13,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
private readonly Container dropletContainer;
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
: base(s)
{
RelativeSizeAxes = Axes.Both;
Origin = Anchor.BottomLeft;
X = 0;
AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, });
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
AddNested(createDrawableRepresentation?.Invoke(o));
AddNested(getVisualRepresentation?.Invoke(o));
}
protected override void AddNested(DrawableHitObject h)
@@ -3,7 +3,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
+82 -52
View File
@@ -1,6 +1,7 @@
// 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.Game.Audio;
@@ -24,11 +25,6 @@ namespace osu.Game.Rulesets.Catch.Objects
public double Velocity;
public double TickDistance;
/// <summary>
/// The length of one span of this <see cref="JuiceStream"/>.
/// </summary>
public double SpanDuration => Duration / this.SpanCount();
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
@@ -45,6 +41,19 @@ namespace osu.Game.Rulesets.Catch.Objects
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
createTicks();
}
private void createTicks()
{
if (TickDistance == 0)
return;
var length = Path.Distance;
var tickDistance = Math.Min(TickDistance, length);
var spanDuration = length / Velocity;
var minDistanceFromEnd = Velocity * 0.01;
var tickSamples = Samples.Select(s => new SampleInfo
{
@@ -53,60 +62,81 @@ namespace osu.Game.Rulesets.Catch.Objects
Volume = s.Volume
}).ToList();
SliderEventDescriptor? lastEvent = null;
foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
AddNested(new Fruit
{
// generate tiny droplets since the last point
if (lastEvent != null)
{
double sinceLastTick = e.Time - lastEvent.Value.Time;
Samples = Samples,
StartTime = StartTime,
X = X
});
if (sinceLastTick > 80)
double lastTickTime = StartTime;
for (int span = 0; span < this.SpanCount(); span++)
{
var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1;
for (double d = tickDistance;; d += tickDistance)
{
bool isLastTick = false;
if (d + minDistanceFromEnd >= length)
{
double timeBetweenTiny = sinceLastTick;
while (timeBetweenTiny > 100)
timeBetweenTiny /= 2;
for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny)
{
AddNested(new TinyDroplet
{
Samples = tickSamples,
StartTime = t + lastEvent.Value.Time,
X = X + Path.PositionAt(
lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X / CatchPlayfield.BASE_WIDTH,
});
}
d = length;
isLastTick = true;
}
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
double time = spanStartTime + timeProgress * spanDuration;
if (LegacyLastTickOffset != null)
{
// If we're the last tick, apply the legacy offset
if (span == this.SpanCount() - 1 && isLastTick)
time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value);
}
int tinyTickCount = 1;
double tinyTickInterval = time - lastTickTime;
while (tinyTickInterval > 100 && tinyTickCount < 10000)
{
tinyTickInterval /= 2;
tinyTickCount *= 2;
}
for (int tinyTickIndex = 0; tinyTickIndex < tinyTickCount - 1; tinyTickIndex++)
{
var t = lastTickTime + (tinyTickIndex + 1) * tinyTickInterval;
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
AddNested(new TinyDroplet
{
StartTime = t,
X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = tickSamples
});
}
lastTickTime = time;
if (isLastTick)
break;
AddNested(new Droplet
{
StartTime = time,
X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = tickSamples
});
}
// this also includes LegacyLastTick and this is used for TinyDroplet generation above.
// this means that the final segment of TinyDroplets are increasingly mistimed where LegacyLastTickOffset is being applied.
lastEvent = e;
switch (e.Type)
AddNested(new Fruit
{
case SliderEventType.Tick:
AddNested(new Droplet
{
Samples = tickSamples,
StartTime = e.Time,
X = X + Path.PositionAt(e.PathProgress).X / CatchPlayfield.BASE_WIDTH,
});
break;
case SliderEventType.Head:
case SliderEventType.Tail:
case SliderEventType.Repeat:
AddNested(new Fruit
{
Samples = Samples,
StartTime = e.Time,
X = X + Path.PositionAt(e.PathProgress).X / CatchPlayfield.BASE_WIDTH,
});
break;
}
Samples = Samples,
StartTime = spanStartTime + spanDuration,
X = X + Path.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
}
}
@@ -6,20 +6,17 @@ using System.Linq;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Catch.Replays
{
internal class CatchAutoGenerator : AutoGenerator
internal class CatchAutoGenerator : AutoGenerator<CatchHitObject>
{
public const double RELEASE_DELAY = 20;
public new CatchBeatmap Beatmap => (CatchBeatmap)base.Beatmap;
public CatchAutoGenerator(IBeatmap beatmap)
public CatchAutoGenerator(Beatmap<CatchHitObject> beatmap)
: base(beatmap)
{
Replay = new Replay();
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Game.Replays;
@@ -23,14 +22,10 @@ namespace osu.Game.Rulesets.Catch.Replays
{
get
{
var frame = CurrentFrame;
if (frame == null)
if (!HasFrames)
return null;
Debug.Assert(CurrentTime != null);
return NextFrame != null ? Interpolation.ValueAt(CurrentTime.Value, frame.Position, NextFrame.Position, frame.Time, NextFrame.Time) : frame.Position;
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
}
}
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
@@ -12,8 +13,8 @@ namespace osu.Game.Rulesets.Catch.Scoring
{
public class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
{
public CatchScoreProcessor(DrawableRuleset<CatchHitObject> drawableRuleset)
: base(drawableRuleset)
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
: base(rulesetContainer)
{
}
@@ -26,16 +27,20 @@ namespace osu.Game.Rulesets.Catch.Scoring
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
{
switch (result.Type)
{
case HitResult.Miss:
return hpDrainRate;
private const double harshness = 0.01;
default:
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
protected override void ApplyResult(JudgementResult result)
{
base.ApplyResult(result);
if (result.Type == HitResult.Miss)
{
if (!result.Judgement.IsBonus)
Health.Value -= hpDrainRate * (harshness * 2);
return;
}
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
public override HitWindows CreateHitWindows() => new CatchHitWindows();
+23 -13
View File
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
{
@@ -19,24 +20,33 @@ namespace osu.Game.Rulesets.Catch.UI
internal readonly CatcherArea CatcherArea;
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
{
Container explodingFruitContainer;
InternalChildren = new Drawable[]
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate
InternalChild = new PlayfieldAdjustmentContainer
{
explodingFruitContainer = new Container
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
},
CatcherArea = new CatcherArea(difficulty)
{
CreateDrawableRepresentation = createDrawableRepresentation,
ExplodingFruitTarget = explodingFruitContainer,
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
},
HitObjectContainer
explodingFruitContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
CatcherArea = new CatcherArea(difficulty)
{
GetVisualRepresentation = getVisualRepresentation,
ExplodingFruitTarget = explodingFruitContainer,
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
},
HitObjectContainer
}
};
}
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
@@ -11,7 +10,6 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -19,14 +17,14 @@ using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Catch.UI
{
public class DrawableCatchRuleset : DrawableScrollingRuleset<CatchHitObject>
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
{
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Constant;
protected override bool UserScrollSpeedAdjustment => false;
public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
Direction.Value = ScrollingDirection.Down;
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
@@ -36,31 +34,24 @@ namespace osu.Game.Rulesets.Catch.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
protected override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
public override DrawableHitObject<CatchHitObject> CreateDrawableRepresentation(CatchHitObject h)
public override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
{
switch (h)
{
case Banana banana:
return new DrawableBanana(banana);
case Fruit fruit:
return new DrawableFruit(fruit);
case JuiceStream stream:
return new DrawableJuiceStream(stream, CreateDrawableRepresentation);
return new DrawableJuiceStream(stream, GetVisualRepresentation);
case BananaShower shower:
return new DrawableBananaShower(shower, CreateDrawableRepresentation);
return new DrawableBananaShower(shower, GetVisualRepresentation);
case TinyDroplet tiny:
return new DrawableTinyDroplet(tiny);
case Droplet droplet:
return new DrawableDroplet(droplet);
}
+5 -10
View File
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.UI
protected internal readonly Catcher MovableCatcher;
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> CreateDrawableRepresentation;
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;
public Container ExplodingFruitTarget
{
@@ -60,12 +60,12 @@ namespace osu.Game.Rulesets.Catch.UI
if (lastPlateableFruit.IsLoaded)
action();
else
lastPlateableFruit.OnLoadComplete += _ => action();
lastPlateableFruit.OnLoadComplete = _ => action();
}
if (result.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject);
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
if (caughtFruit == null) return;
@@ -292,7 +292,6 @@ namespace osu.Game.Rulesets.Catch.UI
const float hyper_dash_transition_length = 180;
bool previouslyHyperDashing = HyperDashing;
if (modifier <= 1 || X == targetPosition)
{
hyperDashModifier = 1;
@@ -326,11 +325,9 @@ namespace osu.Game.Rulesets.Catch.UI
case CatchAction.MoveLeft:
currentDirection--;
return true;
case CatchAction.MoveRight:
currentDirection++;
return true;
case CatchAction.Dash:
Dashing = true;
return true;
@@ -346,11 +343,9 @@ namespace osu.Game.Rulesets.Catch.UI
case CatchAction.MoveLeft:
currentDirection++;
return true;
case CatchAction.MoveRight:
currentDirection--;
return true;
case CatchAction.Dash:
Dashing = false;
return true;
@@ -379,8 +374,8 @@ namespace osu.Game.Rulesets.Catch.UI
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
// Correct overshooting.
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
if (hyperDashDirection > 0 && hyperDashTargetPosition < X ||
hyperDashDirection < 0 && hyperDashTargetPosition > X)
{
X = hyperDashTargetPosition;
SetHyperDashState();
@@ -3,23 +3,17 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.UI;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatchPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
public class PlayfieldAdjustmentContainer : Container
{
protected override Container<Drawable> Content => content;
private readonly Container content;
public CatchPlayfieldAdjustmentContainer()
public PlayfieldAdjustmentContainer()
{
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate
InternalChild = new Container
{
Anchor = Anchor.Centre,
@@ -8,12 +8,12 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public abstract class ManiaInputTestScene : OsuTestScene
public abstract class ManiaInputTestCase : OsuTestCase
{
private readonly Container<Drawable> content;
protected override Container<Drawable> Content => content ?? base.Content;
protected ManiaInputTestScene(int keys)
protected ManiaInputTestCase(int keys)
{
base.Content.Add(content = new LocalInputManager(keys));
}
@@ -1,8 +1,6 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -10,7 +8,6 @@ using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
@@ -20,14 +17,11 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests
{
[Cached(Type = typeof(IManiaHitObjectComposer))]
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene, IManiaHitObjectComposer
public abstract class ManiaPlacementBlueprintTestCase : PlacementBlueprintTestCase, IManiaHitObjectComposer
{
private readonly Column column;
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
protected ManiaPlacementBlueprintTestScene()
protected ManiaPlacementBlueprintTestCase()
{
Add(column = new Column(0)
{
@@ -13,14 +13,14 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests
{
[Cached(Type = typeof(IManiaHitObjectComposer))]
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene, IManiaHitObjectComposer
public abstract class ManiaSelectionBlueprintTestCase : SelectionBlueprintTestCase, IManiaHitObjectComposer
{
[Cached(Type = typeof(IAdjustableClock))]
private readonly IAdjustableClock clock = new StopwatchClock();
private readonly Column column;
protected ManiaSelectionBlueprintTestScene()
protected ManiaSelectionBlueprintTestCase()
{
Add(column = new Column(0)
{
@@ -12,7 +12,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestSceneAutoGeneration : OsuTestScene
public class TestCaseAutoGeneration : OsuTestCase
{
[Test]
public void TestSingleNote()
@@ -13,7 +13,6 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
using osuTK;
@@ -22,7 +21,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestSceneColumn : ManiaInputTestScene
public class TestCaseColumn : ManiaInputTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@@ -32,12 +31,9 @@ namespace osu.Game.Rulesets.Mania.Tests
typeof(ColumnHitObjectArea)
};
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly List<Column> columns = new List<Column>();
public TestSceneColumn()
public TestCaseColumn()
: base(2)
{
}
@@ -11,11 +11,11 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestSceneEditor : EditorTestScene
public class TestCaseEditor : EditorTestCase
{
private readonly Bindable<ManiaScrollingDirection> direction = new Bindable<ManiaScrollingDirection>();
public TestSceneEditor()
public TestCaseEditor()
: base(new ManiaRuleset())
{
AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up);
@@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
[BackgroundDependencyLoader]
private void load(RulesetConfigCache configCache)
{
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
config.BindWith(ManiaRulesetSetting.ScrollDirection, direction);
var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
config.BindWith(ManiaSetting.ScrollDirection, direction);
}
}
}
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneHoldNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
public class TestCaseHoldNotePlacementBlueprint : ManiaPlacementBlueprintTestCase
{
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableHoldNote((HoldNote)hitObject);
protected override PlacementBlueprint CreateBlueprint() => new HoldNotePlacementBlueprint();
@@ -15,14 +15,14 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneHoldNoteSelectionBlueprint : ManiaSelectionBlueprintTestScene
public class TestCaseHoldNoteSelectionBlueprint : ManiaSelectionBlueprintTestCase
{
private readonly DrawableHoldNote drawableObject;
protected override Container<Drawable> Content => content ?? base.Content;
private readonly Container content;
public TestSceneHoldNoteSelectionBlueprint()
public TestCaseHoldNoteSelectionBlueprint()
{
var holdNote = new HoldNote { Column = 0, Duration = 1000 };
holdNote.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
public class TestCaseNotePlacementBlueprint : ManiaPlacementBlueprintTestCase
{
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject);
protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint();
@@ -15,14 +15,14 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneNoteSelectionBlueprint : ManiaSelectionBlueprintTestScene
public class TestCaseNoteSelectionBlueprint : ManiaSelectionBlueprintTestCase
{
private readonly DrawableNote drawableObject;
protected override Container<Drawable> Content => content ?? base.Content;
private readonly Container content;
public TestSceneNoteSelectionBlueprint()
public TestCaseNoteSelectionBlueprint()
{
var note = new Note { Column = 0 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@@ -11,10 +11,10 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
@@ -27,7 +27,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestSceneNotes : OsuTestScene
public class TestCaseNotes : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Tests
content = new Container { RelativeSizeAxes = Axes.Both }
}
},
new OsuSpriteText
new SpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -168,13 +168,11 @@ namespace osu.Game.Rulesets.Mania.Tests
foreach (var nested in obj.NestedHitObjects)
{
double finalPosition = (nested.HitObject.StartTime - obj.HitObject.StartTime) / endTime.Duration;
switch (direction)
{
case ScrollingDirection.Up:
nested.Y = (float)(finalPosition * content.DrawHeight);
break;
case ScrollingDirection.Down:
nested.Y = (float)(-finalPosition * content.DrawHeight);
break;
@@ -1,7 +1,6 @@
// 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 NUnit.Framework;
@@ -14,7 +13,6 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
using osuTK;
@@ -22,18 +20,15 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestSceneStage : ManiaInputTestScene
public class TestCaseStage : ManiaInputTestCase
{
private const int columns = 4;
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly List<ManiaStage> stages = new List<ManiaStage>();
private FillFlowContainer<ScrollingTestContainer> fill;
public TestSceneStage()
public TestCaseStage()
: base(columns)
{
}
@@ -2,9 +2,9 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -3,8 +3,8 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
@@ -42,13 +42,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
{
Name = @"Note Count",
Content = notes.ToString(),
Icon = FontAwesome.Regular.Circle
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Hold Note Count",
Content = holdnotes.ToString(),
Icon = FontAwesome.Regular.Circle
Icon = FontAwesome.fa_circle
},
};
}
@@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
if (IsForCurrentRuleset)
{
TargetColumns = (int)Math.Max(1, roundedCircleSize);
if (TargetColumns >= 10)
{
TargetColumns = TargetColumns / 2;
@@ -179,7 +179,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
int nextColumn = GetRandomColumn();
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{
// Find available column
@@ -218,7 +217,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
@@ -301,7 +299,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i <= spanCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
@@ -344,19 +341,16 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p3 = 0;
p4 = 0;
break;
case 3:
p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
break;
case 4:
p2 = Math.Min(p2, 0.3);
p3 = Math.Min(p3, 0.04);
p4 = 0;
break;
case 5:
p2 = Math.Min(p2, 0.34);
p3 = Math.Min(p3, 0.1);
@@ -446,7 +440,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
var rowPattern = new Pattern();
for (int i = 0; i <= spanCount; i++)
{
if (!(ignoreHead && startTime == HitObject.StartTime))
@@ -38,11 +38,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
addToPattern(pattern, 0, generateHold);
break;
case 8:
addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold);
break;
default:
if (TotalColumns > 0)
addToPattern(pattern, GetRandomColumn(), generateHold);
@@ -233,7 +233,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++)
{
nextColumn = allowStacking
@@ -304,7 +303,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
int nextColumn = GetRandomColumn(upperBound: columnLimit);
for (int i = 0; i < noteCount; i++)
{
nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern);
@@ -342,21 +340,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p4 = 0;
p5 = 0;
break;
case 3:
p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
p5 = 0;
break;
case 4:
p2 = Math.Min(p2, 0.23);
p3 = Math.Min(p3, 0.04);
p4 = 0;
p5 = 0;
break;
case 5:
p3 = Math.Min(p3, 0.15);
p4 = Math.Min(p4, 0.03);
@@ -389,24 +384,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p2 = 0;
p3 = 0;
break;
case 3:
centreProbability = Math.Min(centreProbability, 0.03);
p2 = 0;
p3 = 0;
break;
case 4:
centreProbability = 0;
p2 = Math.Min(p2 * 2, 0.2);
p3 = 0;
break;
case 5:
centreProbability = Math.Min(centreProbability, 0.03);
p3 = 0;
break;
case 6:
centreProbability = 0;
p2 = Math.Min(p2 * 2, 0.5);
@@ -158,7 +158,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Ensure that we have at least one free column, so that an endless loop is avoided
bool hasValidColumns = false;
for (int i = lowerBound.Value; i < upperBound.Value; i++)
{
hasValidColumns = isValid(i);
@@ -8,9 +8,9 @@ using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Configuration
{
public class ManiaRulesetConfigManager : RulesetConfigManager<ManiaRulesetSetting>
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
{
public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
: base(settings, ruleset, variant)
{
}
@@ -19,17 +19,17 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<double>(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
};
}
public enum ManiaRulesetSetting
public enum ManiaSetting
{
ScrollTime,
ScrollDirection
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{
if (beatmap.HitObjects.Count == 0)
return new ManiaDifficultyAttributes { Mods = mods, Skills = skills };
return new ManiaDifficultyAttributes { Mods = mods };
return new ManiaDifficultyAttributes
{
@@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
Skills = skills
};
}
@@ -1,23 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Graphics;
using osuTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.Edit
{
public class DrawableManiaEditRuleset : DrawableManiaRuleset
public class ManiaEditRulesetContainer : ManiaRulesetContainer
{
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
public ManiaEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
@@ -11,7 +11,6 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
@@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Edit
[Cached(Type = typeof(IManiaHitObjectComposer))]
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
{
protected new DrawableManiaEditRuleset DrawableRuleset { get; private set; }
protected new ManiaEditRulesetContainer RulesetContainer { get; private set; }
public ManiaHitObjectComposer(Ruleset ruleset)
: base(ruleset)
@@ -33,23 +32,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) => RulesetContainer.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)RulesetContainer.Playfield).TotalColumns;
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
protected override RulesetContainer<ManiaHitObject> CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
{
DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
RulesetContainer = new ManiaEditRulesetContainer(ruleset, beatmap);
// 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(RulesetContainer.ScrollingInfo);
return DrawableRuleset;
return RulesetContainer;
}
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
@@ -66,7 +65,6 @@ namespace osu.Game.Rulesets.Mania.Edit
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
@@ -10,17 +10,5 @@ namespace osu.Game.Rulesets.Mania.Judgements
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 20;
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return 0;
default:
return 0.040;
}
}
}
}
@@ -14,47 +14,16 @@ namespace osu.Game.Rulesets.Mania.Judgements
{
default:
return 0;
case HitResult.Meh:
return 50;
case HitResult.Ok:
return 100;
case HitResult.Good:
return 200;
case HitResult.Great:
case HitResult.Perfect:
return 300;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return -0.125;
case HitResult.Meh:
return 0.005;
case HitResult.Ok:
return 0.010;
case HitResult.Good:
return 0.035;
case HitResult.Great:
return 0.055;
case HitResult.Perfect:
return 0.065;
default:
return 0;
}
}
}
}
+3 -17
View File
@@ -10,10 +10,8 @@ using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using System.Linq;
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 +30,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 RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new ManiaRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
@@ -117,7 +115,6 @@ namespace osu.Game.Rulesets.Mania
new ManiaModNoFail(),
new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()),
};
case ModType.DifficultyIncrease:
return new Mod[]
{
@@ -127,7 +124,6 @@ namespace osu.Game.Rulesets.Mania
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
new ManiaModFlashlight(),
};
case ModType.Conversion:
return new Mod[]
{
@@ -144,19 +140,11 @@ namespace osu.Game.Rulesets.Mania
new ManiaModDualStages(),
new ManiaModMirror(),
};
case ModType.Automation:
return new Mod[]
{
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
};
case ModType.Fun:
return new Mod[]
{
new MultiMod(new ModWindUp<ManiaHitObject>(), new ModWindDown<ManiaHitObject>())
};
default:
return new Mod[] { };
}
@@ -166,7 +154,7 @@ namespace osu.Game.Rulesets.Mania
public override string ShortName => "mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania };
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
@@ -174,7 +162,7 @@ namespace osu.Game.Rulesets.Mania
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo);
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
@@ -219,7 +207,6 @@ namespace osu.Game.Rulesets.Mania
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1,
}.GenerateKeyBindingsFor(variant, out _);
case PlayfieldType.Dual:
int keys = getDualStageKeyCount(variant);
@@ -277,7 +264,6 @@ namespace osu.Game.Rulesets.Mania
{
default:
return $"{variant}K";
case PlayfieldType.Dual:
{
var keys = getDualStageKeyCount(variant);
@@ -22,19 +22,19 @@ namespace osu.Game.Rulesets.Mania
[BackgroundDependencyLoader]
private void load()
{
var config = (ManiaRulesetConfigManager)Config;
var config = (ManiaConfigManager)Config;
Children = new Drawable[]
{
new SettingsEnumDropdown<ManiaScrollingDirection>
{
LabelText = "Scrolling direction",
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
},
new SettingsSlider<double, TimeSlider>
{
LabelText = "Scroll speed",
Bindable = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime)
Bindable = config.GetBindable<double>(ManiaSetting.ScrollTime)
},
};
}
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
@@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Fade In";
public override string Acronym => "FI";
public override IconUsage Icon => OsuIcon.ModHidden;
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;
@@ -3,7 +3,6 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
@@ -18,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Random";
public override string Acronym => "RD";
public override ModType Type => ModType.Conversion;
public override IconUsage Icon => OsuIcon.Dice;
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
RelativeSizeAxes = Axes.X;
AddRangeInternal(new Drawable[]
InternalChildren = new Drawable[]
{
bodyPiece = new BodyPiece
{
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
});
};
foreach (var tick in tickContainer)
AddNested(tick);
@@ -7,7 +7,6 @@ using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
@@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X;
Size = new Vector2(1);
AddRangeInternal(new[]
InternalChildren = new[]
{
glowContainer = new CircularContainer
{
@@ -52,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
}
});
};
}
public override Color4 AccentColour
@@ -65,7 +65,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
case ArmedState.Miss:
this.FadeOut(150, Easing.In).Expire();
break;
case ArmedState.Hit:
this.FadeOut(150, Easing.OutQuint).Expire();
break;
@@ -5,7 +5,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
CornerRadius = 5;
Masking = true;
AddInternal(headPiece = new NotePiece());
InternalChild = headPiece = new NotePiece();
}
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
@@ -7,7 +7,6 @@ using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
@@ -145,7 +144,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
const float animation_length = 50;
Foreground.ClearTransforms(false, nameof(Foreground.Colour));
if (hitting)
{
// wait for the next sync point
@@ -4,7 +4,6 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
@@ -5,12 +5,13 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaAutoGenerator : AutoGenerator
internal class ManiaAutoGenerator : AutoGenerator<ManiaHitObject>
{
public const double RELEASE_DELAY = 20;
@@ -28,7 +29,6 @@ namespace osu.Game.Rulesets.Mania.Replays
var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1;
int totalCounter = 0;
foreach (var stage in Beatmap.Stages)
{
for (int i = 0; i < stage.Columns; i++)
@@ -52,7 +52,6 @@ namespace osu.Game.Rulesets.Mania.Replays
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
var actions = new List<ManiaAction>();
foreach (var group in pointGroups)
{
foreach (var point in group)
@@ -62,7 +61,6 @@ namespace osu.Game.Rulesets.Mania.Replays
case HitPoint _:
actions.Add(columnActions[point.Column]);
break;
case ReleasePoint _:
actions.Remove(columnActions[point.Column]);
break;
@@ -18,6 +18,6 @@ namespace osu.Game.Rulesets.Mania.Replays
protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any();
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame?.Actions ?? new List<ManiaAction>() } };
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame.Actions } };
}
}
@@ -39,7 +39,6 @@ namespace osu.Game.Rulesets.Mania.Replays
int activeColumns = (int)(legacyFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0)
{
var isSpecial = stage.IsSpecialColumn(counter);
@@ -3,6 +3,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
@@ -27,6 +28,36 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary>
private const double hp_multiplier_max = 1;
/// <summary>
/// The default BAD hit HP increase.
/// </summary>
private const double hp_increase_bad = 0.005;
/// <summary>
/// The default OK hit HP increase.
/// </summary>
private const double hp_increase_ok = 0.010;
/// <summary>
/// The default GOOD hit HP increase.
/// </summary>
private const double hp_increase_good = 0.035;
/// <summary>
/// The default tick hit HP increase.
/// </summary>
private const double hp_increase_tick = 0.040;
/// <summary>
/// The default GREAT hit HP increase.
/// </summary>
private const double hp_increase_great = 0.055;
/// <summary>
/// The default PERFECT hit HP increase.
/// </summary>
private const double hp_increase_perfect = 0.065;
/// <summary>
/// The MISS HP multiplier at OD = 0.
/// </summary>
@@ -42,6 +73,11 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary>
private const double hp_multiplier_miss_max = 1;
/// <summary>
/// The default MISS HP increase.
/// </summary>
private const double hp_increase_miss = -0.125;
/// <summary>
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
/// </summary>
@@ -52,8 +88,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary>
private double hpMultiplier = 1;
public ManiaScoreProcessor(DrawableRuleset<ManiaHitObject> drawableRuleset)
: base(drawableRuleset)
public ManiaScoreProcessor()
{
}
public ManiaScoreProcessor(RulesetContainer<ManiaHitObject> rulesetContainer)
: base(rulesetContainer)
{
}
@@ -82,8 +122,42 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
protected override void ApplyResult(JudgementResult result)
{
base.ApplyResult(result);
bool isTick = result.Judgement is HoldNoteTickJudgement;
if (isTick)
{
if (result.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
else
{
switch (result.Type)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
break;
case HitResult.Meh:
Health.Value += hpMultiplier * hp_increase_bad;
break;
case HitResult.Ok:
Health.Value += hpMultiplier * hp_increase_ok;
break;
case HitResult.Good:
Health.Value += hpMultiplier * hp_increase_good;
break;
case HitResult.Great:
Health.Value += hpMultiplier * hp_increase_great;
break;
case HitResult.Perfect:
Health.Value += hpMultiplier * hp_increase_perfect;
break;
}
}
}
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
@@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.UI;
@@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
@@ -22,15 +22,22 @@ namespace osu.Game.Rulesets.Mania.UI
JudgementText.Font = JudgementText.Font.With(size: 25);
}
protected override double FadeInDuration => 50;
protected override void ApplyHitAnimations()
protected override void LoadComplete()
{
JudgementBody.ScaleTo(0.8f);
JudgementBody.ScaleTo(1, 250, Easing.OutElastic);
base.LoadComplete();
JudgementBody.Delay(FadeInDuration).ScaleTo(0.75f, 250);
this.Delay(FadeInDuration).FadeOut(200);
this.FadeInFromZero(50, Easing.OutQuint);
if (Result.IsHit)
{
JudgementBody.ScaleTo(0.8f);
JudgementBody.ScaleTo(1, 250, Easing.OutElastic);
JudgementBody.Delay(50).ScaleTo(0.75f, 250);
this.Delay(50).FadeOut(200);
}
Expire();
}
}
}
@@ -4,7 +4,6 @@
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
+2 -2
View File
@@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages.");
Size = new Vector2(1, 0.8f);
GridContainer playfieldGrid;
AddInternal(playfieldGrid = new GridContainer
{
@@ -38,7 +40,6 @@ namespace osu.Game.Rulesets.Mania.UI
var normalColumnAction = ManiaAction.Key1;
var specialColumnAction = ManiaAction.Special1;
int firstColumnIndex = 0;
for (int i = 0; i < stageDefinitions.Count; i++)
{
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
@@ -93,7 +94,6 @@ namespace osu.Game.Rulesets.Mania.UI
private ManiaStage getStageByColumn(int column)
{
int sum = 0;
foreach (var stage in stages)
{
sum = sum + stage.Columns.Count;
@@ -1,20 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
using osuTK;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
{
public ManiaPlayfieldAdjustmentContainer()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(1, 0.8f);
}
}
}
@@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
@@ -18,7 +19,6 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@@ -28,20 +28,18 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.UI
{
public class DrawableManiaRuleset : DrawableScrollingRuleset<ManiaHitObject>
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject>
{
protected new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield;
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
public IEnumerable<BarLine> BarLines;
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
// Generate the bar lines
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
@@ -57,7 +55,6 @@ namespace osu.Game.Rulesets.Mania.UI
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
int index = 0;
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
{
barLines.Add(new BarLine
@@ -77,10 +74,10 @@ namespace osu.Game.Rulesets.Mania.UI
{
BarLines.ForEach(Playfield.Add);
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
Config.BindWith(ManiaSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
Config.BindWith(ManiaSetting.ScrollTime, TimeRange);
}
/// <summary>
@@ -90,26 +87,26 @@ namespace osu.Game.Rulesets.Mania.UI
/// <returns>The column which intersects with <paramref name="screenSpacePosition"/>.</returns>
public Column GetColumnByPosition(Vector2 screenSpacePosition) => Playfield.GetColumnByPosition(screenSpacePosition);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h)
public override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
{
switch (h)
{
case HoldNote holdNote:
return new DrawableHoldNote(holdNote);
case Note note:
return new DrawableNote(note);
default:
return null;
}

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