Merge branch 'master' into return_to_large_logo_after_idle

This commit is contained in:
UselessToucan
2018-09-25 19:48:38 +03:00
committed by GitHub
66 changed files with 1153 additions and 294 deletions

View File

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

View File

@ -74,42 +74,42 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
private void initialiseHyperDash(List<CatchHitObject> objects) private void initialiseHyperDash(List<CatchHitObject> objects)
{ {
// todo: add difficulty adjust. List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
foreach (var currentObject in objects)
{
if (currentObject is Fruit)
objectWithDroplets.Add(currentObject);
if (currentObject is JuiceStream)
foreach (var currentJuiceElement in currentObject.NestedHitObjects)
if (!(currentJuiceElement is TinyDroplet))
objectWithDroplets.Add((CatchHitObject)currentJuiceElement);
}
objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2;
int lastDirection = 0; int lastDirection = 0;
double lastExcess = halfCatcherWidth; double lastExcess = halfCatcherWidth;
int objCount = objects.Count; for (int i = 0; i < objectWithDroplets.Count - 1; i++)
for (int i = 0; i < objCount - 1; i++)
{ {
CatchHitObject currentObject = objects[i]; CatchHitObject currentObject = objectWithDroplets[i];
CatchHitObject nextObject = objectWithDroplets[i + 1];
// not needed?
// if (currentObject is TinyDroplet) continue;
CatchHitObject nextObject = objects[i + 1];
// while (nextObject is TinyDroplet)
// {
// if (++i == objCount - 1) break;
// nextObject = objects[i + 1];
// }
int thisDirection = nextObject.X > currentObject.X ? 1 : -1; int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; double timeToNext = nextObject.StartTime - currentObject.StartTime;
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) if (distanceToHyper < 0)
{ {
currentObject.HyperDashTarget = nextObject; currentObject.HyperDashTarget = nextObject;
lastExcess = halfCatcherWidth; lastExcess = halfCatcherWidth;
} }
else else
{ {
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext; currentObject.DistanceToHyperDash = distanceToHyper;
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth);
} }
lastDirection = thisDirection; lastDirection = thisDirection;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public int IndexInBeatmap { get; set; } public int IndexInBeatmap { get; set; }
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4);
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }
@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects
public int ComboIndex { get; set; } public int ComboIndex { get; set; }
/// <summary> /// <summary>
/// The distance for a fruit to to next hyper if it's not a hyper. /// Difference between the distance to the next object
/// and the distance that would have triggered a hyper dash.
/// A value close to 0 indicates a difficult jump (for difficulty calculation).
/// </summary> /// </summary>
public float DistanceToHyperDash { get; set; } public float DistanceToHyperDash { get; set; }

View File

