1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 07:42:57 +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
build_script:
- 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
- cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
- appveyor DownloadFile https://puu.sh/BCrS8/7faccf7876.enc # signing certificate
- 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
- cd osu-deploy
- nuget restore -verbosity quiet

View File

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

View File

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

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects
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; }

View File

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

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input.States;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Mods
wasLeft = !wasLeft;
}
osuInputManager.HandleCustomInput(new InputState(), state);
state.Apply(osuInputManager.CurrentState, osuInputManager);
}
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 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)
{
// 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
&& lastState != null
&& ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
&& ReceivePositionalInputAt(lastState.Mouse.NativeState.Position)
&& (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);
}
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)
{

View File

@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SpinnerBackground : CircularContainer, IHasAccentColour
{
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
public override bool HandleNonPositionalInput => false;
public override bool HandlePositionalInput => false;
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;
public bool Tracking

View File

@ -118,9 +118,9 @@ namespace osu.Game.Rulesets.Osu
new OsuModAutopilot(),
};
case ModType.Fun:
return new Mod[]
{
new OsuModDeflate(),
return new Mod[] {
new OsuModTransform(),
new OsuModWiggle(),
};
default:
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]
private void load(ShaderManager shaders, TextureStore textures)

View File

@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
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()
{

View File

@ -72,7 +72,8 @@ namespace osu.Game.Rulesets.Osu.UI
DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
{
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);

View File

@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private bool validActionPressed;
private bool pressHandledThisFrame;
protected DrawableHit(Hit hit)
: base(hit)
{
@ -51,6 +53,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public override bool OnPressed(TaikoAction action)
{
if (pressHandledThisFrame)
return true;
if (Judged)
return false;
@ -62,6 +67,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (IsHit)
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;
}
@ -76,6 +85,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
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;
}

View File

@ -1,23 +1,24 @@
// 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.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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Framework.Allocation;
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.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.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
{
@ -40,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override bool UserScrollSpeedAdjustment => false;
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping;
private readonly Container<HitExplosion> hitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;

View File

@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual
public CursorContainer Cursor { 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;

View File

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

View File

@ -1,32 +1,45 @@
// 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 System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Screens.Play;
using OpenTK.Input;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseKeyCounter : OsuTestCase
public class TestCaseKeyCounter : ManualInputManagerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(KeyCounterKeyboard),
typeof(KeyCounterMouse),
typeof(KeyCounterCollection)
};
public TestCaseKeyCounter()
{
KeyCounterKeyboard rewindTestKeyCounterKeyboard;
KeyCounterCollection kc = new KeyCounterCollection
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new KeyCounter[]
{
new KeyCounterKeyboard(Key.Z),
rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),
},
};
AddStep("Add random", () =>
{
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);
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);
}
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")]
public int? OnlineBeatmapID
{
get { return onlineBeatmapID; }
set { onlineBeatmapID = value > 0 ? value : null; }
get => onlineBeatmapID;
set => onlineBeatmapID = value > 0 ? value : null;
}
[JsonIgnore]
public int BeatmapSetInfoID { get; set; }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
[Required]
public BeatmapSetInfo BeatmapSet { get; set; }
@ -82,7 +84,7 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public string StoredBookmarks
{
get { return string.Join(",", Bookmarks); }
get => string.Join(",", Bookmarks);
set
{
if (string.IsNullOrEmpty(value))
@ -93,8 +95,7 @@ namespace osu.Game.Beatmaps
Bookmarks = value.Split(',').Select(v =>
{
int val;
bool result = int.TryParse(v, out val);
bool result = int.TryParse(v, out int val);
return new { result, val };
}).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
using System;
@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps
validateOnlineIds(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.
if (beatmapSet.OnlineBeatmapSetID != null)
@ -388,21 +388,22 @@ namespace osu.Game.Beatmaps
}
/// <summary>
/// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties.
/// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
/// </summary>
/// <param name="beatmap">The beatmap to populate.</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>
/// <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)
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;
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database);
try
{
@ -414,6 +415,9 @@ namespace osu.Game.Beatmaps
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))
{
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.OnlineBeatmapID = res.OnlineBeatmapID;
return true;
}
catch (Exception e)

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps
@ -44,6 +45,25 @@ namespace osu.Game.Beatmaps
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
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
get => onlineBeatmapSetID;
set => onlineBeatmapSetID = value > 0 ? value : null;
}
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapMetadata Metadata { get; set; }
public List<BeatmapInfo> Beatmaps { get; set; }

View File

