Merge branch 'master' into fl-skill
30
.github/ISSUE_TEMPLATE/01-bug-issues.md
vendored
@ -1,30 +0,0 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug or crash to desktop
|
||||
---
|
||||
|
||||
<!--
|
||||
IMPORTANT: Your issue may already be reported.
|
||||
|
||||
Please check:
|
||||
- Pinned issues, at the top of https://github.com/ppy/osu/issues
|
||||
- Current priority 0 issues at https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0
|
||||
- Search for your issue. If you find that it already exists, please respond with a reaction or add any further information that may be helpful.
|
||||
-->
|
||||
|
||||
|
||||
**Describe the bug:**
|
||||
|
||||
**Screenshots or videos showing encountered issue:**
|
||||
|
||||
**osu!lazer version:**
|
||||
|
||||
**Logs:**
|
||||
|
||||
<!--
|
||||
*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/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)
|
||||
-->
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,12 +1,12 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Suggestions or feature request
|
||||
url: https://github.com/ppy/osu/discussions/categories/ideas
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||
- name: Help
|
||||
url: https://github.com/ppy/osu/discussions/categories/q-a
|
||||
about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section!
|
||||
- name: Suggestions or feature request
|
||||
url: https://github.com/ppy/osu/discussions/categories/ideas
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||
- name: osu!stable issues
|
||||
url: https://github.com/ppy/osu-stable-issues
|
||||
about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports.
|
||||
about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ContentModelUserStore">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
|
@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK;
|
||||
@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Pippidon.UI
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(PippidonAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<PippidonAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case PippidonAction.MoveUp:
|
||||
changeLane(-1);
|
||||
@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Pippidon.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReleased(PippidonAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<PippidonAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -51,11 +51,11 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.913.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.907.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.918.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.916.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
<PackageReference Include="Realm" Version="10.3.0" />
|
||||
<PackageReference Include="Realm" Version="10.5.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
}
|
||||
|
||||
// disable keyboard controls
|
||||
public bool OnPressed(CatchAction action) => true;
|
||||
public bool OnPressed(KeyBindingPressEvent<CatchAction> e) => true;
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<CatchAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
@ -144,9 +145,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
Catcher.VisualDirection = Direction.Left;
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<CatchAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
@ -164,9 +165,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<CatchAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.9 KiB |
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
c.Add(CreateHitObject().With(h =>
|
||||
{
|
||||
h.HitObject.StartTime = START_TIME;
|
||||
h.HitObject.StartTime = Time.Current + 5000;
|
||||
h.AccentColour.Value = Color4.Orange;
|
||||
}));
|
||||
})
|
||||
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
c.Add(CreateHitObject().With(h =>
|
||||
{
|
||||
h.HitObject.StartTime = START_TIME;
|
||||
h.HitObject.StartTime = Time.Current + 5000;
|
||||
h.AccentColour.Value = Color4.Orange;
|
||||
}));
|
||||
})
|
||||
|
@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
/// </summary>
|
||||
public abstract class ManiaSkinnableTestScene : SkinnableTestScene
|
||||
{
|
||||
protected const double START_TIME = 1000000000;
|
||||
|
||||
[Cached(Type = typeof(IScrollingInfo))]
|
||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||
|
||||
@ -55,27 +53,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(1000);
|
||||
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ZeroScrollAlgorithm();
|
||||
}
|
||||
|
||||
private class ZeroScrollAlgorithm : IScrollAlgorithm
|
||||
{
|
||||
public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength)
|
||||
=> double.MinValue;
|
||||
|
||||
public float GetLength(double startTime, double endTime, double timeRange, float scrollLength)
|
||||
=> scrollLength;
|
||||
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
||||
=> (float)((time - START_TIME) / timeRange) * scrollLength;
|
||||
|
||||
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
|
||||
=> 0;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(5000);
|
||||
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AddStep("Hold key", () =>
|
||||
{
|
||||
clock.CurrentTime = 0;
|
||||
note.OnPressed(ManiaAction.Key1);
|
||||
note.OnPressed(new KeyBindingPressEvent<ManiaAction>(GetContainingInputManager().CurrentState, ManiaAction.Key1));
|
||||
});
|
||||
AddStep("progress time", () => clock.CurrentTime = 500);
|
||||
AddAssert("head is visible", () => note.Head.Alpha == 1);
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Default;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -253,12 +254,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
HoldBrokenTime = Time.Current;
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return false;
|
||||
|
||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||
@ -288,12 +289,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
isHitting.Value = true;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (AllJudged)
|
||||
return;
|
||||
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return;
|
||||
|
||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@ -43,9 +44,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
// it will be hidden along with its parenting hold note when required.
|
||||
}
|
||||
|
||||
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
||||
public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
|
||||
|
||||
public override void OnReleased(ManiaAction action)
|
||||
public override void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@ -68,9 +69,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
});
|
||||
}
|
||||
|
||||
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
||||
public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
|
||||
|
||||
public override void OnReleased(ManiaAction action)
|
||||
public override void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
@ -97,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
ApplyResult(r => r.Type = result);
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(ManiaAction action)
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return false;
|
||||
|
||||
if (CheckHittable?.Invoke(this, Time.Current) == false)
|
||||
@ -108,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return UpdateResult(true);
|
||||
}
|
||||
|
||||
public virtual void OnReleased(ManiaAction action)
|
||||
public virtual void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == Column.Action.Value)
|
||||
if (e.Action == Column.Action.Value)
|
||||
{
|
||||
light.FadeIn();
|
||||
light.ScaleTo(Vector2.One);
|
||||
@ -87,12 +88,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
// Todo: Should be 400 * 100 / CurrentBPM
|
||||
const double animation_length = 250;
|
||||
|
||||
if (action == Column.Action.Value)
|
||||
if (e.Action == Column.Action.Value)
|
||||
{
|
||||
light.FadeTo(0, animation_length);
|
||||
light.ScaleTo(new Vector2(1, 0), animation_length);
|
||||
|
@ -1,18 +1,18 @@
|
||||
// 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.Textures;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
public class LegacyHoldNoteHeadPiece : LegacyNotePiece
|
||||
{
|
||||
protected override Texture GetTexture(ISkinSource skin)
|
||||
protected override Drawable GetAnimation(ISkinSource skin)
|
||||
{
|
||||
// TODO: Should fallback to the head from default legacy skin instead of note.
|
||||
return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -18,12 +18,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
: new ValueChangedEvent<ScrollingDirection>(ScrollingDirection.Up, ScrollingDirection.Up));
|
||||
}
|
||||
|
||||
protected override Texture GetTexture(ISkinSource skin)
|
||||
protected override Drawable GetAnimation(ISkinSource skin)
|
||||
{
|
||||
// TODO: Should fallback to the head from default legacy skin instead of note.
|
||||
return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage)
|
||||
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage)
|
||||
?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
{
|
||||
upSprite.FadeTo(0);
|
||||
downSprite.FadeTo(1);
|
||||
@ -97,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
{
|
||||
upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1);
|
||||
downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0);
|
||||
|
@ -1,9 +1,11 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -19,7 +21,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
private Container directionContainer;
|
||||
private Sprite noteSprite;
|
||||
|
||||
[CanBeNull]
|
||||
private Drawable noteAnimation;
|
||||
|
||||
private float? minimumColumnWidth;
|
||||
|
||||
@ -39,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = noteSprite = new Sprite { Texture = GetTexture(skin) }
|
||||
Child = noteAnimation = GetAnimation(skin) ?? Empty()
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
@ -50,12 +54,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (noteSprite.Texture != null)
|
||||
Texture texture = null;
|
||||
|
||||
if (noteAnimation is Sprite sprite)
|
||||
texture = sprite.Texture;
|
||||
else if (noteAnimation is TextureAnimation textureAnimation && textureAnimation.FrameCount > 0)
|
||||
texture = textureAnimation.CurrentFrame;
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
// The height is scaled to the minimum column width, if provided.
|
||||
float minimumWidth = minimumColumnWidth ?? DrawWidth;
|
||||
|
||||
noteSprite.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), noteSprite.Texture.DisplayWidth);
|
||||
noteAnimation.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), texture.DisplayWidth);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,9 +83,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Texture GetTexture(ISkinSource skin) => GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
[CanBeNull]
|
||||
protected virtual Drawable GetAnimation(ISkinSource skin) => GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
|
||||
protected Texture GetTextureFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup)
|
||||
[CanBeNull]
|
||||
protected Drawable GetAnimationFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup)
|
||||
{
|
||||
string suffix = string.Empty;
|
||||
|
||||
@ -93,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
string noteImage = GetColumnSkinConfig<string>(skin, lookup)?.Value
|
||||
?? $"mania-note{FallbackColumnIndex}{suffix}";
|
||||
|
||||
return skin.GetTexture(noteImage, WrapMode.ClampToEdge, WrapMode.ClampToEdge);
|
||||
return skin.GetAnimation(noteImage, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@ -122,16 +123,16 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result)));
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return false;
|
||||
|
||||
sampleTriggerSource.Play();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
@ -91,16 +92,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint);
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == this.action.Value)
|
||||
if (e.Action == action.Value)
|
||||
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == this.action.Value)
|
||||
if (e.Action == action.Value)
|
||||
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -74,16 +75,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -101,16 +102,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
174
osu.Game.Rulesets.Osu.Tests/TestSceneCursorParticles.cs
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneCursorParticles : TestSceneOsuPlayer
|
||||
{
|
||||
protected override bool Autoplay => autoplay;
|
||||
protected override bool HasCustomSteps => true;
|
||||
|
||||
private bool autoplay;
|
||||
private IBeatmap currentBeatmap;
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skinManager { get; set; }
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentBeatmap ?? base.CreateBeatmap(ruleset);
|
||||
|
||||
[Test]
|
||||
public void TestLegacyBreakParticles()
|
||||
{
|
||||
LegacyCursorParticles cursorParticles = null;
|
||||
|
||||
createLegacyTest(false, () => new Beatmap
|
||||
{
|
||||
Breaks =
|
||||
{
|
||||
new BreakPeriod(8500, 10000),
|
||||
},
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 8000,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 11000,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
AddUntilStep("fetch cursor particles", () =>
|
||||
{
|
||||
cursorParticles = this.ChildrenOfType<LegacyCursorParticles>().SingleOrDefault();
|
||||
return cursorParticles != null;
|
||||
});
|
||||
|
||||
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||
|
||||
AddAssert("particles are being spawned", () => cursorParticles.Active);
|
||||
|
||||
AddStep("press left mouse button", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddStep("press right mouse button", () => InputManager.PressButton(MouseButton.Right));
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddStep("release left mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddStep("release right mouse button", () => InputManager.ReleaseButton(MouseButton.Right));
|
||||
|
||||
AddUntilStep("wait for beatmap start", () => !Player.IsBreakTime.Value);
|
||||
AddAssert("particle spawning stopped", () => !cursorParticles.Active);
|
||||
|
||||
AddUntilStep("wait for break", () => Player.IsBreakTime.Value);
|
||||
AddAssert("particles are being spawned", () => cursorParticles.Active);
|
||||
|
||||
AddUntilStep("wait for break end", () => !Player.IsBreakTime.Value);
|
||||
AddAssert("particle spawning stopped", () => !cursorParticles.Active);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLegacyKiaiParticles()
|
||||
{
|
||||
LegacyCursorParticles cursorParticles = null;
|
||||
DrawableSpinner spinner = null;
|
||||
DrawableSlider slider = null;
|
||||
|
||||
createLegacyTest(true, () =>
|
||||
{
|
||||
var controlPointInfo = new ControlPointInfo();
|
||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
|
||||
return new Beatmap
|
||||
{
|
||||
ControlPointInfo = controlPointInfo,
|
||||
HitObjects =
|
||||
{
|
||||
new Spinner
|
||||
{
|
||||
StartTime = 0,
|
||||
Duration = 1000,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
},
|
||||
new Slider
|
||||
{
|
||||
StartTime = 2500,
|
||||
RepeatCount = 0,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero),
|
||||
new PathControlPoint(new Vector2(100, 0)),
|
||||
})
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 4500,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
AddUntilStep("fetch cursor particles", () =>
|
||||
{
|
||||
cursorParticles = this.ChildrenOfType<LegacyCursorParticles>().SingleOrDefault();
|
||||
return cursorParticles != null;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for spinner tracking", () =>
|
||||
{
|
||||
spinner = this.ChildrenOfType<DrawableSpinner>().SingleOrDefault();
|
||||
return spinner?.RotationTracker.Tracking == true;
|
||||
});
|
||||
AddAssert("particles are being spawned", () => cursorParticles.Active);
|
||||
|
||||
AddUntilStep("spinner tracking stopped", () => !spinner.RotationTracker.Tracking);
|
||||
AddAssert("particle spawning stopped", () => !cursorParticles.Active);
|
||||
|
||||
AddUntilStep("wait for slider tracking", () =>
|
||||
{
|
||||
slider = this.ChildrenOfType<DrawableSlider>().SingleOrDefault();
|
||||
return slider?.Tracking.Value == true;
|
||||
});
|
||||
AddAssert("particles are being spawned", () => cursorParticles.Active);
|
||||
|
||||
AddUntilStep("slider tracking stopped", () => !slider.Tracking.Value);
|
||||
AddAssert("particle spawning stopped", () => !cursorParticles.Active);
|
||||
}
|
||||
|
||||
private void createLegacyTest(bool autoplay, Func<IBeatmap> beatmap) => CreateTest(() =>
|
||||
{
|
||||
AddStep("set beatmap", () =>
|
||||
{
|
||||
this.autoplay = autoplay;
|
||||
currentBeatmap = beatmap();
|
||||
});
|
||||
AddStep("setup default legacy skin", () =>
|
||||
{
|
||||
skinManager.CurrentSkinInfo.Value = skinManager.DefaultLegacySkin.SkinInfo;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Testing.Input;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
@ -143,9 +144,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
pressed = value;
|
||||
if (value)
|
||||
OnPressed(OsuAction.LeftButton);
|
||||
OnPressed(new KeyBindingPressEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton));
|
||||
else
|
||||
OnReleased(OsuAction.LeftButton);
|
||||
OnReleased(new KeyBindingReleaseEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@ -97,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
private void scheduleHit() => AddStep("schedule action", () =>
|
||||
{
|
||||
var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current;
|
||||
Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(OsuAction.LeftButton), delay);
|
||||
Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(new KeyBindingPressEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton)), delay);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Visual;
|
||||
@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
if (firstObject == null)
|
||||
return false;
|
||||
|
||||
var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable;
|
||||
var skinnable = firstObject.ApproachCircle;
|
||||
|
||||
if (skin == null && skinnable?.Drawable is Sprite)
|
||||
if (skin == null && skinnable?.Drawable is DefaultApproachCircle)
|
||||
// check for default skin provider
|
||||
return true;
|
||||
|
||||
|
@ -127,9 +127,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnPressed(PlatformAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case PlatformAction.Delete:
|
||||
return DeleteSelected();
|
||||
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(PlatformAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<PlatformAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public OsuAction? HitAction => HitArea.HitAction;
|
||||
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
|
||||
|
||||
public ApproachCircle ApproachCircle { get; private set; }
|
||||
public SkinnableDrawable ApproachCircle { get; private set; }
|
||||
public HitReceptor HitArea { get; private set; }
|
||||
public SkinnableDrawable CirclePiece { get; private set; }
|
||||
|
||||
@ -74,8 +75,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
ApproachCircle = new ApproachCircle
|
||||
ApproachCircle = new ProxyableSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ApproachCircle), _ => new DefaultApproachCircle())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
}
|
||||
@ -88,7 +92,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||
StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
||||
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -228,15 +231,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
CornerExponent = 2;
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
if (IsHovered && (Hit?.Invoke() ?? false))
|
||||
{
|
||||
HitAction = action;
|
||||
HitAction = e.Action;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -246,7 +249,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(OsuAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class ProxyableSkinnableDrawable : SkinnableDrawable
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public ProxyableSkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(component, defaultImplementation, confineMode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public SliderBall Ball { get; private set; }
|
||||
public SkinnableDrawable Body { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A target container which can be used to add top level elements to the slider's display.
|
||||
/// Intended to be used for proxy purposes only.
|
||||
/// </summary>
|
||||
public Container OverlayElementContainer { get; private set; }
|
||||
|
||||
public override bool DisplayResult => !HitObject.OnlyJudgeNestedObjects;
|
||||
|
||||
[CanBeNull]
|
||||
@ -65,6 +71,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
|
||||
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
|
||||
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
|
||||
OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, },
|
||||
Ball = new SliderBall(this)
|
||||
{
|
||||
GetInitialHitAction = () => HeadCircle.HitAction,
|
||||
@ -72,7 +80,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
|
||||
slidingSample = new PausableSkinnableSound { Looping = true }
|
||||
};
|
||||
|
||||
@ -179,6 +186,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
tailContainer.Clear(false);
|
||||
repeatContainer.Clear(false);
|
||||
tickContainer.Clear(false);
|
||||
|
||||
OverlayElementContainer.Clear(false);
|
||||
}
|
||||
|
||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
[CanBeNull]
|
||||
public Slider Slider => DrawableSlider?.HitObject;
|
||||
|
||||
protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
|
||||
public DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
|
||||
|
||||
public override bool DisplayResult => HitObject?.JudgeAsNormalHitCircle ?? base.DisplayResult;
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
[CanBeNull]
|
||||
public Slider Slider => DrawableSlider?.HitObject;
|
||||
|
||||
protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
|
||||
public DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
|
||||
|
||||
private double animDuration;
|
||||
|
||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
FollowPoint,
|
||||
Cursor,
|
||||
CursorTrail,
|
||||
CursorParticles,
|
||||
SliderScorePoint,
|
||||
ReverseArrow,
|
||||
HitCircleText,
|
||||
@ -18,5 +19,6 @@ namespace osu.Game.Rulesets.Osu
|
||||
SliderBall,
|
||||
SliderBody,
|
||||
SpinnerBody,
|
||||
ApproachCircle,
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class ApproachCircle : Container
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public ApproachCircle()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
Child = new SkinnableApproachCircle();
|
||||
}
|
||||
|
||||
private class SkinnableApproachCircle : SkinnableSprite
|
||||
{
|
||||
public SkinnableApproachCircle()
|
||||
: base("Gameplay/osu/approachcircle")
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefault(ISkinComponent component)
|
||||
{
|
||||
var drawable = base.CreateDefault(component);
|
||||
|
||||
// account for the sprite being used for the default approach circle being taken from stable,
|
||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
||||
drawable.Scale = new Vector2(128 / 118f);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class DefaultApproachCircle : SkinnableSprite
|
||||
{
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
|
||||
public DefaultApproachCircle()
|
||||
: base("Gameplay/osu/approachcircle")
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
accentColour.BindValueChanged(colour => Colour = colour.NewValue, true);
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefault(ISkinComponent component)
|
||||
{
|
||||
var drawable = base.CreateDefault(component);
|
||||
|
||||
// Although this is a non-legacy component, osu-resources currently stores approach circle as a legacy-like texture.
|
||||
// See LegacyApproachCircle for documentation as to why this is required.
|
||||
drawable.Scale = new Vector2(128 / 118f);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyApproachCircle : SkinnableSprite
|
||||
{
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
|
||||
public LegacyApproachCircle()
|
||||
: base("Gameplay/osu/approachcircle")
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefault(ISkinComponent component)
|
||||
{
|
||||
var drawable = base.CreateDefault(component);
|
||||
|
||||
// account for the sprite being used for the default approach circle being taken from stable,
|
||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
||||
drawable.Scale = new Vector2(128 / 118f);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
}
|
256
osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorParticles.cs
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCursorParticles : CompositeDrawable, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
public bool Active => breakSpewer?.Active.Value == true || kiaiSpewer?.Active.Value == true;
|
||||
|
||||
private LegacyCursorParticleSpewer breakSpewer;
|
||||
private LegacyCursorParticleSpewer kiaiSpewer;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private Player player { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuPlayfield playfield { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private GameplayBeatmap gameplayBeatmap { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private GameplayClock gameplayClock { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, OsuColour colours)
|
||||
{
|
||||
var texture = skin.GetTexture("star2");
|
||||
var starBreakAdditive = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.StarBreakAdditive)?.Value ?? new Color4(255, 182, 193, 255);
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
// stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation.
|
||||
texture.ScaleAdjust *= 1.6f;
|
||||
}
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
breakSpewer = new LegacyCursorParticleSpewer(texture, 20)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = starBreakAdditive,
|
||||
Direction = SpewDirection.None,
|
||||
},
|
||||
kiaiSpewer = new LegacyCursorParticleSpewer(texture, 60)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = starBreakAdditive,
|
||||
Direction = SpewDirection.None,
|
||||
},
|
||||
};
|
||||
|
||||
if (player != null)
|
||||
((IBindable<bool>)breakSpewer.Active).BindTo(player.IsBreakTime);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (playfield == null || gameplayBeatmap == null) return;
|
||||
|
||||
DrawableHitObject kiaiHitObject = null;
|
||||
|
||||
// Check whether currently in a kiai section first. This is only done as an optimisation to avoid enumerating AliveObjects when not necessary.
|
||||
if (gameplayBeatmap.ControlPointInfo.EffectPointAt(gameplayBeatmap.Time.Current).KiaiMode)
|
||||
kiaiHitObject = playfield.HitObjectContainer.AliveObjects.FirstOrDefault(isTracking);
|
||||
|
||||
kiaiSpewer.Active.Value = kiaiHitObject != null;
|
||||
}
|
||||
|
||||
private bool isTracking(DrawableHitObject h)
|
||||
{
|
||||
if (!h.HitObject.Kiai)
|
||||
return false;
|
||||
|
||||
switch (h)
|
||||
{
|
||||
case DrawableSlider slider:
|
||||
return slider.Tracking.Value;
|
||||
|
||||
case DrawableSpinner spinner:
|
||||
return spinner.RotationTracker.Tracking;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
{
|
||||
handleInput(e.Action, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
handleInput(e.Action, false);
|
||||
}
|
||||
|
||||
private bool leftPressed;
|
||||
private bool rightPressed;
|
||||
|
||||
private void handleInput(OsuAction action, bool pressed)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
leftPressed = pressed;
|
||||
break;
|
||||
|
||||
case OsuAction.RightButton:
|
||||
rightPressed = pressed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (leftPressed && rightPressed)
|
||||
breakSpewer.Direction = SpewDirection.Omni;
|
||||
else if (leftPressed)
|
||||
breakSpewer.Direction = SpewDirection.Left;
|
||||
else if (rightPressed)
|
||||
breakSpewer.Direction = SpewDirection.Right;
|
||||
else
|
||||
breakSpewer.Direction = SpewDirection.None;
|
||||
}
|
||||
|
||||
private class LegacyCursorParticleSpewer : ParticleSpewer, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
private const int particle_duration_min = 300;
|
||||
private const int particle_duration_max = 1000;
|
||||
|
||||
public SpewDirection Direction { get; set; }
|
||||
|
||||
protected override bool CanSpawnParticles => base.CanSpawnParticles && cursorScreenPosition.HasValue;
|
||||
protected override float ParticleGravity => 240;
|
||||
|
||||
public LegacyCursorParticleSpewer(Texture texture, int perSecond)
|
||||
: base(texture, perSecond, particle_duration_max)
|
||||
{
|
||||
Active.BindValueChanged(_ => resetVelocityCalculation());
|
||||
}
|
||||
|
||||
private Vector2? cursorScreenPosition;
|
||||
private Vector2 cursorVelocity;
|
||||
|
||||
private const double max_velocity_frame_length = 15;
|
||||
private double velocityFrameLength;
|
||||
private Vector2 totalPosDifference;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
if (cursorScreenPosition == null)
|
||||
{
|
||||
cursorScreenPosition = e.ScreenSpaceMousePosition;
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
// calculate cursor velocity.
|
||||
totalPosDifference += e.ScreenSpaceMousePosition - cursorScreenPosition.Value;
|
||||
cursorScreenPosition = e.ScreenSpaceMousePosition;
|
||||
|
||||
velocityFrameLength += Math.Abs(Clock.ElapsedFrameTime);
|
||||
|
||||
if (velocityFrameLength > max_velocity_frame_length)
|
||||
{
|
||||
cursorVelocity = totalPosDifference / (float)velocityFrameLength;
|
||||
|
||||
totalPosDifference = Vector2.Zero;
|
||||
velocityFrameLength = 0;
|
||||
}
|
||||
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private void resetVelocityCalculation()
|
||||
{
|
||||
cursorScreenPosition = null;
|
||||
totalPosDifference = Vector2.Zero;
|
||||
velocityFrameLength = 0;
|
||||
}
|
||||
|
||||
protected override FallingParticle CreateParticle() =>
|
||||
new FallingParticle
|
||||
{
|
||||
StartPosition = ToLocalSpace(cursorScreenPosition ?? Vector2.Zero),
|
||||
Duration = RNG.NextSingle(particle_duration_min, particle_duration_max),
|
||||
StartAngle = (float)(RNG.NextDouble() * 4 - 2),
|
||||
EndAngle = RNG.NextSingle(-2f, 2f),
|
||||
EndScale = RNG.NextSingle(2f),
|
||||
Velocity = getVelocity(),
|
||||
};
|
||||
|
||||
private Vector2 getVelocity()
|
||||
{
|
||||
Vector2 velocity = Vector2.Zero;
|
||||
|
||||
switch (Direction)
|
||||
{
|
||||
case SpewDirection.Left:
|
||||
velocity = new Vector2(
|
||||
RNG.NextSingle(-460f, 0),
|
||||
RNG.NextSingle(-40f, 40f)
|
||||
);
|
||||
break;
|
||||
|
||||
case SpewDirection.Right:
|
||||
velocity = new Vector2(
|
||||
RNG.NextSingle(0, 460f),
|
||||
RNG.NextSingle(-40f, 40f)
|
||||
);
|
||||
break;
|
||||
|
||||
case SpewDirection.Omni:
|
||||
velocity = new Vector2(
|
||||
RNG.NextSingle(-460f, 460f),
|
||||
RNG.NextSingle(-160f, 160f)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
velocity += cursorVelocity * 40;
|
||||
|
||||
return velocity;
|
||||
}
|
||||
}
|
||||
|
||||
private enum SpewDirection
|
||||
{
|
||||
None,
|
||||
Left,
|
||||
Right,
|
||||
Omni,
|
||||
}
|
||||
}
|
||||
}
|
@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
}
|
||||
|
||||
private Container circleSprites;
|
||||
private Drawable hitCircleSprite;
|
||||
private Drawable hitCircleOverlay;
|
||||
|
||||
protected Drawable HitCircleOverlay { get; private set; }
|
||||
|
||||
private SkinnableSpriteText hitCircleText;
|
||||
|
||||
@ -70,28 +70,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin).
|
||||
Texture overlayTexture = getTextureWithFallback("overlay");
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
circleSprites = new Container
|
||||
hitCircleSprite = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = baseTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
HitCircleOverlay = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = overlayTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
hitCircleSprite = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = baseTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
hitCircleOverlay = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = overlayTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -111,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
|
||||
|
||||
if (overlayAboveNumber)
|
||||
AddInternal(hitCircleOverlay.CreateProxy());
|
||||
ChangeInternalChildDepth(HitCircleOverlay, float.MinValue);
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
@ -153,8 +144,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
|
||||
HitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
HitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
|
||||
if (hasNumber)
|
||||
{
|
||||
|
67
osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs
Normal file
@ -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 System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyReverseArrow : CompositeDrawable
|
||||
{
|
||||
private ISkin skin { get; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableHitObject drawableHitObject { get; set; }
|
||||
|
||||
private Drawable proxy;
|
||||
|
||||
public LegacyReverseArrow(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
string lookupName = new OsuSkinComponent(OsuSkinComponents.ReverseArrow).LookupName;
|
||||
|
||||
InternalChild = skin.GetAnimation(lookupName, true, true) ?? Empty();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
proxy = CreateProxy();
|
||||
|
||||
if (drawableHitObject != null)
|
||||
{
|
||||
drawableHitObject.HitObjectApplied += onHitObjectApplied;
|
||||
onHitObjectApplied(drawableHitObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void onHitObjectApplied(DrawableHitObject drawableObject)
|
||||
{
|
||||
Debug.Assert(proxy.Parent == null);
|
||||
|
||||
// see logic in LegacySliderHeadHitCircle.
|
||||
(drawableObject as DrawableSliderRepeat)?.DrawableSlider
|
||||
.OverlayElementContainer.Add(proxy);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
if (drawableHitObject != null)
|
||||
drawableHitObject.HitObjectApplied -= onHitObjectApplied;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacySliderHeadHitCircle : LegacyMainCirclePiece
|
||||
{
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableHitObject drawableHitObject { get; set; }
|
||||
|
||||
private Drawable proxiedHitCircleOverlay;
|
||||
|
||||
public LegacySliderHeadHitCircle()
|
||||
: base("sliderstartcircle")
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
proxiedHitCircleOverlay = HitCircleOverlay.CreateProxy();
|
||||
|
||||
if (drawableHitObject != null)
|
||||
{
|
||||
drawableHitObject.HitObjectApplied += onHitObjectApplied;
|
||||
onHitObjectApplied(drawableHitObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void onHitObjectApplied(DrawableHitObject drawableObject)
|
||||
{
|
||||
Debug.Assert(proxiedHitCircleOverlay.Parent == null);
|
||||
|
||||
// see logic in LegacyReverseArrow.
|
||||
(drawableObject as DrawableSliderHead)?.DrawableSlider
|
||||
.OverlayElementContainer.Add(proxiedHitCircleOverlay.With(d => d.Depth = float.MinValue));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableHitObject != null)
|
||||
drawableHitObject.HitObjectApplied -= onHitObjectApplied;
|
||||
}
|
||||
}
|
||||
}
|
@ -67,7 +67,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
case OsuSkinComponents.SliderHeadHitCircle:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyMainCirclePiece("sliderstartcircle");
|
||||
return new LegacySliderHeadHitCircle();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ReverseArrow:
|
||||
if (hasHitCircle.Value)
|
||||
return new LegacyReverseArrow(this);
|
||||
|
||||
return null;
|
||||
|
||||
@ -89,6 +95,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorParticles:
|
||||
if (GetTexture("star2") != null)
|
||||
return new LegacyCursorParticles();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitCircleText:
|
||||
if (!this.HasFont(LegacyFont.HitCircle))
|
||||
return null;
|
||||
@ -108,6 +120,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return new LegacyOldStyleSpinner();
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.ApproachCircle:
|
||||
return new LegacyApproachCircle();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
SliderBorder,
|
||||
SliderBall,
|
||||
SpinnerBackground,
|
||||
StarBreakAdditive,
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
@ -42,7 +43,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
InternalChild = fadeContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling)
|
||||
Children = new[]
|
||||
{
|
||||
cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling),
|
||||
new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -115,9 +120,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
(ActiveCursor as OsuCursor)?.Contract();
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
@ -129,9 +134,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(OsuAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
|
@ -24,6 +24,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
[Cached]
|
||||
public class OsuPlayfield : Playfield
|
||||
{
|
||||
private readonly PlayfieldBorder playfieldBorder;
|
||||
|
@ -89,9 +89,9 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(OsuAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -37,6 +38,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
Result.Type = Type;
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
@ -30,6 +31,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
nestedStrongHit.Result.Type = hitBoth ? Type : HitResult.Miss;
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
|
@ -36,24 +36,27 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public TaikoFlashlight(TaikoPlayfield taikoPlayfield)
|
||||
{
|
||||
this.taikoPlayfield = taikoPlayfield;
|
||||
FlashlightSize = new Vector2(0, getSizeFor(0));
|
||||
FlashlightSize = getSizeFor(0);
|
||||
|
||||
AddLayout(flashlightProperties);
|
||||
}
|
||||
|
||||
private float getSizeFor(int combo)
|
||||
private Vector2 getSizeFor(int combo)
|
||||
{
|
||||
float size = default_flashlight_size;
|
||||
|
||||
if (combo > 200)
|
||||
return default_flashlight_size * 0.8f;
|
||||
size *= 0.8f;
|
||||
else if (combo > 100)
|
||||
return default_flashlight_size * 0.9f;
|
||||
else
|
||||
return default_flashlight_size;
|
||||
size *= 0.9f;
|
||||
|
||||
// Preserve flashlight size through the playfield's aspect adjustment.
|
||||
return new Vector2(0, size * taikoPlayfield.DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT);
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
{
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
|
||||
this.TransformTo(nameof(FlashlightSize), getSizeFor(e.NewValue), FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
|
||||
protected override string FragmentShader => "CircularFlashlight";
|
||||
@ -64,7 +67,11 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
|
||||
if (!flashlightProperties.IsValid)
|
||||
{
|
||||
FlashlightPosition = taikoPlayfield.HitTarget.ToSpaceOfOtherDrawable(taikoPlayfield.HitTarget.OriginPosition, this);
|
||||
FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre);
|
||||
|
||||
ClearTransforms(targetMember: nameof(FlashlightSize));
|
||||
FlashlightSize = getSizeFor(Combo.Value);
|
||||
|
||||
flashlightProperties.Validate();
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -112,7 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollBody),
|
||||
_ => new ElongatedCirclePiece());
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
|
||||
private void onNewResult(DrawableHitObject obj, JudgementResult result)
|
||||
{
|
||||
@ -196,7 +197,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
JudgementType = action == TaikoAction.LeftRim || action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre;
|
||||
JudgementType = e.Action == TaikoAction.LeftRim || e.Action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre;
|
||||
return UpdateResult(true);
|
||||
}
|
||||
|
||||
@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -145,19 +146,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = result);
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
if (pressHandledThisFrame)
|
||||
return true;
|
||||
if (Judged)
|
||||
return false;
|
||||
|
||||
validActionPressed = HitActions.Contains(action);
|
||||
validActionPressed = HitActions.Contains(e.Action);
|
||||
|
||||
// Only count this as handled if the new judgement is a hit
|
||||
var result = UpdateResult(true);
|
||||
if (IsHit)
|
||||
HitAction = action;
|
||||
HitAction = e.Action;
|
||||
|
||||
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
|
||||
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
|
||||
@ -165,11 +166,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void OnReleased(TaikoAction action)
|
||||
public override void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||
{
|
||||
if (action == HitAction)
|
||||
if (e.Action == HitAction)
|
||||
HitAction = null;
|
||||
base.OnReleased(action);
|
||||
base.OnReleased(e);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -265,7 +266,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
// Don't process actions until the main hitobject is hit
|
||||
if (!ParentHitObject.IsHit)
|
||||
@ -276,7 +277,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
return false;
|
||||
|
||||
// Don't handle invalid hit action presses
|
||||
if (!ParentHitObject.HitActions.Contains(action))
|
||||
if (!ParentHitObject.HitActions.Contains(e.Action))
|
||||
return false;
|
||||
|
||||
return UpdateResult(true);
|
||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Skinning.Default;
|
||||
@ -266,13 +267,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
private bool? lastWasCentre;
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
// Don't handle keys before the swell starts
|
||||
if (Time.Current < HitObject.StartTime)
|
||||
return false;
|
||||
|
||||
var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre;
|
||||
var isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre;
|
||||
|
||||
// Ensure alternating centre and rim hits
|
||||
if (lastWasCentre == isCentre)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Taiko.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
}
|
||||
|
||||
public override bool OnPressed(TaikoAction action) => false;
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
|
||||
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick),
|
||||
_ => new TickPiece());
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
/// </summary>
|
||||
public Drawable CreateProxiedContent() => proxiedContent.CreateProxy();
|
||||
|
||||
public abstract bool OnPressed(TaikoAction action);
|
||||
public abstract bool OnPressed(KeyBindingPressEvent<TaikoAction> e);
|
||||
|
||||
public virtual void OnReleased(TaikoAction action)
|
||||
public virtual void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Skinning;
|
||||
@ -141,16 +142,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
Centre.Texture = skin.GetTexture(@"taiko-drum-inner");
|
||||
}
|
||||
|
||||
public bool OnPressed(TaikoAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
Drawable target = null;
|
||||
|
||||
if (action == CentreAction)
|
||||
if (e.Action == CentreAction)
|
||||
{
|
||||
target = Centre;
|
||||
sampleTriggerSource.Play(HitType.Centre);
|
||||
}
|
||||
else if (action == RimAction)
|
||||
else if (e.Action == RimAction)
|
||||
{
|
||||
target = Rim;
|
||||
sampleTriggerSource.Play(HitType.Rim);
|
||||
@ -173,7 +174,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(TaikoAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@ -151,19 +152,19 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
[Resolved(canBeNull: true)]
|
||||
private GameplayClock gameplayClock { get; set; }
|
||||
|
||||
public bool OnPressed(TaikoAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
Drawable target = null;
|
||||
Drawable back = null;
|
||||
|
||||
if (action == CentreAction)
|
||||
if (e.Action == CentreAction)
|
||||
{
|
||||
target = centreHit;
|
||||
back = centre;
|
||||
|
||||
sampleTriggerSource.Play(HitType.Centre);
|
||||
}
|
||||
else if (action == RimAction)
|
||||
else if (e.Action == RimAction)
|
||||
{
|
||||
target = rimHit;
|
||||
back = rim;
|
||||
@ -195,7 +196,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(TaikoAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -424,14 +424,14 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
checkBeatmapCount(osu, 12);
|
||||
checkSingleReferencedFileCount(osu, 18);
|
||||
|
||||
var breakTemp = TestResources.GetTestBeatmapForImport();
|
||||
var brokenTempFilename = TestResources.GetTestBeatmapForImport();
|
||||
|
||||
MemoryStream brokenOsu = new MemoryStream();
|
||||
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(breakTemp));
|
||||
MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename));
|
||||
|
||||
File.Delete(breakTemp);
|
||||
File.Delete(brokenTempFilename);
|
||||
|
||||
using (var outStream = File.Open(breakTemp, FileMode.CreateNew))
|
||||
using (var outStream = File.Open(brokenTempFilename, FileMode.CreateNew))
|
||||
using (var zip = ZipArchive.Open(brokenOsz))
|
||||
{
|
||||
zip.AddEntry("broken.osu", brokenOsu, false);
|
||||
@ -441,7 +441,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
// this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
|
||||
try
|
||||
{
|
||||
await manager.Import(new ImportTask(breakTemp));
|
||||
await manager.Import(new ImportTask(brokenTempFilename));
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -456,6 +456,8 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
checkSingleReferencedFileCount(osu, 18);
|
||||
|
||||
Assert.AreEqual(1, loggedExceptionCount);
|
||||
|
||||
File.Delete(brokenTempFilename);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -1,9 +1,11 @@
|
||||
// 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.IO;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Testing;
|
||||
|
||||
namespace osu.Game.Tests.Resources
|
||||
{
|
||||
@ -11,6 +13,8 @@ namespace osu.Game.Tests.Resources
|
||||
{
|
||||
public const double QUICK_BEATMAP_LENGTH = 10000;
|
||||
|
||||
private static readonly TemporaryNativeStorage temp_storage = new TemporaryNativeStorage("TestResources");
|
||||
|
||||
public static DllResourceStore GetStore() => new DllResourceStore(typeof(TestResources).Assembly);
|
||||
|
||||
public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}");
|
||||
@ -25,7 +29,7 @@ namespace osu.Game.Tests.Resources
|
||||
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||
public static string GetQuickTestBeatmapForImport()
|
||||
{
|
||||
var tempPath = Path.GetTempFileName() + ".osz";
|
||||
var tempPath = getTempFilename();
|
||||
using (var stream = OpenResource("Archives/241526 Soleily - Renatus_virtual_quick.osz"))
|
||||
using (var newFile = File.Create(tempPath))
|
||||
stream.CopyTo(newFile);
|
||||
@ -41,7 +45,7 @@ namespace osu.Game.Tests.Resources
|
||||
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||
public static string GetTestBeatmapForImport(bool virtualTrack = false)
|
||||
{
|
||||
var tempPath = Path.GetTempFileName() + ".osz";
|
||||
var tempPath = getTempFilename();
|
||||
|
||||
using (var stream = GetTestBeatmapStream(virtualTrack))
|
||||
using (var newFile = File.Create(tempPath))
|
||||
@ -50,5 +54,7 @@ namespace osu.Game.Tests.Resources
|
||||
Assert.IsTrue(File.Exists(tempPath));
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz");
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,59 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClockPositionPreservedBetweenSwitches()
|
||||
{
|
||||
BeatmapInfo targetDifficulty = null;
|
||||
AddStep("seek editor to 00:05:00", () => EditorClock.Seek(5000));
|
||||
|
||||
AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
|
||||
switchToDifficulty(() => targetDifficulty);
|
||||
confirmEditingBeatmap(() => targetDifficulty);
|
||||
AddAssert("editor clock at 00:05:00", () => EditorClock.CurrentTime == 5000);
|
||||
|
||||
AddStep("exit editor", () => Stack.Exit());
|
||||
// ensure editor loader didn't resume.
|
||||
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClipboardPreservedAfterSwitch([Values] bool sameRuleset)
|
||||
{
|
||||
BeatmapInfo targetDifficulty = null;
|
||||
|
||||
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First()));
|
||||
AddStep("copy object", () => Editor.Copy());
|
||||
|
||||
AddStep("set target difficulty", () =>
|
||||
{
|
||||
targetDifficulty = sameRuleset
|
||||
? importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID == Beatmap.Value.BeatmapInfo.RulesetID)
|
||||
: importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID != Beatmap.Value.BeatmapInfo.RulesetID);
|
||||
});
|
||||
switchToDifficulty(() => targetDifficulty);
|
||||
confirmEditingBeatmap(() => targetDifficulty);
|
||||
|
||||
AddAssert("no objects selected", () => !EditorBeatmap.SelectedHitObjects.Any());
|
||||
AddStep("paste object", () => Editor.Paste());
|
||||
|
||||
if (sameRuleset)
|
||||
AddAssert("object was pasted", () => EditorBeatmap.SelectedHitObjects.Any());
|
||||
else
|
||||
AddAssert("object was not pasted", () => !EditorBeatmap.SelectedHitObjects.Any());
|
||||
|
||||
AddStep("exit editor", () => Stack.Exit());
|
||||
|
||||
if (sameRuleset)
|
||||
{
|
||||
AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog);
|
||||
AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction());
|
||||
}
|
||||
|
||||
// ensure editor loader didn't resume.
|
||||
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPreventSwitchDueToUnsavedChanges()
|
||||
{
|
||||
@ -118,7 +171,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private void confirmEditingBeatmap(Func<BeatmapInfo> targetDifficulty)
|
||||
{
|
||||
AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke()));
|
||||
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
|
||||
AddUntilStep("current screen is editor", () => Stack.CurrentScreen == Editor && Editor?.IsLoaded == true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
@ -137,6 +138,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClear()
|
||||
{
|
||||
AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1));
|
||||
|
||||
AddStep("hit", () => newJudgement(0.2D));
|
||||
AddAssert("bar added", () => this.ChildrenOfType<BarHitErrorMeter>().All(
|
||||
meter => meter.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Count() == 1));
|
||||
AddAssert("circle added", () => this.ChildrenOfType<ColourHitErrorMeter>().All(
|
||||
meter => meter.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Count() == 1));
|
||||
|
||||
AddStep("clear", () => this.ChildrenOfType<HitErrorMeter>().ForEach(meter => meter.Clear()));
|
||||
|
||||
AddAssert("bar cleared", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("colour cleared", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||
}
|
||||
|
||||
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
|
||||
{
|
||||
hitWindows?.SetDifficulty(overallDifficulty);
|
||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
@ -80,13 +81,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public bool ReceivedAction;
|
||||
|
||||
public bool OnPressed(TestAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
ReceivedAction = action == TestAction.Down;
|
||||
ReceivedAction = e.Action == TestAction.Down;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(TestAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
128
osu.Game.Tests/Visual/Gameplay/TestSceneParticleSpewer.cs
Normal file
@ -0,0 +1,128 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneParticleSpewer : OsuTestScene
|
||||
{
|
||||
private TestParticleSpewer spewer;
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skinManager { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = spewer = createSpewer();
|
||||
|
||||
AddToggleStep("toggle spawning", value => spewer.Active.Value = value);
|
||||
AddSliderStep("particle gravity", 0f, 1f, 0f, value => spewer.Gravity = value);
|
||||
AddSliderStep("particle velocity", 0f, 1f, 0.5f, value => spewer.MaxVelocity = value);
|
||||
AddStep("move to new location", () =>
|
||||
{
|
||||
spewer.TransformTo(nameof(spewer.SpawnPosition), new Vector2(RNG.NextSingle(), RNG.NextSingle()), 1000, Easing.Out);
|
||||
});
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create spewer", () => Child = spewer = createSpewer());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPresence()
|
||||
{
|
||||
AddStep("start spewer", () => spewer.Active.Value = true);
|
||||
AddAssert("is present", () => spewer.IsPresent);
|
||||
|
||||
AddWaitStep("wait for some particles", 3);
|
||||
AddStep("stop spewer", () => spewer.Active.Value = false);
|
||||
|
||||
AddWaitStep("wait for clean screen", 8);
|
||||
AddAssert("is not present", () => !spewer.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTimeJumps()
|
||||
{
|
||||
ManualClock testClock = new ManualClock();
|
||||
|
||||
AddStep("prepare clock", () =>
|
||||
{
|
||||
testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -3;
|
||||
spewer.Clock = new FramedClock(testClock);
|
||||
});
|
||||
AddStep("start spewer", () => spewer.Active.Value = true);
|
||||
AddAssert("spawned first particle", () => spewer.TotalCreatedParticles == 1);
|
||||
|
||||
AddStep("move clock forward", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * 3);
|
||||
AddAssert("spawned second particle", () => spewer.TotalCreatedParticles == 2);
|
||||
|
||||
AddStep("move clock backwards", () => testClock.CurrentTime = TestParticleSpewer.MAX_DURATION * -1);
|
||||
AddAssert("spawned third particle", () => spewer.TotalCreatedParticles == 3);
|
||||
}
|
||||
|
||||
private TestParticleSpewer createSpewer() =>
|
||||
new TestParticleSpewer(skinManager.DefaultLegacySkin.GetTexture("star2"))
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Position = new Vector2(0.5f),
|
||||
Size = new Vector2(0.5f),
|
||||
};
|
||||
|
||||
private class TestParticleSpewer : ParticleSpewer
|
||||
{
|
||||
public const int MAX_DURATION = 1500;
|
||||
private const int rate = 250;
|
||||
|
||||
public int TotalCreatedParticles { get; private set; }
|
||||
|
||||
public float Gravity;
|
||||
|
||||
public float MaxVelocity = 0.25f;
|
||||
|
||||
public Vector2 SpawnPosition { get; set; } = new Vector2(0.5f);
|
||||
|
||||
protected override float ParticleGravity => Gravity;
|
||||
|
||||
public TestParticleSpewer(Texture texture)
|
||||
: base(texture, rate, MAX_DURATION)
|
||||
{
|
||||
}
|
||||
|
||||
protected override FallingParticle CreateParticle()
|
||||
{
|
||||
TotalCreatedParticles++;
|
||||
|
||||
return new FallingParticle
|
||||
{
|
||||
Velocity = new Vector2(
|
||||
RNG.NextSingle(-MaxVelocity, MaxVelocity),
|
||||
RNG.NextSingle(-MaxVelocity, MaxVelocity)
|
||||
),
|
||||
StartPosition = SpawnPosition,
|
||||
Duration = RNG.NextSingle(MAX_DURATION),
|
||||
StartAngle = RNG.NextSingle(MathF.PI * 2),
|
||||
EndAngle = RNG.NextSingle(MathF.PI * 2),
|
||||
EndScale = RNG.NextSingle(0.5f, 1.5f)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay })
|
||||
Recorder = recorder = new TestReplayRecorder(new Score
|
||||
{
|
||||
Replay = replay,
|
||||
ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
|
||||
})
|
||||
{
|
||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
|
||||
},
|
||||
@ -222,13 +226,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(TestAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(TestAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
|
||||
{
|
||||
box.Colour = Color4.Black;
|
||||
}
|
||||
|
@ -45,7 +45,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
Recorder = new TestReplayRecorder(new Score { Replay = replay })
|
||||
Recorder = new TestReplayRecorder(new Score
|
||||
{
|
||||
Replay = replay,
|
||||
ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
|
||||
})
|
||||
{
|
||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
|
||||
},
|
||||
@ -155,13 +159,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(TestAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(TestAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
|
||||
{
|
||||
box.Colour = Color4.Black;
|
||||
}
|
||||
|
@ -279,13 +279,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(TestAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(TestAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
|
||||
{
|
||||
box.Colour = Color4.Black;
|
||||
}
|
||||
@ -354,7 +354,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder()
|
||||
: base(new Score())
|
||||
: base(new Score { ScoreInfo = { Beatmap = new BeatmapInfo() } })
|
||||
{
|
||||
}
|
||||
|
||||
|
41
osu.Game.Tests/Visual/Menus/TestSceneLoginPanel.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Login;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneLoginPanel : OsuManualInputManagerTestScene
|
||||
{
|
||||
private LoginPanel loginPanel;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create login dialog", () =>
|
||||
{
|
||||
Add(loginPanel = new LoginPanel
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicLogin()
|
||||
{
|
||||
AddStep("logout", () => API.Logout());
|
||||
|
||||
AddStep("enter password", () => loginPanel.ChildrenOfType<OsuPasswordTextBox>().First().Text = "password");
|
||||
AddStep("submit", () => loginPanel.ChildrenOfType<OsuButton>().First(b => b.Text.ToString() == "Sign in").TriggerClick());
|
||||
}
|
||||
}
|
||||
}
|
@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithIncorrectPassword()
|
||||
public void TestJoinRoomWithIncorrectPasswordViaButton()
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
|
||||
@ -96,6 +96,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
|
||||
AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible);
|
||||
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithIncorrectPasswordViaEnter()
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "wrong");
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
|
||||
AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible);
|
||||
AddAssert("textbox still focused", () => InputManager.FocusedDrawable is OsuPasswordTextBox);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
|
||||
@ -15,11 +16,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new MultiplayerMatchFooter
|
||||
Child = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Height = 50
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Child = new MultiplayerMatchFooter()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -75,7 +75,6 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
typeof(FileStore),
|
||||
typeof(ScoreManager),
|
||||
typeof(BeatmapManager),
|
||||
typeof(SettingsStore),
|
||||
typeof(RulesetConfigCache),
|
||||
typeof(OsuColour),
|
||||
typeof(IBindable<WorkingBeatmap>),
|
||||
@ -97,9 +96,6 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
AddStep("create game", () =>
|
||||
{
|
||||
game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@ -107,8 +103,9 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(game = new OsuGame());
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => game.IsLoaded);
|
||||
|
@ -77,7 +77,13 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||
AddUntilStep("wait for player", () =>
|
||||
{
|
||||
// dismiss any notifications that may appear (ie. muted notification).
|
||||
clickMouseInCentre();
|
||||
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
||||
});
|
||||
|
||||
AddAssert("retry count is 0", () => player.RestartCount == 0);
|
||||
|
||||
AddStep("attempt to retry", () => player.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||
@ -104,7 +110,14 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } });
|
||||
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||
|
||||
AddUntilStep("wait for player", () =>
|
||||
{
|
||||
// dismiss any notifications that may appear (ie. muted notification).
|
||||
clickMouseInCentre();
|
||||
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning);
|
||||
AddStep("seek to near end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000));
|
||||
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
||||
@ -131,7 +144,13 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||
AddUntilStep("wait for player", () =>
|
||||
{
|
||||
// dismiss any notifications that may appear (ie. muted notification).
|
||||
clickMouseInCentre();
|
||||
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for fail", () => player.HasFailed);
|
||||
|
||||
AddUntilStep("wait for track stop", () => !Game.MusicController.IsPlaying);
|
||||
@ -399,7 +418,15 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddStep("Hold escape", () => InputManager.PressKey(Key.Escape));
|
||||
AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles);
|
||||
AddStep("Release escape", () => InputManager.ReleaseKey(Key.Escape));
|
||||
AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null);
|
||||
AddStep("test dispose doesn't crash", () => Game.Dispose());
|
||||
}
|
||||
|
||||
private void clickMouseInCentre()
|
||||
{
|
||||
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
}
|
||||
|
||||
private void pushEscape() =>
|
||||
|
@ -234,7 +234,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
AddAssert($"Check {name} is bound to {keyName}", () =>
|
||||
{
|
||||
var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text == name));
|
||||
var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text.ToString() == name));
|
||||
var firstButton = firstRow.ChildrenOfType<KeyBindingRow.KeyButton>().First();
|
||||
|
||||
return firstButton.Text.Text == keyName;
|
||||
@ -247,7 +247,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
|
||||
AddStep($"Scroll to {name}", () =>
|
||||
{
|
||||
var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text == name));
|
||||
var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text.ToString() == name));
|
||||
firstButton = firstRow.ChildrenOfType<KeyBindingRow.KeyButton>().First();
|
||||
|
||||
panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollTo(firstButton);
|
||||
|
@ -5,8 +5,8 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Graphics;
|
||||
@ -18,10 +18,18 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
private TestOsuScreenStack stack;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
[Cached]
|
||||
private MusicController musicController = new MusicController();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Create new screen stack", () => { Child = stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both }; });
|
||||
stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
Add(musicController);
|
||||
Add(stack);
|
||||
|
||||
LoadComponent(stack);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -42,6 +50,44 @@ namespace osu.Game.Tests.Visual
|
||||
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllowTrackAdjustmentsTest()
|
||||
{
|
||||
AddStep("push allowing screen", () => stack.Push(loadNewScreen<AllowScreen>()));
|
||||
AddAssert("allows adjustments 1", () => musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
|
||||
AddAssert("allows adjustments 2", () => musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("push disallowing screen", () => stack.Push(loadNewScreen<DisallowScreen>()));
|
||||
AddAssert("disallows adjustments 3", () => !musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
|
||||
AddAssert("disallows adjustments 4", () => !musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
|
||||
AddAssert("disallows adjustments 5", () => !musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("push allowing screen", () => stack.Push(loadNewScreen<AllowScreen>()));
|
||||
AddAssert("allows adjustments 6", () => musicController.AllowTrackAdjustments);
|
||||
|
||||
// Now start exiting from screens
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("disallows adjustments 7", () => !musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("disallows adjustments 8", () => !musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("disallows adjustments 9", () => !musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("allows adjustments 10", () => musicController.AllowTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("allows adjustments 11", () => musicController.AllowTrackAdjustments);
|
||||
}
|
||||
|
||||
public class TestScreen : ScreenWithBeatmapBackground
|
||||
{
|
||||
private readonly string screenText;
|
||||
@ -78,5 +124,26 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public new float ParallaxAmount => base.ParallaxAmount;
|
||||
}
|
||||
|
||||
private class AllowScreen : OsuScreen
|
||||
{
|
||||
public override bool? AllowTrackAdjustments => true;
|
||||
}
|
||||
|
||||
public class DisallowScreen : OsuScreen
|
||||
{
|
||||
public override bool? AllowTrackAdjustments => false;
|
||||
}
|
||||
|
||||
private class InheritScreen : OsuScreen
|
||||
{
|
||||
}
|
||||
|
||||
private OsuScreen loadNewScreen<T>() where T : OsuScreen, new()
|
||||
{
|
||||
OsuScreen screen = new T();
|
||||
LoadComponent(screen);
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public abstract class DatabasedConfigManager<TLookup> : ConfigManager<TLookup>
|
||||
where TLookup : struct, Enum
|
||||
{
|
||||
private readonly SettingsStore settings;
|
||||
|
||||
private readonly int? variant;
|
||||
|
||||
private List<DatabasedSetting> databasedSettings;
|
||||
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
private bool legacySettingsExist;
|
||||
|
||||
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.ruleset = ruleset;
|
||||
this.variant = variant;
|
||||
|
||||
Load();
|
||||
|
||||
InitialiseDefaults();
|
||||
}
|
||||
|
||||
protected override void PerformLoad()
|
||||
{
|
||||
databasedSettings = settings.Query(ruleset?.ID, variant);
|
||||
legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out _));
|
||||
}
|
||||
|
||||
protected override bool PerformSave()
|
||||
{
|
||||
lock (dirtySettings)
|
||||
{
|
||||
foreach (var setting in dirtySettings)
|
||||
settings.Update(setting);
|
||||
dirtySettings.Clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
|
||||
|
||||
protected override void AddBindable<TBindable>(TLookup lookup, Bindable<TBindable> bindable)
|
||||
{
|
||||
base.AddBindable(lookup, bindable);
|
||||
|
||||
if (legacySettingsExist)
|
||||
{
|
||||
var legacySetting = databasedSettings.Find(s => s.Key == ((int)(object)lookup).ToString());
|
||||
|
||||
if (legacySetting != null)
|
||||
{
|
||||
bindable.Parse(legacySetting.Value);
|
||||
settings.Delete(legacySetting);
|
||||
}
|
||||
}
|
||||
|
||||
var setting = databasedSettings.Find(s => s.Key == lookup.ToString());
|
||||
|
||||
if (setting != null)
|
||||
{
|
||||
bindable.Parse(setting.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Update(setting = new DatabasedSetting
|
||||
{
|
||||
Key = lookup.ToString(),
|
||||
Value = bindable.Value,
|
||||
RulesetID = ruleset?.ID,
|
||||
Variant = variant,
|
||||
});
|
||||
|
||||
databasedSettings.Add(setting);
|
||||
}
|
||||
|
||||
bindable.ValueChanged += b =>
|
||||
{
|
||||
setting.Value = b.NewValue;
|
||||
|
||||
lock (dirtySettings)
|
||||
{
|
||||
if (!dirtySettings.Contains(setting))
|
||||
dirtySettings.Add(setting);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ using osu.Game.Database;
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
[Table("Settings")]
|
||||
public class DatabasedSetting : IHasPrimaryKey
|
||||
public class DatabasedSetting : IHasPrimaryKey // can be removed 20220315.
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
|
32
osu.Game/Configuration/RealmRulesetSetting.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Database;
|
||||
using Realms;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
[MapTo(@"RulesetSetting")]
|
||||
public class RealmRulesetSetting : RealmObject, IHasGuidPrimaryKey
|
||||
{
|
||||
[PrimaryKey]
|
||||
public Guid ID { get; set; } = Guid.NewGuid();
|
||||
|
||||
[Indexed]
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
[Indexed]
|
||||
public int Variant { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
public override string ToString() => $"{Key} => {Value}";
|
||||
}
|
||||
}
|
@ -1,46 +1,20 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Configuration
|
||||
{
|
||||
public class SettingsStore : DatabaseBackedStore
|
||||
public class SettingsStore
|
||||
{
|
||||
public event Action SettingChanged;
|
||||
// this class mostly exists as a wrapper to avoid breaking the ruleset API (see usage in RulesetConfigManager).
|
||||
// it may cease to exist going forward, depending on how the structure of the config data layer changes.
|
||||
|
||||
public SettingsStore(DatabaseContextFactory contextFactory)
|
||||
: base(contextFactory)
|
||||
public readonly RealmContextFactory Realm;
|
||||
|
||||
public SettingsStore(RealmContextFactory realmFactory)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve <see cref="DatabasedSetting"/>s for a specified ruleset/variant content.
|
||||
/// </summary>
|
||||
/// <param name="rulesetId">The ruleset's internal ID.</param>
|
||||
/// <param name="variant">An optional variant.</param>
|
||||
public List<DatabasedSetting> Query(int? rulesetId = null, int? variant = null) =>
|
||||
ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
|
||||
|
||||
public void Update(DatabasedSetting setting)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
var newValue = setting.Value;
|
||||
Refresh(ref setting);
|
||||
setting.Value = newValue;
|
||||
}
|
||||
|
||||
SettingChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void Delete(DatabasedSetting setting)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
usage.Context.Remove(setting);
|
||||
Realm = realmFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,8 +163,11 @@ namespace osu.Game.Database
|
||||
|
||||
public void FlushConnections()
|
||||
{
|
||||
foreach (var context in threadContexts.Values)
|
||||
context.Dispose();
|
||||
if (threadContexts != null)
|
||||
{
|
||||
foreach (var context in threadContexts.Values)
|
||||
context.Dispose();
|
||||
}
|
||||
|
||||
recycleThreadContexts();
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ using osu.Game.Configuration;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -24,14 +23,13 @@ namespace osu.Game.Database
|
||||
public DbSet<BeatmapDifficulty> BeatmapDifficulty { get; set; }
|
||||
public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; }
|
||||
public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; }
|
||||
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
|
||||
public DbSet<FileInfo> FileInfo { get; set; }
|
||||
public DbSet<RulesetInfo> RulesetInfo { get; set; }
|
||||
public DbSet<SkinInfo> SkinInfo { get; set; }
|
||||
public DbSet<ScoreInfo> ScoreInfo { get; set; }
|
||||
|
||||
// migrated to realm
|
||||
public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; }
|
||||
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
|
||||
|
||||
private readonly string connectionString;
|
||||
|
||||
@ -138,11 +136,6 @@ namespace osu.Game.Database
|
||||
modelBuilder.Entity<SkinInfo>().HasIndex(b => b.Hash).IsUnique();
|
||||
modelBuilder.Entity<SkinInfo>().HasIndex(b => b.DeletePending);
|
||||
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.KeyCombination);
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.Action);
|
||||
|
||||
modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });
|
||||
|
||||
modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique();
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Batches;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||
using osu.Framework.Lists;
|
||||
|
||||
@ -181,7 +182,10 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
private void addTriangles(bool randomY)
|
||||
{
|
||||
AimCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
|
||||
// limited by the maximum size of QuadVertexBuffer for safety.
|
||||
const int max_triangles = QuadVertexBuffer<TexturedVertex2D>.MAX_QUADS;
|
||||
|
||||
AimCount = (int)Math.Min(max_triangles, (DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio));
|
||||
|
||||
for (int i = 0; i < AimCount - parts.Count; i++)
|
||||
parts.Add(createTriangle(randomY));
|
||||
|
@ -88,9 +88,9 @@ namespace osu.Game.Graphics.Containers
|
||||
base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(GlobalAction action)
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
Hide();
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
198
osu.Game/Graphics/ParticleSpewer.cs
Normal file
@ -0,0 +1,198 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Utils;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics
|
||||
{
|
||||
public abstract class ParticleSpewer : Sprite
|
||||
{
|
||||
private readonly FallingParticle[] particles;
|
||||
private int currentIndex;
|
||||
private double lastParticleAdded;
|
||||
|
||||
private readonly double cooldown;
|
||||
private readonly double maxDuration;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether particles are being spawned.
|
||||
/// </summary>
|
||||
public readonly BindableBool Active = new BindableBool();
|
||||
|
||||
public override bool IsPresent => base.IsPresent && hasActiveParticles;
|
||||
|
||||
protected virtual bool CanSpawnParticles => true;
|
||||
protected virtual float ParticleGravity => 0;
|
||||
|
||||
private bool hasActiveParticles => Active.Value || (lastParticleAdded + maxDuration) > Time.Current;
|
||||
|
||||
protected ParticleSpewer(Texture texture, int perSecond, double maxDuration)
|
||||
{
|
||||
Texture = texture;
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
particles = new FallingParticle[perSecond * (int)Math.Ceiling(maxDuration / 1000)];
|
||||
|
||||
cooldown = 1000f / perSecond;
|
||||
this.maxDuration = maxDuration;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Active.Value && CanSpawnParticles && Math.Abs(Time.Current - lastParticleAdded) > cooldown)
|
||||
{
|
||||
var newParticle = CreateParticle();
|
||||
newParticle.StartTime = (float)Time.Current;
|
||||
|
||||
particles[currentIndex] = newParticle;
|
||||
|
||||
currentIndex = (currentIndex + 1) % particles.Length;
|
||||
lastParticleAdded = Time.Current;
|
||||
}
|
||||
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called each time a new particle should be spawned.
|
||||
/// </summary>
|
||||
protected virtual FallingParticle CreateParticle() => new FallingParticle();
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new ParticleSpewerDrawNode(this);
|
||||
|
||||
# region DrawNode
|
||||
|
||||
private class ParticleSpewerDrawNode : SpriteDrawNode
|
||||
{
|
||||
private readonly FallingParticle[] particles;
|
||||
|
||||
protected new ParticleSpewer Source => (ParticleSpewer)base.Source;
|
||||
|
||||
private readonly float maxDuration;
|
||||
|
||||
private float currentTime;
|
||||
private float gravity;
|
||||
private Axes relativePositionAxes;
|
||||
private Vector2 sourceSize;
|
||||
|
||||
public ParticleSpewerDrawNode(ParticleSpewer source)
|
||||
: base(source)
|
||||
{
|
||||
particles = new FallingParticle[Source.particles.Length];
|
||||
maxDuration = (float)Source.maxDuration;
|
||||
}
|
||||
|
||||
public override void ApplyState()
|
||||
{
|
||||
base.ApplyState();
|
||||
|
||||
Source.particles.CopyTo(particles, 0);
|
||||
|
||||
currentTime = (float)Source.Time.Current;
|
||||
gravity = Source.ParticleGravity;
|
||||
relativePositionAxes = Source.RelativePositionAxes;
|
||||
sourceSize = Source.DrawSize;
|
||||
}
|
||||
|
||||
protected override void Blit(Action<TexturedVertex2D> vertexAction)
|
||||
{
|
||||
foreach (var p in particles)
|
||||
{
|
||||
var timeSinceStart = currentTime - p.StartTime;
|
||||
|
||||
// ignore particles from the future.
|
||||
// these can appear when seeking in replays.
|
||||
if (timeSinceStart < 0) continue;
|
||||
|
||||
var alpha = p.AlphaAtTime(timeSinceStart);
|
||||
if (alpha <= 0) continue;
|
||||
|
||||
var pos = p.PositionAtTime(timeSinceStart, gravity, maxDuration);
|
||||
var scale = p.ScaleAtTime(timeSinceStart);
|
||||
var angle = p.AngleAtTime(timeSinceStart);
|
||||
|
||||
var rect = createDrawRect(pos, scale);
|
||||
|
||||
var quad = new Quad(
|
||||
transformPosition(rect.TopLeft, rect.Centre, angle),
|
||||
transformPosition(rect.TopRight, rect.Centre, angle),
|
||||
transformPosition(rect.BottomLeft, rect.Centre, angle),
|
||||
transformPosition(rect.BottomRight, rect.Centre, angle)
|
||||
);
|
||||
|
||||
DrawQuad(Texture, quad, DrawColourInfo.Colour.MultiplyAlpha(alpha), null, vertexAction,
|
||||
new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height),
|
||||
null, TextureCoords);
|
||||
}
|
||||
}
|
||||
|
||||
private RectangleF createDrawRect(Vector2 position, float scale)
|
||||
{
|
||||
var width = Texture.DisplayWidth * scale;
|
||||
var height = Texture.DisplayHeight * scale;
|
||||
|
||||
if (relativePositionAxes.HasFlagFast(Axes.X))
|
||||
position.X *= sourceSize.X;
|
||||
if (relativePositionAxes.HasFlagFast(Axes.Y))
|
||||
position.Y *= sourceSize.Y;
|
||||
|
||||
return new RectangleF(
|
||||
position.X - width / 2,
|
||||
position.Y - height / 2,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
private Vector2 transformPosition(Vector2 pos, Vector2 centre, float angle)
|
||||
{
|
||||
float cos = MathF.Cos(angle);
|
||||
float sin = MathF.Sin(angle);
|
||||
|
||||
float x = centre.X + (pos.X - centre.X) * cos + (pos.Y - centre.Y) * sin;
|
||||
float y = centre.Y + (pos.Y - centre.Y) * cos - (pos.X - centre.X) * sin;
|
||||
|
||||
return Vector2Extensions.Transform(new Vector2(x, y), DrawInfo.Matrix);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected struct FallingParticle
|
||||
{
|
||||
public float StartTime;
|
||||
public Vector2 StartPosition;
|
||||
public Vector2 Velocity;
|
||||
public float Duration;
|
||||
public float StartAngle;
|
||||
public float EndAngle;
|
||||
public float EndScale;
|
||||
|
||||
public float AlphaAtTime(float timeSinceStart) => 1 - progressAtTime(timeSinceStart);
|
||||
|
||||
public float ScaleAtTime(float timeSinceStart) => (float)Interpolation.Lerp(1, EndScale, progressAtTime(timeSinceStart));
|
||||
|
||||
public float AngleAtTime(float timeSinceStart) => (float)Interpolation.Lerp(StartAngle, EndAngle, progressAtTime(timeSinceStart));
|
||||
|
||||
public Vector2 PositionAtTime(float timeSinceStart, float gravity, float maxDuration)
|
||||
{
|
||||
var progress = progressAtTime(timeSinceStart);
|
||||
var currentGravity = new Vector2(0, gravity * Duration / maxDuration * progress);
|
||||
|
||||
return StartPosition + (Velocity + currentGravity) * timeSinceStart / maxDuration;
|
||||
}
|
||||
|
||||
private float progressAtTime(float timeSinceStart) => Math.Clamp(timeSinceStart / Duration, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Configuration;
|
||||
@ -57,9 +58,9 @@ namespace osu.Game.Graphics
|
||||
shutter = audio.Samples.Get("UI/shutter");
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.TakeScreenshot:
|
||||
shutter.Play();
|
||||
@ -70,7 +71,7 @@ namespace osu.Game.Graphics
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
@ -61,9 +62,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public Action OnBackPressed;
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
OnBackPressed?.Invoke();
|
||||
@ -73,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -70,11 +70,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(GlobalAction action)
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (!HasFocus) return false;
|
||||
|
||||
if (action == GlobalAction.Back)
|
||||
if (e.Action == GlobalAction.Back)
|
||||
{
|
||||
if (Text.Length > 0)
|
||||
{
|
||||
@ -86,7 +86,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -30,9 +30,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
PlaceholderText = "type to search";
|
||||
}
|
||||
|
||||
public override bool OnPressed(PlatformAction action)
|
||||
public override bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case PlatformAction.MoveBackwardLine:
|
||||
case PlatformAction.MoveForwardLine:
|
||||
@ -43,7 +43,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnPressed(action);
|
||||
return base.OnPressed(e);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
@ -55,12 +56,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
this.FadeOut(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (State.Value == Visibility.Hidden)
|
||||
return false;
|
||||
|
||||
if (action == GlobalAction.Back)
|
||||
if (e.Action == GlobalAction.Back)
|
||||
{
|
||||
Hide();
|
||||
return true;
|
||||
@ -69,7 +70,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Input.Bindings
|
||||
{
|
||||
[Table("KeyBinding")]
|
||||
public class DatabasedKeyBinding : IKeyBinding, IHasPrimaryKey
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
public int? RulesetID { get; set; }
|
||||
|
||||
public int? Variant { get; set; }
|
||||
|
||||
[Column("Keys")]
|
||||
public string KeysString { get; set; }
|
||||
|
||||
[Column("Action")]
|
||||
public int IntAction { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public KeyCombination KeyCombination
|
||||
{
|
||||
get => KeysString;
|
||||
set => KeysString = value.ToString();
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public object Action
|
||||
{
|
||||
get => IntAction;
|
||||
set => IntAction = (int)value;
|
||||
}
|
||||
}
|
||||
}
|