@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea; private readonly CatcherArea catcherArea;
protected override bool UserScrollSpeedAdjustment => false;
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(BASE_WIDTH) : base(BASE_WIDTH)
{ {

View File

@ -107,6 +107,11 @@ namespace osu.Game.Rulesets.Catch.UI
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
public static float GetCatcherSize(BeatmapDifficulty difficulty)
{
return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
}
public class Catcher : Container, IKeyBindingHandler<CatchAction> public class Catcher : Container, IKeyBindingHandler<CatchAction>
{ {
/// <summary> /// <summary>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps
public bool BackgroundLoaded => background.IsResultAvailable; public bool BackgroundLoaded => background.IsResultAvailable;
public Texture Background => background.Value; public Texture Background => background.Value;
protected virtual bool BackgroundStillValid(Texture b) => b == null || !b.IsDisposed; protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available;
protected abstract Texture GetBackground(); protected abstract Texture GetBackground();
private readonly RecyclableLazy<Texture> background; private readonly RecyclableLazy<Texture> background;

View File

@ -59,7 +59,7 @@ namespace osu.Game.Database
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ArchiveImportIPCChannel ipc; private ArchiveImportIPCChannel ipc;
private readonly List<Action> cachedEvents = new List<Action>(); private readonly List<Action> queuedEvents = new List<Action>();
/// <summary> /// <summary>
/// Allows delaying of outwards events until an operation is confirmed (at a database level). /// Allows delaying of outwards events until an operation is confirmed (at a database level).
@ -77,20 +77,27 @@ namespace osu.Game.Database
/// <param name="perform">Whether the flushed events should be performed.</param> /// <param name="perform">Whether the flushed events should be performed.</param>
private void flushEvents(bool perform) private void flushEvents(bool perform)
{ {
Action[] events;
lock (queuedEvents)
{
events = queuedEvents.ToArray();
queuedEvents.Clear();
}
if (perform) if (perform)
{ {
foreach (var a in cachedEvents) foreach (var a in events)
a.Invoke(); a.Invoke();
} }
cachedEvents.Clear();
delayingEvents = false; delayingEvents = false;
} }
private void handleEvent(Action a) private void handleEvent(Action a)
{ {
if (delayingEvents) if (delayingEvents)
cachedEvents.Add(a); lock (queuedEvents)
queuedEvents.Add(a);
else else
a.Invoke(); a.Invoke();
} }
@ -432,6 +439,13 @@ namespace osu.Game.Database
return Task.CompletedTask; return Task.CompletedTask;
} }
if (!stable.ExistsDirectory(ImportFromStablePath))
{
// This handles situations like when the user does not have a Skins folder
Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error);
return Task.CompletedTask;
}
return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -199,7 +199,7 @@ namespace osu.Game.Overlays.Dialog
switch (action) switch (action)
{ {
case GlobalAction.Select: case GlobalAction.Select:
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick(); Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.Click();
return true; return true;
} }
@ -252,7 +252,7 @@ namespace osu.Game.Overlays.Dialog
if (!actionInvoked) if (!actionInvoked)
// In the case a user did not choose an action before a hide was triggered, press the last button. // In the case a user did not choose an action before a hide was triggered, press the last button.
// This is presumed to always be a sane default "cancel" action. // This is presumed to always be a sane default "cancel" action.
buttonsContainer.Last().TriggerOnClick(); buttonsContainer.Last().Click();
base.PopOut(); base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine); content.FadeOut(EXIT_DURATION, Easing.InSine);
@ -261,7 +261,7 @@ namespace osu.Game.Overlays.Dialog
private void pressButtonAtIndex(int index) private void pressButtonAtIndex(int index)
{ {
if (index < Buttons.Count()) if (index < Buttons.Count())
Buttons.Skip(index).First().TriggerOnClick(); Buttons.Skip(index).First().Click();
} }
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,8 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -165,6 +167,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
} }
} }
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds)
{
if (!AllJudged)
return false;
return base.UpdateSubTreeMasking(source, maskingBounds);
}
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()
{ {
base.UpdateAfterChildren(); base.UpdateAfterChildren();

View File

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

View File

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

View File

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

View File

@ -0,0 +1,20 @@
// 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.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Backgrounds;
using OpenTK;
namespace osu.Game.Screens
{
public abstract class BlurrableBackgroundScreen : BackgroundScreen
{
protected Background Background;
protected Vector2 BlurTarget;
public TransformSequence<Background> BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None)
=> Background?.BlurTo(BlurTarget = sigma, duration, easing);
}
}

View File

@ -115,7 +115,7 @@ namespace osu.Game.Screens.Menu
case GlobalAction.Back: case GlobalAction.Back:
return goBack(); return goBack();
case GlobalAction.Select: case GlobalAction.Select:
logo?.TriggerOnClick(); logo?.Click();
return true; return true;
default: default:
return false; return false;
@ -133,7 +133,7 @@ namespace osu.Game.Screens.Menu
sampleBack?.Play(); sampleBack?.Play();
return true; return true;
case ButtonSystemState.Play: case ButtonSystemState.Play:
backButton.TriggerOnClick(); backButton.Click();
return true; return true;
default: default:
return false; return false;
@ -150,10 +150,10 @@ namespace osu.Game.Screens.Menu
State = ButtonSystemState.TopLevel; State = ButtonSystemState.TopLevel;
return true; return true;
case ButtonSystemState.TopLevel: case ButtonSystemState.TopLevel:
buttonsTopLevel.First().TriggerOnClick(); buttonsTopLevel.First().Click();
return false; return false;
case ButtonSystemState.Play: case ButtonSystemState.Play:
buttonsPlay.First().TriggerOnClick(); buttonsPlay.First().Click();
return false; return false;
} }
} }

