1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 14:03:21 +08:00

Merge with ppy/osu master

This commit is contained in:
MaxOhn 2018-10-01 12:10:42 +02:00
commit 0f0ee48bfd
137 changed files with 1517 additions and 481 deletions

View File

@ -10,8 +10,8 @@ before_build:
- cmd: nuget restore -verbosity quiet - cmd: nuget restore -verbosity quiet
build_script: build_script:
- ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1')) - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
- appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate - appveyor DownloadFile https://puu.sh/BCrS8/7faccf7876.enc # signing certificate
- cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx - cmd: appveyor-tools\secure-file -decrypt 7faccf7876.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
- appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
- cd osu-deploy - cd osu-deploy
- nuget restore -verbosity quiet - nuget restore -verbosity quiet

View File

@ -27,8 +27,8 @@ namespace osu.Desktop.Overlays
private NotificationOverlay notificationOverlay; private NotificationOverlay notificationOverlay;
private GameHost host; private GameHost host;
public override bool HandleKeyboardInput => false; public override bool HandleNonPositionalInput => false;
public override bool HandleMouseInput => false; public override bool HandlePositionalInput => false;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host) private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host)

View File

@ -27,9 +27,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" /> <PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.5" /> <PackageReference Include="ppy.squirrel.windows" Version="1.8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public int IndexInBeatmap { get; set; } public int IndexInBeatmap { get; set; }
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4);
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }

View File

@ -5,6 +5,7 @@ using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -22,6 +23,10 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea; private readonly CatcherArea catcherArea;
protected override bool UserScrollSpeedAdjustment => false;
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(BASE_WIDTH) : base(BASE_WIDTH)
{ {
@ -53,6 +58,8 @@ namespace osu.Game.Rulesets.Catch.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
}); });
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
} }
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);

View File

@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
} }
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input. // Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
public override bool HandleMouseInput => false; public override bool HandlePositionalInput => false;
} }
} }
} }

View File

@ -56,6 +56,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
} }
// Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input.
public override bool HandleMouseInput => false; public override bool HandlePositionalInput => false;
} }
} }

View File

@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
body.UpdateProgress(0); body.UpdateProgress(0);
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => body.ReceivePositionalInputAt(screenSpacePos);
public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
public override Quad SelectionQuad => body.PathDrawQuad; public override Quad SelectionQuad => body.PathDrawQuad;

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Input.States;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Mods
wasLeft = !wasLeft; wasLeft = !wasLeft;
} }
osuInputManager.HandleCustomInput(new InputState(), state); state.Apply(osuInputManager.CurrentState, osuInputManager);
} }
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer) public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects
{
public override string Name => "Transform";
public override string ShortenedName => "TR";
public override FontAwesome Icon => FontAwesome.fa_arrows;
public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle) };
private float theta;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var drawable in drawables)
{
var hitObject = (OsuHitObject) drawable.HitObject;
float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
Vector2 originalPosition = drawable.Position;
Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance;
//the - 1 and + 1 prevents the hit objects to appear in the wrong position.
double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
double moveDuration = hitObject.TimePreempt + 1;
using (drawable.BeginAbsoluteSequence(appearTime, true))
{
drawable
.MoveToOffset(appearOffset)
.MoveTo(originalPosition, moveDuration, Easing.InOutSine);
}
theta += (float) hitObject.TimeFadeIn / 1000;
}
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects
{
public override string Name => "Wiggle";
public override string ShortenedName => "WG";
public override FontAwesome Icon => FontAwesome.fa_certificate;
public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform) };
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
private const int wiggle_strength = 10; // Higher = stronger wiggles
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var drawable in drawables)
drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState;
}
private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state)
{
var osuObject = (OsuHitObject)drawable.HitObject;
Vector2 origin = drawable.Position;
Random objRand = new Random((int)osuObject.StartTime);
// Wiggle all objects during TimePreempt
int amountWiggles = (int)osuObject.TimePreempt / wiggle_duration;
void wiggle()
{
float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI);
float nextDist = (float)(objRand.NextDouble() * wiggle_strength);
drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration);
}
for (int i = 0; i < amountWiggles; i++)
using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true))
wiggle();
// Keep wiggling sliders and spinners for their duration
if (!(osuObject is IHasEndTime endTime))
return;
amountWiggles = (int)(endTime.Duration / wiggle_duration);
for (int i = 0; i < amountWiggles; i++)
using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true))
wiggle();
}
}
}

View File

@ -184,6 +184,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public Drawable ProxiedLayer => HeadCircle.ApproachCircle; public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Body.ReceivePositionalInputAt(screenSpacePos);
} }
} }

View File

@ -153,10 +153,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (Time.Current < slider.EndTime) if (Time.Current < slider.EndTime)
{ {
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. // Make sure to use the base version of ReceivePositionalInputAt so that we correctly check the position.
Tracking = canCurrentlyTrack Tracking = canCurrentlyTrack
&& lastState != null && lastState != null
&& ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ReceivePositionalInputAt(lastState.Mouse.NativeState.Position)
&& (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); && (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
} }
} }

View File

@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
container.Attach(RenderbufferInternalFormat.DepthComponent16); container.Attach(RenderbufferInternalFormat.DepthComponent16);
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
public void SetRange(double p0, double p1) public void SetRange(double p0, double p1)
{ {

View File

@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SpinnerBackground : CircularContainer, IHasAccentColour public class SpinnerBackground : CircularContainer, IHasAccentColour
{ {
public override bool HandleKeyboardInput => false; public override bool HandleNonPositionalInput => false;
public override bool HandleMouseInput => false; public override bool HandlePositionalInput => false;
protected Box Disc; protected Box Disc;

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}; };
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private bool tracking; private bool tracking;
public bool Tracking public bool Tracking

View File

@ -118,9 +118,9 @@ namespace osu.Game.Rulesets.Osu
new OsuModAutopilot(), new OsuModAutopilot(),
}; };
case ModType.Fun: case ModType.Fun:
return new Mod[] return new Mod[] {
{ new OsuModTransform(),
new OsuModDeflate(), new OsuModWiggle(),
}; };
default: default:
return new Mod[] { }; return new Mod[] { };

View File

@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
} }
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ShaderManager shaders, TextureStore textures) private void load(ShaderManager shaders, TextureStore textures)

View File

@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
return false; return false;
} }
public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
protected override void PopIn() protected override void PopIn()
{ {

View File

@ -72,7 +72,8 @@ namespace osu.Game.Rulesets.Osu.UI
DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject) DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition,
Scale = new Vector2(((OsuHitObject)judgedObject.HitObject).Scale * 1.65f)
}; };
judgementLayer.Add(explosion); judgementLayer.Add(explosion);

View File

@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private bool validActionPressed; private bool validActionPressed;
private bool pressHandledThisFrame;
protected DrawableHit(Hit hit) protected DrawableHit(Hit hit)
: base(hit) : base(hit)
{ {
@ -51,6 +53,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action)
{ {
if (pressHandledThisFrame)
return true;
if (Judged) if (Judged)
return false; return false;
@ -62,6 +67,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (IsHit) if (IsHit)
HitAction = action; HitAction = 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
pressHandledThisFrame = true;
return result; return result;
} }
@ -76,6 +85,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
base.Update(); base.Update();
// The input manager processes all input prior to us updating, so this is the perfect time
// for us to remove the extra press blocking, before input is handled in the next frame
pressHandledThisFrame = false;
Size = BaseSize * Parent.RelativeChildSize; Size = BaseSize * Parent.RelativeChildSize;
} }

View File

