mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 11:07:52 +08:00
Merge branch 'master' into realm-key-binding-store
This commit is contained in:
commit
df703b68ec
@ -31,6 +31,12 @@
|
||||
"commands": [
|
||||
"CodeFileSanity"
|
||||
]
|
||||
},
|
||||
"ppy.localisationanalyser.tools": {
|
||||
"version": "2021.524.0",
|
||||
"commands": [
|
||||
"localisation"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
2
.github/ISSUE_TEMPLATE/01-bug-issues.md
vendored
2
.github/ISSUE_TEMPLATE/01-bug-issues.md
vendored
@ -25,6 +25,6 @@ Please check:
|
||||
*please attach logs here, which are located at:*
|
||||
- `%AppData%/osu/logs` *(on Windows),*
|
||||
- `~/.local/share/osu/logs` *(on Linux & macOS).*
|
||||
- `Android/Data/sh.ppy.osulazer/logs` *(on Android)*,
|
||||
- `Android/data/sh.ppy.osulazer/files/logs` *(on Android)*,
|
||||
- on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer)
|
||||
-->
|
||||
|
46
.github/dependabot.yml
vendored
Normal file
46
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: nuget
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "17:00"
|
||||
open-pull-requests-limit: 99
|
||||
ignore:
|
||||
- dependency-name: Microsoft.EntityFrameworkCore.Design
|
||||
versions:
|
||||
- "> 2.2.6"
|
||||
- dependency-name: Microsoft.EntityFrameworkCore.Sqlite
|
||||
versions:
|
||||
- "> 2.2.6"
|
||||
- dependency-name: Microsoft.EntityFrameworkCore.Sqlite.Core
|
||||
versions:
|
||||
- "> 2.2.6"
|
||||
- dependency-name: Microsoft.Extensions.DependencyInjection
|
||||
versions:
|
||||
- ">= 5.a, < 6"
|
||||
- dependency-name: NUnit3TestAdapter
|
||||
versions:
|
||||
- ">= 3.16.a, < 3.17"
|
||||
- dependency-name: Microsoft.NET.Test.Sdk
|
||||
versions:
|
||||
- 16.9.1
|
||||
- dependency-name: Microsoft.Extensions.DependencyInjection
|
||||
versions:
|
||||
- 3.1.11
|
||||
- 3.1.12
|
||||
- dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson
|
||||
versions:
|
||||
- 3.1.11
|
||||
- dependency-name: Microsoft.NETCore.Targets
|
||||
versions:
|
||||
- 5.0.0
|
||||
- dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack
|
||||
versions:
|
||||
- 5.0.2
|
||||
- dependency-name: NUnit
|
||||
versions:
|
||||
- 3.13.1
|
||||
- dependency-name: Microsoft.AspNetCore.SignalR.Client
|
||||
versions:
|
||||
- 3.1.11
|
@ -24,7 +24,7 @@ Issues, bug reports and feature suggestions are welcomed, though please keep in
|
||||
* the in-game logs, which are located at:
|
||||
* `%AppData%/osu/logs` (on Windows),
|
||||
* `~/.local/share/osu/logs` (on Linux and macOS),
|
||||
* `Android/Data/sh.ppy.osulazer/logs` (on Android),
|
||||
* `Android/data/sh.ppy.osulazer/files/logs` (on Android),
|
||||
* on iOS they can be obtained by connecting your device to your desktop and [copying the `logs` directory from the app's own document storage using iTunes](https://support.apple.com/en-us/HT201301#copy-to-computer),
|
||||
* your system specifications (including the operating system and platform you are playing on),
|
||||
* a reproduction scenario (list of steps you have performed leading up to the occurrence of the bug),
|
||||
|
@ -17,7 +17,7 @@ The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commo
|
||||
|
||||
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||
|
||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passses come at the end of development, preceeded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passes come at the end of development, preceded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||
|
||||
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -1,28 +1,22 @@
|
||||
// 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.Game.Beatmaps;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.EmptyFreeform.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.EmptyFreeform.Replays
|
||||
{
|
||||
public class EmptyFreeformAutoGenerator : AutoGenerator
|
||||
public class EmptyFreeformAutoGenerator : AutoGenerator<EmptyFreeformReplayFrame>
|
||||
{
|
||||
protected Replay Replay;
|
||||
protected List<ReplayFrame> Frames => Replay.Frames;
|
||||
|
||||
public new Beatmap<EmptyFreeformHitObject> Beatmap => (Beatmap<EmptyFreeformHitObject>)base.Beatmap;
|
||||
|
||||
public EmptyFreeformAutoGenerator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay();
|
||||
}
|
||||
|
||||
public override Replay Generate()
|
||||
protected override void GenerateFrames()
|
||||
{
|
||||
Frames.Add(new EmptyFreeformReplayFrame());
|
||||
|
||||
@ -35,8 +29,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Replays
|
||||
// todo: add required inputs and extra frames.
|
||||
});
|
||||
}
|
||||
|
||||
return Replay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -1,28 +1,22 @@
|
||||
// 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.Game.Beatmaps;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
{
|
||||
public class PippidonAutoGenerator : AutoGenerator
|
||||
public class PippidonAutoGenerator : AutoGenerator<PippidonReplayFrame>
|
||||
{
|
||||
protected Replay Replay;
|
||||
protected List<ReplayFrame> Frames => Replay.Frames;
|
||||
|
||||
public new Beatmap<PippidonHitObject> Beatmap => (Beatmap<PippidonHitObject>)base.Beatmap;
|
||||
|
||||
public PippidonAutoGenerator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay();
|
||||
}
|
||||
|
||||
public override Replay Generate()
|
||||
protected override void GenerateFrames()
|
||||
{
|
||||
Frames.Add(new PippidonReplayFrame());
|
||||
|
||||
@ -34,8 +28,6 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
Position = hitObject.Position,
|
||||
});
|
||||
}
|
||||
|
||||
return Replay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -1,28 +1,22 @@
|
||||
// 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.Game.Beatmaps;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.EmptyScrolling.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.EmptyScrolling.Replays
|
||||
{
|
||||
public class EmptyScrollingAutoGenerator : AutoGenerator
|
||||
public class EmptyScrollingAutoGenerator : AutoGenerator<EmptyScrollingReplayFrame>
|
||||
{
|
||||
protected Replay Replay;
|
||||
protected List<ReplayFrame> Frames => Replay.Frames;
|
||||
|
||||
public new Beatmap<EmptyScrollingHitObject> Beatmap => (Beatmap<EmptyScrollingHitObject>)base.Beatmap;
|
||||
|
||||
public EmptyScrollingAutoGenerator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay();
|
||||
}
|
||||
|
||||
public override Replay Generate()
|
||||
protected override void GenerateFrames()
|
||||
{
|
||||
Frames.Add(new EmptyScrollingReplayFrame());
|
||||
|
||||
@ -34,8 +28,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Replays
|
||||
// todo: add required inputs and extra frames.
|
||||
});
|
||||
}
|
||||
|
||||
return Replay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -2,29 +2,23 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Pippidon.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
{
|
||||
public class PippidonAutoGenerator : AutoGenerator
|
||||
public class PippidonAutoGenerator : AutoGenerator<PippidonReplayFrame>
|
||||
{
|
||||
protected Replay Replay;
|
||||
protected List<ReplayFrame> Frames => Replay.Frames;
|
||||
|
||||
public new Beatmap<PippidonHitObject> Beatmap => (Beatmap<PippidonHitObject>)base.Beatmap;
|
||||
|
||||
public PippidonAutoGenerator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay();
|
||||
}
|
||||
|
||||
public override Replay Generate()
|
||||
protected override void GenerateFrames()
|
||||
{
|
||||
int currentLane = 0;
|
||||
|
||||
@ -55,8 +49,6 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
|
||||
currentLane = hitObject.Lane;
|
||||
}
|
||||
|
||||
return Replay;
|
||||
}
|
||||
|
||||
private void addFrame(double time, PippidonAction direction)
|
||||
|
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.422.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.422.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.524.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -9,6 +9,7 @@ using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using osu.Desktop.Security;
|
||||
using osu.Desktop.Overlays;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
@ -113,6 +114,8 @@ namespace osu.Desktop
|
||||
|
||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
||||
LoadComponentAsync(new GameplayWinKeyBlocker(), Add);
|
||||
|
||||
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
|
||||
}
|
||||
|
||||
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
||||
|
83
osu.Desktop/Security/ElevatedPrivilegesChecker.cs
Normal file
83
osu.Desktop/Security/ElevatedPrivilegesChecker.cs
Normal file
@ -0,0 +1,83 @@
|
||||
// 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.Security.Principal;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
|
||||
namespace osu.Desktop.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the game is running with elevated privileges (as admin in Windows, root in Unix) and displays a warning notification if so.
|
||||
/// </summary>
|
||||
public class ElevatedPrivilegesChecker : Component
|
||||
{
|
||||
[Resolved]
|
||||
private NotificationOverlay notifications { get; set; }
|
||||
|
||||
private bool elevated;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
elevated = checkElevated();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (elevated)
|
||||
notifications.Post(new ElevatedPrivilegesNotification());
|
||||
}
|
||||
|
||||
private bool checkElevated()
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (RuntimeInfo.OS)
|
||||
{
|
||||
case RuntimeInfo.Platform.Windows:
|
||||
if (!OperatingSystem.IsWindows()) return false;
|
||||
|
||||
var windowsIdentity = WindowsIdentity.GetCurrent();
|
||||
var windowsPrincipal = new WindowsPrincipal(windowsIdentity);
|
||||
|
||||
return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
case RuntimeInfo.Platform.macOS:
|
||||
case RuntimeInfo.Platform.Linux:
|
||||
return Mono.Unix.Native.Syscall.geteuid() == 0;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class ElevatedPrivilegesNotification : SimpleNotification
|
||||
{
|
||||
public override bool IsImportant => true;
|
||||
|
||||
public ElevatedPrivilegesNotification()
|
||||
{
|
||||
Text = $"Running osu! as {(RuntimeInfo.IsUnix ? "root" : "administrator")} does not improve performance, may break integrations and poses a security risk. Please run the game as a normal user.";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, NotificationOverlay notificationOverlay)
|
||||
{
|
||||
Icon = FontAwesome.Solid.ShieldAlt;
|
||||
IconBackgound.Colour = colours.YellowDark;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="5.0.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -127,7 +127,8 @@ namespace osu.Game.Rulesets.Catch
|
||||
case ModType.Fun:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ModWindUp(), new ModWindDown())
|
||||
new MultiMod(new ModWindUp(), new ModWindDown()),
|
||||
new CatchModFloatingFruits()
|
||||
};
|
||||
|
||||
default:
|
||||
|
29
osu.Game.Rulesets.Catch/Mods/CatchModFloatingFruits.cs
Normal file
29
osu.Game.Rulesets.Catch/Mods/CatchModFloatingFruits.cs
Normal file
@ -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 osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModFloatingFruits : Mod, IApplicableToDrawableRuleset<CatchHitObject>
|
||||
{
|
||||
public override string Name => "Floating Fruits";
|
||||
public override string Acronym => "FF";
|
||||
public override string Description => "The fruits are... floating?";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Cloud;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||
{
|
||||
drawableRuleset.Anchor = Anchor.Centre;
|
||||
drawableRuleset.Origin = Anchor.Centre;
|
||||
|
||||
drawableRuleset.Scale = new Vector2(1, -1);
|
||||
}
|
||||
}
|
||||
}
|
@ -28,10 +28,12 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
catchPlayfield.CatcherArea.MovableCatcher.CatchFruitOnPlate = false;
|
||||
}
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
base.ApplyNormalVisibilityState(hitObject, state);
|
||||
|
||||
if (!(hitObject is DrawableCatchHitObject catchDrawable))
|
||||
return;
|
||||
|
||||
@ -54,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
var offset = hitObject.TimePreempt * fade_out_offset_multiplier;
|
||||
var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier;
|
||||
|
||||
using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset, true))
|
||||
using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset))
|
||||
drawable.FadeOut(duration);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Utils;
|
||||
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;
|
||||
@ -13,26 +12,19 @@ using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
internal class CatchAutoGenerator : AutoGenerator
|
||||
internal class CatchAutoGenerator : AutoGenerator<CatchReplayFrame>
|
||||
{
|
||||
public const double RELEASE_DELAY = 20;
|
||||
|
||||
public new CatchBeatmap Beatmap => (CatchBeatmap)base.Beatmap;
|
||||
|
||||
public CatchAutoGenerator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay();
|
||||
}
|
||||
|
||||
protected Replay Replay;
|
||||
|
||||
private CatchReplayFrame currentFrame;
|
||||
|
||||
public override Replay Generate()
|
||||
protected override void GenerateFrames()
|
||||
{
|
||||
if (Beatmap.HitObjects.Count == 0)
|
||||
return Replay;
|
||||
return;
|
||||
|
||||
// todo: add support for HT DT
|
||||
const double dash_speed = Catcher.BASE_SPEED;
|
||||
@ -119,15 +111,11 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Replay;
|
||||
}
|
||||
|
||||
private void addFrame(double time, float? position = null, bool dashing = false)
|
||||
{
|
||||
var last = currentFrame;
|
||||
currentFrame = new CatchReplayFrame(time, position, dashing, last);
|
||||
Replay.Frames.Add(currentFrame);
|
||||
Frames.Add(new CatchReplayFrame(time, position, dashing, LastFrame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
explosion = new LegacyRollingCounter(skin, LegacyFont.Combo)
|
||||
explosion = new LegacyRollingCounter(LegacyFont.Combo)
|
||||
{
|
||||
Alpha = 0.65f,
|
||||
Blending = BlendingParameters.Additive,
|
||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1.5f),
|
||||
},
|
||||
counter = new LegacyRollingCounter(skin, LegacyFont.Combo)
|
||||
counter = new LegacyRollingCounter(LegacyFont.Combo)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
}
|
||||
};
|
||||
|
||||
AddBlueprint(new HoldNoteSelectionBlueprint(drawableObject));
|
||||
AddBlueprint(new HoldNoteSelectionBlueprint(holdNote), drawableObject);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -184,8 +184,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
AddAssert("head note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.BottomLeft, holdNote.Head.ScreenSpaceDrawQuad.BottomLeft));
|
||||
AddAssert("tail note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.TopLeft, holdNote.Tail.ScreenSpaceDrawQuad.BottomLeft));
|
||||
|
||||
AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition);
|
||||
AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition);
|
||||
AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteOverlay>().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition);
|
||||
AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteOverlay>().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition);
|
||||
}
|
||||
|
||||
private void setScrollStep(ScrollingDirection direction)
|
||||
|
@ -15,7 +15,6 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
@ -35,7 +34,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
[Test]
|
||||
public void TestPlaceBeforeCurrentTimeDownwards()
|
||||
{
|
||||
AddStep("move mouse before current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single().ScreenSpaceDrawQuad.BottomLeft - new Vector2(0, 10)));
|
||||
AddStep("move mouse before current time", () =>
|
||||
{
|
||||
var column = this.ChildrenOfType<Column>().Single();
|
||||
InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(-100));
|
||||
});
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
@ -45,7 +48,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
[Test]
|
||||
public void TestPlaceAfterCurrentTimeDownwards()
|
||||
{
|
||||
AddStep("move mouse after current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single()));
|
||||
AddStep("move mouse after current time", () =>
|
||||
{
|
||||
var column = this.ChildrenOfType<Column>().Single();
|
||||
InputManager.MoveMouseTo(column.ScreenSpacePositionAtTime(100));
|
||||
});
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
Child = drawableObject = new DrawableNote(note)
|
||||
};
|
||||
|
||||
AddBlueprint(new NoteSelectionBlueprint(drawableObject));
|
||||
AddBlueprint(new NoteSelectionBlueprint(note), drawableObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 1), _ => new DefaultColumnBackground())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 0), _ => new DefaultKeyArea())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 1), _ => new DefaultKeyArea())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
|
@ -0,0 +1,67 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
public class TestSceneDrawableManiaHitObject : OsuTestScene
|
||||
{
|
||||
private readonly ManualClock clock = new ManualClock();
|
||||
|
||||
private Column column;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Child = new ScrollingTestContainer(ScrollingDirection.Down)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
TimeRange = 2000,
|
||||
Clock = new FramedClock(clock),
|
||||
Child = column = new Column(0)
|
||||
{
|
||||
Action = { Value = ManiaAction.Key1 },
|
||||
Height = 0.85f,
|
||||
AccentColour = Color4.Gray
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestHoldNoteHeadVisibility()
|
||||
{
|
||||
DrawableHoldNote note = null;
|
||||
AddStep("Add hold note", () =>
|
||||
{
|
||||
var h = new HoldNote
|
||||
{
|
||||
StartTime = 0,
|
||||
Duration = 1000
|
||||
};
|
||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
column.Add(note = new DrawableHoldNote(h));
|
||||
});
|
||||
AddStep("Hold key", () =>
|
||||
{
|
||||
clock.CurrentTime = 0;
|
||||
note.OnPressed(ManiaAction.Key1);
|
||||
});
|
||||
AddStep("progress time", () => clock.CurrentTime = 500);
|
||||
AddAssert("head is visible", () => note.Head.Alpha == 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
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.Objects;
|
||||
@ -414,14 +412,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||
|
||||
AddUntilStep("wait for head", () => currentPlayer.GameplayClockContainer.GameplayClock.CurrentTime >= time_head);
|
||||
AddAssert("head is visible",
|
||||
() => currentPlayer.ChildrenOfType<DrawableHoldNote>()
|
||||
.Single(note => note.HitObject == beatmap.HitObjects[0])
|
||||
.Head
|
||||
.Alpha == 1);
|
||||
|
||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor?.HasCompleted.Value == true);
|
||||
}
|
||||
|
||||
private class ScoreAccessibleReplayPlayer : ReplayPlayer
|
||||
|
@ -0,0 +1,80 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneTimingBasedNoteColouring : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private RulesetConfigCache configCache { get; set; }
|
||||
|
||||
private readonly Bindable<bool> configTimingBasedNoteColouring = new Bindable<bool>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
const double beat_length = 500;
|
||||
|
||||
var ruleset = new ManiaRuleset();
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 })
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Note { StartTime = 0 },
|
||||
new Note { StartTime = beat_length / 16 },
|
||||
new Note { StartTime = beat_length / 12 },
|
||||
new Note { StartTime = beat_length / 8 },
|
||||
new Note { StartTime = beat_length / 6 },
|
||||
new Note { StartTime = beat_length / 4 },
|
||||
new Note { StartTime = beat_length / 3 },
|
||||
new Note { StartTime = beat_length / 2 },
|
||||
new Note { StartTime = beat_length }
|
||||
},
|
||||
ControlPointInfo = new ControlPointInfo(),
|
||||
BeatmapInfo = { Ruleset = ruleset.RulesetInfo },
|
||||
};
|
||||
|
||||
foreach (var note in beatmap.HitObjects)
|
||||
{
|
||||
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
}
|
||||
|
||||
beatmap.ControlPointInfo.Add(0, new TimingControlPoint
|
||||
{
|
||||
BeatLength = beat_length
|
||||
});
|
||||
|
||||
Child = new Container
|
||||
{
|
||||
Clock = new FramedClock(new ManualClock()),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new[]
|
||||
{
|
||||
ruleset.CreateDrawableRulesetWith(beatmap)
|
||||
}
|
||||
};
|
||||
|
||||
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
config.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
|
||||
|
||||
AddStep("Enable", () => configTimingBasedNoteColouring.Value = true);
|
||||
AddStep("Disable", () => configTimingBasedNoteColouring.Value = false);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
|
||||
SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
|
||||
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
@ -34,6 +35,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
public enum ManiaRulesetSetting
|
||||
{
|
||||
ScrollTime,
|
||||
ScrollDirection
|
||||
ScrollDirection,
|
||||
TimingBasedNoteColouring
|
||||
}
|
||||
}
|
||||
|
@ -2,34 +2,35 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
public class HoldNoteNoteSelectionBlueprint : ManiaSelectionBlueprint
|
||||
public class HoldNoteNoteOverlay : CompositeDrawable
|
||||
{
|
||||
protected new DrawableHoldNote DrawableObject => (DrawableHoldNote)base.DrawableObject;
|
||||
|
||||
private readonly HoldNoteSelectionBlueprint holdNoteBlueprint;
|
||||
private readonly HoldNotePosition position;
|
||||
|
||||
public HoldNoteNoteSelectionBlueprint(DrawableHoldNote holdNote, HoldNotePosition position)
|
||||
: base(holdNote)
|
||||
public HoldNoteNoteOverlay(HoldNoteSelectionBlueprint holdNoteBlueprint, HoldNotePosition position)
|
||||
{
|
||||
this.holdNoteBlueprint = holdNoteBlueprint;
|
||||
this.position = position;
|
||||
InternalChild = new EditNotePiece { RelativeSizeAxes = Axes.X };
|
||||
|
||||
Select();
|
||||
InternalChild = new EditNotePiece { RelativeSizeAxes = Axes.X };
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var drawableObject = holdNoteBlueprint.DrawableObject;
|
||||
|
||||
// Todo: This shouldn't exist, mania should not reference the drawable hitobject directly.
|
||||
if (DrawableObject.IsLoaded)
|
||||
if (drawableObject.IsLoaded)
|
||||
{
|
||||
DrawableNote note = position == HoldNotePosition.Start ? (DrawableNote)DrawableObject.Head : DrawableObject.Tail;
|
||||
DrawableNote note = position == HoldNotePosition.Start ? (DrawableNote)drawableObject.Head : drawableObject.Tail;
|
||||
|
||||
Anchor = note.Anchor;
|
||||
Origin = note.Origin;
|
||||
@ -38,8 +39,5 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
Position = note.DrawPosition;
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
|
||||
public override bool HandlePositionalInput => false;
|
||||
}
|
||||
}
|
@ -8,13 +8,14 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
public class HoldNoteSelectionBlueprint : ManiaSelectionBlueprint
|
||||
public class HoldNoteSelectionBlueprint : ManiaSelectionBlueprint<HoldNote>
|
||||
{
|
||||
public new DrawableHoldNote DrawableObject => (DrawableHoldNote)base.DrawableObject;
|
||||
|
||||
@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
public HoldNoteSelectionBlueprint(DrawableHoldNote hold)
|
||||
public HoldNoteSelectionBlueprint(HoldNote hold)
|
||||
: base(hold)
|
||||
{
|
||||
}
|
||||
@ -32,16 +33,11 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new HoldNoteNoteSelectionBlueprint(DrawableObject, HoldNotePosition.Start),
|
||||
new HoldNoteNoteSelectionBlueprint(DrawableObject, HoldNotePosition.End),
|
||||
new HoldNoteNoteOverlay(this, HoldNotePosition.Start),
|
||||
new HoldNoteNoteOverlay(this, HoldNotePosition.End),
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -4,22 +4,23 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||
public abstract class ManiaSelectionBlueprint<T> : HitObjectSelectionBlueprint<T>
|
||||
where T : ManiaHitObject
|
||||
{
|
||||
public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject;
|
||||
|
||||
[Resolved]
|
||||
private IScrollingInfo scrollingInfo { get; set; }
|
||||
|
||||
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
||||
: base(drawableObject)
|
||||
protected ManiaSelectionBlueprint(T hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
}
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
public class NoteSelectionBlueprint : ManiaSelectionBlueprint
|
||||
public class NoteSelectionBlueprint : ManiaSelectionBlueprint<Note>
|
||||
{
|
||||
public NoteSelectionBlueprint(DrawableNote note)
|
||||
public NoteSelectionBlueprint(Note note)
|
||||
: base(note)
|
||||
{
|
||||
AddInternal(new EditNotePiece { RelativeSizeAxes = Axes.X });
|
||||
|
@ -12,16 +12,16 @@ using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class DrawableManiaEditRuleset : DrawableManiaRuleset
|
||||
public class DrawableManiaEditorRuleset : DrawableManiaRuleset
|
||||
{
|
||||
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
||||
|
||||
public DrawableManiaEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
public DrawableManiaEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
|
||||
protected override Playfield CreatePlayfield() => new ManiaEditorPlayfield(Beatmap.Stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
foreach (var line in grid.Objects.OfType<DrawableGridLine>())
|
||||
availableLines.Push(line);
|
||||
|
||||
grid.Clear(false);
|
||||
grid.Clear();
|
||||
}
|
||||
|
||||
if (selectionTimeRange == null)
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
@ -16,20 +16,20 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
}
|
||||
|
||||
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
|
||||
public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableNote note:
|
||||
case Note note:
|
||||
return new NoteSelectionBlueprint(note);
|
||||
|
||||
case DrawableHoldNote holdNote:
|
||||
case HoldNote holdNote:
|
||||
return new HoldNoteSelectionBlueprint(holdNote);
|
||||
}
|
||||
|
||||
return base.CreateBlueprintFor(hitObject);
|
||||
return base.CreateHitObjectBlueprintFor(hitObject);
|
||||
}
|
||||
|
||||
protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
|
||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new ManiaSelectionHandler();
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaEditPlayfield : ManiaPlayfield
|
||||
public class ManiaEditorPlayfield : ManiaPlayfield
|
||||
{
|
||||
public ManiaEditPlayfield(List<StageDefinition> stages)
|
||||
public ManiaEditorPlayfield(List<StageDefinition> stages)
|
||||
: base(stages)
|
||||
{
|
||||
}
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>
|
||||
{
|
||||
private DrawableManiaEditRuleset drawableRuleset;
|
||||
private DrawableManiaEditorRuleset drawableRuleset;
|
||||
private ManiaBeatSnapGrid beatSnapGrid;
|
||||
private InputManager inputManager;
|
||||
|
||||
@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
|
||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
{
|
||||
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||
drawableRuleset = new DrawableManiaEditorRuleset(ruleset, beatmap, mods);
|
||||
|
||||
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
|
||||
dependencies.CacheAs(drawableRuleset.ScrollingInfo);
|
||||
|
@ -5,14 +5,14 @@ using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaSelectionHandler : SelectionHandler
|
||||
public class ManiaSelectionHandler : EditorSelectionHandler
|
||||
{
|
||||
[Resolved]
|
||||
private IScrollingInfo scrollingInfo { get; set; }
|
||||
@ -20,21 +20,21 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
[Resolved]
|
||||
private HitObjectComposer composer { get; set; }
|
||||
|
||||
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
||||
public override bool HandleMovement(MoveSelectionEvent<HitObject> moveEvent)
|
||||
{
|
||||
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
|
||||
int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column;
|
||||
var hitObjectBlueprint = (HitObjectSelectionBlueprint)moveEvent.Blueprint;
|
||||
int lastColumn = ((ManiaHitObject)hitObjectBlueprint.Item).Column;
|
||||
|
||||
performColumnMovement(lastColumn, moveEvent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
|
||||
private void performColumnMovement(int lastColumn, MoveSelectionEvent<HitObject> moveEvent)
|
||||
{
|
||||
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
|
||||
|
||||
var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
|
||||
var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.Blueprint.ScreenSpaceSelectionPoint + moveEvent.ScreenSpaceDelta);
|
||||
if (currentColumn == null)
|
||||
return;
|
||||
|
||||
@ -58,8 +58,9 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
if (h is ManiaHitObject maniaObj)
|
||||
maniaObj.Column += columnDelta;
|
||||
maniaPlayfield.Remove(h);
|
||||
((ManiaHitObject)h).Column += columnDelta;
|
||||
maniaPlayfield.Add(h);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
|
||||
|
||||
public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new DrainingHealthProcessor(drainStartTime, 0.2);
|
||||
public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new DrainingHealthProcessor(drainStartTime, 0.5);
|
||||
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
|
||||
|
||||
|
@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Mania
|
||||
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime),
|
||||
KeyboardStep = 5
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Timing-based note colouring",
|
||||
Current = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,12 +9,6 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
public class ManiaSkinComponent : GameplaySkinComponent<ManiaSkinComponents>
|
||||
{
|
||||
/// <summary>
|
||||
/// The intended <see cref="Column"/> index for this component.
|
||||
/// May be null if the component does not exist in a <see cref="Column"/>.
|
||||
/// </summary>
|
||||
public readonly int? TargetColumn;
|
||||
|
||||
/// <summary>
|
||||
/// The intended <see cref="StageDefinition"/> for this component.
|
||||
/// May be null if the component is not a direct member of a <see cref="Stage"/>.
|
||||
@ -25,12 +19,10 @@ namespace osu.Game.Rulesets.Mania
|
||||
/// Creates a new <see cref="ManiaSkinComponent"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The component.</param>
|
||||
/// <param name="targetColumn">The intended <see cref="Column"/> index for this component. May be null if the component does not exist in a <see cref="Column"/>.</param>
|
||||
/// <param name="stageDefinition">The intended <see cref="StageDefinition"/> for this component. May be null if the component is not a direct member of a <see cref="Stage"/>.</param>
|
||||
public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null, StageDefinition? stageDefinition = null)
|
||||
public ManiaSkinComponent(ManiaSkinComponents component, StageDefinition? stageDefinition = null)
|
||||
: base(component)
|
||||
{
|
||||
TargetColumn = targetColumn;
|
||||
StageDefinition = stageDefinition;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
@ -39,5 +40,13 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -12,6 +13,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
@ -29,21 +31,21 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
public DrawableHoldNoteHead Head => headContainer.Child;
|
||||
public DrawableHoldNoteTail Tail => tailContainer.Child;
|
||||
|
||||
private readonly Container<DrawableHoldNoteHead> headContainer;
|
||||
private readonly Container<DrawableHoldNoteTail> tailContainer;
|
||||
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
||||
private Container<DrawableHoldNoteHead> headContainer;
|
||||
private Container<DrawableHoldNoteTail> tailContainer;
|
||||
private Container<DrawableHoldNoteTick> tickContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed.
|
||||
/// </summary>
|
||||
private readonly Container sizingContainer;
|
||||
private Container sizingContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the contents of the hold note that should be masked as the hold note is being pressed. Follows changes in the size of <see cref="sizingContainer"/>.
|
||||
/// </summary>
|
||||
private readonly Container maskingContainer;
|
||||
private Container maskingContainer;
|
||||
|
||||
private readonly SkinnableDrawable bodyPiece;
|
||||
private SkinnableDrawable bodyPiece;
|
||||
|
||||
/// <summary>
|
||||
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
|
||||
@ -60,11 +62,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// </summary>
|
||||
private double? releaseTime;
|
||||
|
||||
public DrawableHoldNote()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableHoldNote(HoldNote hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Container maskedContents;
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
@ -86,7 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
},
|
||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece
|
||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody), _ => new DefaultBodyPiece
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
})
|
||||
@ -105,6 +115,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
||||
sizingContainer.Size = Vector2.One;
|
||||
HoldStartTime = null;
|
||||
HoldBrokenTime = null;
|
||||
releaseTime = null;
|
||||
}
|
||||
|
||||
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
||||
{
|
||||
base.AddNestedHitObject(hitObject);
|
||||
@ -128,37 +148,23 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
protected override void ClearNestedHitObjects()
|
||||
{
|
||||
base.ClearNestedHitObjects();
|
||||
headContainer.Clear();
|
||||
tailContainer.Clear();
|
||||
tickContainer.Clear();
|
||||
headContainer.Clear(false);
|
||||
tailContainer.Clear(false);
|
||||
tickContainer.Clear(false);
|
||||
}
|
||||
|
||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case TailNote _:
|
||||
return new DrawableHoldNoteTail(this)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AccentColour = { BindTarget = AccentColour }
|
||||
};
|
||||
case TailNote tail:
|
||||
return new DrawableHoldNoteTail(tail);
|
||||
|
||||
case Note _:
|
||||
return new DrawableHoldNoteHead(this)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AccentColour = { BindTarget = AccentColour }
|
||||
};
|
||||
case HeadNote head:
|
||||
return new DrawableHoldNoteHead(head);
|
||||
|
||||
case HoldNoteTick tick:
|
||||
return new DrawableHoldNoteTick(tick)
|
||||
{
|
||||
HoldStartTime = () => HoldStartTime,
|
||||
AccentColour = { BindTarget = AccentColour }
|
||||
};
|
||||
return new DrawableHoldNoteTick(tick);
|
||||
}
|
||||
|
||||
return base.CreateNestedHitObject(hitObject);
|
||||
|
@ -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 osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@ -12,11 +13,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteHead;
|
||||
|
||||
public DrawableHoldNoteHead(DrawableHoldNote holdNote)
|
||||
: base(holdNote.HitObject.Head)
|
||||
public DrawableHoldNoteHead()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableHoldNoteHead(HeadNote headNote)
|
||||
: base(headNote)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
}
|
||||
|
||||
public void UpdateResult() => base.UpdateResult(true);
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@ -20,12 +21,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail;
|
||||
|
||||
private readonly DrawableHoldNote holdNote;
|
||||
protected DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject;
|
||||
|
||||
public DrawableHoldNoteTail(DrawableHoldNote holdNote)
|
||||
: base(holdNote.HitObject.Tail)
|
||||
public DrawableHoldNoteTail()
|
||||
: this(null)
|
||||
{
|
||||
this.holdNote = holdNote;
|
||||
}
|
||||
|
||||
public DrawableHoldNoteTail(TailNote tailNote)
|
||||
: base(tailNote)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
}
|
||||
|
||||
public void UpdateResult() => base.UpdateResult(true);
|
||||
@ -54,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
ApplyResult(r =>
|
||||
{
|
||||
// If the head wasn't hit or the hold note was broken, cap the max score to Meh.
|
||||
if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HoldBrokenTime != null))
|
||||
if (result > HitResult.Meh && (!HoldNote.Head.IsHit || HoldNote.HoldBrokenTime != null))
|
||||
result = HitResult.Meh;
|
||||
|
||||
r.Type = result;
|
||||
|
@ -2,7 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osuTK;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -19,38 +20,48 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// <summary>
|
||||
/// References the time at which the user started holding the hold note.
|
||||
/// </summary>
|
||||
public Func<double?> HoldStartTime;
|
||||
private Func<double?> holdStartTime;
|
||||
|
||||
private Container glowContainer;
|
||||
|
||||
public DrawableHoldNoteTick()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableHoldNoteTick(HoldNoteTick hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
Container glowContainer;
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Size = new Vector2(1);
|
||||
}
|
||||
|
||||
AddRangeInternal(new[]
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(glowContainer = new CircularContainer
|
||||
{
|
||||
glowContainer = new CircularContainer
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new[]
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new[]
|
||||
new Box
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AccentColour.BindValueChanged(colour =>
|
||||
{
|
||||
@ -64,12 +75,29 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
||||
Debug.Assert(ParentHitObject != null);
|
||||
|
||||
var holdNote = (DrawableHoldNote)ParentHitObject;
|
||||
holdStartTime = () => holdNote.HoldStartTime;
|
||||
}
|
||||
|
||||
protected override void OnFree()
|
||||
{
|
||||
base.OnFree();
|
||||
|
||||
holdStartTime = null;
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (Time.Current < HitObject.StartTime)
|
||||
return;
|
||||
|
||||
var startTime = HoldStartTime?.Invoke();
|
||||
var startTime = holdStartTime?.Invoke();
|
||||
|
||||
if (startTime == null || startTime > HitObject.StartTime)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
|
@ -6,6 +6,7 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
@ -24,6 +25,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
[Resolved(canBeNull: true)]
|
||||
private ManiaPlayfield playfield { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the samples that are played by this object during gameplay.
|
||||
/// </summary>
|
||||
public ISampleInfo[] GetGameplaySamples() => Samples.Samples;
|
||||
|
||||
protected override float SamplePlaybackPosition
|
||||
{
|
||||
get
|
||||
@ -44,6 +50,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
protected DrawableManiaHitObject(ManiaHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
@ -53,9 +60,31 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Action.BindTo(action);
|
||||
|
||||
Direction.BindTo(scrollingInfo.Direction);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Direction.BindValueChanged(OnDirectionChanged, true);
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
||||
if (ParentHitObject != null)
|
||||
AccentColour.BindTo(ParentHitObject.AccentColour);
|
||||
}
|
||||
|
||||
protected override void OnFree()
|
||||
{
|
||||
base.OnFree();
|
||||
|
||||
if (ParentHitObject != null)
|
||||
AccentColour.UnbindFrom(ParentHitObject.AccentColour);
|
||||
}
|
||||
|
||||
private double computedLifetimeStart;
|
||||
|
||||
public override double LifetimeStart
|
||||
@ -141,12 +170,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||
where TObject : ManiaHitObject
|
||||
{
|
||||
public new readonly TObject HitObject;
|
||||
public new TObject HitObject => (TObject)base.HitObject;
|
||||
|
||||
protected DrawableManiaHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,19 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Default;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
@ -17,23 +23,49 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// </summary>
|
||||
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IBeatmap beatmap { get; set; }
|
||||
|
||||
private readonly Bindable<bool> configTimingBasedNoteColouring = new Bindable<bool>();
|
||||
|
||||
protected virtual ManiaSkinComponents Component => ManiaSkinComponents.Note;
|
||||
|
||||
private readonly Drawable headPiece;
|
||||
private Drawable headPiece;
|
||||
|
||||
public DrawableNote()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableNote(Note hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
}
|
||||
|
||||
AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component, hitObject.Column), _ => new DefaultNotePiece())
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ManiaRulesetConfigManager rulesetConfig)
|
||||
{
|
||||
rulesetConfig?.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
|
||||
|
||||
AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component), _ => new DefaultNotePiece())
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
configTimingBasedNoteColouring.BindValueChanged(_ => updateSnapColour());
|
||||
StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true);
|
||||
}
|
||||
|
||||
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||
{
|
||||
base.OnDirectionChanged(e);
|
||||
@ -73,5 +105,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
public virtual void OnReleased(ManiaAction action)
|
||||
{
|
||||
}
|
||||
|
||||
private void updateSnapColour()
|
||||
{
|
||||
if (beatmap == null || HitObject == null) return;
|
||||
|
||||
int snapDivisor = beatmap.ControlPointInfo.GetClosestBeatDivisor(HitObject.StartTime);
|
||||
|
||||
Colour = configTimingBasedNoteColouring.Value ? BindableBeatDivisor.GetColourFor(snapDivisor, colours) : Color4.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
osu.Game.Rulesets.Mania/Objects/HeadNote.cs
Normal file
9
osu.Game.Rulesets.Mania/Objects/HeadNote.cs
Normal file
@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
public class HeadNote : Note
|
||||
{
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// <summary>
|
||||
/// The head note of the hold.
|
||||
/// </summary>
|
||||
public Note Head { get; private set; }
|
||||
public HeadNote Head { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The tail note of the hold.
|
||||
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
|
||||
createTicks(cancellationToken);
|
||||
|
||||
AddNested(Head = new Note
|
||||
AddNested(Head = new HeadNote
|
||||
{
|
||||
StartTime = StartTime,
|
||||
Column = Column,
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Mania.Objects.Types;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
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;
|
||||
@ -11,7 +10,7 @@ using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
internal class ManiaAutoGenerator : AutoGenerator
|
||||
internal class ManiaAutoGenerator : AutoGenerator<ManiaReplayFrame>
|
||||
{
|
||||
public const double RELEASE_DELAY = 20;
|
||||
|
||||
@ -22,8 +21,6 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
public ManiaAutoGenerator(ManiaBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
Replay = new Replay();
|
||||
|
||||
columnActions = new ManiaAction[Beatmap.TotalColumns];
|
||||
|
||||
var normalAction = ManiaAction.Key1;
|
||||
@ -43,12 +40,10 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
}
|
||||
}
|
||||
|
||||
protected Replay Replay;
|
||||
|
||||
public override Replay Generate()
|
||||
protected override void GenerateFrames()
|
||||
{
|
||||
if (Beatmap.HitObjects.Count == 0)
|
||||
return Replay;
|
||||
return;
|
||||
|
||||
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
||||
|
||||
@ -70,10 +65,8 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
}
|
||||
}
|
||||
|
||||
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
|
||||
Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
|
||||
}
|
||||
|
||||
return Replay;
|
||||
}
|
||||
|
||||
private IEnumerable<IActionPoint> generateActionPoints()
|
||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
@ -27,6 +28,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public const float COLUMN_WIDTH = 80;
|
||||
public const float SPECIAL_COLUMN_WIDTH = 70;
|
||||
|
||||
/// <summary>
|
||||
/// For hitsounds played by this <see cref="Column"/> (i.e. not as a result of hitting a hitobject),
|
||||
/// a certain number of samples are allowed to be played concurrently so that it feels better when spam-pressing the key.
|
||||
/// </summary>
|
||||
private const int max_concurrent_hitsounds = OsuGameBase.SAMPLE_CONCURRENCY;
|
||||
|
||||
/// <summary>
|
||||
/// The index of this column as part of the whole playfield.
|
||||
/// </summary>
|
||||
@ -38,6 +45,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
internal readonly Container TopLevelContainer;
|
||||
private readonly DrawablePool<PoolableHitExplosion> hitExplosionPool;
|
||||
private readonly OrderedHitPolicy hitPolicy;
|
||||
private readonly Container<SkinnableSound> hitSounds;
|
||||
|
||||
public Container UnderlayElements => HitObjectArea.UnderlayElements;
|
||||
|
||||
@ -48,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = COLUMN_WIDTH;
|
||||
|
||||
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, Index), _ => new DefaultColumnBackground())
|
||||
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
@ -59,17 +67,36 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
||||
background.CreateProxy(),
|
||||
HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both },
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, Index), _ => new DefaultKeyArea())
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
background,
|
||||
hitSounds = new Container<SkinnableSound>
|
||||
{
|
||||
Name = "Column samples pool",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new SkinnableSound()).ToArray()
|
||||
},
|
||||
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||
};
|
||||
|
||||
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
||||
|
||||
TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy());
|
||||
|
||||
RegisterPool<Note, DrawableNote>(10, 50);
|
||||
RegisterPool<HoldNote, DrawableHoldNote>(10, 50);
|
||||
RegisterPool<HeadNote, DrawableHoldNoteHead>(10, 50);
|
||||
RegisterPool<TailNote, DrawableHoldNoteTail>(10, 50);
|
||||
RegisterPool<HoldNoteTick, DrawableHoldNoteTick>(50, 250);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
NewResult += OnNewResult;
|
||||
}
|
||||
|
||||
public ColumnType ColumnType { get; set; }
|
||||
@ -85,28 +112,14 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a DrawableHitObject to this Playfield.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The DrawableHitObject to add.</param>
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
hitObject.AccentColour.Value = AccentColour;
|
||||
hitObject.OnNewResult += OnNewResult;
|
||||
base.OnNewDrawableHitObject(drawableHitObject);
|
||||
|
||||
DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)hitObject;
|
||||
DrawableManiaHitObject maniaObject = (DrawableManiaHitObject)drawableHitObject;
|
||||
|
||||
maniaObject.AccentColour.Value = AccentColour;
|
||||
maniaObject.CheckHittable = hitPolicy.IsHittable;
|
||||
|
||||
base.Add(hitObject);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject h)
|
||||
{
|
||||
if (!base.Remove(h))
|
||||
return false;
|
||||
|
||||
h.OnNewResult -= OnNewResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
@ -120,6 +133,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result)));
|
||||
}
|
||||
|
||||
private int nextHitSoundIndex;
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
if (action != Action.Value)
|
||||
@ -131,7 +146,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
HitObjectContainer.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ??
|
||||
HitObjectContainer.Objects.LastOrDefault();
|
||||
|
||||
nextObject?.PlaySamples();
|
||||
if (nextObject is DrawableManiaHitObject maniaObject)
|
||||
{
|
||||
var hitSound = hitSounds[nextHitSoundIndex];
|
||||
|
||||
hitSound.Samples = maniaObject.GetGameplaySamples();
|
||||
hitSound.Play();
|
||||
|
||||
nextHitSoundIndex = (nextHitSoundIndex + 1) % max_concurrent_hitsounds;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = 2,
|
||||
},
|
||||
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget())
|
||||
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget), _ => new DefaultHitTarget())
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Depth = 1
|
||||
|
@ -18,7 +18,6 @@ using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -134,20 +133,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
||||
|
||||
public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h)
|
||||
{
|
||||
switch (h)
|
||||
{
|
||||
case HoldNote holdNote:
|
||||
return new DrawableHoldNote(holdNote);
|
||||
|
||||
case Note note:
|
||||
return new DrawableNote(note);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h) => null;
|
||||
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||
|
||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
@ -56,6 +57,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Add(hitObject);
|
||||
|
||||
public override bool Remove(HitObject hitObject) => getStageByColumn(((ManiaHitObject)hitObject).Column).Remove(hitObject);
|
||||
|
||||
public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h);
|
||||
|
||||
public override bool Remove(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Remove(h);
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, column.Index), _ => new DefaultHitExplosion())
|
||||
InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion), _ => new DefaultHitExplosion())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ 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.Components;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@ -132,33 +133,19 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||
|
||||
int columnIndex = -1;
|
||||
|
||||
maniaObject.ColumnBindable.BindValueChanged(_ =>
|
||||
{
|
||||
if (columnIndex != -1)
|
||||
Columns.ElementAt(columnIndex).Remove(h);
|
||||
|
||||
columnIndex = maniaObject.Column - firstColumnIndex;
|
||||
Columns.ElementAt(columnIndex).Add(h);
|
||||
}, true);
|
||||
|
||||
h.OnNewResult += OnNewResult;
|
||||
base.LoadComplete();
|
||||
NewResult += OnNewResult;
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject h)
|
||||
{
|
||||
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||
int columnIndex = maniaObject.Column - firstColumnIndex;
|
||||
Columns.ElementAt(columnIndex).Remove(h);
|
||||
public override void Add(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Add(hitObject);
|
||||
|
||||
h.OnNewResult -= OnNewResult;
|
||||
return true;
|
||||
}
|
||||
public override bool Remove(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Remove(hitObject);
|
||||
|
||||
public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Add(h);
|
||||
|
||||
public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h);
|
||||
|
||||
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Edit.Checks;
|
||||
@ -224,12 +225,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
|
||||
private void assertOk(IBeatmap beatmap)
|
||||
{
|
||||
Assert.That(check.Run(beatmap, new TestWorkingBeatmap(beatmap)), Is.Empty);
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
Assert.That(check.Run(context), Is.Empty);
|
||||
}
|
||||
|
||||
private void assertOffscreenCircle(IBeatmap beatmap)
|
||||
{
|
||||
var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle);
|
||||
@ -237,7 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
|
||||
private void assertOffscreenSlider(IBeatmap beatmap)
|
||||
{
|
||||
var issues = check.Run(beatmap, new TestWorkingBeatmap(beatmap)).ToList();
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider);
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 });
|
||||
|
||||
Add(drawableObject = new DrawableHitCircle(hitCircle));
|
||||
AddBlueprint(blueprint = new TestBlueprint(drawableObject));
|
||||
AddBlueprint(blueprint = new TestBlueprint(hitCircle), drawableObject);
|
||||
});
|
||||
|
||||
[Test]
|
||||
@ -63,8 +63,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public new HitCirclePiece CirclePiece => base.CirclePiece;
|
||||
|
||||
public TestBlueprint(DrawableHitCircle drawableCircle)
|
||||
: base(drawableCircle)
|
||||
public TestBlueprint(HitCircle circle)
|
||||
: base(circle)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 });
|
||||
|
||||
Add(drawableObject = new DrawableSlider(slider));
|
||||
AddBlueprint(new TestSliderBlueprint(drawableObject));
|
||||
AddBlueprint(new TestSliderBlueprint(slider), drawableObject);
|
||||
});
|
||||
|
||||
[Test]
|
||||
@ -150,23 +150,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
private class TestSliderBlueprint : SliderSelectionBlueprint
|
||||
{
|
||||
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
||||
public new TestSliderCircleBlueprint HeadBlueprint => (TestSliderCircleBlueprint)base.HeadBlueprint;
|
||||
public new TestSliderCircleBlueprint TailBlueprint => (TestSliderCircleBlueprint)base.TailBlueprint;
|
||||
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
||||
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
||||
public new PathControlPointVisualiser ControlPointVisualiser => base.ControlPointVisualiser;
|
||||
|
||||
public TestSliderBlueprint(DrawableSlider slider)
|
||||
public TestSliderBlueprint(Slider slider)
|
||||
: base(slider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override SliderCircleSelectionBlueprint CreateCircleSelectionBlueprint(DrawableSlider slider, SliderPosition position) => new TestSliderCircleBlueprint(slider, position);
|
||||
protected override SliderCircleOverlay CreateCircleOverlay(Slider slider, SliderPosition position) => new TestSliderCircleOverlay(slider, position);
|
||||
}
|
||||
|
||||
private class TestSliderCircleBlueprint : SliderCircleSelectionBlueprint
|
||||
private class TestSliderCircleOverlay : SliderCircleOverlay
|
||||
{
|
||||
public new HitCirclePiece CirclePiece => base.CirclePiece;
|
||||
|
||||
public TestSliderCircleBlueprint(DrawableSlider slider, SliderPosition position)
|
||||
public TestSliderCircleOverlay(Slider slider, SliderPosition position)
|
||||
: base(slider, position)
|
||||
{
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 });
|
||||
|
||||
Add(drawableObject = new DrawableSlider(slider));
|
||||
AddBlueprint(blueprint = new TestSliderBlueprint(drawableObject));
|
||||
AddBlueprint(blueprint = new TestSliderBlueprint(slider), drawableObject);
|
||||
});
|
||||
|
||||
[Test]
|
||||
@ -174,10 +174,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("body positioned correctly", () => blueprint.BodyPiece.Position == slider.StackedPosition);
|
||||
|
||||
AddAssert("head positioned correctly",
|
||||
() => Precision.AlmostEquals(blueprint.HeadBlueprint.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.HeadCircle.ScreenSpaceDrawQuad.Centre));
|
||||
() => Precision.AlmostEquals(blueprint.HeadOverlay.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.HeadCircle.ScreenSpaceDrawQuad.Centre));
|
||||
|
||||
AddAssert("tail positioned correctly",
|
||||
() => Precision.AlmostEquals(blueprint.TailBlueprint.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.TailCircle.ScreenSpaceDrawQuad.Centre));
|
||||
() => Precision.AlmostEquals(blueprint.TailOverlay.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.TailCircle.ScreenSpaceDrawQuad.Centre));
|
||||
}
|
||||
|
||||
private void moveMouseToControlPoint(int index)
|
||||
@ -195,23 +195,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
private class TestSliderBlueprint : SliderSelectionBlueprint
|
||||
{
|
||||
public new SliderBodyPiece BodyPiece => base.BodyPiece;
|
||||
public new TestSliderCircleBlueprint HeadBlueprint => (TestSliderCircleBlueprint)base.HeadBlueprint;
|
||||
public new TestSliderCircleBlueprint TailBlueprint => (TestSliderCircleBlueprint)base.TailBlueprint;
|
||||
public new TestSliderCircleOverlay HeadOverlay => (TestSliderCircleOverlay)base.HeadOverlay;
|
||||
public new TestSliderCircleOverlay TailOverlay => (TestSliderCircleOverlay)base.TailOverlay;
|
||||
public new PathControlPointVisualiser ControlPointVisualiser => base.ControlPointVisualiser;
|
||||
|
||||
public TestSliderBlueprint(DrawableSlider slider)
|
||||
public TestSliderBlueprint(Slider slider)
|
||||
: base(slider)
|
||||
{
|
||||
}
|
||||
|
||||
protected override SliderCircleSelectionBlueprint CreateCircleSelectionBlueprint(DrawableSlider slider, SliderPosition position) => new TestSliderCircleBlueprint(slider, position);
|
||||
protected override SliderCircleOverlay CreateCircleOverlay(Slider slider, SliderPosition position) => new TestSliderCircleOverlay(slider, position);
|
||||
}
|
||||
|
||||
private class TestSliderCircleBlueprint : SliderCircleSelectionBlueprint
|
||||
private class TestSliderCircleOverlay : SliderCircleOverlay
|
||||
{
|
||||
public new HitCirclePiece CirclePiece => base.CirclePiece;
|
||||
|
||||
public TestSliderCircleBlueprint(DrawableSlider slider, SliderPosition position)
|
||||
public TestSliderCircleOverlay(Slider slider, SliderPosition position)
|
||||
: base(slider, position)
|
||||
{
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
Child = drawableSpinner = new DrawableSpinner(spinner)
|
||||
});
|
||||
|
||||
AddBlueprint(new SpinnerSelectionBlueprint(drawableSpinner) { Size = new Vector2(0.5f) });
|
||||
AddBlueprint(new SpinnerSelectionBlueprint(spinner) { Size = new Vector2(0.5f) }, drawableSpinner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
Child = new SkinProvidingContainer(new DefaultSkin())
|
||||
Child = new SkinProvidingContainer(new DefaultSkin(null))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
|
||||
|
@ -34,6 +34,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private List<JudgementResult> judgementResults;
|
||||
|
||||
[Test]
|
||||
public void TestPressBothKeysSimultaneouslyAndReleaseOne()
|
||||
{
|
||||
performTest(new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame { Position = Vector2.Zero, Actions = { OsuAction.LeftButton, OsuAction.RightButton }, Time = time_slider_start },
|
||||
new OsuReplayFrame { Position = Vector2.Zero, Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
|
||||
});
|
||||
|
||||
AddAssert("Tracking retained", assertMaxJudge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scenario:
|
||||
/// - Press a key before a slider starts
|
||||
|
@ -5,12 +5,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
@ -32,6 +34,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpinningSamplePitchShift()
|
||||
{
|
||||
AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000)));
|
||||
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
|
||||
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
|
||||
|
||||
PausableSkinnableSound getSpinningSample() => drawableSpinner.ChildrenOfType<PausableSkinnableSound>().FirstOrDefault(s => s.Samples.Any(i => i.LookupNames.Any(l => l.Contains("spinnerspin"))));
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestLongSpinner(bool autoplay)
|
||||
@ -93,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
base.Update();
|
||||
if (auto)
|
||||
RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * 3));
|
||||
RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
</ItemGroup>
|
||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
||||
|
||||
protected readonly HitCirclePiece CirclePiece;
|
||||
|
||||
public HitCircleSelectionBlueprint(DrawableHitCircle drawableCircle)
|
||||
: base(drawableCircle)
|
||||
public HitCircleSelectionBlueprint(HitCircle circle)
|
||||
: base(circle)
|
||||
{
|
||||
InternalChild = CirclePiece = new HitCirclePiece();
|
||||
}
|
||||
|
@ -2,20 +2,20 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
||||
{
|
||||
public abstract class OsuSelectionBlueprint<T> : OverlaySelectionBlueprint
|
||||
public abstract class OsuSelectionBlueprint<T> : HitObjectSelectionBlueprint<T>
|
||||
where T : OsuHitObject
|
||||
{
|
||||
protected new T HitObject => (T)DrawableObject.HitObject;
|
||||
protected new DrawableOsuHitObject DrawableObject => (DrawableOsuHitObject)base.DrawableObject;
|
||||
|
||||
protected override bool AlwaysShowWhenSelected => true;
|
||||
|
||||
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
|
||||
: base(drawableObject)
|
||||
protected OsuSelectionBlueprint(T hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
||||
int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type.Value == type);
|
||||
|
||||
var item = new PathTypeMenuItem(type, () =>
|
||||
var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ =>
|
||||
{
|
||||
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
|
||||
updatePathType(p, type);
|
||||
@ -258,15 +258,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private class PathTypeMenuItem : TernaryStateMenuItem
|
||||
{
|
||||
public PathTypeMenuItem(PathType? type, Action action)
|
||||
: base(type == null ? "Inherit" : type.ToString().Humanize(), changeState, MenuItemType.Standard, _ => action?.Invoke())
|
||||
{
|
||||
}
|
||||
|
||||
private static TernaryState changeState(TernaryState state) => TernaryState.True;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
AccentColour = Color4.Transparent
|
||||
};
|
||||
|
||||
// SliderSelectionBlueprint relies on calling ReceivePositionalInputAt on this drawable to determine whether selection should occur.
|
||||
// Without AlwaysPresent, a movement in a parent container (ie. the editor composer area resizing) could cause incorrect input handling.
|
||||
AlwaysPresent = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -1,36 +1,32 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// 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.Containers;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
public class SliderCircleSelectionBlueprint : OsuSelectionBlueprint<Slider>
|
||||
public class SliderCircleOverlay : CompositeDrawable
|
||||
{
|
||||
protected readonly HitCirclePiece CirclePiece;
|
||||
|
||||
private readonly Slider slider;
|
||||
private readonly SliderPosition position;
|
||||
|
||||
public SliderCircleSelectionBlueprint(DrawableSlider slider, SliderPosition position)
|
||||
: base(slider)
|
||||
public SliderCircleOverlay(Slider slider, SliderPosition position)
|
||||
{
|
||||
this.slider = slider;
|
||||
this.position = position;
|
||||
|
||||
InternalChild = CirclePiece = new HitCirclePiece();
|
||||
|
||||
Select();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
CirclePiece.UpdateFrom(position == SliderPosition.Start ? (HitCircle)HitObject.HeadCircle : HitObject.TailCircle);
|
||||
CirclePiece.UpdateFrom(position == SliderPosition.Start ? (HitCircle)slider.HeadCircle : slider.TailCircle);
|
||||
}
|
||||
|
||||
// Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input.
|
||||
public override bool HandlePositionalInput => false;
|
||||
}
|
||||
}
|
@ -207,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
var lastPiece = controlPointVisualiser.Pieces.Single(p => p.ControlPoint == last);
|
||||
|
||||
lastPoint = last;
|
||||
return lastPiece?.IsHovered != true;
|
||||
return lastPiece.IsHovered != true;
|
||||
}
|
||||
|
||||
private void updateSlider()
|
||||
|
@ -26,15 +26,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
public class SliderSelectionBlueprint : OsuSelectionBlueprint<Slider>
|
||||
{
|
||||
protected new DrawableSlider DrawableObject => (DrawableSlider)base.DrawableObject;
|
||||
|
||||
protected SliderBodyPiece BodyPiece { get; private set; }
|
||||
protected SliderCircleSelectionBlueprint HeadBlueprint { get; private set; }
|
||||
protected SliderCircleSelectionBlueprint TailBlueprint { get; private set; }
|
||||
protected SliderCircleOverlay HeadOverlay { get; private set; }
|
||||
protected SliderCircleOverlay TailOverlay { get; private set; }
|
||||
|
||||
[CanBeNull]
|
||||
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
|
||||
|
||||
private readonly DrawableSlider slider;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private HitObjectComposer composer { get; set; }
|
||||
|
||||
@ -52,10 +52,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||
|
||||
public SliderSelectionBlueprint(DrawableSlider slider)
|
||||
public SliderSelectionBlueprint(Slider slider)
|
||||
: base(slider)
|
||||
{
|
||||
this.slider = slider;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -64,8 +63,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
BodyPiece = new SliderBodyPiece(),
|
||||
HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start),
|
||||
TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End),
|
||||
HeadOverlay = CreateCircleOverlay(HitObject, SliderPosition.Start),
|
||||
TailOverlay = CreateCircleOverlay(HitObject, SliderPosition.End),
|
||||
};
|
||||
}
|
||||
|
||||
@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
|
||||
protected override void OnSelected()
|
||||
{
|
||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(slider.HitObject, true)
|
||||
AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(HitObject, true)
|
||||
{
|
||||
RemoveControlPointsRequested = removeControlPoints
|
||||
});
|
||||
@ -215,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
}
|
||||
|
||||
// If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted
|
||||
if (controlPoints.Count <= 1 || !slider.HitObject.Path.HasValidLength)
|
||||
if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength)
|
||||
{
|
||||
placementHandler?.Delete(HitObject);
|
||||
return;
|
||||
@ -240,11 +239,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
||||
};
|
||||
|
||||
public override Vector2 ScreenSpaceSelectionPoint => BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation);
|
||||
// Always refer to the drawable object's slider body so subsequent movement deltas are calculated with updated positions.
|
||||
public override Vector2 ScreenSpaceSelectionPoint => DrawableObject.SliderBody?.ToScreenSpace(DrawableObject.SliderBody.PathOffset)
|
||||
?? BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation);
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||
BodyPiece.ReceivePositionalInputAt(screenSpacePos) || ControlPointVisualiser?.Pieces.Any(p => p.ReceivePositionalInputAt(screenSpacePos)) == true;
|
||||
|
||||
protected virtual SliderCircleSelectionBlueprint CreateCircleSelectionBlueprint(DrawableSlider slider, SliderPosition position) => new SliderCircleSelectionBlueprint(slider, position);
|
||||
protected virtual SliderCircleOverlay CreateCircleOverlay(Slider slider, SliderPosition position) => new SliderCircleOverlay(slider, position);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
|
||||
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
|
||||
{
|
||||
private readonly SpinnerPiece piece;
|
||||
|
||||
public SpinnerSelectionBlueprint(DrawableSpinner spinner)
|
||||
public SpinnerSelectionBlueprint(Spinner spinner)
|
||||
: base(spinner)
|
||||
{
|
||||
InternalChild = piece = new SpinnerPiece();
|
||||
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osuTK;
|
||||
@ -31,9 +31,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks
|
||||
new IssueTemplateOffscreenSlider(this)
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
foreach (var hitobject in playableBeatmap.HitObjects)
|
||||
foreach (var hitobject in context.Beatmap.HitObjects)
|
||||
{
|
||||
switch (hitobject)
|
||||
{
|
||||
|
@ -11,24 +11,25 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class DrawableOsuEditRuleset : DrawableOsuRuleset
|
||||
public class DrawableOsuEditorRuleset : DrawableOsuRuleset
|
||||
{
|
||||
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
public DrawableOsuEditorRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
|
||||
protected override Playfield CreatePlayfield() => new OsuEditorPlayfield();
|
||||
|
||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One };
|
||||
|
||||
private class OsuEditPlayfield : OsuPlayfield
|
||||
private class OsuEditorPlayfield : OsuPlayfield
|
||||
{
|
||||
private Bindable<bool> hitAnimations;
|
||||
|
||||
@ -56,43 +57,45 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
if (state == ArmedState.Idle || hitAnimations.Value)
|
||||
return;
|
||||
|
||||
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
||||
switch (hitObject)
|
||||
if (hitObject is DrawableHitCircle circle)
|
||||
{
|
||||
default:
|
||||
// there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.)
|
||||
return;
|
||||
circle.ApproachCircle
|
||||
.FadeOutFromOne(editor_hit_object_fade_out_extension * 4)
|
||||
.Expire();
|
||||
|
||||
case DrawableSlider _:
|
||||
// no specifics to sliders but let them fade slower below.
|
||||
break;
|
||||
|
||||
case DrawableHitCircle circle: // also handles slider heads
|
||||
circle.ApproachCircle
|
||||
.FadeOutFromOne(editor_hit_object_fade_out_extension * 4)
|
||||
.Expire();
|
||||
|
||||
circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
|
||||
|
||||
var circlePieceDrawable = circle.CirclePiece.Drawable;
|
||||
|
||||
// clear any explode animation logic.
|
||||
circlePieceDrawable.ApplyTransformsAt(circle.HitStateUpdateTime, true);
|
||||
circlePieceDrawable.ClearTransformsAfter(circle.HitStateUpdateTime, true);
|
||||
|
||||
break;
|
||||
circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
|
||||
}
|
||||
|
||||
// Get the existing fade out transform
|
||||
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
||||
if (hitObject is IHasMainCirclePiece mainPieceContainer)
|
||||
{
|
||||
// clear any explode animation logic.
|
||||
mainPieceContainer.CirclePiece.ApplyTransformsAt(hitObject.HitStateUpdateTime, true);
|
||||
mainPieceContainer.CirclePiece.ClearTransformsAfter(hitObject.HitStateUpdateTime, true);
|
||||
}
|
||||
|
||||
if (existing == null)
|
||||
return;
|
||||
if (hitObject is DrawableSliderRepeat repeat)
|
||||
{
|
||||
repeat.Arrow.ApplyTransformsAt(hitObject.HitStateUpdateTime, true);
|
||||
repeat.Arrow.ClearTransformsAfter(hitObject.HitStateUpdateTime, true);
|
||||
}
|
||||
|
||||
hitObject.RemoveTransform(existing);
|
||||
// adjust the visuals of top-level object types to make them stay on screen for longer than usual.
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableSlider _:
|
||||
case DrawableHitCircle _:
|
||||
// Get the existing fade out transform
|
||||
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
||||
|
||||
using (hitObject.BeginAbsoluteSequence(hitObject.HitStateUpdateTime))
|
||||
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
||||
if (existing == null)
|
||||
return;
|
||||
|
||||
hitObject.RemoveTransform(existing);
|
||||
|
||||
using (hitObject.BeginAbsoluteSequence(hitObject.HitStateUpdateTime))
|
||||
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||
using osu.Game.Rulesets.Osu.Edit.Checks;
|
||||
@ -17,9 +16,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
new CheckOffscreenObjects()
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(IBeatmap playableBeatmap, WorkingBeatmap workingBeatmap)
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
return checks.SelectMany(check => check.Run(playableBeatmap, workingBeatmap));
|
||||
return checks.SelectMany(check => check.Run(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
@ -18,23 +18,23 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
}
|
||||
|
||||
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
|
||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new OsuSelectionHandler();
|
||||
|
||||
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
|
||||
public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
case HitCircle circle:
|
||||
return new HitCircleSelectionBlueprint(circle);
|
||||
|
||||
case DrawableSlider slider:
|
||||
case Slider slider:
|
||||
return new SliderSelectionBlueprint(slider);
|
||||
|
||||
case DrawableSpinner spinner:
|
||||
case Spinner spinner:
|
||||
return new SpinnerSelectionBlueprint(spinner);
|
||||
}
|
||||
|
||||
return base.CreateBlueprintFor(hitObject);
|
||||
return base.CreateHitObjectBlueprintFor(hitObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
}
|
||||
|
||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
|
||||
=> new DrawableOsuEditorRuleset(ruleset, beatmap, mods);
|
||||
|
||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||
{
|
||||
@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
if (b.IsSelected)
|
||||
continue;
|
||||
|
||||
var hitObject = (OsuHitObject)b.HitObject;
|
||||
var hitObject = (OsuHitObject)b.Item;
|
||||
|
||||
Vector2? snap = checkSnap(hitObject.Position);
|
||||
if (snap == null && hitObject.Position != hitObject.EndPosition)
|
||||
|
@ -1,12 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -15,8 +15,19 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class OsuSelectionHandler : SelectionHandler
|
||||
public class OsuSelectionHandler : EditorSelectionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// During a transform, the initial origin is stored so it can be used throughout the operation.
|
||||
/// </summary>
|
||||
private Vector2? referenceOrigin;
|
||||
|
||||
/// <summary>
|
||||
/// During a transform, the initial path types of a single selected slider are stored so they
|
||||
/// can be maintained throughout the operation.
|
||||
/// </summary>
|
||||
private List<PathType?> referencePathTypes;
|
||||
|
||||
protected override void OnSelectionChanged()
|
||||
{
|
||||
base.OnSelectionChanged();
|
||||
@ -36,30 +47,19 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
referencePathTypes = null;
|
||||
}
|
||||
|
||||
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
||||
public override bool HandleMovement(MoveSelectionEvent<HitObject> moveEvent)
|
||||
{
|
||||
var hitObjects = selectedMovableObjects;
|
||||
|
||||
// this will potentially move the selection out of bounds...
|
||||
foreach (var h in hitObjects)
|
||||
h.Position += moveEvent.InstantDelta;
|
||||
h.Position += this.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta);
|
||||
|
||||
// but this will be corrected.
|
||||
moveSelectionInBounds();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// During a transform, the initial origin is stored so it can be used throughout the operation.
|
||||
/// </summary>
|
||||
private Vector2? referenceOrigin;
|
||||
|
||||
/// <summary>
|
||||
/// During a transform, the initial path types of a single selected slider are stored so they
|
||||
/// can be maintained throughout the operation.
|
||||
/// </summary>
|
||||
private List<PathType?> referencePathTypes;
|
||||
|
||||
public override bool HandleReverse()
|
||||
{
|
||||
var hitObjects = EditorBeatmap.SelectedHitObjects;
|
||||
@ -113,24 +113,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
var hitObjects = selectedMovableObjects;
|
||||
|
||||
var selectedObjectsQuad = getSurroundingQuad(hitObjects);
|
||||
var centre = selectedObjectsQuad.Centre;
|
||||
|
||||
foreach (var h in hitObjects)
|
||||
{
|
||||
var pos = h.Position;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case Direction.Horizontal:
|
||||
pos.X = centre.X - (pos.X - centre.X);
|
||||
break;
|
||||
|
||||
case Direction.Vertical:
|
||||
pos.Y = centre.Y - (pos.Y - centre.Y);
|
||||
break;
|
||||
}
|
||||
|
||||
h.Position = pos;
|
||||
h.Position = GetFlippedPosition(direction, selectedObjectsQuad, h.Position);
|
||||
|
||||
if (h is Slider slider)
|
||||
{
|
||||
@ -186,12 +172,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
foreach (var h in hitObjects)
|
||||
{
|
||||
h.Position = rotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
|
||||
h.Position = RotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
|
||||
|
||||
if (h is IHasPath path)
|
||||
{
|
||||
foreach (var point in path.Path.ControlPoints)
|
||||
point.Position.Value = rotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
|
||||
point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +189,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList();
|
||||
|
||||
Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value));
|
||||
Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value));
|
||||
|
||||
// Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0.
|
||||
scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size;
|
||||
@ -238,26 +224,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
||||
{
|
||||
scale = getClampedScale(hitObjects, reference, scale);
|
||||
|
||||
// move the selection before scaling if dragging from top or left anchors.
|
||||
float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0;
|
||||
float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0;
|
||||
|
||||
Quad selectionQuad = getSurroundingQuad(hitObjects);
|
||||
|
||||
foreach (var h in hitObjects)
|
||||
{
|
||||
var newPosition = h.Position;
|
||||
|
||||
// guard against no-ops and NaN.
|
||||
if (scale.X != 0 && selectionQuad.Width > 0)
|
||||
newPosition.X = selectionQuad.TopLeft.X + xOffset + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X);
|
||||
|
||||
if (scale.Y != 0 && selectionQuad.Height > 0)
|
||||
newPosition.Y = selectionQuad.TopLeft.Y + yOffset + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y);
|
||||
|
||||
h.Position = newPosition;
|
||||
}
|
||||
h.Position = GetScaledPosition(reference, scale, selectionQuad, h.Position);
|
||||
}
|
||||
|
||||
private (bool X, bool Y) isQuadInBounds(Quad quad)
|
||||
@ -332,7 +302,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The hit objects to calculate a quad for.</param>
|
||||
private Quad getSurroundingQuad(OsuHitObject[] hitObjects) =>
|
||||
getSurroundingQuad(hitObjects.SelectMany(h =>
|
||||
GetSurroundingQuad(hitObjects.SelectMany(h =>
|
||||
{
|
||||
if (h is IHasPath path)
|
||||
{
|
||||
@ -347,59 +317,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
return new[] { h.Position };
|
||||
}));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a gamefield-space quad surrounding the provided points.
|
||||
/// </summary>
|
||||
/// <param name="points">The points to calculate a quad for.</param>
|
||||
private Quad getSurroundingQuad(IEnumerable<Vector2> points)
|
||||
{
|
||||
if (!EditorBeatmap.SelectedHitObjects.Any())
|
||||
return new Quad();
|
||||
|
||||
Vector2 minPosition = new Vector2(float.MaxValue, float.MaxValue);
|
||||
Vector2 maxPosition = new Vector2(float.MinValue, float.MinValue);
|
||||
|
||||
// Go through all hitobjects to make sure they would remain in the bounds of the editor after movement, before any movement is attempted
|
||||
foreach (var p in points)
|
||||
{
|
||||
minPosition = Vector2.ComponentMin(minPosition, p);
|
||||
maxPosition = Vector2.ComponentMax(maxPosition, p);
|
||||
}
|
||||
|
||||
Vector2 size = maxPosition - minPosition;
|
||||
|
||||
return new Quad(minPosition.X, minPosition.Y, size.X, size.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All osu! hitobjects which can be moved/rotated/scaled.
|
||||
/// </summary>
|
||||
private OsuHitObject[] selectedMovableObjects => EditorBeatmap.SelectedHitObjects
|
||||
.OfType<OsuHitObject>()
|
||||
private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType<OsuHitObject>()
|
||||
.Where(h => !(h is Spinner))
|
||||
.ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Rotate a point around an arbitrary origin.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <param name="origin">The centre origin to rotate around.</param>
|
||||
/// <param name="angle">The angle to rotate (in degrees).</param>
|
||||
private static Vector2 rotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle)
|
||||
{
|
||||
angle = -angle;
|
||||
|
||||
point.X -= origin.X;
|
||||
point.Y -= origin.Y;
|
||||
|
||||
Vector2 ret;
|
||||
ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle));
|
||||
ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle));
|
||||
|
||||
ret.X += origin.X;
|
||||
ret.Y += origin.Y;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,53 +2,15 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToDrawableHitObjects
|
||||
public class OsuModBarrelRoll : ModBarrelRoll<OsuHitObject>, IApplicableToDrawableHitObjects
|
||||
{
|
||||
private float currentRotation;
|
||||
|
||||
[SettingSource("Roll speed", "Rotations per minute")]
|
||||
public BindableNumber<double> SpinSpeed { get; } = new BindableDouble(0.5)
|
||||
{
|
||||
MinValue = 0.02,
|
||||
MaxValue = 12,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Direction", "The direction of rotation")]
|
||||
public Bindable<RotationDirection> Direction { get; } = new Bindable<RotationDirection>(RotationDirection.Clockwise);
|
||||
|
||||
public override string Name => "Barrel Roll";
|
||||
public override string Acronym => "BR";
|
||||
public override string Description => "The whole playfield is on a wheel!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override string SettingDescription => $"{SpinSpeed.Value} rpm {Direction.Value.GetDescription().ToLowerInvariant()}";
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
playfield.Rotation = currentRotation = (Direction.Value == RotationDirection.Counterclockwise ? -1 : 1) * 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value);
|
||||
}
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
// scale the playfield to allow all hitobjects to stay within the visible region.
|
||||
drawableRuleset.Playfield.Scale = new Vector2(OsuPlayfield.BASE_SIZE.Y / OsuPlayfield.BASE_SIZE.X);
|
||||
}
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var d in drawables)
|
||||
@ -58,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
switch (d)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
circle.CirclePiece.Rotation = -currentRotation;
|
||||
circle.CirclePiece.Rotation = -CurrentRotation;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
@ -43,13 +43,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
base.ApplyIncreasedVisibilityState(hitObject, state);
|
||||
applyState(hitObject, true);
|
||||
}
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
base.ApplyNormalVisibilityState(hitObject, state);
|
||||
applyState(hitObject, false);
|
||||
}
|
||||
|
||||
@ -60,20 +58,20 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
OsuHitObject hitObject = drawableOsuObject.HitObject;
|
||||
|
||||
(double startTime, double duration) fadeOut = getFadeOutParameters(drawableOsuObject);
|
||||
(double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject);
|
||||
|
||||
switch (drawableObject)
|
||||
{
|
||||
case DrawableSliderTail _:
|
||||
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
|
||||
drawableObject.FadeOut(fadeOut.duration);
|
||||
using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
|
||||
drawableObject.FadeOut(fadeDuration);
|
||||
|
||||
break;
|
||||
|
||||
case DrawableSliderRepeat sliderRepeat:
|
||||
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
|
||||
using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
|
||||
// only apply to circle piece – reverse arrow is not affected by hidden.
|
||||
sliderRepeat.CirclePiece.FadeOut(fadeOut.duration);
|
||||
sliderRepeat.CirclePiece.FadeOut(fadeDuration);
|
||||
|
||||
break;
|
||||
|
||||
@ -88,23 +86,23 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
else
|
||||
{
|
||||
// we don't want to see the approach circle
|
||||
using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt, true))
|
||||
using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt))
|
||||
circle.ApproachCircle.Hide();
|
||||
}
|
||||
|
||||
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
|
||||
fadeTarget.FadeOut(fadeOut.duration);
|
||||
using (drawableObject.BeginAbsoluteSequence(fadeStartTime))
|
||||
fadeTarget.FadeOut(fadeDuration);
|
||||
break;
|
||||
|
||||
case DrawableSlider slider:
|
||||
using (slider.BeginAbsoluteSequence(fadeOut.startTime, true))
|
||||
slider.Body.FadeOut(fadeOut.duration, Easing.Out);
|
||||
using (slider.BeginAbsoluteSequence(fadeStartTime))
|
||||
slider.Body.FadeOut(fadeDuration, Easing.Out);
|
||||
|
||||
break;
|
||||
|
||||
case DrawableSliderTick sliderTick:
|
||||
using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime, true))
|
||||
sliderTick.FadeOut(fadeOut.duration);
|
||||
using (sliderTick.BeginAbsoluteSequence(fadeStartTime))
|
||||
sliderTick.FadeOut(fadeDuration);
|
||||
|
||||
break;
|
||||
|
||||
@ -112,14 +110,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
// hide elements we don't care about.
|
||||
// todo: hide background
|
||||
|
||||
using (spinner.BeginAbsoluteSequence(fadeOut.startTime, true))
|
||||
spinner.FadeOut(fadeOut.duration);
|
||||
using (spinner.BeginAbsoluteSequence(fadeStartTime))
|
||||
spinner.FadeOut(fadeDuration);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private (double startTime, double duration) getFadeOutParameters(DrawableOsuHitObject drawableObject)
|
||||
private (double fadeStartTime, double fadeDuration) getFadeOutParameters(DrawableOsuHitObject drawableObject)
|
||||
{
|
||||
switch (drawableObject)
|
||||
{
|
||||
@ -137,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
return getParameters(drawableObject.HitObject);
|
||||
}
|
||||
|
||||
static (double startTime, double duration) getParameters(OsuHitObject hitObject)
|
||||
static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject)
|
||||
{
|
||||
var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn;
|
||||
var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier;
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
Entry = null;
|
||||
}
|
||||
|
||||
private void onEntryInvalidated() => refreshPoints();
|
||||
private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints);
|
||||
|
||||
private void refreshPoints()
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableHitCircle : DrawableOsuHitObject
|
||||
public class DrawableHitCircle : DrawableOsuHitObject, IHasMainCirclePiece
|
||||
{
|
||||
public OsuAction? HitAction => HitArea.HitAction;
|
||||
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
|
||||
|
@ -34,7 +34,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
public override bool DisplayResult => !HitObject.OnlyJudgeNestedObjects;
|
||||
|
||||
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
|
||||
[CanBeNull]
|
||||
public PlaySliderBody SliderBody => Body.Drawable as PlaySliderBody;
|
||||
|
||||
public IBindable<int> PathVersion => pathVersion;
|
||||
private readonly Bindable<int> pathVersion = new Bindable<int>();
|
||||
@ -215,16 +216,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1);
|
||||
|
||||
Ball.UpdateProgress(completionProgress);
|
||||
sliderBody?.UpdateProgress(completionProgress);
|
||||
SliderBody?.UpdateProgress(completionProgress);
|
||||
|
||||
foreach (DrawableHitObject hitObject in NestedHitObjects)
|
||||
{
|
||||
if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(sliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(sliderBody?.SnakedEnd ?? 0));
|
||||
if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0));
|
||||
if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking;
|
||||
}
|
||||
|
||||
Size = sliderBody?.Size ?? Vector2.Zero;
|
||||
OriginPosition = sliderBody?.PathOffset ?? Vector2.Zero;
|
||||
Size = SliderBody?.Size ?? Vector2.Zero;
|
||||
OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero;
|
||||
|
||||
if (DrawSize != Vector2.Zero)
|
||||
{
|
||||
@ -238,7 +239,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
sliderBody?.RecyclePath();
|
||||
SliderBody?.RecyclePath();
|
||||
}
|
||||
|
||||
protected override void ApplySkin(ISkinSource skin, bool allowFallback)
|
||||
@ -324,7 +325,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
|
||||
if (sliderBody?.SnakingOut.Value == true)
|
||||
if (SliderBody?.SnakingOut.Value == true)
|
||||
Body.FadeOut(40); // short fade to allow for any body colour to smoothly disappear.
|
||||
break;
|
||||
}
|
||||
@ -332,7 +333,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
||||
}
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => sliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => SliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
private class DefaultSliderBody : PlaySliderBody
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
|
||||
public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking, IHasMainCirclePiece
|
||||
{
|
||||
public new SliderRepeat HitObject => (SliderRepeat)base.HitObject;
|
||||
|
||||
@ -26,9 +26,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private double animDuration;
|
||||
|
||||
public Drawable CirclePiece { get; private set; }
|
||||
public SkinnableDrawable CirclePiece { get; private set; }
|
||||
|
||||
public ReverseArrowPiece Arrow { get; private set; }
|
||||
|
||||
private Drawable scaleContainer;
|
||||
private ReverseArrowPiece arrow;
|
||||
|
||||
public override bool DisplayResult => false;
|
||||
|
||||
@ -53,11 +55,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
// no default for this; only visible in legacy skins.
|
||||
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty()),
|
||||
arrow = new ReverseArrowPiece(),
|
||||
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
Arrow = new ReverseArrowPiece(),
|
||||
}
|
||||
};
|
||||
|
||||
@ -91,6 +97,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
@ -102,8 +110,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
break;
|
||||
|
||||
case ArmedState.Hit:
|
||||
this.FadeOut(animDuration, Easing.Out)
|
||||
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
|
||||
this.FadeOut(animDuration, Easing.Out);
|
||||
|
||||
const float final_scale = 1.5f;
|
||||
|
||||
Arrow.ScaleTo(Scale * final_scale, animDuration, Easing.Out);
|
||||
CirclePiece.ScaleTo(Scale * final_scale, animDuration, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -139,18 +151,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
|
||||
float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
|
||||
while (Math.Abs(aimRotation - arrow.Rotation) > 180)
|
||||
aimRotation += aimRotation < arrow.Rotation ? 360 : -360;
|
||||
while (Math.Abs(aimRotation - Arrow.Rotation) > 180)
|
||||
aimRotation += aimRotation < Arrow.Rotation ? 360 : -360;
|
||||
|
||||
if (!hasRotation)
|
||||
{
|
||||
arrow.Rotation = aimRotation;
|
||||
Arrow.Rotation = aimRotation;
|
||||
hasRotation = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
|
||||
arrow.Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), arrow.Rotation, aimRotation, 0, 50, Easing.OutQuint);
|
||||
Arrow.Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), Arrow.Rotation, aimRotation, 0, 50, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,13 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking
|
||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking, IHasMainCirclePiece
|
||||
{
|
||||
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
|
||||
|
||||
@ -34,7 +35,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
public bool Tracking { get; set; }
|
||||
|
||||
private SkinnableDrawable circlePiece;
|
||||
public SkinnableDrawable CirclePiece { get; private set; }
|
||||
|
||||
private Container scaleContainer;
|
||||
|
||||
public DrawableSliderTail()
|
||||
@ -63,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Children = new Drawable[]
|
||||
{
|
||||
// no default for this; only visible in legacy skins.
|
||||
circlePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
|
||||
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -75,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
circlePiece.FadeInFromZero(HitObject.TimeFadeIn);
|
||||
CirclePiece.FadeInFromZero(HitObject.TimeFadeIn);
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
@ -84,6 +86,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Debug.Assert(HitObject.HitWindows != null);
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
|
@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private Bindable<bool> isSpinning;
|
||||
private bool spinnerFrequencyModulate;
|
||||
|
||||
private const float spinning_sample_initial_frequency = 1.0f;
|
||||
private const float spinning_sample_modulated_base_frequency = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of bonus score gained from spinning after the required number of spins, for display purposes.
|
||||
/// </summary>
|
||||
@ -106,9 +109,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
isSpinning.BindValueChanged(updateSpinningSample);
|
||||
}
|
||||
|
||||
private const float spinning_sample_initial_frequency = 1.0f;
|
||||
private const float spinning_sample_modulated_base_frequency = 0.5f;
|
||||
|
||||
protected override void OnFree()
|
||||
{
|
||||
base.OnFree();
|
||||
|
@ -0,0 +1,12 @@
|
||||
// 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.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public interface IHasMainCirclePiece
|
||||
{
|
||||
SkinnableDrawable CirclePiece { get; }
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -134,6 +135,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
/// </summary>
|
||||
private double? timeToAcceptAnyKeyAfter;
|
||||
|
||||
/// <summary>
|
||||
/// The actions that were pressed in the previous frame.
|
||||
/// </summary>
|
||||
private readonly List<OsuAction> lastPressedActions = new List<OsuAction>();
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -152,8 +158,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
var otherKey = headCircleHitAction == OsuAction.RightButton ? OsuAction.LeftButton : OsuAction.RightButton;
|
||||
|
||||
// we can return to accepting all keys if the initial head circle key is the *only* key pressed, or all keys have been released.
|
||||
if (actions?.Contains(otherKey) != true)
|
||||
// we can start accepting any key once all other keys have been released in the previous frame.
|
||||
if (!lastPressedActions.Contains(otherKey))
|
||||
timeToAcceptAnyKeyAfter = Time.Current;
|
||||
}
|
||||
|
||||
@ -164,6 +170,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
|
||||
// valid action
|
||||
(actions?.Any(isValidTrackingAction) ?? false);
|
||||
|
||||
lastPressedActions.Clear();
|
||||
if (actions != null)
|
||||
lastPressedActions.AddRange(actions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Scale = new Vector2(SPRITE_SCALE),
|
||||
Y = SPINNER_TOP_OFFSET + 115,
|
||||
},
|
||||
bonusCounter = new LegacySpriteText(source, LegacyFont.Score)
|
||||
bonusCounter = new LegacySpriteText(LegacyFont.Score)
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.TopCentre,
|
||||
@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Scale = new Vector2(SPRITE_SCALE),
|
||||
Position = new Vector2(-87, 445 + spm_hide_offset),
|
||||
},
|
||||
spmCounter = new LegacySpriteText(source, LegacyFont.Score)
|
||||
spmCounter = new LegacySpriteText(LegacyFont.Score)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopRight,
|
||||
|
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
|
||||
return new LegacySpriteText(Source, LegacyFont.HitCircle)
|
||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||
{
|
||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||
Scale = new Vector2(0.8f),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user