View File

@ -10,6 +10,7 @@ using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States; using osu.Framework.Input.States;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Charts; using osu.Game.Screens.Charts;
@ -64,6 +65,20 @@ namespace osu.Game.Screens.Menu
}, },
sideFlashes = new MenuSideFlashes(), sideFlashes = new MenuSideFlashes(),
}; };
buttons.StateChanged += state =>
{
switch (state)
{
case ButtonSystemState.Initial:
case ButtonSystemState.Exit:
background.FadeColour(Color4.White, 500, Easing.OutSine);
break;
default:
background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine);
break;
}
};
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]

View File

@ -84,11 +84,10 @@ namespace osu.Game.Screens.Menu
private const double early_activation = 60; private const double early_activation = 60;
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
public OsuLogo() public OsuLogo()
{ {
// Required to make Schedule calls run in OsuScreen even when we are not visible.
AlwaysPresent = true;
EarlyActivationMilliseconds = early_activation; EarlyActivationMilliseconds = early_activation;
Size = new Vector2(default_size); Size = new Vector2(default_size);

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation; using osu.Framework.Localisation;
@ -14,8 +13,6 @@ namespace osu.Game.Screens.Multi.Components
{ {
private readonly OsuSpriteText beatmapTitle, beatmapDash, beatmapArtist; private readonly OsuSpriteText beatmapTitle, beatmapDash, beatmapArtist;
private LocalisationEngine localisation;
public float TextSize public float TextSize
{ {
set { beatmapTitle.TextSize = beatmapDash.TextSize = beatmapArtist.TextSize = value; } set { beatmapTitle.TextSize = beatmapDash.TextSize = beatmapArtist.TextSize = value; }
@ -48,12 +45,6 @@ namespace osu.Game.Screens.Multi.Components
}; };
} }
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
this.localisation = localisation;
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -64,15 +55,14 @@ namespace osu.Game.Screens.Multi.Components
{ {
if (beatmap == null) if (beatmap == null)
{ {
beatmapTitle.Current = beatmapArtist.Current = null;
beatmapTitle.Text = "Changing map"; beatmapTitle.Text = "Changing map";
beatmapDash.Text = beatmapArtist.Text = string.Empty; beatmapDash.Text = beatmapArtist.Text = string.Empty;
} }
else else
{ {
beatmapTitle.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); beatmapTitle.Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title));
beatmapDash.Text = @" - "; beatmapDash.Text = @" - ";
beatmapArtist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); beatmapArtist.Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist));
} }
} }
} }

View File

@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play
/// <summary> /// <summary>
/// Action that is invoked when <see cref="GlobalAction.Back"/> is triggered. /// Action that is invoked when <see cref="GlobalAction.Back"/> is triggered.
/// </summary> /// </summary>
protected virtual Action BackAction => () => InternalButtons.Children.Last().TriggerOnClick(); protected virtual Action BackAction => () => InternalButtons.Children.Last().Click();
public abstract string Header { get; } public abstract string Header { get; }
public abstract string Description { get; } public abstract string Description { get; }

View File

@ -69,7 +69,7 @@ namespace osu.Game.Screens.Play
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
KeyCounter = CreateKeyCounter(), KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock),
HoldToQuit = CreateQuitButton(), HoldToQuit = CreateQuitButton(),
} }
} }
@ -194,12 +194,13 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20 } Margin = new MarginPadding { Top = 20 }
}; };
protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection protected virtual KeyCounterCollection CreateKeyCounter(IFrameBasedClock offsetClock) => new KeyCounterCollection
{ {
FadeTime = 50, FadeTime = 50,
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
Margin = new MarginPadding(10), Margin = new MarginPadding(10),
AudioClock = offsetClock
}; };
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6) protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -19,6 +21,9 @@ namespace osu.Game.Screens.Play
private Container textLayer; private Container textLayer;
private SpriteText countSpriteText; private SpriteText countSpriteText;
private readonly List<KeyCounterState> states = new List<KeyCounterState>();
private KeyCounterState currentState;
public bool IsCounting { get; set; } = true; public bool IsCounting { get; set; } = true;
private int countPresses; private int countPresses;
public int CountPresses public int CountPresses
@ -45,7 +50,10 @@ namespace osu.Game.Screens.Play
isLit = value; isLit = value;
updateGlowSprite(value); updateGlowSprite(value);
if (value && IsCounting) if (value && IsCounting)
{
CountPresses++; CountPresses++;
saveState();
}
} }
} }
} }
@ -128,6 +136,32 @@ namespace osu.Game.Screens.Play
} }
} }
public void ResetCount() => CountPresses = 0; public void ResetCount()
{
CountPresses = 0;
states.Clear();
}
protected override void Update()
{
base.Update();
if (currentState?.Time > Clock.CurrentTime)
restoreStateTo(Clock.CurrentTime);
}
private void saveState()
{
if (currentState == null || currentState.Time < Clock.CurrentTime)
states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses));
}
private void restoreStateTo(double time)
{
states.RemoveAll(state => state.Time > time);
currentState = states.LastOrDefault();
CountPresses = currentState?.Count ?? 0;
}
} }
} }