@ -1,7 +1,6 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -14,20 +13,35 @@ namespace osu.Game.Beatmaps.Drawables
{
private readonly OsuSpriteText statusText;
private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None;
private BeatmapSetOnlineStatus status;
public BeatmapSetOnlineStatus Status
{
get { return status; }
get => status;
set
{
if (value == status) return;
if (status == value)
return;
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;
Masking = true;
@ -45,10 +59,10 @@ namespace osu.Game.Beatmaps.Drawables
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
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.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
Set(OsuSetting.IncreaseFirstObjectVisibility, true);
// Update
@ -143,7 +141,6 @@ namespace osu.Game.Configuration
ChatDisplayHeight,
Version,
ShowConvertedBeatmaps,
SpeedChangeVisualisation,
Skin,
ScreenshotFormat,
ScreenshotCaptureMenuCursor,

View File

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

View File

@ -96,7 +96,8 @@ namespace osu.Game.Database
private void handleEvent(Action a)
{
if (delayingEvents)
lock (queuedEvents) queuedEvents.Add(a);
lock (queuedEvents)
queuedEvents.Add(a);
else
a.Invoke();
}
@ -281,17 +282,19 @@ namespace osu.Game.Database
/// Is a no-op for already deleted items.
/// </summary>
/// <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())
{
// 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))
Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray());
return true;
}
}
@ -438,6 +441,13 @@ namespace osu.Game.Database
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);
}

View File

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

View File

@ -30,8 +30,8 @@ namespace osu.Game.Graphics.Backgrounds
/// </summary>
private const float edge_smoothness = 1;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
public override bool HandleNonPositionalInput => false;
public override bool HandlePositionalInput => false;
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;

View File