@ -1,23 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Objects;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Judgements; using osu.Framework.Allocation;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
{ {
@ -40,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override bool UserScrollSpeedAdjustment => false; protected override bool UserScrollSpeedAdjustment => false;
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping;
private readonly Container<HitExplosion> hitExplosionContainer; private readonly Container<HitExplosion> hitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer; private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer; private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;

View File

@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual
public CursorContainer Cursor { get; } public CursorContainer Cursor { get; }
public bool ProvidingUserCursor { get; } public bool ProvidingUserCursor { get; }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
private readonly Box background; private readonly Box background;

View File

@ -8,14 +8,14 @@ using System.Linq;
using OpenTK.Input; using OpenTK.Input;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.EventArgs;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using OpenTK;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[Description("player pause/fail screens")] [Description("player pause/fail screens")]
public class TestCaseGameplayMenuOverlay : OsuTestCase public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
@ -73,12 +73,18 @@ namespace osu.Game.Tests.Visual
{ {
AddStep("Show overlay", () => failOverlay.Show()); AddStep("Show overlay", () => failOverlay.Show());
AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null)); AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
AddStep("Hide overlay", () => failOverlay.Hide()); AddStep("Hide overlay", () => failOverlay.Hide());
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
} }
private void press(Key key)
{
InputManager.PressKey(key);
InputManager.ReleaseKey(key);
}
/// <summary> /// <summary>
/// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred.
/// </summary> /// </summary>
@ -86,7 +92,7 @@ namespace osu.Game.Tests.Visual
{ {
AddStep("Show overlay", () => pauseOverlay.Show()); AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter })); AddStep("Press enter", () => press(Key.Enter));
AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible); AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
AddStep("Hide overlay", () => pauseOverlay.Hide()); AddStep("Hide overlay", () => pauseOverlay.Hide());
@ -99,7 +105,7 @@ namespace osu.Game.Tests.Visual
{ {
AddStep("Show overlay", () => pauseOverlay.Show()); AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); AddStep("Up arrow", () => press(Key.Up));
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide()); AddStep("Hide overlay", () => pauseOverlay.Hide());
@ -112,7 +118,7 @@ namespace osu.Game.Tests.Visual
{ {
AddStep("Show overlay", () => pauseOverlay.Show()); AddStep("Show overlay", () => pauseOverlay.Show());
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide()); AddStep("Hide overlay", () => pauseOverlay.Hide());
@ -125,11 +131,11 @@ namespace osu.Game.Tests.Visual
{ {
AddStep("Show overlay", () => failOverlay.Show()); AddStep("Show overlay", () => failOverlay.Show());
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); AddStep("Up arrow", () => press(Key.Up));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); AddStep("Up arrow", () => press(Key.Up));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); AddStep("Up arrow", () => press(Key.Up));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Hide overlay", () => failOverlay.Hide()); AddStep("Hide overlay", () => failOverlay.Hide());
@ -142,11 +148,11 @@ namespace osu.Game.Tests.Visual
{ {
AddStep("Show overlay", () => failOverlay.Show()); AddStep("Show overlay", () => failOverlay.Show());
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddStep("Down arrow", () => press(Key.Down));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => failOverlay.Hide()); AddStep("Hide overlay", () => failOverlay.Hide());
@ -161,8 +167,8 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First(); var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddStep("Down arrow", () => press(Key.Down));
AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected); AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
AddAssert("Second button selected", () => secondButton.Selected); AddAssert("Second button selected", () => secondButton.Selected);
@ -174,12 +180,16 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
private void testKeySelectionAfterMouseSelection() private void testKeySelectionAfterMouseSelection()
{ {
AddStep("Show overlay", () => pauseOverlay.Show()); AddStep("Show overlay", () =>
{
pauseOverlay.Show();
InputManager.MoveMouseTo(Vector2.Zero);
});
var secondButton = pauseOverlay.Buttons.Skip(1).First(); var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); AddStep("Up arrow", () => press(Key.Up));
AddAssert("Second button not selected", () => !secondButton.Selected); AddAssert("Second button not selected", () => !secondButton.Selected);
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
@ -195,9 +205,9 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First(); var secondButton = pauseOverlay.Buttons.Skip(1).First();
AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null)); AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
AddStep("Hide overlay", () => pauseOverlay.Hide()); AddStep("Hide overlay", () => pauseOverlay.Hide());
@ -218,7 +228,7 @@ namespace osu.Game.Tests.Visual
var lastAction = pauseOverlay.OnRetry; var lastAction = pauseOverlay.OnRetry;
pauseOverlay.OnRetry = () => triggered = true; pauseOverlay.OnRetry = () => triggered = true;
retryButton.TriggerOnClick(); retryButton.Click();
pauseOverlay.OnRetry = lastAction; pauseOverlay.OnRetry = lastAction;
}); });
@ -235,23 +245,28 @@ namespace osu.Game.Tests.Visual
AddStep("Select second button", () => AddStep("Select second button", () =>
{ {
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); press(Key.Down);
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); press(Key.Down);
}); });
var retryButton = pauseOverlay.Buttons.Skip(1).First();
bool triggered = false; bool triggered = false;
Action lastAction = null;
AddStep("Press enter", () => AddStep("Press enter", () =>
{ {
var lastAction = pauseOverlay.OnRetry; lastAction = pauseOverlay.OnRetry;
pauseOverlay.OnRetry = () => triggered = true; pauseOverlay.OnRetry = () => triggered = true;
press(Key.Enter);
retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter });
pauseOverlay.OnRetry = lastAction;
}); });
AddAssert("Action was triggered", () => triggered); AddAssert("Action was triggered", () =>
{
if (lastAction != null)
{
pauseOverlay.OnRetry = lastAction;
lastAction = null;
}
return triggered;
});
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
} }
} }

View File

@ -1,32 +1,45 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using OpenTK.Input; using OpenTK.Input;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[TestFixture] [TestFixture]
public class TestCaseKeyCounter : OsuTestCase public class TestCaseKeyCounter : ManualInputManagerTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(KeyCounterKeyboard),
typeof(KeyCounterMouse),
typeof(KeyCounterCollection)
};
public TestCaseKeyCounter() public TestCaseKeyCounter()
{ {
KeyCounterKeyboard rewindTestKeyCounterKeyboard;
KeyCounterCollection kc = new KeyCounterCollection KeyCounterCollection kc = new KeyCounterCollection
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Children = new KeyCounter[] Children = new KeyCounter[]
{ {
new KeyCounterKeyboard(Key.Z), rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X), new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left), new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right), new KeyCounterMouse(MouseButton.Right),
}, },
}; };
AddStep("Add random", () => AddStep("Add random", () =>
{ {
Key key = (Key)((int)Key.A + RNG.Next(26)); Key key = (Key)((int)Key.A + RNG.Next(26));
@ -34,7 +47,57 @@ namespace osu.Game.Tests.Visual
}); });
AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v); AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
double time1 = 0;
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
});
AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
time1 = Clock.CurrentTime;
});
AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
IFrameBasedClock oldClock = null;
AddStep($"Rewind {testKey} counter once", () =>
{
oldClock = rewindTestKeyCounterKeyboard.Clock;
rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
});
AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
Add(kc); Add(kc);
} }
private class FixedClock : IClock
{
private readonly double time;
public FixedClock(double time)
{
this.time = time;
}
public double CurrentTime => time;
public double Rate => 1;
public bool IsRunning => false;
}
} }
} }

View File

@ -27,13 +27,15 @@ namespace osu.Game.Beatmaps
[JsonProperty("id")] [JsonProperty("id")]
public int? OnlineBeatmapID public int? OnlineBeatmapID
{ {
get { return onlineBeatmapID; } get => onlineBeatmapID;
set { onlineBeatmapID = value > 0 ? value : null; } set => onlineBeatmapID = value > 0 ? value : null;
} }
[JsonIgnore] [JsonIgnore]
public int BeatmapSetInfoID { get; set; } public int BeatmapSetInfoID { get; set; }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
[Required] [Required]
public BeatmapSetInfo BeatmapSet { get; set; } public BeatmapSetInfo BeatmapSet { get; set; }
@ -82,7 +84,7 @@ namespace osu.Game.Beatmaps
[JsonIgnore] [JsonIgnore]
public string StoredBookmarks public string StoredBookmarks
{ {
get { return string.Join(",", Bookmarks); } get => string.Join(",", Bookmarks);
set set
{ {
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
@ -93,8 +95,7 @@ namespace osu.Game.Beatmaps
Bookmarks = value.Split(',').Select(v => Bookmarks = value.Split(',').Select(v =>
{ {
int val; bool result = int.TryParse(v, out int val);
bool result = int.TryParse(v, out val);
return new { result, val }; return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray(); }).Where(p => p.result).Select(p => p.val).ToArray();
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps
validateOnlineIds(beatmapSet.Beatmaps); validateOnlineIds(beatmapSet.Beatmaps);
foreach (BeatmapInfo b in beatmapSet.Beatmaps) foreach (BeatmapInfo b in beatmapSet.Beatmaps)
fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps); fetchAndPopulateOnlineValues(b, beatmapSet.Beatmaps);
// check if a set already exists with the same online id, delete if it does. // check if a set already exists with the same online id, delete if it does.
if (beatmapSet.OnlineBeatmapSetID != null) if (beatmapSet.OnlineBeatmapSetID != null)
@ -388,21 +388,22 @@ namespace osu.Game.Beatmaps
} }
/// <summary> /// <summary>
/// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties. /// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
/// </summary> /// </summary>
/// <param name="beatmap">The beatmap to populate.</param> /// <param name="beatmap">The beatmap to populate.</param>
/// <param name="otherBeatmaps">The other beatmaps contained within this set.</param> /// <param name="otherBeatmaps">The other beatmaps contained within this set.</param>
/// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param> /// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param>
/// <returns>True if population was successful.</returns> /// <returns>True if population was successful.</returns>
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false) private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false)
{ {
if (api?.State != APIState.Online) if (api?.State != APIState.Online)
return false; return false;
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null) if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null
&& beatmap.Status != BeatmapSetOnlineStatus.None && beatmap.BeatmapSet.Status != BeatmapSetOnlineStatus.None)
return true; return true;
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database); Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database);
try try
{ {
@ -414,6 +415,9 @@ namespace osu.Game.Beatmaps
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
beatmap.Status = res.Status;
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID)) if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID))
{ {
Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database); Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database);
@ -422,6 +426,7 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
return true; return true;
} }
catch (Exception e) catch (Exception e)

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -44,6 +45,25 @@ namespace osu.Game.Beatmaps
public virtual void PostProcess() public virtual void PostProcess()
{ {
void updateNestedCombo(HitObject obj, int comboIndex, int indexInCurrentCombo)
{
if (obj is IHasComboInformation objectComboInfo)
{
objectComboInfo.ComboIndex = comboIndex;
objectComboInfo.IndexInCurrentCombo = indexInCurrentCombo;
foreach (var nestedObject in obj.NestedHitObjects)
updateNestedCombo(nestedObject, comboIndex, indexInCurrentCombo);
}
}
foreach (var hitObject in Beatmap.HitObjects)
{
if (hitObject is IHasComboInformation objectComboInfo)
{
foreach (var nested in hitObject.NestedHitObjects)
updateNestedCombo(nested, objectComboInfo.ComboIndex, objectComboInfo.IndexInCurrentCombo);
}
}
} }
} }
} }