View File

@ -3,15 +3,15 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using OpenTK.Graphics; using osu.Framework.Input.Events;
using osu.Framework.Configuration; using osu.Framework.Timing;
using osu.Framework.Allocation;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Game.Configuration; using osu.Game.Configuration;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -37,6 +37,9 @@ namespace osu.Game.Screens.Play
key.FadeTime = FadeTime; key.FadeTime = FadeTime;
key.KeyDownTextColor = KeyDownTextColor; key.KeyDownTextColor = KeyDownTextColor;
key.KeyUpTextColor = KeyUpTextColor; key.KeyUpTextColor = KeyUpTextColor;
// Use the same clock object as SongProgress for saving KeyCounter state
if (AudioClock != null)
key.Clock = AudioClock;
} }
public void ResetCount() public void ResetCount()
@ -118,6 +121,8 @@ namespace osu.Game.Screens.Play
public override bool HandleKeyboardInput => receptor == null; public override bool HandleKeyboardInput => receptor == null;
public override bool HandleMouseInput => receptor == null; public override bool HandleMouseInput => receptor == null;
public IFrameBasedClock AudioClock { get; set; }
private Receptor receptor; private Receptor receptor;
public Receptor GetReceptor() public Receptor GetReceptor()
@ -146,13 +151,18 @@ namespace osu.Game.Screens.Play
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => Target.Children.Any(c => c.TriggerOnKeyDown(state, args)); protected override bool Handle(UIEvent e)
{
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => Target.Children.Any(c => c.TriggerOnKeyUp(state, args)); switch (e)
{
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => Target.Children.Any(c => c.TriggerOnMouseDown(state, args)); case KeyDownEvent _:
case KeyUpEvent _:
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => Target.Children.Any(c => c.TriggerOnMouseUp(state, args)); case MouseDownEvent _:
case MouseUpEvent _:
return Target.Children.Any(c => c.TriggerEvent(e));
}
return base.Handle(e);
}
} }
} }
} }

View File

@ -0,0 +1,17 @@
// 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.Screens.Play
{
public class KeyCounterState
{
public KeyCounterState(double time, int count)
{
Time = time;
Count = count;
}
public readonly double Time;
public readonly int Count;
}
}

View File

@ -132,7 +132,7 @@ namespace osu.Game.Screens.Play
public override string Header => "paused"; public override string Header => "paused";
public override string Description => "you're not going to do what i think you're going to do, are ya?"; public override string Description => "you're not going to do what i think you're going to do, are ya?";
protected override Action BackAction => () => InternalButtons.Children.First().TriggerOnClick(); protected override Action BackAction => () => InternalButtons.Children.First().Click();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)

View File

@ -290,7 +290,7 @@ namespace osu.Game.Screens.Play
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(LocalisationEngine localisation) private void load()
{ {
var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata();
@ -307,7 +307,7 @@ namespace osu.Game.Screens.Play
{ {
new OsuSpriteText new OsuSpriteText
{ {
Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title), Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)),
TextSize = 36, TextSize = 36,
Font = @"Exo2.0-MediumItalic", Font = @"Exo2.0-MediumItalic",
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -315,7 +315,7 @@ namespace osu.Game.Screens.Play
}, },
new OsuSpriteText new OsuSpriteText
{ {
Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist), Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)),
TextSize = 26, TextSize = 26,
Font = @"Exo2.0-MediumItalic", Font = @"Exo2.0-MediumItalic",
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,

