diff --git a/osu.Desktop.Deploy/.vscode/launch.json b/osu.Desktop.Deploy/.vscode/launch.json
deleted file mode 100644
index 8c35d211bd..0000000000
--- a/osu.Desktop.Deploy/.vscode/launch.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [{
- "name": "Deploy (Debug)",
- "request": "launch",
- "type": "mono",
- "program": "${workspaceRoot}/bin/Debug/net471/osu.Desktop.Deploy.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Debug)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- },
- {
- "name": "Deploy (Release)",
- "request": "launch",
- "type": "clr",
- "program": "${workspaceRoot}/bin/Release/net471/osu.Desktop.Deploy.exe",
- "cwd": "${workspaceRoot}",
- "preLaunchTask": "Build (Release)",
- "runtimeExecutable": null,
- "env": {},
- "console": "internalConsole"
- }
- ]
-}
\ No newline at end of file
diff --git a/osu.Desktop.Deploy/.vscode/tasks.json b/osu.Desktop.Deploy/.vscode/tasks.json
deleted file mode 100644
index 35bf9e7a0e..0000000000
--- a/osu.Desktop.Deploy/.vscode/tasks.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- // See https://go.microsoft.com/fwlink/?LinkId=733558
- // for the documentation about the tasks.json format
- "version": "2.0.0",
- "command": "msbuild",
- "type": "shell",
- "suppressTaskName": true,
- "args": [
- "/property:GenerateFullPaths=true",
- "/property:DebugType=portable",
- "/verbosity:minimal",
- "/m" //parallel compiling support.
- ],
- "tasks": [{
- "taskName": "Build (Debug)",
- "group": {
- "kind": "build",
- "isDefault": true
- },
- "problemMatcher": [
- "$msCompile"
- ]
- },
- {
- "taskName": "Build (Release)",
- "group": "build",
- "args": [
- "/property:Configuration=Release"
- ],
- "problemMatcher": [
- "$msCompile"
- ]
- },
- {
- "taskName": "Clean (Debug)",
- "args": [
- "/target:Clean"
- ],
- "problemMatcher": [
- "$msCompile"
- ]
- },
- {
- "taskName": "Clean (Release)",
- "args": [
- "/target:Clean",
- "/property:Configuration=Release"
- ],
- "problemMatcher": [
- "$msCompile"
- ]
- },
- {
- "taskName": "Clean All",
- "dependsOn": [
- "Clean (Debug)",
- "Clean (Release)"
- ],
- "problemMatcher": [
- "$msCompile"
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 3cf95e9b3e..29d3b0e394 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -30,6 +30,7 @@
+
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
new file mode 100644
index 0000000000..f6535380c8
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Mods;
+
+namespace osu.Game.Rulesets.Catch.Difficulty
+{
+ public class CatchDifficultyAttributes : DifficultyAttributes
+ {
+ public double ApproachRate;
+ public int MaxCombo;
+
+ public CatchDifficultyAttributes(Mod[] mods, double starRating)
+ : base(mods, starRating)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
index 562374087e..3d1013aad3 100644
--- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs
@@ -1,19 +1,146 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.UI;
namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyCalculator : DifficultyCalculator
{
+
+ ///
+ /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
+ /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
+ /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
+ ///
+ private const double strain_step = 750;
+
+ ///
+ /// The weighting of each strain value decays to this number * it's previous value
+ ///
+ private const double decay_weight = 0.94;
+
+ private const double star_scaling_factor = 0.145;
+
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
- protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => new DifficultyAttributes(mods, 0);
+ protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
+ {
+ if (!beatmap.HitObjects.Any())
+ return new CatchDifficultyAttributes(mods, 0);
+
+ var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
+ float halfCatchWidth = catcher.CatchWidth * 0.5f;
+
+ var difficultyHitObjects = new List();
+
+ foreach (var hitObject in beatmap.HitObjects)
+ {
+ // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
+ if (hitObject is Fruit)
+ {
+ difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth));
+ }
+ if (hitObject is JuiceStream)
+ difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
+ }
+
+ difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
+
+ if (!calculateStrainValues(difficultyHitObjects, timeRate))
+ return new CatchDifficultyAttributes(mods, 0);
+
+ // this is the same as osu!, so there's potential to share the implementation... maybe
+ double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
+ double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
+
+ return new CatchDifficultyAttributes(mods, starRating)
+ {
+ ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0,
+ MaxCombo = difficultyHitObjects.Count
+ };
+ }
+
+ private bool calculateStrainValues(List objects, double timeRate)
+ {
+ CatchDifficultyHitObject lastObject = null;
+
+ if (!objects.Any()) return false;
+
+ // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
+ foreach (var currentObject in objects)
+ {
+ if (lastObject != null)
+ currentObject.CalculateStrains(lastObject, timeRate);
+
+ lastObject = currentObject;
+ }
+
+ return true;
+ }
+
+ private double calculateDifficulty(List objects, double timeRate)
+ {
+ // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods
+ double actualStrainStep = strain_step * timeRate;
+
+ // Find the highest strain value within each strain step
+ var highestStrains = new List();
+ double intervalEndTime = actualStrainStep;
+ double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
+
+ CatchDifficultyHitObject previousHitObject = null;
+ foreach (CatchDifficultyHitObject hitObject in objects)
+ {
+ // While we are beyond the current interval push the currently available maximum to our strain list
+ while (hitObject.BaseHitObject.StartTime > intervalEndTime)
+ {
+ highestStrains.Add(maximumStrain);
+
+ // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
+ // until the beginning of the next interval.
+ if (previousHitObject == null)
+ {
+ maximumStrain = 0;
+ }
+ else
+ {
+ double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
+ maximumStrain = previousHitObject.Strain * decay;
+ }
+
+ // Go to the next time interval
+ intervalEndTime += actualStrainStep;
+ }
+
+ // Obtain maximum strain
+ maximumStrain = Math.Max(hitObject.Strain, maximumStrain);
+
+ previousHitObject = hitObject;
+ }
+
+ // Build the weighted sum over the highest strains for each interval
+ double difficulty = 0;
+ double weight = 1;
+ highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
+
+ foreach (double strain in highestStrains)
+ {
+ difficulty += weight * strain;
+ weight *= decay_weight;
+ }
+
+ return difficulty;
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs
new file mode 100644
index 0000000000..720c1d8653
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs
@@ -0,0 +1,130 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.UI;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Catch.Difficulty
+{
+ public class CatchDifficultyHitObject
+ {
+ internal static readonly double DECAY_BASE = 0.20;
+ private const float normalized_hitobject_radius = 41.0f;
+ private const float absolute_player_positioning_error = 16f;
+ private readonly float playerPositioningError;
+
+ internal CatchHitObject BaseHitObject;
+
+ ///
+ /// Measures jump difficulty. CtB doesn't have something like button pressing speed or accuracy
+ ///
+ internal double Strain = 1;
+
+ ///
+ /// This is required to keep track of lazy player movement (always moving only as far as necessary)
+ /// Without this quick repeat sliders / weirdly shaped streams might become ridiculously overrated
+ ///
+ internal float PlayerPositionOffset;
+ internal float LastMovement;
+
+ internal float NormalizedPosition;
+ internal float ActualNormalizedPosition => NormalizedPosition + PlayerPositionOffset;
+
+ internal CatchDifficultyHitObject(CatchHitObject baseHitObject, float catcherWidthHalf)
+ {
+ BaseHitObject = baseHitObject;
+
+ // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
+ float scalingFactor = normalized_hitobject_radius / catcherWidthHalf;
+
+ playerPositioningError = absolute_player_positioning_error; // * scalingFactor;
+ NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
+ }
+
+ private const double direction_change_bonus = 12.5;
+ internal void CalculateStrains(CatchDifficultyHitObject previousHitObject, double timeRate)
+ {
+ // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make.
+ // See Taiko feedback thread.
+ double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
+ double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
+
+ // Update new position with lazy movement.
+ PlayerPositionOffset =
+ MathHelper.Clamp(
+ previousHitObject.ActualNormalizedPosition,
+ NormalizedPosition - (normalized_hitobject_radius - playerPositioningError),
+ NormalizedPosition + (normalized_hitobject_radius - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player.
+ - NormalizedPosition; // Subtract HitObject position to obtain offset
+
+ LastMovement = DistanceTo(previousHitObject);
+ double addition = spacingWeight(LastMovement);
+
+ if (NormalizedPosition < previousHitObject.NormalizedPosition)
+ {
+ LastMovement = -LastMovement;
+ }
+
+ CatchHitObject previousHitCircle = previousHitObject.BaseHitObject;
+
+ double additionBonus = 0;
+ double sqrtTime = Math.Sqrt(Math.Max(timeElapsed, 25));
+
+ // Direction changes give an extra point!
+ if (Math.Abs(LastMovement) > 0.1)
+ {
+ if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement))
+ {
+ double bonus = direction_change_bonus / sqrtTime;
+
+ // Weight bonus by how
+ double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError;
+
+ // We want time to play a role twice here!
+ addition += bonus * bonusFactor;
+
+ // Bonus for tougher direction switches and "almost" hyperdashes at this point
+ if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
+ {
+ additionBonus += 0.3 * bonusFactor;
+ }
+ }
+
+ // Base bonus for every movement, giving some weight to streams.
+ addition += 7.5 * Math.Min(Math.Abs(LastMovement), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtTime;
+ }
+
+ // Bonus for "almost" hyperdashes at corner points
+ if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
+ {
+ if (!previousHitCircle.HyperDash)
+ {
+ additionBonus += 1.0;
+ }
+ else
+ {
+ // After a hyperdash we ARE in the correct position. Always!
+ PlayerPositionOffset = 0;
+ }
+
+ addition *= 1.0 + additionBonus * ((10 - previousHitCircle.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
+ }
+
+ addition *= 850.0 / Math.Max(timeElapsed, 25);
+
+ Strain = previousHitObject.Strain * decay + addition;
+ }
+
+ private static double spacingWeight(float distance)
+ {
+ return Math.Pow(distance, 1.3) / 500;
+ }
+
+ internal float DistanceTo(CatchDifficultyHitObject other)
+ {
+ return Math.Abs(ActualNormalizedPosition - other.ActualNormalizedPosition);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index 548813fbd2..d55cdac115 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Catch.Objects
public int ComboIndex { get; set; }
+ ///
+ /// The distance for a fruit to to next hyper if it's not a hyper.
+ ///
+ public float DistanceToHyperDash { get; set; }
+
///
/// The next fruit starts a new combo. Used for explodey.
///
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index b62e9997d4..3b4a7b13e7 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -105,6 +105,11 @@ namespace osu.Game.Rulesets.Catch.UI
public class Catcher : Container, IKeyBindingHandler
{
+ ///
+ /// Width of the area that can be used to attempt catches during gameplay.
+ ///
+ internal float CatchWidth => CATCHER_SIZE * Math.Abs(Scale.X);
+
private Container caughtFruit;
public Container ExplodingFruitTarget;
@@ -232,15 +237,15 @@ namespace osu.Game.Rulesets.Catch.UI
/// Whether the catch is possible.
public bool AttemptCatch(CatchHitObject fruit)
{
- double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f;
+ float halfCatchWidth = CatchWidth * 0.5f;
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
var validCatch =
- catchObjectPosition >= catcherPosition - halfCatcherWidth &&
- catchObjectPosition <= catcherPosition + halfCatcherWidth;
+ catchObjectPosition >= catcherPosition - halfCatchWidth &&
+ catchObjectPosition <= catcherPosition + halfCatchWidth;
if (validCatch && fruit.HyperDash)
{
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 7b4978694b..609fd27bb4 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -27,14 +27,12 @@ namespace osu.Game.Rulesets.Taiko
public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[]
{
+ new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
+ new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim),
new KeyBinding(InputKey.D, TaikoAction.LeftRim),
new KeyBinding(InputKey.F, TaikoAction.LeftCentre),
new KeyBinding(InputKey.J, TaikoAction.RightCentre),
new KeyBinding(InputKey.K, TaikoAction.RightRim),
- new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
- new KeyBinding(InputKey.MouseLeft, TaikoAction.RightCentre),
- new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim),
- new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
};
public override IEnumerable ConvertLegacyMods(LegacyMods mods)
diff --git a/osu.Game.Tests/Visual/TestCaseLounge.cs b/osu.Game.Tests/Visual/TestCaseLounge.cs
index b96d705d5c..c5e0d1c4bf 100644
--- a/osu.Game.Tests/Visual/TestCaseLounge.cs
+++ b/osu.Game.Tests/Visual/TestCaseLounge.cs
@@ -8,6 +8,8 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
+using osu.Game.Screens;
+using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Multi.Components;
using osu.Game.Screens.Multi.Screens.Lounge;
using osu.Game.Users;
@@ -198,6 +200,8 @@ namespace osu.Game.Tests.Visual
private class TestLounge : Lounge
{
+ protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault();
+
public IEnumerable ChildRooms => RoomsContainer.Children.Where(r => r.MatchingFilter);
public Room SelectedRoom => Inspector.Room;
diff --git a/osu.Game.Tests/Visual/TestCaseMatch.cs b/osu.Game.Tests/Visual/TestCaseMatch.cs
new file mode 100644
index 0000000000..bb22358425
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseMatch.cs
@@ -0,0 +1,142 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets;
+using osu.Game.Screens.Multi.Screens.Match;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseMatch : OsuTestCase
+ {
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ Room room = new Room
+ {
+ Name = { Value = @"One Awesome Room" },
+ Status = { Value = new RoomStatusOpen() },
+ Availability = { Value = RoomAvailability.Public },
+ Type = { Value = new GameTypeTeamVersus() },
+ Beatmap =
+ {
+ Value = new BeatmapInfo
+ {
+ StarDifficulty = 5.02,
+ Ruleset = rulesets.GetRuleset(1),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Paradigm Shift",
+ Artist = @"Morimori Atsushi",
+ AuthorString = @"eiri-",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/765055/covers/cover.jpg?1526955337",
+ },
+ },
+ },
+ },
+ },
+ MaxParticipants = { Value = 5 },
+ Participants =
+ {
+ Value = new[]
+ {
+ new User
+ {
+ Username = @"eiri-",
+ Id = 3388410,
+ Country = new Country { FlagName = @"US" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3388410/00a8486a247831e1cc4375db519f611ac970bda8bc0057d78b0f540ea38c3e58.jpeg",
+ IsSupporter = true,
+ },
+ new User
+ {
+ Username = @"Nepuri",
+ Id = 6637817,
+ Country = new Country { FlagName = @"DE" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/6637817/9085fc60248b6b5327a72c1dcdecf2dbedba810ae0ab6bcf7224e46b1339632a.jpeg",
+ IsSupporter = true,
+ },
+ new User
+ {
+ Username = @"goheegy",
+ Id = 8057655,
+ Country = new Country { FlagName = @"GB" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8057655/21cec27c25a11dc197a4ec6a74253dbabb495949b0e0697113352f12007018c5.jpeg",
+ },
+ new User
+ {
+ Username = @"Alumetri",
+ Id = 5371497,
+ Country = new Country { FlagName = @"RU" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/5371497/e023b8c7fbe3613e64bd4856703517ea50fbed8a5805dc9acda9efe9897c67e2.jpeg",
+ },
+ }
+ },
+ };
+
+ Match match = new Match(room);
+
+ AddStep(@"show", () => Add(match));
+ AddStep(@"null beatmap", () => room.Beatmap.Value = null);
+ AddStep(@"change name", () => room.Name.Value = @"Two Awesome Rooms");
+ AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying());
+ AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.FriendsOnly);
+ AddStep(@"change type", () => room.Type.Value = new GameTypeTag());
+ AddStep(@"change beatmap", () => room.Beatmap.Value = new BeatmapInfo
+ {
+ StarDifficulty = 4.33,
+ Ruleset = rulesets.GetRuleset(2),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Yasashisa no Riyuu",
+ Artist = @"ChouCho",
+ AuthorString = @"celerih",
+ },
+ BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/685391/covers/cover.jpg?1524597970",
+ },
+ },
+ },
+ });
+
+ AddStep(@"null max participants", () => room.MaxParticipants.Value = null);
+ AddStep(@"change participants", () => room.Participants.Value = new[]
+ {
+ new User
+ {
+ Username = @"Spectator",
+ Id = 702598,
+ Country = new Country { FlagName = @"KR" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/702598/3bbf4cb8b8d2cf8b03145000a975ff27e191ab99b0920832e7dd67386280e288.jpeg",
+ IsSupporter = true,
+ },
+ new User
+ {
+ Username = @"celerih",
+ Id = 4696296,
+ Country = new Country { FlagName = @"CA" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/4696296/7f8500731d0ac66d5472569d146a7be07d9460273361913f22c038867baddaef.jpeg",
+ },
+ });
+
+ AddStep(@"exit", match.Exit);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs
new file mode 100644
index 0000000000..34f98f97c2
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs
@@ -0,0 +1,43 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Screens.Multi.Screens.Match;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseMatchHeader : OsuTestCase
+ {
+ public TestCaseMatchHeader()
+ {
+ Header header = new Header();
+ Add(header);
+
+ AddStep(@"set beatmap set", () => header.BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/760757/covers/cover.jpg?1526944540",
+ },
+ },
+ });
+
+ AddStep(@"change beatmap set", () => header.BeatmapSet = new BeatmapSetInfo
+ {
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/761883/covers/cover.jpg?1525557400",
+ },
+ },
+ });
+
+ AddStep(@"null beatmap set", () => header.BeatmapSet = null);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs
new file mode 100644
index 0000000000..205da6932f
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs
@@ -0,0 +1,57 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Rulesets;
+using osu.Game.Screens.Multi.Screens.Match;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseMatchInfo : OsuTestCase
+ {
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ Info info = new Info();
+ Add(info);
+
+ AddStep(@"set name", () => info.Name = @"Room Name?");
+ AddStep(@"set availability", () => info.Availability = RoomAvailability.FriendsOnly);
+ AddStep(@"set status", () => info.Status = new RoomStatusPlaying());
+ AddStep(@"set beatmap", () => info.Beatmap = new BeatmapInfo
+ {
+ StarDifficulty = 2.4,
+ Ruleset = rulesets.GetRuleset(0),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"My Song",
+ Artist = @"VisualTests",
+ AuthorString = @"osu!lazer",
+ },
+ });
+
+ AddStep(@"set type", () => info.Type = new GameTypeTagTeam());
+
+ AddStep(@"change name", () => info.Name = @"Room Name!");
+ AddStep(@"change availability", () => info.Availability = RoomAvailability.InviteOnly);
+ AddStep(@"change status", () => info.Status = new RoomStatusOpen());
+ AddStep(@"null beatmap", () => info.Beatmap = null);
+ AddStep(@"change type", () => info.Type = new GameTypeTeamVersus());
+ AddStep(@"change beatmap", () => info.Beatmap = new BeatmapInfo
+ {
+ StarDifficulty = 4.2,
+ Ruleset = rulesets.GetRuleset(3),
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Your Song",
+ Artist = @"Tester",
+ AuthorString = @"Someone",
+ },
+ });
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs
new file mode 100644
index 0000000000..d6ae07252b
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs
@@ -0,0 +1,56 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Screens.Multi.Screens.Match;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseMatchParticipants : OsuTestCase
+ {
+ public TestCaseMatchParticipants()
+ {
+ Participants participants;
+ Add(participants = new Participants
+ {
+ RelativeSizeAxes = Axes.Both,
+ });
+
+ AddStep(@"set max to null", () => participants.Max = null);
+ AddStep(@"set users", () => participants.Users = new[]
+ {
+ new User
+ {
+ Username = @"Feppla",
+ Id = 4271601,
+ Country = new Country { FlagName = @"SE" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
+ IsSupporter = true,
+ },
+ new User
+ {
+ Username = @"Xilver",
+ Id = 3099689,
+ Country = new Country { FlagName = @"IL" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
+ IsSupporter = true,
+ },
+ new User
+ {
+ Username = @"Wucki",
+ Id = 5287410,
+ Country = new Country { FlagName = @"FI" },
+ CoverUrl = @"https://assets.ppy.sh/user-profile-covers/5287410/5cfeaa9dd41cbce038ecdc9d781396ed4b0108089170bf7f50492ef8eadeb368.jpeg",
+ IsSupporter = true,
+ },
+ });
+
+ AddStep(@"set max", () => participants.Max = 10);
+ AddStep(@"clear users", () => participants.Users = new User[] { });
+ AddStep(@"set max to null", () => participants.Max = null);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs
new file mode 100644
index 0000000000..8c12589c6f
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Graphics.Containers;
+using osu.Game.Screens.Backgrounds;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseParallaxContainer : OsuTestCase
+ {
+ public TestCaseParallaxContainer()
+ {
+ ParallaxContainer parallax;
+
+ Add(parallax = new ParallaxContainer
+ {
+ Child = new BackgroundScreenDefault { Alpha = 0.8f }
+ });
+
+ AddStep("default parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT);
+ AddStep("high parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * 10);
+ AddStep("no parallax", () => parallax.ParallaxAmount = 0);
+ AddStep("negative parallax", () => parallax.ParallaxAmount = -ParallaxContainer.DEFAULT_PARALLAX_AMOUNT);
+ }
+ }
+}
diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs
index dc635ce7e7..8e1e5d54fa 100644
--- a/osu.Game/Graphics/Containers/ParallaxContainer.cs
+++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs
@@ -16,6 +16,9 @@ namespace osu.Game.Graphics.Containers
{
public const float DEFAULT_PARALLAX_AMOUNT = 0.02f;
+ ///
+ /// The amount of parallax movement. Negative values will reverse the direction of parallax relative to user input.
+ ///
public float ParallaxAmount = DEFAULT_PARALLAX_AMOUNT;
private Bindable parallaxEnabled;
@@ -45,7 +48,7 @@ namespace osu.Game.Graphics.Containers
if (!parallaxEnabled)
{
content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint);
- content.Scale = new Vector2(1 + ParallaxAmount);
+ content.Scale = new Vector2(1 + System.Math.Abs(ParallaxAmount));
}
};
}
@@ -69,7 +72,7 @@ namespace osu.Game.Graphics.Containers
double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000);
content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint);
- content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint);
+ content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + System.Math.Abs(ParallaxAmount)), 0, 1000, Easing.OutQuint);
}
firstUpdate = false;
diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs
new file mode 100644
index 0000000000..aaa11e88b6
--- /dev/null
+++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.Designer.cs
@@ -0,0 +1,376 @@
+//
+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("20180621044111_UpdateTaikoDefaultBindings")]
+ partial class UpdateTaikoDefaultBindings
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("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("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ 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("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("SkinInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.ToTable("SkinFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Creator");
+
+ b.Property("DeletePending");
+
+ b.Property("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
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs
new file mode 100644
index 0000000000..98ce5def08
--- /dev/null
+++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using osu.Framework.Logging;
+
+namespace osu.Game.Migrations
+{
+ public partial class UpdateTaikoDefaultBindings : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1");
+ Logger.Log("osu!taiko bindings have been reset due to new defaults", LoggingTarget.Runtime, LogLevel.Important);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ // we can't really tell if these should be restored or not, so let's just not do so.
+ }
+ }
+}
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index d750d50b5b..bd80cb743b 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -1,11 +1,9 @@
//
+using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
-using System;
namespace osu.Game.Migrations
{
@@ -16,7 +14,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "2.0.3-rtm-10026");
+ .HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 36c76851c6..ba8685b5b2 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -101,6 +101,8 @@ namespace osu.Game
public OsuGame(string[] args = null)
{
this.args = args;
+
+ forwardLoggedErrorsToNotifications();
}
public void ToggleSettings() => settings.ToggleVisibility();
@@ -305,8 +307,6 @@ namespace osu.Game
Depth = -6,
}, overlayContent.Add);
- forwardLoggedErrorsToNotifications();
-
dependencies.Cache(settings);
dependencies.Cache(onscreenDisplay);
dependencies.Cache(social);
@@ -394,31 +394,40 @@ namespace osu.Game
private void forwardLoggedErrorsToNotifications()
{
- int recentErrorCount = 0;
+ int recentLogCount = 0;
const double debounce = 5000;
Logger.NewEntry += entry =>
{
- if (entry.Level < LogLevel.Error || entry.Target == null) return;
+ if (entry.Level < LogLevel.Important || entry.Target == null) return;
- if (recentErrorCount < 2)
+ const int short_term_display_limit = 3;
+
+ if (recentLogCount < short_term_display_limit)
{
- notifications.Post(new SimpleNotification
+ Schedule(() => notifications.Post(new SimpleNotification
{
- Icon = FontAwesome.fa_bomb,
- Text = (recentErrorCount == 0 ? entry.Message : "Subsequent errors occurred and have been logged.") + "\nClick to view log files.",
+ Icon = entry.Level == LogLevel.Important ? FontAwesome.fa_exclamation_circle : FontAwesome.fa_bomb,
+ Text = entry.Message,
+ }));
+ }
+ else if (recentLogCount == short_term_display_limit)
+ {
+ Schedule(() => notifications.Post(new SimpleNotification
+ {
+ Icon = FontAwesome.fa_ellipsis_h,
+ Text = "Subsequent messages have been logged. Click to view log files.",
Activated = () =>
{
Host.Storage.GetStorageForDirectory("logs").OpenInNativeExplorer();
return true;
}
- });
+ }));
}
- Interlocked.Increment(ref recentErrorCount);
-
- Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentErrorCount), debounce);
+ Interlocked.Increment(ref recentLogCount);
+ Scheduler.AddDelayed(() => Interlocked.Decrement(ref recentLogCount), debounce);
};
}
diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs
index a78bc8da81..25a832941e 100644
--- a/osu.Game/Overlays/Notifications/SimpleNotification.cs
+++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Notifications
}
});
- Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16)
+ Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 14)
{
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 2a2111c8d6..7eeabd3e5e 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -185,9 +185,9 @@ namespace osu.Game.Screens.Edit
protected override bool OnScroll(InputState state)
{
if (state.Mouse.ScrollDelta.X + state.Mouse.ScrollDelta.Y > 0)
- clock.SeekBackward(true);
+ clock.SeekBackward(!clock.IsRunning);
else
- clock.SeekForward(true);
+ clock.SeekForward(!clock.IsRunning);
return true;
}
diff --git a/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs b/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs
index 016babcaa5..51dea355bf 100644
--- a/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs
+++ b/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs
@@ -164,7 +164,7 @@ namespace osu.Game.Screens.Multi.Screens.Lounge
// open the room if its selected and is clicked again
if (room.State == SelectionState.Selected)
- Push(new Match());
+ Push(new Match.Match(room.Room));
}
private class RoomsFilterContainer : FillFlowContainer, IHasFilterableChildren
diff --git a/osu.Game/Screens/Multi/Screens/Match.cs b/osu.Game/Screens/Multi/Screens/Match.cs
deleted file mode 100644
index 4ba7fe9f6a..0000000000
--- a/osu.Game/Screens/Multi/Screens/Match.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// 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.Framework.Screens;
-using osu.Game.Screens.Backgrounds;
-using osu.Game.Screens.Play;
-using osu.Game.Screens.Select;
-using OpenTK.Graphics;
-
-namespace osu.Game.Screens.Multi.Screens
-{
- public class Match : ScreenWhiteBox
- {
- protected override IEnumerable PossibleChildren => new[] {
- typeof(MatchSongSelect),
- typeof(Player),
- };
-
- protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
-
- protected override void OnEntering(Screen last)
- {
- base.OnEntering(last);
-
- Background.FadeColour(Color4.DarkGray, 500);
- }
-
- protected override bool OnExiting(Screen next)
- {
- Background.FadeColour(Color4.White, 500);
- return base.OnExiting(next);
- }
- }
-}
diff --git a/osu.Game/Screens/Multi/Screens/Match/Header.cs b/osu.Game/Screens/Multi/Screens/Match/Header.cs
new file mode 100644
index 0000000000..02e717d4be
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/Match/Header.cs
@@ -0,0 +1,184 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.SearchableList;
+using OpenTK.Graphics;
+
+namespace osu.Game.Screens.Multi.Screens.Match
+{
+ public class Header : Container
+ {
+ public const float HEIGHT = 200;
+
+ private readonly Box tabStrip;
+ private readonly UpdateableBeatmapSetCover cover;
+
+ public readonly PageTabControl Tabs;
+
+ public BeatmapSetInfo BeatmapSet
+ {
+ set => cover.BeatmapSet = value;
+ }
+
+ public Action OnRequestSelectBeatmap;
+
+ public Header()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = HEIGHT;
+
+ BeatmapSelectButton beatmapButton;
+ Children = new Drawable[]
+ {
+ cover = new UpdateableBeatmapSetCover
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Color4.Black.Opacity(0.5f)),
+ },
+ tabStrip = new Box
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = 1,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = 200,
+ Padding = new MarginPadding { Vertical = 5 },
+ Child = beatmapButton = new BeatmapSelectButton
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ },
+ Tabs = new PageTabControl
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ },
+ },
+ },
+ };
+
+ beatmapButton.Action = () => OnRequestSelectBeatmap?.Invoke();
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ tabStrip.Colour = colours.Yellow;
+ }
+
+ private class BeatmapSelectButton : OsuClickableContainer
+ {
+ private const float corner_radius = 5;
+ private const float bg_opacity = 0.5f;
+ private const float transition_duration = 100;
+
+ private readonly Box bg;
+ private readonly Container border;
+
+ public BeatmapSelectButton()
+ {
+ Masking = true;
+ CornerRadius = corner_radius;
+
+ Children = new Drawable[]
+ {
+ bg = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ Alpha = bg_opacity,
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Font = @"Exo2.0-Bold",
+ Text = "Select Beatmap",
+ },
+ border = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ CornerRadius = corner_radius,
+ BorderThickness = 4,
+ Alpha = 0,
+ Child = new Box // needs a child to show the border
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ border.BorderColour = colours.Yellow;
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ border.FadeIn(transition_duration);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ base.OnHoverLost(state);
+ border.FadeOut(transition_duration);
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ bg.FadeTo(0.75f, 1000, Easing.Out);
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ bg.FadeTo(bg_opacity, transition_duration);
+ return base.OnMouseUp(state, args);
+ }
+ }
+ }
+
+ public enum MatchHeaderPage
+ {
+ Settings,
+ Room,
+ }
+}
diff --git a/osu.Game/Screens/Multi/Screens/Match/Info.cs b/osu.Game/Screens/Multi/Screens/Match/Info.cs
new file mode 100644
index 0000000000..ec93eb90b1
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/Match/Info.cs
@@ -0,0 +1,210 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Overlays.SearchableList;
+using osu.Game.Screens.Multi.Components;
+using OpenTK;
+
+namespace osu.Game.Screens.Multi.Screens.Match
+{
+ public class Info : Container
+ {
+ public const float HEIGHT = 128;
+
+ private readonly OsuSpriteText name, availabilityStatus;
+ private readonly BeatmapTypeInfo beatmapTypeInfo;
+ private readonly ReadyButton readyButton;
+
+ private OsuColour colours;
+
+ public Bindable Ready => readyButton.Ready;
+
+ public string Name
+ {
+ set { name.Text = value; }
+ }
+
+ private RoomAvailability availability;
+ public RoomAvailability Availability
+ {
+ set
+ {
+ if (value == availability) return;
+ availability = value;
+
+ if (IsLoaded)
+ updateAvailabilityStatus();
+ }
+ }
+
+ private RoomStatus status;
+ public RoomStatus Status
+ {
+ set
+ {
+ if (value == status) return;
+ status = value;
+
+ if (IsLoaded)
+ updateAvailabilityStatus();
+ }
+ }
+
+ public BeatmapInfo Beatmap
+ {
+ set { beatmapTypeInfo.Beatmap = value; }
+ }
+
+ public GameType Type
+ {
+ set { beatmapTypeInfo.Type = value; }
+ }
+
+ public Info()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = HEIGHT;
+
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.FromHex(@"28242d"),
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
+ Padding = new MarginPadding { Vertical = 20 },
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ name = new OsuSpriteText
+ {
+ TextSize = 30,
+ },
+ availabilityStatus = new OsuSpriteText
+ {
+ TextSize = 14,
+ },
+ },
+ },
+ beatmapTypeInfo = new BeatmapTypeInfo
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ },
+ },
+ },
+ readyButton = new ReadyButton
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Size = new Vector2(200, 1),
+ Padding = new MarginPadding { Vertical = 10 },
+ },
+ },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ this.colours = colours;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ updateAvailabilityStatus();
+ }
+
+ private void updateAvailabilityStatus()
+ {
+ if (status != null)
+ {
+ availabilityStatus.FadeColour(status.GetAppropriateColour(colours), 100);
+ availabilityStatus.Text = $"{availability.GetDescription()}, {status.Message}";
+ }
+ }
+
+ private class ReadyButton : TriangleButton
+ {
+ public readonly Bindable Ready = new Bindable();
+
+ protected override SpriteText CreateText() => new OsuSpriteText
+ {
+ Depth = -1,
+ Origin = Anchor.Centre,
+ Anchor = Anchor.Centre,
+ Font = @"Exo2.0-Light",
+ TextSize = 30,
+ };
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ BackgroundColour = OsuColour.FromHex(@"1187aa");
+ Triangles.ColourLight = OsuColour.FromHex(@"277b9c");
+ Triangles.ColourDark = OsuColour.FromHex(@"1f6682");
+ Triangles.TriangleScale = 1.5f;
+
+ Container active;
+ Add(active = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0f,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.15f,
+ Blending = BlendingMode.Additive,
+ },
+ });
+
+ Action = () => Ready.Value = !Ready.Value;
+
+ Ready.BindValueChanged(value =>
+ {
+ if (value)
+ {
+ Text = "Not Ready";
+ active.FadeIn(200);
+ }
+ else
+ {
+ Text = "Ready";
+ active.FadeOut(200);
+ }
+ }, true);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Screens/Match/Match.cs b/osu.Game/Screens/Multi/Screens/Match/Match.cs
new file mode 100644
index 0000000000..ce3f7825a4
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/Match/Match.cs
@@ -0,0 +1,81 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Online.Multiplayer;
+using osu.Game.Screens.Select;
+using osu.Game.Users;
+
+namespace osu.Game.Screens.Multi.Screens.Match
+{
+ public class Match : MultiplayerScreen
+ {
+ private readonly Room room;
+ private readonly Participants participants;
+
+ private readonly Bindable nameBind = new Bindable();
+ private readonly Bindable statusBind = new Bindable();
+ private readonly Bindable availabilityBind = new Bindable();
+ private readonly Bindable typeBind = new Bindable();
+ private readonly Bindable beatmapBind = new Bindable();
+ private readonly Bindable maxParticipantsBind = new Bindable();
+ private readonly Bindable> participantsBind = new Bindable>();
+
+ protected override Container TransitionContent => participants;
+
+ public override string Type => "room";
+ public override string Title => room.Name.Value;
+
+ public Match(Room room)
+ {
+ this.room = room;
+ Header header;
+ Info info;
+
+ Children = new Drawable[]
+ {
+ header = new Header(),
+ info = new Info
+ {
+ Margin = new MarginPadding { Top = Header.HEIGHT },
+ },
+ participants = new Participants
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = Header.HEIGHT + Info.HEIGHT },
+ },
+ };
+
+ header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect());
+
+ beatmapBind.BindTo(room.Beatmap);
+ beatmapBind.BindValueChanged(b =>
+ {
+ header.BeatmapSet = b?.BeatmapSet;
+ info.Beatmap = b;
+ }, true);
+
+ nameBind.BindTo(room.Name);
+ nameBind.BindValueChanged(n => info.Name = n, true);
+
+ statusBind.BindTo(room.Status);
+ statusBind.BindValueChanged(s => info.Status = s, true);
+
+ availabilityBind.BindTo(room.Availability);
+ availabilityBind.BindValueChanged(a => info.Availability = a, true);
+
+ typeBind.BindTo(room.Type);
+ typeBind.BindValueChanged(t => info.Type = t, true);
+
+ maxParticipantsBind.BindTo(room.MaxParticipants);
+ maxParticipantsBind.BindValueChanged(m => { participants.Max = m; }, true);
+
+ participantsBind.BindTo(room.Participants);
+ participantsBind.BindValueChanged(p => participants.Users = p, true);
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Screens/Match/Participants.cs b/osu.Game/Screens/Multi/Screens/Match/Participants.cs
new file mode 100644
index 0000000000..9fa90f8752
--- /dev/null
+++ b/osu.Game/Screens/Multi/Screens/Match/Participants.cs
@@ -0,0 +1,74 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Overlays.SearchableList;
+using osu.Game.Screens.Multi.Components;
+using osu.Game.Users;
+using OpenTK;
+
+namespace osu.Game.Screens.Multi.Screens.Match
+{
+ public class Participants : Container
+ {
+ private readonly ParticipantCount count;
+ private readonly FillFlowContainer usersFlow;
+
+ public IEnumerable Users
+ {
+ set {
+ usersFlow.Children = value.Select(u => new UserPanel(u)
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Width = 300,
+ OnLoadComplete = d => d.FadeInFromZero(60),
+ }).ToList();
+
+ count.Count = value.Count();
+ }
+ }
+
+ public int? Max
+ {
+ set => count.Max = value;
+ }
+
+ public Participants()
+ {
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING },
+ Children = new Drawable[]
+ {
+ new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 10 },
+ Children = new Drawable[]
+ {
+ count = new ParticipantCount
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ },
+ usersFlow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(5),
+ Padding = new MarginPadding { Top = 40 },
+ LayoutDuration = 200,
+ LayoutEasing = Easing.OutQuint,
+ },
+ },
+ },
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Screens/Multi/Screens/MatchCreate.cs b/osu.Game/Screens/Multi/Screens/MatchCreate.cs
deleted file mode 100644
index 6b4e26d5e5..0000000000
--- a/osu.Game/Screens/Multi/Screens/MatchCreate.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-
-namespace osu.Game.Screens.Multi.Screens
-{
- public class MatchCreate : ScreenWhiteBox
- {
- protected override IEnumerable PossibleChildren => new[] {
- typeof(Match)
- };
-
- public MatchCreate()
- {
- ValidForResume = false;
- }
- }
-}
diff --git a/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs b/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs
index fa9b40684c..00c2613d54 100644
--- a/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs
+++ b/osu.Game/Screens/Multi/Screens/MultiplayerScreen.cs
@@ -10,9 +10,6 @@ namespace osu.Game.Screens.Multi.Screens
{
public abstract class MultiplayerScreen : OsuScreen
{
- private const Easing in_easing = Easing.OutQuint;
- private const Easing out_easing = Easing.InSine;
-
protected virtual Container TransitionContent => Content;
///
@@ -24,16 +21,15 @@ namespace osu.Game.Screens.Multi.Screens
{
base.OnEntering(last);
- TransitionContent.MoveToX(200);
-
- TransitionContent.FadeInFromZero(WaveContainer.APPEAR_DURATION, in_easing);
- TransitionContent.MoveToX(0, WaveContainer.APPEAR_DURATION, in_easing);
+ Content.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
+ TransitionContent.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
+ TransitionContent.MoveToX(200).MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
}
protected override bool OnExiting(Screen next)
{
- Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, out_easing);
- TransitionContent.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, out_easing);
+ Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint);
+ TransitionContent.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint);
return base.OnExiting(next);
}
@@ -42,16 +38,16 @@ namespace osu.Game.Screens.Multi.Screens
{
base.OnResuming(last);
- Content.FadeIn(WaveContainer.APPEAR_DURATION, in_easing);
- TransitionContent.MoveToX(0, WaveContainer.APPEAR_DURATION, in_easing);
+ Content.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
+ TransitionContent.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
}
protected override void OnSuspending(Screen next)
{
base.OnSuspending(next);
- Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, out_easing);
- TransitionContent.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, out_easing);
+ Content.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint);
+ TransitionContent.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint);
}
}
}
diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs
index a0c96d0cee..339392d5cf 100644
--- a/osu.Game/Screens/Select/MatchSongSelect.cs
+++ b/osu.Game/Screens/Select/MatchSongSelect.cs
@@ -7,12 +7,7 @@ namespace osu.Game.Screens.Select
{
protected override bool OnStart()
{
- Schedule(() =>
- {
- // needs to be scheduled else we enter an infinite feedback loop.
- if (IsCurrentScreen) Exit();
- });
-
+ if (IsCurrentScreen) Exit();
return true;
}
}
diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs
index 738bb2d642..217af8eb77 100644
--- a/osu.Game/Tests/OsuTestBrowser.cs
+++ b/osu.Game/Tests/OsuTestBrowser.cs
@@ -3,6 +3,7 @@
using osu.Framework.Platform;
using osu.Framework.Testing;
+using osu.Game.Graphics;
using osu.Game.Screens.Backgrounds;
namespace osu.Game.Tests
@@ -13,7 +14,11 @@ namespace osu.Game.Tests
{
base.LoadComplete();
- LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal);
+ LoadComponentAsync(new BackgroundScreenDefault
+ {
+ Colour = OsuColour.Gray(0.5f),
+ Depth = 10
+ }, AddInternal);
// Have to construct this here, rather than in the constructor, because
// we depend on some dependencies to be loaded within OsuGameBase.load().