@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Containers
protected virtual bool PlaySamplesOnStateChange => true;
protected override bool BlockPassThroughKeyboard => true;
protected override bool BlockNonPositionalInput => true;
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.
/// Performing mouse actions outside of the valid extents will hide the overlay.
/// </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.
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)
{
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
if (!base.ReceivePositionalInputAt(state.Mouse.NativeState.Position))
{
State = Visibility.Hidden;
return true;

View File

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

View File

@ -47,10 +47,10 @@ namespace osu.Game.Graphics.UserInterface
public readonly SpriteIcon Chevron;
//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 HandleMouseInput => State == Visibility.Visible;
public override bool HandleNonPositionalInput => State == Visibility.Visible;
public override bool HandlePositionalInput => State == Visibility.Visible;
public override bool IsRemovable => true;
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)
{

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
public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput;
public override bool HandleNonPositionalInput => HoldFocus || base.HandleNonPositionalInput;
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)
{

View File

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

View File

@ -3,10 +3,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.StateChanges;
using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States;
using osu.Framework.Platform;
using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Input.Handlers
@ -40,7 +43,29 @@ namespace osu.Game.Input.Handlers
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 />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
@ -13,7 +14,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
@ -78,6 +79,8 @@ namespace osu.Game.Migrations
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
@ -173,6 +176,8 @@ namespace osu.Game.Migrations
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");

View File

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

View File

@ -2,34 +2,19 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.IO.Network;
using osu.Game.Online.Chat;
namespace osu.Game.Online.API.Requests
{
public class GetMessagesRequest : APIRequest<List<Message>>
{
private readonly List<Channel> channels;
private readonly long? since;
private readonly Channel channel;
public GetMessagesRequest(List<Channel> channels, long? sinceId)
public GetMessagesRequest(Channel channel)
{
this.channels = channels;
since = sinceId;
this.channel = channel;
}
protected override WebRequest CreateWebRequest()
{
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";
protected override string Target => $@"chat/channels/{channel.Id}/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>.
// 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.Game.Online.Chat;
@ -20,15 +20,13 @@ namespace osu.Game.Online.API.Requests
{
var req = base.CreateWebRequest();
req.Method = HttpMethod.POST;
req.AddParameter(@"target_type", message.TargetType.GetDescription());
req.AddParameter(@"target_id", message.TargetId.ToString());
req.Method = HttpMethod.Post;
req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant());
req.AddParameter(@"message", message.Content);
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")]
public int OnlineBeatmapSetID { get; set; }
[JsonProperty(@"status")]
public BeatmapSetOnlineStatus Status { get; set; }
[JsonProperty(@"beatmapset")]
public APIBeatmapSet BeatmapSet { get; set; }
[JsonProperty(@"playcount")]
private int playCount { get; set; }
@ -59,11 +65,13 @@ namespace osu.Game.Online.API.Requests.Responses
Ruleset = rulesets.GetRuleset(ruleset),
StarDifficulty = starDifficulty,
OnlineBeatmapID = OnlineBeatmapID,
Version = version,
Status = Status,
BeatmapSet = new BeatmapSetInfo
{
OnlineBeatmapSetID = OnlineBeatmapSetID,
Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None
},
Version = version,
BaseDifficulty = new BeatmapDifficulty
{
DrainRate = drainRate,

View File

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

View File

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

View File

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

View File

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

View File

@ -154,7 +154,7 @@ namespace osu.Game
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
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(SettingsStore = new SettingsStore(contextFactory));
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));
@ -243,7 +243,7 @@ namespace osu.Game
public void Import(params string[] paths)
{
var extension = Path.GetExtension(paths.First());
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
foreach (var importer in fileImporters)
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);
}

View File

@ -230,10 +230,12 @@ namespace osu.Game.Overlays.BeatmapSet
Spacing = new Vector2(10),
Children = new Drawable[]
{
onlineStatusPill = new BeatmapSetOnlineStatusPill(14, new MarginPadding { Horizontal = 25, Vertical = 8 })
onlineStatusPill = new BeatmapSetOnlineStatusPill
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 }
},
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.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay()
{

View File

@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using System;
@ -143,6 +144,17 @@ namespace osu.Game.Overlays.Chat
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)
{
if (IsRemovable)

View File

@ -47,7 +47,7 @@ namespace osu.Game.Overlays
public const float TAB_AREA_HEIGHT = 50;
private GetMessagesRequest fetchReq;
private GetUpdatesRequest fetchReq;
private readonly ChatTabControl channelTabs;
@ -62,7 +62,7 @@ namespace osu.Game.Overlays
private readonly Container channelSelectionContainer;
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()
{
@ -285,7 +285,7 @@ namespace osu.Game.Overlays
chatBackground.Colour = colours.ChatBlue;
}
private long? lastMessageId;
private long lastMessageId;
private readonly List<Channel> careChannels = new List<Channel>();
@ -304,9 +304,9 @@ namespace osu.Game.Overlays
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 == @"#osu"));
addChannel(channels.Find(c => c.Name == @"#lobby"));
channelSelection.OnRequestJoin = addChannel;
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);
@ -362,7 +362,7 @@ namespace osu.Game.Overlays
loadedChannels.Add(loaded);
LoadComponentAsync(loaded, l =>
{
if (currentChannel.Messages.Any())
if (currentChannel.MessagesLoaded)
loading.Hide();
currentChannelContainer.Clear(false);
@ -394,6 +394,15 @@ namespace osu.Game.Overlays
{
careChannels.Add(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.
@ -415,47 +424,53 @@ namespace osu.Game.Overlays
loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
channelTabs.RemoveItem(channel);
api.Queue(new LeaveChannelRequest(channel, api.LocalUser));
channel.Joined.Value = false;
}
private void fetchInitialMessages(Channel channel)
{
var req = new GetMessagesRequest(new List<Channel> { channel }, null);
req.Success += delegate (List<Message> messages)
var req = new GetMessagesRequest(channel);
req.Success += messages =>
{
loading.Hide();
channel.AddNewMessages(messages.ToArray());
Debug.Write("success!");
};
req.Failure += delegate
{
Debug.Write("failure!");
if (channel == currentChannel)
loading.Hide();
};
api.Queue(req);
}
private void fetchNewMessages()
private void fetchUpdates()
{
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))
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
if (updates?.Presence != null)
{
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.Failure += delegate
{
Debug.Write("failure!");
fetchReq = null;
};
@ -517,8 +532,7 @@ namespace osu.Game.Overlays
{
Sender = api.LocalUser.Value,
Timestamp = DateTimeOffset.Now,
TargetType = TargetType.Channel, //TODO: read this from channel
TargetId = target.Id,
ChannelId = target.Id,
IsAction = isAction,
Content = postText
};

View File

@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Dialog
public static readonly float ENTER_DURATION = 500;
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 ringMinifiedSize = new Vector2(20f);
@ -199,7 +199,7 @@ namespace osu.Game.Overlays.Dialog
switch (action)
{
case GlobalAction.Select:
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.Click();
return true;
}
@ -252,7 +252,7 @@ namespace osu.Game.Overlays.Dialog
if (!actionInvoked)
// 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.
buttonsContainer.Last().TriggerOnClick();
buttonsContainer.Last().Click();
base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine);
@ -261,7 +261,7 @@ namespace osu.Game.Overlays.Dialog
private void pressButtonAtIndex(int index)
{
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 BlockPassThroughKeyboard => true;
protected override bool BlockNonPositionalInput => true;
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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.States;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Direct
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
private void load(OsuColour colours)
{
Content.CornerRadius = 4;
@ -74,13 +74,13 @@ namespace osu.Game.Overlays.Direct
{
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
Font = @"Exo2.0-BoldItalic",
},
},
@ -217,8 +217,10 @@ namespace osu.Game.Overlays.Direct
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,
});

View File

@ -10,8 +10,8 @@ using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Direct
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation, OsuColour colours)
private void load(OsuColour colours)
{
Content.CornerRadius = 5;
@ -94,13 +94,13 @@ namespace osu.Game.Overlays.Direct
{
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)),
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);
}
public override bool HandleKeyboardInput => !bindable.Disabled && base.HandleKeyboardInput;
public override bool HandleMouseInput => !bindable.Disabled && base.HandleMouseInput;
public override bool HandleNonPositionalInput => !bindable.Disabled && base.HandleNonPositionalInput;
public override bool HandlePositionalInput => !bindable.Disabled && base.HandlePositionalInput;
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
{

View File

@ -50,7 +50,7 @@ namespace osu.Game.Overlays.KeyBinding
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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -103,8 +103,8 @@ namespace osu.Game.Overlays.Toolbar
return false;
}
public override bool HandleKeyboardInput => !ruleset.Disabled && base.HandleKeyboardInput;
public override bool HandleMouseInput => !ruleset.Disabled && base.HandleMouseInput;
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
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)
{
if (user == User.SYSTEM_USER) return;
Show();
if (user.Id == Header?.User.Id)
if (user.Id == Header?.User?.Id)
return;
userReq?.Cancel();

View File

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

View File

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

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Edit
public readonly DrawableHitObject HitObject;
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 HitObjectMask(DrawableHitObject hitObject)

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@
using System;
using System.IO;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Database;
@ -13,8 +14,6 @@ namespace osu.Game.Rulesets.Scoring
{
public class ScoreStore : DatabaseBackedStore, ICanAcceptFiles
{
private readonly Storage storage;
private readonly BeatmapManager beatmaps;
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)
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.rulesets = rulesets;
@ -49,8 +47,14 @@ namespace osu.Game.Rulesets.Scoring
public Score ReadReplayFile(string replayFilename)
{
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(s);
if (File.Exists(replayFilename))
{
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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
@ -10,7 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.StateChanges;
using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Configuration;
@ -56,33 +55,20 @@ namespace osu.Game.Rulesets.UI
#region Action mapping (for replays)
private List<T> lastPressedActions = new List<T>();
public override void HandleCustomInput(InputState state, IInput input)
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
{
if (!(input is ReplayState<T> replayState))
if (inputStateChange is ReplayStateChangeEvent<T> replayStateChanged)
{
base.HandleCustomInput(state, input);
return;
}
foreach (var action in replayStateChanged.ReleasedActions)
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

View File

@ -1,7 +1,6 @@
// 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.Framework.Caching;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@ -33,20 +32,16 @@ namespace osu.Game.Rulesets.UI.Scrolling
private Cached initialStateCache = new Cached();
public ScrollingHitObjectContainer()
private readonly ISpeedChangeVisualiser speedChangeVisualiser;
public ScrollingHitObjectContainer(SpeedChangeVisualisationMethod visualisationMethod)
{
RelativeSizeAxes = Axes.Both;
TimeRange.ValueChanged += _ => initialStateCache.Invalidate();
Direction.ValueChanged += _ => initialStateCache.Invalidate();
}
private ISpeedChangeVisualiser speedChangeVisualiser;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
switch (config.Get<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation))
switch (visualisationMethod)
{
case SpeedChangeVisualisationMethod.Sequential:
speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints);
@ -54,6 +49,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
case SpeedChangeVisualisationMethod.Overlapping:
speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints);
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.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Configuration;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables;
@ -62,6 +63,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// </summary>
protected readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential;
/// <summary>
/// Creates a new <see cref="ScrollingPlayfield"/>.
/// </summary>
@ -104,7 +107,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
protected sealed override HitObjectContainer CreateHitObjectContainer()
{
var container = new ScrollingHitObjectContainer();
var container = new ScrollingHitObjectContainer(VisualisationMethod);
container.Direction.BindTo(Direction);
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.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Screens.Backgrounds
{
public class BackgroundScreenBeatmap : BackgroundScreen
public class BackgroundScreenBeatmap : BlurrableBackgroundScreen
{
private Background background;
private WorkingBeatmap beatmap;
private Vector2 blurTarget;
public WorkingBeatmap Beatmap
{
@ -30,21 +25,21 @@ namespace osu.Game.Screens.Backgrounds
Schedule(() =>
{
LoadComponentAsync(new BeatmapBackground(beatmap), b =>
LoadComponentAsync(new BeatmapBackground(beatmap), b => Schedule(() =>
{
float newDepth = 0;
if (background != null)
if (Background != null)
{
newDepth = background.Depth + 1;
background.FinishTransforms();
background.FadeOut(250);
background.Expire();
newDepth = Background.Depth + 1;
Background.FinishTransforms();
Background.FadeOut(250);
Background.Expire();
}
b.Depth = newDepth;
Add(background = b);
background.BlurSigma = blurTarget;
});
Add(Background = b);
Background.BlurSigma = BlurTarget;
}));
});
}
}
@ -54,9 +49,6 @@ namespace osu.Game.Screens.Backgrounds
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)
{
var otherBeatmapBackground = other as BackgroundScreenBeatmap;

View File

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

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