View File

@ -141,7 +141,7 @@ namespace osu.Game.Screens.Play
switch (action) switch (action)
{ {
case GlobalAction.SkipCutscene: case GlobalAction.SkipCutscene:
button.TriggerOnClick(); button.Click();
return true; return true;
} }

View File

@ -13,7 +13,6 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -24,6 +23,7 @@ using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Localisation;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
@ -328,7 +328,7 @@ namespace osu.Game.Screens.Ranking
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation) private void load(OsuColour colours)
{ {
title.Colour = artist.Colour = colours.BlueDarker; title.Colour = artist.Colour = colours.BlueDarker;
versionMapper.Colour = colours.Gray8; versionMapper.Colour = colours.Gray8;
@ -341,8 +341,8 @@ namespace osu.Game.Screens.Ranking
versionMapper.Text = $"{beatmap.Version} - " + versionMapper.Text; versionMapper.Text = $"{beatmap.Version} - " + versionMapper.Text;
} }
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); title.Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title));
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); artist.Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist));
} }
} }

View File

@ -127,11 +127,12 @@ namespace osu.Game.Screens.Select
public OsuSpriteText VersionLabel { get; private set; } public OsuSpriteText VersionLabel { get; private set; }
public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText TitleLabel { get; private set; }
public OsuSpriteText ArtistLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; }
public BeatmapSetOnlineStatusPill StatusPill { get; private set; }
public FillFlowContainer MapperContainer { get; private set; } public FillFlowContainer MapperContainer { get; private set; }
public FillFlowContainer InfoLabelContainer { get; private set; } public FillFlowContainer InfoLabelContainer { get; private set; }
private UnicodeBindableString titleBinding; private ILocalisedBindableString titleBinding;
private UnicodeBindableString artistBinding; private ILocalisedBindableString artistBinding;
private readonly WorkingBeatmap beatmap; private readonly WorkingBeatmap beatmap;
private readonly RulesetInfo ruleset; private readonly RulesetInfo ruleset;
@ -143,7 +144,7 @@ namespace osu.Game.Screens.Select
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(LocalisationEngine localisation) private void load(LocalisationManager localisation)
{ {
var beatmapInfo = beatmap.BeatmapInfo; var beatmapInfo = beatmap.BeatmapInfo;
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
@ -152,8 +153,8 @@ namespace osu.Game.Screens.Select
CacheDrawnFrameBuffer = true; CacheDrawnFrameBuffer = true;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
titleBinding = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title)));
artistBinding = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); artistBinding = localisation.GetLocalisedString(new LocalisedString((metadata.ArtistUnicode, metadata.Artist)));
Children = new Drawable[] Children = new Drawable[]
{ {
@ -190,7 +191,7 @@ namespace osu.Game.Screens.Select
}, },
new FillFlowContainer new FillFlowContainer
{ {
Name = "Top-aligned metadata", Name = "Topleft-aligned metadata",
Anchor = Anchor.TopLeft, Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft, Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
@ -207,6 +208,24 @@ namespace osu.Game.Screens.Select
} }
}, },
new FillFlowContainer new FillFlowContainer
{
Name = "Topright-aligned metadata",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 14, Left = 10, Right = 18, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
StatusPill = new BeatmapSetOnlineStatusPill
{
TextSize = 11,
TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 },
Status = beatmapInfo.Status,
}
}
},
new FillFlowContainer
{ {
Name = "Centre-aligned metadata", Name = "Centre-aligned metadata",
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
@ -244,8 +263,13 @@ namespace osu.Game.Screens.Select
} }
} }
}; };
artistBinding.ValueChanged += value => setMetadata(metadata.Source);
artistBinding.TriggerChange(); titleBinding.BindValueChanged(value => setMetadata(metadata.Source));
artistBinding.BindValueChanged(value => setMetadata(metadata.Source), true);
// no difficulty means it can't have a status to show
if (beatmapInfo.Version == null)
StatusPill.Hide();
} }
private void setMetadata(string source) private void setMetadata(string source)