View File

@ -17,10 +17,12 @@ namespace osu.Game.Beatmaps
public int? OnlineBeatmapSetID public int? OnlineBeatmapSetID
{ {
get { return onlineBeatmapSetID; } get => onlineBeatmapSetID;
set { onlineBeatmapSetID = value > 0 ? value : null; } set => onlineBeatmapSetID = value > 0 ? value : null;
} }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapMetadata Metadata { get; set; } public BeatmapMetadata Metadata { get; set; }
public List<BeatmapInfo> Beatmaps { get; set; } public List<BeatmapInfo> Beatmaps { get; set; }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -14,20 +13,35 @@ namespace osu.Game.Beatmaps.Drawables
{ {
private readonly OsuSpriteText statusText; private readonly OsuSpriteText statusText;
private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None; private BeatmapSetOnlineStatus status;
public BeatmapSetOnlineStatus Status public BeatmapSetOnlineStatus Status
{ {
get { return status; } get => status;
set set
{ {
if (value == status) return; if (status == value)
return;
status = value; status = value;
statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpperInvariant(); Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1;
statusText.Text = value.ToString().ToUpperInvariant();
} }
} }
public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding) public float TextSize
{
get => statusText.TextSize;
set => statusText.TextSize = value;
}
public MarginPadding TextPadding
{
get => statusText.Padding;
set => statusText.Padding = value;
}
public BeatmapSetOnlineStatusPill()
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
Masking = true; Masking = true;
@ -45,10 +59,10 @@ namespace osu.Game.Beatmaps.Drawables
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
TextSize = textSize,
Padding = textPadding,
}, },
}; };
Status = BeatmapSetOnlineStatus.None;
} }
} }
} }

View File

@ -83,8 +83,6 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised);
Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
Set(OsuSetting.IncreaseFirstObjectVisibility, true); Set(OsuSetting.IncreaseFirstObjectVisibility, true);
// Update // Update
@ -143,7 +141,6 @@ namespace osu.Game.Configuration
ChatDisplayHeight, ChatDisplayHeight,
Version, Version,
ShowConvertedBeatmaps, ShowConvertedBeatmaps,
SpeedChangeVisualisation,
Skin, Skin,
ScreenshotFormat, ScreenshotFormat,
ScreenshotCaptureMenuCursor, ScreenshotCaptureMenuCursor,

View File

@ -10,6 +10,8 @@ namespace osu.Game.Configuration
[Description("Sequential")] [Description("Sequential")]
Sequential, Sequential,
[Description("Overlapping")] [Description("Overlapping")]
Overlapping Overlapping,
[Description("Constant")]
Constant
} }
} }

View File

@ -96,7 +96,8 @@ namespace osu.Game.Database
private void handleEvent(Action a) private void handleEvent(Action a)
{ {
if (delayingEvents) if (delayingEvents)
lock (queuedEvents) queuedEvents.Add(a); lock (queuedEvents)
queuedEvents.Add(a);
else else
a.Invoke(); a.Invoke();
} }
@ -281,17 +282,19 @@ namespace osu.Game.Database
/// Is a no-op for already deleted items. /// Is a no-op for already deleted items.
/// </summary> /// </summary>
/// <param name="item">The item to delete.</param> /// <param name="item">The item to delete.</param>
public void Delete(TModel item) /// <returns>false if no operation was performed</returns>
public bool Delete(TModel item)
{ {
using (ContextFactory.GetForWrite()) using (ContextFactory.GetForWrite())
{ {
// re-fetch the model on the import context. // re-fetch the model on the import context.
var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID); var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID);
if (foundModel.DeletePending) return; if (foundModel == null || foundModel.DeletePending) return false;
if (ModelStore.Delete(foundModel)) if (ModelStore.Delete(foundModel))
Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray()); Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray());
return true;
} }
} }
@ -438,6 +441,13 @@ namespace osu.Game.Database
return Task.CompletedTask; return Task.CompletedTask;
} }
if (!stable.ExistsDirectory(ImportFromStablePath))
{
// This handles situations like when the user does not have a Skins folder
Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error);
return Task.CompletedTask;
}
return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
} }

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using OpenTK.Graphics;
namespace osu.Game.Graphics.Backgrounds namespace osu.Game.Graphics.Backgrounds
{ {
@ -28,7 +27,6 @@ namespace osu.Game.Graphics.Backgrounds
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Colour = Color4.DarkGray,
FillMode = FillMode.Fill, FillMode = FillMode.Fill,
}); });
} }

View File

@ -30,8 +30,8 @@ namespace osu.Game.Graphics.Backgrounds
/// </summary> /// </summary>
private const float edge_smoothness = 1; private const float edge_smoothness = 1;
public override bool HandleKeyboardInput => false; public override bool HandleNonPositionalInput => false;
public override bool HandleMouseInput => false; public override bool HandlePositionalInput => false;
public Color4 ColourLight = Color4.White; public Color4 ColourLight = Color4.White;

View File

@ -20,7 +20,7 @@ namespace osu.Game.Graphics.Containers
{ {
} }
public override bool HandleMouseInput => true; public override bool HandlePositionalInput => true;
private OsuGame game; private OsuGame game;

View File

@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Containers
protected virtual bool PlaySamplesOnStateChange => true; protected virtual bool PlaySamplesOnStateChange => true;
protected override bool BlockPassThroughKeyboard => true; protected override bool BlockNonPositionalInput => true;
private PreviewTrackManager previewTrackManager; private PreviewTrackManager previewTrackManager;
@ -54,14 +54,14 @@ namespace osu.Game.Graphics.Containers
/// Whether mouse input should be blocked screen-wide while this overlay is visible. /// Whether mouse input should be blocked screen-wide while this overlay is visible.
/// Performing mouse actions outside of the valid extents will hide the overlay. /// Performing mouse actions outside of the valid extents will hide the overlay.
/// </summary> /// </summary>
public virtual bool BlockScreenWideMouse => BlockPassThroughMouse; public virtual bool BlockScreenWideMouse => BlockPositionalInput;
// receive input outside our bounds so we can trigger a close event on ourselves. // receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos);
protected override bool OnClick(InputState state) protected override bool OnClick(InputState state)
{ {
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) if (!base.ReceivePositionalInputAt(state.Mouse.NativeState.Position))
{ {
State = Visibility.Hidden; State = Visibility.Hidden;
return true; return true;

View File

@ -54,7 +54,7 @@ namespace osu.Game.Graphics
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
} }
public override bool HandleMouseInput => true; public override bool HandlePositionalInput => true;
protected virtual string Format() => Date.Humanize(); protected virtual string Format() => Date.Humanize();

View File

@ -47,10 +47,10 @@ namespace osu.Game.Graphics.UserInterface
public readonly SpriteIcon Chevron; public readonly SpriteIcon Chevron;
//don't allow clicking between transitions and don't make the chevron clickable //don't allow clicking between transitions and don't make the chevron clickable
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceivePositionalInputAt(screenSpacePos);
public override bool HandleKeyboardInput => State == Visibility.Visible; public override bool HandleNonPositionalInput => State == Visibility.Visible;
public override bool HandleMouseInput => State == Visibility.Visible; public override bool HandlePositionalInput => State == Visibility.Visible;
public override bool IsRemovable => true; public override bool IsRemovable => true;
private Visibility state; private Visibility state;

View File

@ -211,7 +211,7 @@ namespace osu.Game.Graphics.UserInterface
} }
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
protected override bool OnClick(InputState state) protected override bool OnClick(InputState state)
{ {

View File

@ -34,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface
} }
// We may not be focused yet, but we need to handle keyboard input to be able to request focus // We may not be focused yet, but we need to handle keyboard input to be able to request focus
public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput; public override bool HandleNonPositionalInput => HoldFocus || base.HandleNonPositionalInput;
protected override void OnFocus(InputState state) protected override void OnFocus(InputState state)
{ {

View File

@ -170,7 +170,7 @@ namespace osu.Game.Graphics.UserInterface
} }
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => IconLayer.ReceiveMouseInputAt(screenSpacePos) || TextLayer.ReceiveMouseInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => IconLayer.ReceivePositionalInputAt(screenSpacePos) || TextLayer.ReceivePositionalInputAt(screenSpacePos);
protected override bool OnHover(InputState state) protected override bool OnHover(InputState state)
{ {

View File

@ -37,7 +37,7 @@ namespace osu.Game.IPC
return; return;
} }
if (importer.HandledExtensions.Contains(Path.GetExtension(path))) if (importer.HandledExtensions.Contains(Path.GetExtension(path)?.ToLowerInvariant()))
importer.Import(path); importer.Import(path);
} }
} }

View File

@ -3,10 +3,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input.Handlers; using osu.Framework.Input.Handlers;
using osu.Framework.Input.StateChanges; using osu.Framework.Input.StateChanges;
using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States; using osu.Framework.Input.States;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Rulesets.UI;
using OpenTK; using OpenTK;
namespace osu.Game.Input.Handlers namespace osu.Game.Input.Handlers
@ -40,7 +43,29 @@ namespace osu.Game.Input.Handlers
public void Apply(InputState state, IInputStateChangeHandler handler) public void Apply(InputState state, IInputStateChangeHandler handler)
{ {
handler.HandleCustomInput(state, this); if (!(state is RulesetInputManagerInputState<T> inputState))
throw new InvalidOperationException($"{nameof(ReplayState<T>)} should only be applied to a {nameof(RulesetInputManagerInputState<T>)}");
var lastPressed = inputState.LastReplayState?.PressedActions ?? new List<T>();
var released = lastPressed.Except(PressedActions).ToArray();
var pressed = PressedActions.Except(lastPressed).ToArray();
inputState.LastReplayState = this;
handler.HandleInputStateChange(new ReplayStateChangeEvent<T>(state, this, released, pressed));
}
}
public class ReplayStateChangeEvent<T> : InputStateChangeEvent
{
public readonly T[] ReleasedActions;
public readonly T[] PressedActions;
public ReplayStateChangeEvent(InputState state, IInput input, T[] releasedActions, T[] pressedActions)
: base(state, input)
{
ReleasedActions = releasedActions;
PressedActions = pressedActions;
} }
} }
} }

View File

@ -0,0 +1,380 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20180913080842_AddRankStatus")]
partial class AddRankStatus
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntKey")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Name");
b.HasKey("ID");
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddRankStatus : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "Status",
table: "BeatmapSetInfo",
nullable: false,
defaultValue: -3); // NONE
migrationBuilder.AddColumn<int>(
name: "Status",
table: "BeatmapInfo",
nullable: false,
defaultValue: -3); // NONE
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Status",
table: "BeatmapSetInfo");
migrationBuilder.DropColumn(
name: "Status",
table: "BeatmapInfo");
}
}
}

View File

@ -1,7 +1,8 @@
// <auto-generated /> // <auto-generated />
using System;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database; using osu.Game.Database;
namespace osu.Game.Migrations namespace osu.Game.Migrations
@ -13,7 +14,7 @@ namespace osu.Game.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); .HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{ {
@ -78,6 +79,8 @@ namespace osu.Game.Migrations
b.Property<double>("StarDifficulty"); b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks"); b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom"); b.Property<double>("TimelineZoom");
@ -173,6 +176,8 @@ namespace osu.Game.Migrations
b.Property<bool>("Protected"); b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID"); b.HasKey("ID");
b.HasIndex("DeletePending"); b.HasIndex("DeletePending");

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Diagnostics; using System.Diagnostics;
using System.Net.Http;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
@ -40,7 +41,7 @@ namespace osu.Game.Online.API
using (var req = new AccessTokenRequestPassword(username, password) using (var req = new AccessTokenRequestPassword(username, password)
{ {
Url = $@"{endpoint}/oauth/token", Url = $@"{endpoint}/oauth/token",
Method = HttpMethod.POST, Method = HttpMethod.Post,
ClientId = clientId, ClientId = clientId,
ClientSecret = clientSecret ClientSecret = clientSecret
}) })
@ -66,7 +67,7 @@ namespace osu.Game.Online.API
using (var req = new AccessTokenRequestRefresh(refresh) using (var req = new AccessTokenRequestRefresh(refresh)
{ {
Url = $@"{endpoint}/oauth/token", Url = $@"{endpoint}/oauth/token",
Method = HttpMethod.POST, Method = HttpMethod.Post,
ClientId = clientId, ClientId = clientId,
ClientSecret = clientSecret ClientSecret = clientSecret
}) })

View File

@ -2,34 +2,19 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.IO.Network;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetMessagesRequest : APIRequest<List<Message>> public class GetMessagesRequest : APIRequest<List<Message>>
{ {
private readonly List<Channel> channels; private readonly Channel channel;
private readonly long? since;
public GetMessagesRequest(List<Channel> channels, long? sinceId) public GetMessagesRequest(Channel channel)
{ {
this.channels = channels; this.channel = channel;
since = sinceId;
} }
protected override WebRequest CreateWebRequest() protected override string Target => $@"chat/channels/{channel.Id}/messages";
{
string channelString = string.Join(",", channels.Select(x => x.Id));
var req = base.CreateWebRequest();
req.AddParameter(@"channels", channelString);
if (since.HasValue) req.AddParameter(@"since", since.Value.ToString());
return req;
}
protected override string Target => @"chat/messages";
} }
} }

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using JetBrains.Annotations;
using osu.Framework.IO.Network;
using osu.Game.Online.Chat;
namespace osu.Game.Online.API.Requests
{
public class GetUpdatesRequest : APIRequest<GetUpdatesResponse>
{
private readonly long since;
private readonly Channel channel;
public GetUpdatesRequest(long sinceId, [CanBeNull] Channel channel = null)
{
this.channel = channel;
since = sinceId;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
if (channel != null) req.AddParameter(@"channel", channel.Id.ToString());
req.AddParameter(@"since", since.ToString());
return req;
}
protected override string Target => @"chat/updates";
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Online.Chat;
namespace osu.Game.Online.API.Requests
{
public class GetUpdatesResponse
{
[JsonProperty]
public List<Channel> Presence;
[JsonProperty]
public List<Message> Messages;
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Net.Http;
using osu.Framework.IO.Network;
using osu.Game.Online.Chat;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
public class JoinChannelRequest : APIRequest
{
private readonly Channel channel;
private readonly User user;
public JoinChannelRequest(Channel channel, User user)
{
this.channel = channel;
this.user = user;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.Method = HttpMethod.Put;
return req;
}
protected override string Target => $@"chat/channels/{channel.Id}/users/{user.Id}";
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Net.Http;
using osu.Framework.IO.Network;
using osu.Game.Online.Chat;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
public class LeaveChannelRequest : APIRequest
{
private readonly Channel channel;
private readonly User user;
public LeaveChannelRequest(Channel channel, User user)
{
this.channel = channel;
this.user = user;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.Method = HttpMethod.Delete;
return req;
}
protected override string Target => $@"chat/channels/{channel.Id}/users/{user.Id}";
}
}

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions; using System.Net.Http;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
@ -20,15 +20,13 @@ namespace osu.Game.Online.API.Requests
{ {
var req = base.CreateWebRequest(); var req = base.CreateWebRequest();
req.Method = HttpMethod.POST; req.Method = HttpMethod.Post;
req.AddParameter(@"target_type", message.TargetType.GetDescription());
req.AddParameter(@"target_id", message.TargetId.ToString());
req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant()); req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant());
req.AddParameter(@"message", message.Content); req.AddParameter(@"message", message.Content);
return req; return req;
} }
protected override string Target => @"chat/messages"; protected override string Target => $@"chat/channels/{message.ChannelId}/messages";
} }
} }

View File

@ -15,6 +15,12 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"beatmapset_id")] [JsonProperty(@"beatmapset_id")]
public int OnlineBeatmapSetID { get; set; } public int OnlineBeatmapSetID { get; set; }
[JsonProperty(@"status")]
public BeatmapSetOnlineStatus Status { get; set; }
[JsonProperty(@"beatmapset")]
public APIBeatmapSet BeatmapSet { get; set; }
[JsonProperty(@"playcount")] [JsonProperty(@"playcount")]
private int playCount { get; set; } private int playCount { get; set; }
@ -59,11 +65,13 @@ namespace osu.Game.Online.API.Requests.Responses
Ruleset = rulesets.GetRuleset(ruleset), Ruleset = rulesets.GetRuleset(ruleset),
StarDifficulty = starDifficulty, StarDifficulty = starDifficulty,
OnlineBeatmapID = OnlineBeatmapID, OnlineBeatmapID = OnlineBeatmapID,
Version = version,
Status = Status,
BeatmapSet = new BeatmapSetInfo BeatmapSet = new BeatmapSetInfo
{ {
OnlineBeatmapSetID = OnlineBeatmapSetID, OnlineBeatmapSetID = OnlineBeatmapSetID,
Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None
}, },
Version = version,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
DrainRate = drainRate, DrainRate = drainRate,

View File