View File

@ -38,11 +38,8 @@ namespace osu.Game.Screens.Select.Carousel
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(LocalisationEngine localisation, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay)
{ {
if (localisation == null)
throw new ArgumentNullException(nameof(localisation));
restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore);
dialogOverlay = overlay; dialogOverlay = overlay;
if (beatmapOverlay != null) if (beatmapOverlay != null)
@ -67,22 +64,39 @@ namespace osu.Game.Screens.Select.Carousel
new OsuSpriteText new OsuSpriteText
{ {
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
Current = localisation.GetUnicodePreference(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title), Text = new LocalisedString((beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title)),
TextSize = 22, TextSize = 22,
Shadow = true, Shadow = true,
}, },
new OsuSpriteText new OsuSpriteText
{ {
Font = @"Exo2.0-SemiBoldItalic", Font = @"Exo2.0-SemiBoldItalic",
Current = localisation.GetUnicodePreference(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist), Text = new LocalisedString((beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist)),
TextSize = 17, TextSize = 17,
Shadow = true, Shadow = true,
}, },
new FillFlowContainer<FilterableDifficultyIcon> new FillFlowContainer
{ {
Margin = new MarginPadding { Top = 5 }, Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList() Margin = new MarginPadding { Top = 5 },
Children = new Drawable[]
{
new BeatmapSetOnlineStatusPill
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Margin = new MarginPadding { Right = 5 },
TextSize = 11,
TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 },
Status = beatmapSet.Status
},
new FillFlowContainer<FilterableDifficultyIcon>
{
AutoSizeAxes = Axes.Both,
Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList()
},
}
} }
} }
} }

View File

@ -16,10 +16,10 @@ namespace osu.Game.Storyboards
public bool HasCommands => commands.Count > 0; public bool HasCommands => commands.Count > 0;
private Cached<double> startTimeBacking; private Cached<double> startTimeBacking;
public double StartTime => startTimeBacking.IsValid ? startTimeBacking : (startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue); public double StartTime => startTimeBacking.IsValid ? startTimeBacking : startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue;
private Cached<double> endTimeBacking; private Cached<double> endTimeBacking;
public double EndTime => endTimeBacking.IsValid ? endTimeBacking : (endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue); public double EndTime => endTimeBacking.IsValid ? endTimeBacking : endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue;
public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T); public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T);
public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T); public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T);

View File

@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual
AddStep(r.Name, () => p = loadPlayerFor(r)); AddStep(r.Name, () => p = loadPlayerFor(r));
AddUntilStep(() => ContinueCondition(p)); AddUntilStep(() => ContinueCondition(p));
AddAssert("no leaked beatmaps", () => AddUntilStep(() =>
{ {
p = null; p = null;
@ -64,9 +64,9 @@ namespace osu.Game.Tests.Visual
workingWeakReferences.ForEachAlive(_ => count++); workingWeakReferences.ForEachAlive(_ => count++);
return count == 1; return count == 1;
}); }, "no leaked beatmaps");
AddAssert("no leaked players", () => AddUntilStep(() =>
{ {
GC.Collect(); GC.Collect();
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual
playerWeakReferences.ForEachAlive(_ => count++); playerWeakReferences.ForEachAlive(_ => count++);
return count == 1; return count == 1;
}); }, "no leaked players");
} }
} }
} }

View File

@ -144,5 +144,14 @@ namespace osu.Game.Users
public Badge[] Badges; public Badge[] Badges;
public override string ToString() => Username; public override string ToString() => Username;
/// <summary>
/// A user instance for displaying locally created system messages.
/// </summary>
public static readonly User SYSTEM_USER = new User
{
Username = "system",
Id = 0
};
} }
} }

View File

@ -16,9 +16,9 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Humanizer" Version="2.4.2" /> <PackageReference Include="Humanizer" Version="2.4.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="ppy.osu.Framework" Version="2018.910.1" /> <PackageReference Include="ppy.osu.Framework" Version="2018.920.1" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -11,7 +11,7 @@
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" /> <ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
<PackageReference Include="DeepEqual" Version="1.6.0" /> <PackageReference Include="DeepEqual" Version="1.6.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>