@ -20,10 +20,13 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"id")] [JsonProperty(@"id")]
public int? OnlineBeatmapSetID public int? OnlineBeatmapSetID
{ {
get { return onlineBeatmapSetID; } get => onlineBeatmapSetID;
set { onlineBeatmapSetID = value > 0 ? value : null; } set => onlineBeatmapSetID = value > 0 ? value : null;
} }
[JsonProperty(@"status")]
public BeatmapSetOnlineStatus Status { get; set; }
[JsonProperty(@"preview_url")] [JsonProperty(@"preview_url")]
private string preview { get; set; } private string preview { get; set; }
@ -42,9 +45,6 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"storyboard")] [JsonProperty(@"storyboard")]
private bool hasStoryboard { get; set; } private bool hasStoryboard { get; set; }
[JsonProperty(@"status")]
private BeatmapSetOnlineStatus status { get; set; }
[JsonProperty(@"submitted_date")] [JsonProperty(@"submitted_date")]
private DateTimeOffset submitted { get; set; } private DateTimeOffset submitted { get; set; }
@ -57,7 +57,7 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"user_id")] [JsonProperty(@"user_id")]
private long creatorId private long creatorId
{ {
set { Author.Id = value; } set => Author.Id = value;
} }
[JsonProperty(@"beatmaps")] [JsonProperty(@"beatmaps")]
@ -69,6 +69,7 @@ namespace osu.Game.Online.API.Requests.Responses
{ {
OnlineBeatmapSetID = OnlineBeatmapSetID, OnlineBeatmapSetID = OnlineBeatmapSetID,
Metadata = this, Metadata = this,
Status = Status,
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo
{ {
Covers = covers, Covers = covers,
@ -76,7 +77,7 @@ namespace osu.Game.Online.API.Requests.Responses
PlayCount = playCount, PlayCount = playCount,
FavouriteCount = favouriteCount, FavouriteCount = favouriteCount,
BPM = bpm, BPM = bpm,
Status = status, Status = Status,
HasVideo = hasVideo, HasVideo = hasVideo,
HasStoryboard = hasStoryboard, HasStoryboard = hasStoryboard,
Submitted = submitted, Submitted = submitted,

View File

@ -19,11 +19,14 @@ namespace osu.Game.Online.Chat
public string Topic; public string Topic;
[JsonProperty(@"type")] [JsonProperty(@"type")]
public string Type; public ChannelType Type;
[JsonProperty(@"channel_id")] [JsonProperty(@"channel_id")]
public int Id; public int Id;
[JsonProperty(@"last_message_id")]
public long? LastMessageId;
public readonly SortedList<Message> Messages = new SortedList<Message>(Comparer<Message>.Default); public readonly SortedList<Message> Messages = new SortedList<Message>(Comparer<Message>.Default);
private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>(); private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>();
@ -51,11 +54,20 @@ namespace osu.Game.Online.Chat
NewMessagesArrived?.Invoke(new[] { message }); NewMessagesArrived?.Invoke(new[] { message });
} }
public bool MessagesLoaded { get; private set; }
public void AddNewMessages(params Message[] messages) public void AddNewMessages(params Message[] messages)
{ {
messages = messages.Except(Messages).ToArray(); messages = messages.Except(Messages).ToArray();
if (messages.Length == 0) return;
Messages.AddRange(messages); Messages.AddRange(messages);
MessagesLoaded = true;
var maxMessageId = messages.Max(m => m.Id);
if (maxMessageId > LastMessageId)
LastMessageId = maxMessageId;
purgeOldMessages(); purgeOldMessages();

View File

@ -0,0 +1,11 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Online.Chat
{
public enum ChannelType
{
PM,
Public
}
}

View File

@ -24,7 +24,7 @@ namespace osu.Game.Online.Chat
/// </summary> /// </summary>
public List<SpriteText> Parts; public List<SpriteText> Parts;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos));
protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts);
@ -53,7 +53,7 @@ namespace osu.Game.Online.Chat
this.parts = parts; this.parts = parts;
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos));
} }
} }
} }

View File

@ -15,11 +15,7 @@ namespace osu.Game.Online.Chat
Timestamp = DateTimeOffset.Now; Timestamp = DateTimeOffset.Now;
Content = message; Content = message;
Sender = new User Sender = User.SYSTEM_USER;
{
Username = @"system",
Colour = @"0000ff",
};
} }
} }
} }

View File

@ -18,11 +18,8 @@ namespace osu.Game.Online.Chat
[JsonProperty(@"sender_id")] [JsonProperty(@"sender_id")]
public int UserId; public int UserId;
[JsonProperty(@"target_type")] [JsonProperty(@"channel_id")]
public TargetType TargetType; public int ChannelId;
[JsonProperty(@"target_id")]
public int TargetId;
[JsonProperty(@"is_action")] [JsonProperty(@"is_action")]
public bool IsAction; public bool IsAction;

View File

@ -355,7 +355,7 @@ namespace osu.Game
loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add);
loadComponentSingleFile(musicController = new MusicController loadComponentSingleFile(musicController = new MusicController
{ {
Depth = -4, Depth = -5,
Position = new Vector2(0, Toolbar.HEIGHT), Position = new Vector2(0, Toolbar.HEIGHT),
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,

View File

@ -154,7 +154,7 @@ namespace osu.Game
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); dependencies.Cache(ScoreStore = new ScoreStore(contextFactory, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));
@ -243,7 +243,7 @@ namespace osu.Game
public void Import(params string[] paths) public void Import(params string[] paths)
{ {
var extension = Path.GetExtension(paths.First()); var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
foreach (var importer in fileImporters) foreach (var importer in fileImporters)
if (importer.HandledExtensions.Contains(extension)) importer.Import(paths); if (importer.HandledExtensions.Contains(extension)) importer.Import(paths);

View File

@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
}, },
}; };
Action = () => playButton.TriggerOnClick(); Action = () => playButton.Click();
Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100); Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
} }

View File

@ -230,10 +230,12 @@ namespace osu.Game.Overlays.BeatmapSet
Spacing = new Vector2(10), Spacing = new Vector2(10),
Children = new Drawable[] Children = new Drawable[]
{ {
onlineStatusPill = new BeatmapSetOnlineStatusPill(14, new MarginPadding { Horizontal = 25, Vertical = 8 }) onlineStatusPill = new BeatmapSetOnlineStatusPill
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 }
}, },
Details = new Details(), Details = new Details(),
}, },

View File

@ -51,7 +51,7 @@ namespace osu.Game.Overlays
} }
// receive input outside our bounds so we can trigger a close event on ourselves. // receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay() public BeatmapSetOverlay()
{ {

View File

@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using OpenTK; using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using System; using System;
@ -143,6 +144,17 @@ namespace osu.Game.Overlays.Chat
textBold.FadeOut(transition_length, Easing.OutQuint); textBold.FadeOut(transition_length, Easing.OutQuint);
} }
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (args.Button == MouseButton.Middle)
{
closeButton.Action();
return true;
}
return false;
}
protected override bool OnHover(InputState state) protected override bool OnHover(InputState state)
{ {
if (IsRemovable) if (IsRemovable)

View File

@ -47,7 +47,7 @@ namespace osu.Game.Overlays
public const float TAB_AREA_HEIGHT = 50; public const float TAB_AREA_HEIGHT = 50;
private GetMessagesRequest fetchReq; private GetUpdatesRequest fetchReq;
private readonly ChatTabControl channelTabs; private readonly ChatTabControl channelTabs;
@ -62,7 +62,7 @@ namespace osu.Game.Overlays
private readonly Container channelSelectionContainer; private readonly Container channelSelectionContainer;
private readonly ChannelSelectionOverlay channelSelection; private readonly ChannelSelectionOverlay channelSelection;
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceiveMouseInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceiveMouseInputAt(screenSpacePos); public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceivePositionalInputAt(screenSpacePos);
public ChatOverlay() public ChatOverlay()
{ {
@ -285,7 +285,7 @@ namespace osu.Game.Overlays
chatBackground.Colour = colours.ChatBlue; chatBackground.Colour = colours.ChatBlue;
} }
private long? lastMessageId; private long lastMessageId;
private readonly List<Channel> careChannels = new List<Channel>(); private readonly List<Channel> careChannels = new List<Channel>();
@ -304,9 +304,9 @@ namespace osu.Game.Overlays
Scheduler.Add(delegate Scheduler.Add(delegate
{ {
//todo: decide how to handle default channels for a user now that they are saved server-side.
addChannel(channels.Find(c => c.Name == @"#lazer")); addChannel(channels.Find(c => c.Name == @"#lazer"));
addChannel(channels.Find(c => c.Name == @"#osu")); addChannel(channels.Find(c => c.Name == @"#osu"));
addChannel(channels.Find(c => c.Name == @"#lobby"));
channelSelection.OnRequestJoin = addChannel; channelSelection.OnRequestJoin = addChannel;
channelSelection.OnRequestLeave = removeChannel; channelSelection.OnRequestLeave = removeChannel;
@ -320,7 +320,7 @@ namespace osu.Game.Overlays
}; };
}); });
messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); messageRequest = Scheduler.AddDelayed(fetchUpdates, 1000, true);
}; };
api.Queue(req); api.Queue(req);
@ -362,7 +362,7 @@ namespace osu.Game.Overlays
loadedChannels.Add(loaded); loadedChannels.Add(loaded);
LoadComponentAsync(loaded, l => LoadComponentAsync(loaded, l =>
{ {
if (currentChannel.Messages.Any()) if (currentChannel.MessagesLoaded)
loading.Hide(); loading.Hide();
currentChannelContainer.Clear(false); currentChannelContainer.Clear(false);
@ -394,6 +394,15 @@ namespace osu.Game.Overlays
{ {
careChannels.Add(channel); careChannels.Add(channel);
channelTabs.AddItem(channel); channelTabs.AddItem(channel);
if (channel.Type == ChannelType.Public && !channel.Joined)
{
var req = new JoinChannelRequest(channel, api.LocalUser);
req.Success += () => addChannel(channel);
req.Failure += ex => removeChannel(channel);
api.Queue(req);
return;
}
} }
// let's fetch a small number of messages to bring us up-to-date with the backlog. // let's fetch a small number of messages to bring us up-to-date with the backlog.
@ -415,47 +424,53 @@ namespace osu.Game.Overlays
loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
channelTabs.RemoveItem(channel); channelTabs.RemoveItem(channel);
api.Queue(new LeaveChannelRequest(channel, api.LocalUser));
channel.Joined.Value = false; channel.Joined.Value = false;
} }
private void fetchInitialMessages(Channel channel) private void fetchInitialMessages(Channel channel)
{ {
var req = new GetMessagesRequest(new List<Channel> { channel }, null); var req = new GetMessagesRequest(channel);
req.Success += messages =>
req.Success += delegate (List<Message> messages)
{ {
loading.Hide();
channel.AddNewMessages(messages.ToArray()); channel.AddNewMessages(messages.ToArray());
Debug.Write("success!"); if (channel == currentChannel)
}; loading.Hide();
req.Failure += delegate
{
Debug.Write("failure!");
}; };
api.Queue(req); api.Queue(req);
} }
private void fetchNewMessages() private void fetchUpdates()
{ {
if (fetchReq != null) return; if (fetchReq != null) return;
fetchReq = new GetMessagesRequest(careChannels, lastMessageId); fetchReq = new GetUpdatesRequest(lastMessageId);
fetchReq.Success += delegate (List<Message> messages) fetchReq.Success += updates =>
{ {
foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId)) if (updates?.Presence != null)
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); {
foreach (var channel in updates.Presence)
{
if (careChannels.Find(c => c.Id == channel.Id) == null)
{
channel.Joined.Value = true;
addChannel(channel);
}
}
lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId; foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
}
Debug.Write("success!");
fetchReq = null; fetchReq = null;
}; };
fetchReq.Failure += delegate fetchReq.Failure += delegate
{ {
Debug.Write("failure!");
fetchReq = null; fetchReq = null;
}; };
@ -517,8 +532,7 @@ namespace osu.Game.Overlays
{ {
Sender = api.LocalUser.Value, Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now, Timestamp = DateTimeOffset.Now,
TargetType = TargetType.Channel, //TODO: read this from channel ChannelId = target.Id,
TargetId = target.Id,
IsAction = isAction, IsAction = isAction,
Content = postText Content = postText
}; };

View File

@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Dialog
public static readonly float ENTER_DURATION = 500; public static readonly float ENTER_DURATION = 500;
public static readonly float EXIT_DURATION = 200; public static readonly float EXIT_DURATION = 200;
protected override bool BlockPassThroughMouse => false; protected override bool BlockPositionalInput => false;
private readonly Vector2 ringSize = new Vector2(100f); private readonly Vector2 ringSize = new Vector2(100f);
private readonly Vector2 ringMinifiedSize = new Vector2(20f); private readonly Vector2 ringMinifiedSize = new Vector2(20f);
@ -199,7 +199,7 @@ namespace osu.Game.Overlays.Dialog
switch (action) switch (action)
{ {
case GlobalAction.Select: case GlobalAction.Select:
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick(); Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.Click();
return true; return true;
} }
@ -252,7 +252,7 @@ namespace osu.Game.Overlays.Dialog
if (!actionInvoked) if (!actionInvoked)
// In the case a user did not choose an action before a hide was triggered, press the last button. // In the case a user did not choose an action before a hide was triggered, press the last button.
// This is presumed to always be a sane default "cancel" action. // This is presumed to always be a sane default "cancel" action.
buttonsContainer.Last().TriggerOnClick(); buttonsContainer.Last().Click();
base.PopOut(); base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine); content.FadeOut(EXIT_DURATION, Easing.InSine);
@ -261,7 +261,7 @@ namespace osu.Game.Overlays.Dialog
private void pressButtonAtIndex(int index) private void pressButtonAtIndex(int index)
{ {
if (index < Buttons.Count()) if (index < Buttons.Count())
Buttons.Skip(index).First().TriggerOnClick(); Buttons.Skip(index).First().Click();
} }
} }
} }

View File

@ -43,7 +43,7 @@ namespace osu.Game.Overlays
protected override bool PlaySamplesOnStateChange => false; protected override bool PlaySamplesOnStateChange => false;
protected override bool BlockPassThroughKeyboard => true; protected override bool BlockNonPositionalInput => true;
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
{ {

View File

@ -7,11 +7,11 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.States; using osu.Framework.Input.States;
using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Direct
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation) private void load(OsuColour colours)
{ {
Content.CornerRadius = 4; Content.CornerRadius = 4;
@ -74,13 +74,13 @@ namespace osu.Game.Overlays.Direct
{ {
new OsuSpriteText new OsuSpriteText
{ {
Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
TextSize = 18, TextSize = 18,
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
}, },
}, },
@ -217,8 +217,10 @@ namespace osu.Game.Overlays.Direct
statusContainer.Add(new IconPill(FontAwesome.fa_image)); statusContainer.Add(new IconPill(FontAwesome.fa_image));
} }
statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 }) statusContainer.Add(new BeatmapSetOnlineStatusPill
{ {
TextSize = 12,
TextPadding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
}); });

View File

@ -10,8 +10,8 @@ using osu.Framework.Graphics.Colour;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Direct
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(LocalisationEngine localisation, OsuColour colours) private void load(OsuColour colours)
{ {
Content.CornerRadius = 5; Content.CornerRadius = 5;
@ -94,13 +94,13 @@ namespace osu.Game.Overlays.Direct
{ {
new OsuSpriteText new OsuSpriteText
{ {
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
TextSize = 18, TextSize = 18,
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
}, },
new OsuSpriteText new OsuSpriteText
{ {
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
}, },
} }

View File

@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Direct
iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100);
} }
public override bool HandleKeyboardInput => !bindable.Disabled && base.HandleKeyboardInput; public override bool HandleNonPositionalInput => !bindable.Disabled && base.HandleNonPositionalInput;
public override bool HandleMouseInput => !bindable.Disabled && base.HandleMouseInput; public override bool HandlePositionalInput => !bindable.Disabled && base.HandlePositionalInput;
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset) public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
{ {

View File

@ -50,7 +50,7 @@ namespace osu.Game.Overlays.KeyBinding
private FillFlowContainer<KeyButton> buttons; private FillFlowContainer<KeyButton> buttons;
public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text); public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend((string)text.Text);
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings) public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
{ {

View File

@ -155,7 +155,7 @@ namespace osu.Game.Overlays
switch (action) switch (action)
{ {
case GlobalAction.Back: case GlobalAction.Back:
TriggerOnClick(); Click();
return true; return true;
} }

View File

@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Mods
protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel; protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel;
private readonly FillFlowContainer footerContainer; private readonly FillFlowContainer footerContainer;
protected override bool BlockPassThroughKeyboard => false; protected override bool BlockNonPositionalInput => false;
protected readonly FillFlowContainer<ModSection> ModSectionsContainer; protected readonly FillFlowContainer<ModSection> ModSectionsContainer;

View File

@ -28,8 +28,8 @@ namespace osu.Game.Overlays.Music
private SpriteIcon handle; private SpriteIcon handle;
private TextFlowContainer text; private TextFlowContainer text;
private IEnumerable<SpriteText> titleSprites; private IEnumerable<SpriteText> titleSprites;
private UnicodeBindableString titleBind; private ILocalisedBindableString titleBind;
private UnicodeBindableString artistBind; private ILocalisedBindableString artistBind;
public readonly BeatmapSetInfo BeatmapSetInfo; public readonly BeatmapSetInfo BeatmapSetInfo;
@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Music
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation) private void load(OsuColour colours, LocalisationManager localisation)
{ {
hoverColour = colours.Yellow; hoverColour = colours.Yellow;
artistColour = colours.Gray9; artistColour = colours.Gray9;
@ -97,11 +97,10 @@ namespace osu.Game.Overlays.Music
}, },
}; };
titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); titleBind = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title)));
artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); artistBind = localisation.GetLocalisedString(new LocalisedString((metadata.ArtistUnicode, metadata.Artist)));
artistBind.ValueChanged += newText => recreateText(); artistBind.BindValueChanged(newText => recreateText(), true);
artistBind.TriggerChange();
} }
private void recreateText() private void recreateText()

View File

@ -47,7 +47,6 @@ namespace osu.Game.Overlays
private PlaylistOverlay playlist; private PlaylistOverlay playlist;
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private LocalisationEngine localisation;
private List<BeatmapSetInfo> beatmapSets; private List<BeatmapSetInfo> beatmapSets;
private BeatmapSetInfo currentSet; private BeatmapSetInfo currentSet;
@ -67,11 +66,10 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours, LocalisationEngine localisation) private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours)
{ {
this.beatmap.BindTo(beatmap); this.beatmap.BindTo(beatmap);
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
this.localisation = localisation;
Children = new Drawable[] Children = new Drawable[]
{ {
@ -351,17 +349,14 @@ namespace osu.Game.Overlays
{ {
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
{ {
title.Current = null;
title.Text = @"Nothing to play"; title.Text = @"Nothing to play";
artist.Current = null;
artist.Text = @"Nothing to play"; artist.Text = @"Nothing to play";
} }
else else
{ {
BeatmapMetadata metadata = beatmap.Metadata; BeatmapMetadata metadata = beatmap.Metadata;
title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
} }
}); });

View File

@ -25,8 +25,8 @@ namespace osu.Game.Overlays
{ {
private readonly Container box; private readonly Container box;
public override bool HandleKeyboardInput => false; public override bool HandleNonPositionalInput => false;
public override bool HandleMouseInput => false; public override bool HandlePositionalInput => false;
private readonly SpriteText textLine1; private readonly SpriteText textLine1;
private readonly SpriteText textLine2; private readonly SpriteText textLine2;

View File

@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections
public string TooltipText { get; } public string TooltipText { get; }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) private void load(BeatmapSetOverlay beatmapSetOverlay)
{ {
Action = () => Action = () =>
{ {
@ -46,16 +46,14 @@ namespace osu.Game.Overlays.Profile.Sections
{ {
new OsuSpriteText new OsuSpriteText
{ {
Current = locale.GetUnicodePreference( Text = new LocalisedString(($"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")),
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] "
),
TextSize = 15, TextSize = 15,
Font = "Exo2.0-SemiBoldItalic", Font = "Exo2.0-SemiBoldItalic",
}, },
new OsuSpriteText new OsuSpriteText
{ {
Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
TextSize = 12, TextSize = 12,
Padding = new MarginPadding { Top = 3 }, Padding = new MarginPadding { Top = 3 },
Font = "Exo2.0-RegularItalic", Font = "Exo2.0-RegularItalic",

View File

@ -1,26 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
public class ScrollingSettings : SettingsSubsection
{
protected override string Header => "Scrolling";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
{
new SettingsEnumDropdown<SpeedChangeVisualisationMethod>
{
LabelText = "Visualise speed changes as",
Bindable = config.GetBindable<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation),
}
};
}
}
}

View File

@ -21,7 +21,6 @@ namespace osu.Game.Overlays.Settings.Sections
{ {
new GeneralSettings(), new GeneralSettings(),
new SongSelectSettings(), new SongSelectSettings(),
new ScrollingSettings(),
new ModsSettings(), new ModsSettings(),
}; };
} }
@ -29,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetStore rulesets) private void load(RulesetStore rulesets)
{ {
foreach(Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) foreach (Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance()))
{ {
SettingsSubsection section = ruleset.CreateSettings(); SettingsSubsection section = ruleset.CreateSettings();
if (section != null) if (section != null)

View File

@ -35,6 +35,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
letterboxing = config.GetBindable<bool>(FrameworkSetting.Letterboxing); letterboxing = config.GetBindable<bool>(FrameworkSetting.Letterboxing);
sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen); sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen);
Container resolutionSettingsContainer;
Children = new Drawable[] Children = new Drawable[]
{ {
windowModeDropdown = new SettingsEnumDropdown<WindowMode> windowModeDropdown = new SettingsEnumDropdown<WindowMode>
@ -42,12 +44,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
LabelText = "Screen mode", LabelText = "Screen mode",
Bindable = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode), Bindable = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode),
}, },
resolutionDropdown = new SettingsDropdown<Size> resolutionSettingsContainer = new Container
{ {
LabelText = "Resolution", RelativeSizeAxes = Axes.X,
ShowsDefaultIndicator = false, AutoSizeAxes = Axes.Y
Items = getResolutions(),
Bindable = sizeFullscreen
}, },
new SettingsCheckbox new SettingsCheckbox
{ {
@ -81,16 +81,29 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
}, },
}; };
windowModeDropdown.Bindable.BindValueChanged(windowMode => var resolutions = getResolutions();
if (resolutions.Count > 1)
{ {
if (windowMode == WindowMode.Fullscreen) resolutionSettingsContainer.Child = resolutionDropdown = new SettingsDropdown<Size>
{ {
resolutionDropdown.Show(); LabelText = "Resolution",
sizeFullscreen.TriggerChange(); ShowsDefaultIndicator = false,
} Items = resolutions,
else Bindable = sizeFullscreen
resolutionDropdown.Hide(); };
}, true);
windowModeDropdown.Bindable.BindValueChanged(windowMode =>
{
if (windowMode == WindowMode.Fullscreen)
{
resolutionDropdown.Show();
sizeFullscreen.TriggerChange();
}
else
resolutionDropdown.Hide();
});
}
letterboxing.BindValueChanged(isVisible => letterboxing.BindValueChanged(isVisible =>
{ {
@ -102,7 +115,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
}, true); }, true);
} }
private IEnumerable<KeyValuePair<string, Size>> getResolutions() private IReadOnlyList<KeyValuePair<string, Size>> getResolutions()
{ {
var resolutions = new KeyValuePair<string, Size>("Default", new Size(9999, 9999)).Yield(); var resolutions = new KeyValuePair<string, Size>("Default", new Size(9999, 9999)).Yield();
@ -112,8 +125,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
.OrderByDescending(r => r.Width) .OrderByDescending(r => r.Width)
.ThenByDescending(r => r.Height) .ThenByDescending(r => r.Height)
.Select(res => new KeyValuePair<string, Size>($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) .Select(res => new KeyValuePair<string, Size>($"{res.Width}x{res.Height}", new Size(res.Width, res.Height)))
.Distinct()).ToList(); .Distinct());
return resolutions; return resolutions.ToList();
} }
} }
} }

View File

@ -13,57 +13,59 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
{ {
public class GeneralSettings : SettingsSubsection public class GeneralSettings : SettingsSubsection
{ {
private TriangleButton importButton; protected override string Header => "General";
private TriangleButton deleteButton;
private TriangleButton importBeatmapsButton;
private TriangleButton importSkinsButton;
private TriangleButton deleteSkinsButton;
private TriangleButton deleteBeatmapsButton;
private TriangleButton restoreButton; private TriangleButton restoreButton;
private TriangleButton undeleteButton; private TriangleButton undeleteButton;
protected override string Header => "General";
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay) private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay)
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
importButton = new SettingsButton importBeatmapsButton = new SettingsButton
{ {
Text = "Import beatmaps from stable", Text = "Import beatmaps from stable",
Action = () => Action = () =>
{ {
importButton.Enabled.Value = false; importBeatmapsButton.Enabled.Value = false;
beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true));
} }
}, },
deleteButton = new DangerousSettingsButton deleteBeatmapsButton = new DangerousSettingsButton
{ {
Text = "Delete ALL beatmaps", Text = "Delete ALL beatmaps",
Action = () => Action = () =>
{ {
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
{ {
deleteButton.Enabled.Value = false; deleteBeatmapsButton.Enabled.Value = false;
Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteBeatmapsButton.Enabled.Value = true));
})); }));
} }
}, },
importButton = new SettingsButton importSkinsButton = new SettingsButton
{ {
Text = "Import skins from stable", Text = "Import skins from stable",
Action = () => Action = () =>
{ {
importButton.Enabled.Value = false; importSkinsButton.Enabled.Value = false;
skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importSkinsButton.Enabled.Value = true));
} }
}, },
deleteButton = new DangerousSettingsButton deleteSkinsButton = new DangerousSettingsButton
{ {
Text = "Delete ALL skins", Text = "Delete ALL skins",
Action = () => Action = () =>
{ {
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
{ {
deleteButton.Enabled.Value = false; deleteSkinsButton.Enabled.Value = false;
Task.Run(() => skins.Delete(skins.GetAllUserSkins())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); Task.Run(() => skins.Delete(skins.GetAllUserSkins())).ContinueWith(t => Schedule(() => deleteSkinsButton.Enabled.Value = true));
})); }));
} }
}, },

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Toolbar
private readonly ToolbarUserArea userArea; private readonly ToolbarUserArea userArea;
protected override bool BlockPassThroughMouse => false; protected override bool BlockPositionalInput => false;
private const double transition_time = 500; private const double transition_time = 500;

View File

@ -103,8 +103,8 @@ namespace osu.Game.Overlays.Toolbar
return false; return false;
} }
public override bool HandleKeyboardInput => !ruleset.Disabled && base.HandleKeyboardInput; public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
public override bool HandleMouseInput => !ruleset.Disabled && base.HandleMouseInput; public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);

View File

@ -77,9 +77,11 @@ namespace osu.Game.Overlays
public void ShowUser(User user, bool fetchOnline = true) public void ShowUser(User user, bool fetchOnline = true)
{ {
if (user == User.SYSTEM_USER) return;
Show(); Show();
if (user.Id == Header?.User.Id) if (user.Id == Header?.User?.Id)
return; return;
userReq?.Cancel(); userReq?.Cancel();

View File

@ -28,7 +28,7 @@ namespace osu.Game.Overlays
private VolumeMeter volumeMeterMusic; private VolumeMeter volumeMeterMusic;
private MuteButton muteButton; private MuteButton muteButton;
protected override bool BlockPassThroughMouse => false; protected override bool BlockPositionalInput => false;
private readonly BindableDouble muteAdjustment = new BindableDouble(); private readonly BindableDouble muteAdjustment = new BindableDouble();

View File

@ -11,7 +11,7 @@ namespace osu.Game.Overlays
{ {
protected readonly WaveContainer Waves; protected readonly WaveContainer Waves;
protected override bool BlockPassThroughKeyboard => true; protected override bool BlockNonPositionalInput => true;
protected override Container<Drawable> Content => Waves; protected override Container<Drawable> Content => Waves;
protected WaveOverlayContainer() protected WaveOverlayContainer()

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Edit
public readonly DrawableHitObject HitObject; public readonly DrawableHitObject HitObject;
protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected;
public override bool HandleMouseInput => ShouldBeAlive; public override bool HandlePositionalInput => ShouldBeAlive;
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
public HitObjectMask(DrawableHitObject hitObject) public HitObjectMask(DrawableHitObject hitObject)

View File

@ -65,13 +65,15 @@ namespace osu.Game.Rulesets.Judgements
this.FadeInFromZero(100, Easing.OutQuint); this.FadeInFromZero(100, Easing.OutQuint);
var origScale = Scale;
switch (Result.Type) switch (Result.Type)
{ {
case HitResult.None: case HitResult.None:
break; break;
case HitResult.Miss: case HitResult.Miss:
this.ScaleTo(1.6f); this.ScaleTo(origScale * 1.6f);
this.ScaleTo(1, 100, Easing.In); this.ScaleTo(origScale, 100, Easing.In);
this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
this.RotateTo(40, 800, Easing.InQuint); this.RotateTo(40, 800, Easing.InQuint);
@ -79,8 +81,8 @@ namespace osu.Game.Rulesets.Judgements
this.Delay(600).FadeOut(200); this.Delay(600).FadeOut(200);
break; break;
default: default:
this.ScaleTo(0.9f); this.ScaleTo(origScale * 0.9f);
this.ScaleTo(1, 500, Easing.OutElastic); this.ScaleTo(origScale, 500, Easing.OutElastic);
this.Delay(100).FadeOut(400); this.Delay(100).FadeOut(400);
break; break;

View File

@ -78,8 +78,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
private bool judgementOccurred; private bool judgementOccurred;
public bool Interactive = true; public bool Interactive = true;
public override bool HandleKeyboardInput => Interactive; public override bool HandleNonPositionalInput => Interactive;
public override bool HandleMouseInput => Interactive; public override bool HandlePositionalInput => Interactive;
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
public override bool RemoveCompletedTransforms => false; public override bool RemoveCompletedTransforms => false;

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Users; using osu.Game.Users;
@ -32,6 +33,7 @@ namespace osu.Game.Rulesets.Scoring
public User User; public User User;
[JsonIgnore]
public Replay Replay; public Replay Replay;
public BeatmapInfo Beatmap; public BeatmapInfo Beatmap;

View File

@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
@ -13,8 +14,6 @@ namespace osu.Game.Rulesets.Scoring
{ {
public class ScoreStore : DatabaseBackedStore, ICanAcceptFiles public class ScoreStore : DatabaseBackedStore, ICanAcceptFiles
{ {
private readonly Storage storage;
private readonly BeatmapManager beatmaps; private readonly BeatmapManager beatmaps;
private readonly RulesetStore rulesets; private readonly RulesetStore rulesets;
@ -25,9 +24,8 @@ namespace osu.Game.Rulesets.Scoring
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ScoreIPCChannel ipc; private ScoreIPCChannel ipc;
public ScoreStore(Storage storage, DatabaseContextFactory factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory) public ScoreStore(DatabaseContextFactory factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory)
{ {
this.storage = storage;
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
this.rulesets = rulesets; this.rulesets = rulesets;
@ -49,8 +47,14 @@ namespace osu.Game.Rulesets.Scoring
public Score ReadReplayFile(string replayFilename) public Score ReadReplayFile(string replayFilename)
{ {
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) if (File.Exists(replayFilename))
return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(s); {
using (var stream = File.OpenRead(replayFilename))
return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(stream);
}
Logger.Log($"Replay file {replayFilename} cannot be found", LoggingTarget.Information, LogLevel.Error);
return null;
} }
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
@ -10,7 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.EventArgs; using osu.Framework.Input.EventArgs;
using osu.Framework.Input.StateChanges; using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States; using osu.Framework.Input.States;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -56,33 +55,20 @@ namespace osu.Game.Rulesets.UI
#region Action mapping (for replays) #region Action mapping (for replays)
private List<T> lastPressedActions = new List<T>(); public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
public override void HandleCustomInput(InputState state, IInput input)
{ {
if (!(input is ReplayState<T> replayState)) if (inputStateChange is ReplayStateChangeEvent<T> replayStateChanged)
{ {
base.HandleCustomInput(state, input); foreach (var action in replayStateChanged.ReleasedActions)
return; KeyBindingContainer.TriggerReleased(action);
}
if (state is RulesetInputManagerInputState<T> inputState) foreach (var action in replayStateChanged.PressedActions)
KeyBindingContainer.TriggerPressed(action);
}
else
{ {
inputState.LastReplayState = replayState; base.HandleInputStateChange(inputStateChange);
} }
// Here we handle states specifically coming from a replay source.
// These have extra action information rather than keyboard keys or mouse buttons.
List<T> newActions = replayState.PressedActions;
foreach (var released in lastPressedActions.Except(newActions))
KeyBindingContainer.TriggerReleased(released);
foreach (var pressed in newActions.Except(lastPressedActions))
KeyBindingContainer.TriggerPressed(pressed);
lastPressedActions = newActions;
} }
#endregion #endregion

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -33,20 +32,16 @@ namespace osu.Game.Rulesets.UI.Scrolling
private Cached initialStateCache = new Cached(); private Cached initialStateCache = new Cached();
public ScrollingHitObjectContainer() private readonly ISpeedChangeVisualiser speedChangeVisualiser;
public ScrollingHitObjectContainer(SpeedChangeVisualisationMethod visualisationMethod)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
TimeRange.ValueChanged += _ => initialStateCache.Invalidate(); TimeRange.ValueChanged += _ => initialStateCache.Invalidate();
Direction.ValueChanged += _ => initialStateCache.Invalidate(); Direction.ValueChanged += _ => initialStateCache.Invalidate();
}
private ISpeedChangeVisualiser speedChangeVisualiser; switch (visualisationMethod)
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
switch (config.Get<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation))
{ {
case SpeedChangeVisualisationMethod.Sequential: case SpeedChangeVisualisationMethod.Sequential:
speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints); speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints);
@ -54,6 +49,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
case SpeedChangeVisualisationMethod.Overlapping: case SpeedChangeVisualisationMethod.Overlapping:
speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints);
break; break;
case SpeedChangeVisualisationMethod.Constant:
speedChangeVisualiser = new ConstantSpeedChangeVisualiser();
break;
} }
} }

View File

@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Configuration;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -62,6 +63,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// </summary> /// </summary>
protected readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>(); protected readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential;
/// <summary> /// <summary>
/// Creates a new <see cref="ScrollingPlayfield"/>. /// Creates a new <see cref="ScrollingPlayfield"/>.
/// </summary> /// </summary>
@ -104,7 +107,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
protected sealed override HitObjectContainer CreateHitObjectContainer() protected sealed override HitObjectContainer CreateHitObjectContainer()
{ {
var container = new ScrollingHitObjectContainer(); var container = new ScrollingHitObjectContainer(VisualisationMethod);
container.Direction.BindTo(Direction); container.Direction.BindTo(Direction);
return container; return container;
} }

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
{
public class ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser
{
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
{
foreach (var obj in hitObjects)
{
obj.LifetimeStart = obj.HitObject.StartTime - timeRange;
if (obj.HitObject is IHasEndTime endTime)
{
var hitObjectLength = (endTime.EndTime - obj.HitObject.StartTime) / timeRange;
switch (direction)
{
case ScrollingDirection.Up:
case ScrollingDirection.Down:
obj.Height = (float)(hitObjectLength * length.Y);
break;
case ScrollingDirection.Left:
case ScrollingDirection.Right:
obj.Width = (float)(hitObjectLength * length.X);
break;
}
}
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
// Nested hitobjects don't need to scroll, but they do need accurate positions
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
}
}
public void UpdatePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
{
foreach (var obj in hitObjects)
{
var position = (obj.HitObject.StartTime - currentTime) / timeRange;
switch (direction)
{
case ScrollingDirection.Up:
obj.Y = (float)(position * length.Y);
break;
case ScrollingDirection.Down:
obj.Y = (float)(-position * length.Y);
break;
case ScrollingDirection.Left:
obj.X = (float)(position * length.X);
break;
case ScrollingDirection.Right:
obj.X = (float)(-position * length.X);
break;
}
}
}
}
}

View File

@ -4,19 +4,14 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Screens.Backgrounds namespace osu.Game.Screens.Backgrounds
{ {
public class BackgroundScreenBeatmap : BackgroundScreen public class BackgroundScreenBeatmap : BlurrableBackgroundScreen
{ {
private Background background;
private WorkingBeatmap beatmap; private WorkingBeatmap beatmap;
private Vector2 blurTarget;
public WorkingBeatmap Beatmap public WorkingBeatmap Beatmap
{ {
@ -30,21 +25,21 @@ namespace osu.Game.Screens.Backgrounds
Schedule(() => Schedule(() =>
{ {
LoadComponentAsync(new BeatmapBackground(beatmap), b => LoadComponentAsync(new BeatmapBackground(beatmap), b => Schedule(() =>
{ {
float newDepth = 0; float newDepth = 0;
if (background != null) if (Background != null)
{ {
newDepth = background.Depth + 1; newDepth = Background.Depth + 1;
background.FinishTransforms(); Background.FinishTransforms();
background.FadeOut(250); Background.FadeOut(250);
background.Expire(); Background.Expire();
} }
b.Depth = newDepth; b.Depth = newDepth;
Add(background = b); Add(Background = b);
background.BlurSigma = blurTarget; Background.BlurSigma = BlurTarget;
}); }));
}); });
} }
} }
@ -54,9 +49,6 @@ namespace osu.Game.Screens.Backgrounds
Beatmap = beatmap; Beatmap = beatmap;
} }
public TransformSequence<Background> BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None)
=> background?.BlurTo(blurTarget = sigma, duration, easing);
public override bool Equals(BackgroundScreen other) public override bool Equals(BackgroundScreen other)
{ {
var otherBeatmapBackground = other as BackgroundScreenBeatmap; var otherBeatmapBackground = other as BackgroundScreenBeatmap;

View File

@ -3,32 +3,32 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Screens.Backgrounds namespace osu.Game.Screens.Backgrounds
{ {
public class BackgroundScreenDefault : BackgroundScreen public class BackgroundScreenDefault : BlurrableBackgroundScreen
{ {
private int currentDisplay; private int currentDisplay;
private const int background_count = 5; private const int background_count = 5;
private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}";
private Background current;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
currentDisplay = RNG.Next(0, background_count);
display(new Background(backgroundName)); display(new Background(backgroundName));
} }
private void display(Background newBackground) private void display(Background newBackground)
{ {
current?.FadeOut(800, Easing.InOutSine); Background?.FadeOut(800, Easing.InOutSine);
current?.Expire(); Background?.Expire();
Add(current = newBackground); Add(Background = newBackground);
currentDisplay++; currentDisplay++;
} }

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