diff --git a/osu-framework b/osu-framework index b32d1542d4..798409058a 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b32d1542d45c02c39e91bd0ebf8cc79aade9dd63 +Subproject commit 798409058a421307b5a92aeea4cd60a065f5a0d4 diff --git a/osu-resources b/osu-resources index 51f2b9b37f..93eb5bf99b 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 51f2b9b37f38cd349a3dd728a78f8fffcb3a54f5 +Subproject commit 93eb5bf99bb642bf339d7dce09c2d946412dadd6 diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 3cd9c80365..7c6efa7f70 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -74,7 +74,7 @@ namespace osu.Desktop.Deploy refreshDirectory(StagingFolder); //increment build number until we have a unique one. - string verBase = DateTime.Now.ToString("yyyy.Md."); + string verBase = DateTime.Now.ToString("yyyy.Mdd."); int increment = 0; while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any()) increment++; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs new file mode 100644 index 0000000000..e1914d4b8c --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Screens.Testing; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Screens.Select.Options; + +namespace osu.Desktop.VisualTests +{ + class TestCaseBeatmapOptionsOverlay : TestCase + { + public override string Name => @"Beatmap Options"; + public override string Description => @"Beatmap options in song select"; + + public override void Reset() + { + base.Reset(); + + var overlay = new BeatmapOptionsOverlay(); + + Add(overlay); + + AddButton(@"Toggle", overlay.ToggleVisibility); + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs new file mode 100644 index 0000000000..1bfdc1df8a --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Screens.Testing; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; + +namespace osu.Desktop.VisualTests.Tests +{ + class TestCaseDialogOverlay : TestCase + { + public override string Name => @"Dialog Overlay"; + public override string Description => @"Display dialogs"; + + DialogOverlay overlay; + + public override void Reset() + { + base.Reset(); + + Add(overlay = new DialogOverlay()); + + AddButton("dialog #1", () => overlay.Push(new PopupDialog + { + Icon = FontAwesome.fa_trash_o, + HeaderText = @"Confirm deletion of", + BodyText = @"Ayase Rie - Yuima-ru*World TVver.", + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"I never want to see this again.", + Action = () => System.Console.WriteLine(@"OK"), + }, + new PopupDialogCancelButton + { + Text = @"Firetruck, I still want quick ranks!", + Action = () => System.Console.WriteLine(@"Cancel"), + }, + }, + })); + + AddButton("dialog #2", () => overlay.Push(new PopupDialog + { + Icon = FontAwesome.fa_gear, + HeaderText = @"What do you want to do with", + BodyText = "Camellia as \"Bang Riot\" - Blastix Riotz", + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Manage collections", + }, + new PopupDialogOkButton + { + Text = @"Delete...", + }, + new PopupDialogOkButton + { + Text = @"Remove from unplayed", + }, + new PopupDialogOkButton + { + Text = @"Clear local scores", + }, + new PopupDialogOkButton + { + Text = @"Edit", + }, + new PopupDialogCancelButton + { + Text = @"Cancel", + }, + }, + })); + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs new file mode 100644 index 0000000000..4fe7e805ec --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs @@ -0,0 +1,94 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Platform; +using osu.Framework.Screens.Testing; +using osu.Game.Screens.Tournament; +using osu.Game.Screens.Tournament.Teams; + +namespace osu.Desktop.VisualTests.Tests +{ + class TestCaseDrawings : TestCase + { + public override string Name => @"Drawings"; + public override string Description => "Tournament drawings"; + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + } + + public override void Reset() + { + base.Reset(); + + Add(new Drawings + { + TeamList = new TestTeamList(), + }); + } + + class TestTeamList : ITeamList + { + public IEnumerable Teams { get; } = new[] + { + new Team + { + FlagName = "GB", + FullName = "United Kingdom", + Acronym = "UK" + }, + new Team + { + FlagName = "FR", + FullName = "France", + Acronym = "FRA" + }, + new Team + { + FlagName = "CN", + FullName = "China", + Acronym = "CHN" + }, + new Team + { + FlagName = "AU", + FullName = "Australia", + Acronym = "AUS" + }, + new Team + { + FlagName = "JP", + FullName = "Japan", + Acronym = "JPN" + }, + new Team + { + FlagName = "RO", + FullName = "Romania", + Acronym = "ROM" + }, + new Team + { + FlagName = "IT", + FullName = "Italy", + Acronym = "PIZZA" + }, + new Team + { + FlagName = "VE", + FullName = "Venezuela", + Acronym = "VNZ" + }, + new Team + { + FlagName = "US", + FullName = "United States of America", + Acronym = "USA" + }, + }; + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index f77025cb06..556d4eceea 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests AddButton(@"slider", () => load(HitObjectType.Slider)); AddButton(@"spinner", () => load(HitObjectType.Spinner)); - AddToggle(@"auto", () => { auto = !auto; load(mode); }); + AddToggle(@"auto", (state) => { auto = state; load(mode); }); ButtonsContainer.Add(new SpriteText { Text = "Playback Speed" }); ButtonsContainer.Add(new BasicSliderBar diff --git a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs new file mode 100644 index 0000000000..d535ac63b8 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Overlays.Mods; +using osu.Framework.Screens.Testing; +using osu.Game.Modes; +using osu.Game.Graphics; +using osu.Framework.Allocation; +using osu.Game.Overlays; + +namespace osu.Desktop.VisualTests.Tests +{ + class TestCaseModSelectOverlay : TestCase + { + public override string Name => @"Mod Select"; + + public override string Description => @"Tests the mod select overlay"; + + private ModSelectOverlay modSelect; + + public override void Reset() + { + base.Reset(); + + Add(modSelect = new ModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }); + + AddButton("Toggle", modSelect.ToggleVisibility); + AddButton("osu!", () => modSelect.PlayMode.Value = PlayMode.Osu); + AddButton("osu!taiko", () => modSelect.PlayMode.Value = PlayMode.Taiko); + AddButton("osu!catch", () => modSelect.PlayMode.Value = PlayMode.Catch); + AddButton("osu!mania", () => modSelect.PlayMode.Value = PlayMode.Mania); + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs index 1a811a8588..8d9213ee13 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs @@ -5,6 +5,7 @@ using osu.Framework.Screens.Testing; using osu.Framework.Graphics; using osu.Framework.Timing; using osu.Game.Overlays; +using osu.Framework.Graphics.Containers; namespace osu.Desktop.VisualTests.Tests { @@ -30,7 +31,7 @@ namespace osu.Desktop.VisualTests.Tests Anchor = Anchor.Centre }; Add(mc); - AddToggle(@"Show", mc.ToggleVisibility); + AddToggle(@"Show", (state) => mc.State = state ? Visibility.Visible : Visibility.Hidden); } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs index 78174a8f64..adf222d95d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs @@ -8,6 +8,7 @@ using osu.Framework.MathUtils; using osu.Game.Overlays; using System.Linq; using osu.Game.Overlays.Notifications; +using osu.Framework.Graphics.Containers; namespace osu.Desktop.VisualTests.Tests { @@ -30,7 +31,7 @@ namespace osu.Desktop.VisualTests.Tests Origin = Anchor.TopRight, }); - AddToggle(@"show", manager.ToggleVisibility); + AddToggle(@"show", (state) => manager.State = state ? Visibility.Visible : Visibility.Hidden); AddButton(@"simple #1", sendNotification1); AddButton(@"simple #2", sendNotification2); diff --git a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs index 990b8b3775..8545e50c69 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs @@ -2,8 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Logging; -using osu.Game.Overlays.Pause; using osu.Framework.Screens.Testing; +using osu.Game.Screens.Play; namespace osu.Desktop.VisualTests.Tests { diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 274eeb9440..6cc2d62402 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -1,9 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using osu.Desktop.VisualTests.Platform; using osu.Framework.Screens.Testing; +using osu.Framework.MathUtils; using osu.Game.Database; using osu.Game.Modes; using osu.Game.Screens.Select; @@ -14,6 +16,7 @@ namespace osu.Desktop.VisualTests.Tests { private BeatmapDatabase db, oldDb; private TestStorage storage; + private PlaySongSelect songSelect; public override string Name => @"Song Select"; public override string Description => @"with fake data"; @@ -35,9 +38,15 @@ namespace osu.Desktop.VisualTests.Tests db.Import(sets); } - Add(new PlaySongSelect()); + + Add(songSelect = new PlaySongSelect()); + + AddButton(@"Sort by Artist", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Artist; }); + AddButton(@"Sort by Title", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Title; }); + AddButton(@"Sort by Author", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Author; }); + AddButton(@"Sort by Difficulty", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Difficulty; }); } - + protected override void Dispose(bool isDisposing) { if (oldDb != null) @@ -59,9 +68,10 @@ namespace osu.Desktop.VisualTests.Tests Metadata = new BeatmapMetadata { OnlineBeatmapSetID = 1234 + i, - Artist = "MONACA", - Title = "Black Song", - Author = "Some Guy", + // Create random metadata, then we can check if sorting works based on these + Artist = "MONACA " + RNG.Next(0, 9), + Title = "Black Song " + RNG.Next(0, 9), + Author = "Some Guy " + RNG.Next(0, 9), }, Beatmaps = new List(new[] { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs b/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs index 7502c623fe..ce3650fe61 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs @@ -22,9 +22,9 @@ namespace osu.Desktop.VisualTests.Tests { base.Reset(); - FlowContainer flow; + FillFlowContainer flow; - Add(flow = new FlowContainer() + Add(flow = new FillFlowContainer() { RelativeSizeAxes = Axes.Both, Size = new Vector2(0.5f), diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 2426ff9d2e..bea56b5cd0 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -183,6 +183,7 @@ + @@ -199,6 +200,9 @@ + + + diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index af4a0de73c..08bf6d15a7 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -18,6 +18,7 @@ using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; using System.Net.Http; +using osu.Framework.Logging; namespace osu.Desktop.Overlays { @@ -57,19 +58,19 @@ namespace osu.Desktop.Overlays Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, + Spacing = new Vector2(5), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Spacing = new Vector2(5), Children = new Drawable[] { new OsuSpriteText @@ -95,6 +96,8 @@ namespace osu.Desktop.Overlays }, new Sprite { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Texture = textures.Get(@"Menu/dev-build-footer"), }, } @@ -157,15 +160,21 @@ namespace osu.Desktop.Overlays Schedule(() => notification.State = ProgressNotificationState.Completed); } - catch (Exception) + catch (Exception e) { if (useDeltaPatching) { + Logger.Error(e, @"delta patching failed!"); + //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) //try again without deltas. checkForUpdateAsync(false, notification); scheduleRetry = false; } + else + { + Logger.Error(e, @"update failed!"); + } } } catch (HttpRequestException) diff --git a/osu.Game.Modes.Catch/CatchMod.cs b/osu.Game.Modes.Catch/CatchMod.cs new file mode 100644 index 0000000000..f8edd1845f --- /dev/null +++ b/osu.Game.Modes.Catch/CatchMod.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Modes.Catch +{ + public class CatchModNoFail : ModNoFail + { + + } + + public class CatchModEasy : ModEasy + { + + } + + public class CatchModHidden : ModHidden + { + public override string Description => @"Play with no approach circles and fading notes for a slight score advantage."; + public override double ScoreMultiplier => 1.06; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class CatchModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1.12; + public override bool Ranked => true; + } + + public class CatchModSuddenDeath : ModSuddenDeath + { + + } + + public class CatchModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.06; + } + + public class CatchModRelax : ModRelax + { + public override string Description => @"Use the mouse to control the catcher."; + } + + public class CatchModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.5; + } + + public class CatchModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.06; + } + + public class CatchModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.12; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class CatchModPerfect : ModPerfect + { + + } +} diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 338129877b..70d99efeca 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Modes.Catch.UI; @@ -21,6 +22,61 @@ namespace osu.Game.Modes.Catch InputManager = input, }; + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new CatchModEasy(), + new CatchModNoFail(), + new CatchModHalfTime(), + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new CatchModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new CatchModSuddenDeath(), + new CatchModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new CatchModDoubleTime(), + new CatchModNightcore(), + }, + }, + new CatchModHidden(), + new CatchModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new CatchModRelax(), + new MultiMod + { + Mods = new Mod[] + { + new ModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + protected override PlayMode PlayMode => PlayMode.Catch; public override FontAwesome Icon => FontAwesome.fa_osu_fruits_o; diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj index 6b37963da1..42f06608b9 100644 --- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj +++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj @@ -58,6 +58,7 @@ + diff --git a/osu.Game.Modes.Mania/ManiaMod.cs b/osu.Game.Modes.Mania/ManiaMod.cs new file mode 100644 index 0000000000..7cd1ee2e79 --- /dev/null +++ b/osu.Game.Modes.Mania/ManiaMod.cs @@ -0,0 +1,155 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Modes.Mania +{ + public class ManiaModNoFail : ModNoFail + { + + } + + public class ManiaModEasy : ModEasy + { + + } + + public class ManiaModHidden : ModHidden + { + public override string Description => @"The notes fade out before you hit them!"; + public override double ScoreMultiplier => 1.0; + public override Mods[] DisablesMods => new Mods[] { Mods.Flashlight }; + } + + public class ManiaModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1.0; + public override bool Ranked => false; + } + + public class ManiaModSuddenDeath : ModSuddenDeath + { + + } + + public class ManiaModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.0; + } + + public class ManiaModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.3; + } + + public class ManiaModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.0; + } + + public class ManiaModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.0; + public override Mods[] DisablesMods => new Mods[] { Mods.Hidden }; + } + + public class ManiaModPerfect : ModPerfect + { + + } + + public class ManiaModFadeIn : Mod + { + public override Mods Name => Mods.FadeIn; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; + public override string Description => @""; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.Flashlight }; + } + + public class ManiaModRandom : Mod + { + public override Mods Name => Mods.Random; + public override FontAwesome Icon => FontAwesome.fa_close; + public override string Description => @"Shuffle around the notes!"; + public override double ScoreMultiplier => 1; + public override bool Ranked => false; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public abstract class ManiaKeyMod : Mod + { + public abstract int KeyCount { get; } + public override FontAwesome Icon => FontAwesome.fa_close; // TODO: Add proper key icons + public override string Description => @""; + public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class ManiaModKey1 : ManiaKeyMod + { + public override int KeyCount => 1; + public override Mods Name => Mods.Key1; + } + + public class ManiaModKey2 : ManiaKeyMod + { + public override int KeyCount => 2; + public override Mods Name => Mods.Key2; + } + + public class ManiaModKey3 : ManiaKeyMod + { + public override int KeyCount => 3; + public override Mods Name => Mods.Key3; + } + + public class ManiaModKey4 : ManiaKeyMod + { + public override int KeyCount => 4; + public override Mods Name => Mods.Key4; + } + + public class ManiaModKey5 : ManiaKeyMod + { + public override int KeyCount => 5; + public override Mods Name => Mods.Key5; + } + + public class ManiaModKey6 : ManiaKeyMod + { + public override int KeyCount => 6; + public override Mods Name => Mods.Key6; + } + + public class ManiaModKey7 : ManiaKeyMod + { + public override int KeyCount => 7; + public override Mods Name => Mods.Key7; + } + + public class ManiaModKey8 : ManiaKeyMod + { + public override int KeyCount => 8; + public override Mods Name => Mods.Key8; + } + + public class ManiaModKey9 : ManiaKeyMod + { + public override int KeyCount => 9; + public override Mods Name => Mods.Key9; + } + + public class ManiaModKeyCoop : Mod + { + public override Mods Name => Mods.KeyCoop; + public override FontAwesome Icon => FontAwesome.fa_close; + public override string Description => @"Double the key amount, double the fun!"; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { }; + } +} diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index 1fb4d055a4..94c2c0c31f 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Input; +using System.Collections.Generic; using osu.Game.Graphics; using osu.Game.Modes.Mania.UI; using osu.Game.Modes.Objects; @@ -21,6 +22,77 @@ namespace osu.Game.Modes.Mania InputManager = input, }; + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new ManiaModEasy(), + new ManiaModNoFail(), + new ManiaModHalfTime(), + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new ManiaModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new ManiaModSuddenDeath(), + new ManiaModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new ManiaModDoubleTime(), + new ManiaModNightcore(), + }, + }, + new ManiaModHidden(), + new ManiaModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new MultiMod + { + Mods = new Mod[] + { + new ManiaModKey4(), + new ManiaModKey5(), + new ManiaModKey6(), + new ManiaModKey7(), + new ManiaModKey8(), + new ManiaModKey9(), + new ManiaModKey1(), + new ManiaModKey2(), + new ManiaModKey3(), + }, + }, + new ManiaModKeyCoop(), + new ManiaModRandom(), + new MultiMod + { + Mods = new Mod[] + { + new ModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + protected override PlayMode PlayMode => PlayMode.Mania; public override FontAwesome Icon => FontAwesome.fa_osu_mania_o; diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj index b9b7e35ae7..457fb38e00 100644 --- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj +++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj @@ -58,6 +58,7 @@ + diff --git a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs b/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs index 8f53041017..95bc0954de 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs @@ -13,7 +13,7 @@ using OpenTK.Graphics; namespace osu.Game.Modes.Osu.Objects.Drawables { - public class HitExplosion : FlowContainer + public class HitExplosion : FillFlowContainer { private readonly OsuJudgementInfo judgement; private SpriteText line1; @@ -25,7 +25,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables AutoSizeAxes = Axes.Both; Origin = Anchor.Centre; - Direction = FlowDirections.Vertical; + Direction = FillDirection.Down; Spacing = new Vector2(0, 2); Position = (h?.StackedEndPosition ?? Vector2.Zero) + judgement.PositionOffset; diff --git a/osu.Game.Modes.Osu/Objects/HitCircle.cs b/osu.Game.Modes.Osu/Objects/HitCircle.cs index 80f9317d96..2d86f0225f 100644 --- a/osu.Game.Modes.Osu/Objects/HitCircle.cs +++ b/osu.Game.Modes.Osu/Objects/HitCircle.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Modes.Osu.Objects { public class HitCircle : OsuHitObject diff --git a/osu.Game.Modes.Osu/Objects/SliderTick.cs b/osu.Game.Modes.Osu/Objects/SliderTick.cs index d9b8c03c3c..4492af86f9 100644 --- a/osu.Game.Modes.Osu/Objects/SliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/SliderTick.cs @@ -1,4 +1,7 @@ -namespace osu.Game.Modes.Osu.Objects +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Modes.Osu.Objects { public class SliderTick : OsuHitObject { diff --git a/osu.Game.Modes.Osu/Objects/Spinner.cs b/osu.Game.Modes.Osu/Objects/Spinner.cs index 8a9b5bbbe3..d55b9eb59c 100644 --- a/osu.Game.Modes.Osu/Objects/Spinner.cs +++ b/osu.Game.Modes.Osu/Objects/Spinner.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Modes.Osu.Objects { public class Spinner : OsuHitObject diff --git a/osu.Game.Modes.Osu/OsuMod.cs b/osu.Game.Modes.Osu/OsuMod.cs new file mode 100644 index 0000000000..790b87204f --- /dev/null +++ b/osu.Game.Modes.Osu/OsuMod.cs @@ -0,0 +1,96 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Modes.Osu +{ + public class OsuModNoFail : ModNoFail + { + + } + + public class OsuModEasy : ModEasy + { + + } + + public class OsuModHidden : ModHidden + { + public override string Description => @"Play with no approach circles and fading notes for a slight score advantage."; + public override double ScoreMultiplier => 1.06; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class OsuModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1.06; + public override bool Ranked => true; + } + + public class OsuModSuddenDeath : ModSuddenDeath + { + + } + + public class OsuModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.12; + } + + public class OsuModRelax : ModRelax + { + public override string Description => "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things."; + } + + public class OsuModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.5; + } + + public class OsuModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.12; + } + + public class OsuModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.12; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class OsuModPerfect : ModPerfect + { + + } + + public class OsuModSpunOut : Mod + { + public override Mods Name => Mods.SpunOut; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout; + public override string Description => @"Spinners will be automatically completed"; + public override double ScoreMultiplier => 0.9; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.Autoplay, Mods.Cinema, Mods.Autopilot }; + } + + public class OsuModAutopilot : Mod + { + public override Mods Name => Mods.Autopilot; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot; + public override string Description => @"Automatic cursor movement - just follow the rhythm."; + public override double ScoreMultiplier => 0; + public override bool Ranked => false; + public override Mods[] DisablesMods => new Mods[] { Mods.SpunOut, Mods.Relax, Mods.SuddenDeath, Mods.Perfect, Mods.NoFail, Mods.Autoplay, Mods.Cinema }; + } + + public class OsuModTarget : Mod + { + public override Mods Name => Mods.Target; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_target; + public override string Description => @""; + public override double ScoreMultiplier => 1; + public override bool Ranked => false; + public override Mods[] DisablesMods => new Mods[] { }; + } +} diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs index 068b317fc6..33cbeafc03 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -39,6 +39,64 @@ namespace osu.Game.Modes.Osu } }; + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new OsuModEasy(), + new OsuModNoFail(), + new OsuModHalfTime(), + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new OsuModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new OsuModSuddenDeath(), + new OsuModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new OsuModDoubleTime(), + new OsuModNightcore(), + }, + }, + new OsuModHidden(), + new OsuModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new OsuModRelax(), + new OsuModAutopilot(), + new OsuModTarget(), + new OsuModSpunOut(), + new MultiMod + { + Mods = new Mod[] + { + new ModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + public override FontAwesome Icon => FontAwesome.fa_osu_osu_o; public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); diff --git a/osu.Game.Modes.Osu/OsuScore.cs b/osu.Game.Modes.Osu/OsuScore.cs index ce651e0809..6ea8eff890 100644 --- a/osu.Game.Modes.Osu/OsuScore.cs +++ b/osu.Game.Modes.Osu/OsuScore.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Modes.Osu { class OsuScore : Score diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index 1d3ee7a6a4..f53066ecde 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -85,6 +85,7 @@ + diff --git a/osu.Game.Modes.Taiko/TaikoMod.cs b/osu.Game.Modes.Taiko/TaikoMod.cs new file mode 100644 index 0000000000..104c3fe1e2 --- /dev/null +++ b/osu.Game.Modes.Taiko/TaikoMod.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Modes.Taiko +{ + public class TaikoModNoFail : ModNoFail + { + + } + + public class TaikoModEasy : ModEasy + { + + } + + public class TaikoModHidden : ModHidden + { + public override string Description => @"The notes fade out before you hit them!"; + public override double ScoreMultiplier => 1.06; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class TaikoModHardRock : ModHardRock + { + public override double ScoreMultiplier => 1.06; + public override bool Ranked => true; + } + + public class TaikoModSuddenDeath : ModSuddenDeath + { + + } + + public class TaikoModDoubleTime : ModDoubleTime + { + public override double ScoreMultiplier => 1.12; + } + + public class TaikoModRelax : ModRelax + { + public override string Description => @"Relax! You will no longer get dizzyfied by ninja-like spinners, demanding drumrolls or unexpected katu's."; + } + + public class TaikoModHalfTime : ModHalfTime + { + public override double ScoreMultiplier => 0.5; + } + + public class TaikoModNightcore : ModNightcore + { + public override double ScoreMultiplier => 1.12; + } + + public class TaikoModFlashlight : ModFlashlight + { + public override double ScoreMultiplier => 1.12; + public override Mods[] DisablesMods => new Mods[] { }; + } + + public class TaikoModPerfect : ModPerfect + { + + } +} diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index 9c1cef3530..f9e7aeba21 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Input; +using System.Collections.Generic; using osu.Game.Graphics; using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.UI; @@ -21,6 +22,61 @@ namespace osu.Game.Modes.Taiko InputManager = input, }; + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new TaikoModEasy(), + new TaikoModNoFail(), + new TaikoModHalfTime(), + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new TaikoModHardRock(), + new MultiMod + { + Mods = new Mod[] + { + new TaikoModSuddenDeath(), + new TaikoModPerfect(), + }, + }, + new MultiMod + { + Mods = new Mod[] + { + new TaikoModDoubleTime(), + new TaikoModNightcore(), + }, + }, + new TaikoModHidden(), + new TaikoModFlashlight(), + }; + + case ModType.Special: + return new Mod[] + { + new TaikoModRelax(), + new MultiMod + { + Mods = new Mod[] + { + new ModAutoplay(), + new ModCinema(), + }, + }, + }; + + default: + return new Mod[] { }; + } + } + protected override PlayMode PlayMode => PlayMode.Taiko; public override FontAwesome Icon => FontAwesome.fa_osu_taiko_o; diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index a964d48288..6c379b1b70 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -56,6 +56,7 @@ + diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 84635af39a..90d1aa542f 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.IO; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -40,8 +41,16 @@ namespace osu.Game.Tests.Beatmaps.IO using (HeadlessGameHost host = new HeadlessGameHost()) { var osu = loadOsu(host); - osu.Dependencies.Get().Import(osz_path); + + var temp = prepareTempCopy(osz_path); + + Assert.IsTrue(File.Exists(temp)); + + osu.Dependencies.Get().Import(temp); + ensureLoaded(osu); + + Assert.IsFalse(File.Exists(temp)); } } @@ -56,14 +65,51 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = loadOsu(host); + var temp = prepareTempCopy(osz_path); + + Assert.IsTrue(File.Exists(temp)); + var importer = new BeatmapImporter(client); - if (!importer.ImportAsync(osz_path).Wait(1000)) + if (!importer.ImportAsync(temp).Wait(1000)) Assert.Fail(@"IPC took too long to send"); ensureLoaded(osu); + + Assert.IsFalse(File.Exists(temp)); } } + [Test] + public void TestImportWhenFileOpen() + { + //unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. + using (HeadlessGameHost host = new HeadlessGameHost()) + { + var osu = loadOsu(host); + + var temp = prepareTempCopy(osz_path); + + Assert.IsTrue(File.Exists(temp)); + + using (FileStream stream = File.OpenRead(temp)) + osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + Assert.IsTrue(File.Exists(temp)); + + File.Delete(temp); + + Assert.IsFalse(File.Exists(temp)); + } + } + + private string prepareTempCopy(string path) + { + var temp = Path.GetTempFileName(); + return new FileInfo(osz_path).CopyTo(temp, true).FullName; + } + private OsuGameBase loadOsu(GameHost host) { var osu = new OsuGameBase(); diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs index bcdb1c0b5d..9c1f1a02fc 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs @@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps.Drawables BeatmapSet = beatmapSet; WorkingBeatmap beatmap = database.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); foreach (var b in BeatmapSet.Beatmaps) - b.StarDifficulty = (float)(database.GetWorkingBeatmap(b).Beatmap?.CalculateStarDifficulty() ?? 0); + b.StarDifficulty = (float)(database.GetWorkingBeatmap(b).Beatmap?.CalculateStarDifficulty() ?? -1f); Header = new BeatmapSetHeader(beatmap) { diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index 214712af72..1db339a5b6 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -83,10 +83,10 @@ namespace osu.Game.Beatmaps.Drawables ColourLight = OsuColour.FromHex(@"3a7285"), ColourDark = OsuColour.FromHex(@"123744") }, - new FlowContainer + new FillFlowContainer { Padding = new MarginPadding(5), - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, AutoSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -98,18 +98,18 @@ namespace osu.Game.Beatmaps.Drawables Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, - new FlowContainer + new FillFlowContainer { Padding = new MarginPadding { Left = 5 }, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { - Direction = FlowDirections.Horizontal, - AutoSizeAxes = Axes.Both, + Direction = FillDirection.Right, Spacing = new Vector2(4, 0), + AutoSizeAxes = Axes.Both, Children = new[] { new OsuSpriteText diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs index 8681259272..0a1fc5f331 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs @@ -24,7 +24,7 @@ namespace osu.Game.Beatmaps.Drawables private OsuConfigManager config; private Bindable preferUnicode; private WorkingBeatmap beatmap; - private FlowContainer difficultyIcons; + private FillFlowContainer difficultyIcons; public BeatmapSetHeader(WorkingBeatmap beatmap) { @@ -36,9 +36,9 @@ namespace osu.Game.Beatmaps.Drawables { RelativeSizeAxes = Axes.Both, }, - new FlowContainer + new FillFlowContainer { - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Padding = new MarginPadding { Top = 5, Left = 18, Right = 10, Bottom = 10 }, AutoSizeAxes = Axes.Both, Children = new[] @@ -52,13 +52,12 @@ namespace osu.Game.Beatmaps.Drawables }, artist = new OsuSpriteText { - Margin = new MarginPadding { Top = -1 }, Font = @"Exo2.0-SemiBoldItalic", Text = beatmap.BeatmapSetInfo.Metadata.Artist, TextSize = 17, Shadow = true, }, - difficultyIcons = new FlowContainer + difficultyIcons = new FillFlowContainer { Margin = new MarginPadding { Top = 5 }, AutoSizeAxes = Axes.Both, @@ -109,10 +108,10 @@ namespace osu.Game.Beatmaps.Drawables Children = new[] { - new FlowContainer + new FillFlowContainer { Depth = -1, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, RelativeSizeAxes = Axes.Both, // This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle Shear = new Vector2(0.8f, 0), diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 84116a1f70..70bfbb5992 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -33,6 +33,8 @@ namespace osu.Game.Configuration Set(OsuConfig.SnakingOutSliders, false); Set(OsuConfig.MenuParallax, true); + + Set(OsuConfig.KeyOverlay, false); //todo: implement all settings below this line (remove the Disabled set when doing so). Set(OsuConfig.MouseSpeed, 1.0).Disabled = true; @@ -80,7 +82,6 @@ namespace osu.Game.Configuration Set(OsuConfig.IgnoreBeatmapSamples, false).Disabled = true; Set(OsuConfig.IgnoreBeatmapSkins, false).Disabled = true; Set(OsuConfig.IgnoreList, string.Empty).Disabled = true; - Set(OsuConfig.KeyOverlay, false).Disabled = true; Set(OsuConfig.Language, @"unknown").Disabled = true; Set(OsuConfig.AllowNowPlayingHighlights, false).Disabled = true; Set(OsuConfig.LastVersion, string.Empty).Disabled = true; diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index fc2c7558b0..03f904b7e8 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -116,13 +116,61 @@ namespace osu.Game.Database connection.DeleteAll(); } + /// + /// Import multiple from . + /// + /// Multiple locations on disk public void Import(IEnumerable paths) { + Stack sets = new Stack(); + foreach (string p in paths) - Import(p); + try + { + BeatmapSetInfo set = getBeatmapSet(p); + + //If we have an ID then we already exist in the database. + if (set.ID == 0) + sets.Push(set); + + // We may or may not want to delete the file depending on where it is stored. + // e.g. reconstructing/repairing database with beatmaps from default storage. + // Also, not always a single file, i.e. for LegacyFilesystemReader + // TODO: Add a check to prevent files from storage to be deleted. + try + { + File.Delete(p); + } + catch (Exception e) + { + Logger.Error(e, $@"Could not delete file at {p}"); + } + } + catch (Exception e) + { + e = e.InnerException ?? e; + Logger.Error(e, $@"Could not import beatmap set"); + } + + // Batch commit with multiple sets to database + Import(sets); } + /// + /// Import from . + /// + /// Location on disk public void Import(string path) + { + Import(new [] { path }); + } + + /// + /// Duplicates content from to storage and returns a representing . + /// + /// Content location + /// + private BeatmapSetInfo getBeatmapSet(string path) { string hash = null; @@ -156,7 +204,7 @@ namespace osu.Game.Database BeatmapSetAdded?.Invoke(existing); } - return; + return existing; } var beatmapSet = new BeatmapSetInfo @@ -172,7 +220,6 @@ namespace osu.Game.Database { string[] mapNames = reader.BeatmapFilenames; foreach (var name in mapNames) - { using (var stream = new StreamReader(reader.GetStream(name))) { var decoder = BeatmapDecoder.GetDecoder(stream); @@ -184,11 +231,10 @@ namespace osu.Game.Database beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); } - beatmapSet.StoryboardFile = reader.StoryboardFilename; - } + beatmapSet.StoryboardFile = reader.StoryboardFilename; } - Import(new[] { beatmapSet }); + return beatmapSet; } public void Import(IEnumerable beatmapSets) diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index 7a976a1f7a..1c2ae2bf78 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -88,7 +88,7 @@ namespace osu.Game.Database return ID == other?.ID; } - public bool AudioEquals(BeatmapInfo other) => other != null && + public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && BeatmapSet.Path == other.BeatmapSet.Path && (Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile; } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index f3b75ce91a..f760919fb2 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -10,7 +10,6 @@ using osu.Framework.MathUtils; using OpenTK; using OpenTK.Graphics; using System; -using osu.Framework.Graphics.Colour; namespace osu.Game.Graphics.Backgrounds { diff --git a/osu.Game/Graphics/TextAwesome.cs b/osu.Game/Graphics/TextAwesome.cs index 03769e6f6b..13d3cbc8c3 100644 --- a/osu.Game/Graphics/TextAwesome.cs +++ b/osu.Game/Graphics/TextAwesome.cs @@ -902,6 +902,6 @@ namespace osu.Game.Graphics fa_osu_mod_spunout = 0xe046, fa_osu_mod_suddendeath = 0xe047, fa_osu_mod_target = 0xe048, - fa_osu_mod_bg = 0xe049, + fa_osu_mod_bg = 0xe04a, } } diff --git a/osu.Game/Overlays/Pause/PauseButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs similarity index 80% rename from osu.Game/Overlays/Pause/PauseButton.cs rename to osu.Game/Graphics/UserInterface/DialogButton.cs index a14c1fdade..fc7d142a8f 100644 --- a/osu.Game/Overlays/Pause/PauseButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -13,17 +13,15 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Graphics.UserInterface { - public class PauseButton : ClickableContainer + public class DialogButton : ClickableContainer { private const float hover_width = 0.9f; private const float hover_duration = 500; private const float glow_fade_duration = 250; private const float click_duration = 200; - private Color4 backgroundColour = OsuColour.Gray(34); - private Color4 buttonColour; public Color4 ButtonColour { @@ -35,8 +33,21 @@ namespace osu.Game.Overlays.Pause { buttonColour = value; updateGlow(); - if (colourContainer == null) return; - colourContainer.Colour = ButtonColour; + colourContainer.Colour = value; + } + } + + private Color4 backgroundColour = OsuColour.Gray(34); + public Color4 BackgroundColour + { + get + { + return backgroundColour; + } + set + { + backgroundColour = value; + background.Colour = value; } } @@ -50,16 +61,30 @@ namespace osu.Game.Overlays.Pause set { text = value; - if (spriteText == null) return; spriteText.Text = Text; } } + private float textSize = 28; + internal float TextSize + { + get + { + return textSize; + } + set + { + textSize = value; + spriteText.TextSize = value; + } + } + public SampleChannel SampleClick, SampleHover; private Container backgroundContainer, colourContainer, glowContainer; - private Box leftGlow, centerGlow, rightGlow; + private Box leftGlow, centerGlow, rightGlow, background; private SpriteText spriteText; + private Vector2 hoverSpacing => new Vector2(3f, 0f); private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking @@ -85,8 +110,9 @@ namespace osu.Game.Overlays.Pause protected override bool OnHover(Framework.Input.InputState state) { + spriteText.TransformSpacingTo(hoverSpacing, hover_duration, EasingTypes.OutElastic); + colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, EasingTypes.OutElastic); - spriteText.TransformSpacingTo(new Vector2(3f, 0f), hover_duration, EasingTypes.OutElastic); glowContainer.FadeIn(glow_fade_duration, EasingTypes.Out); SampleHover?.Play(); return true; @@ -127,8 +153,10 @@ namespace osu.Game.Overlays.Pause rightGlow.ColourInfo = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f)); } - public PauseButton() + public DialogButton() { + RelativeSizeAxes = Axes.X; + Children = new Drawable[] { backgroundContainer = new Container @@ -137,12 +165,12 @@ namespace osu.Game.Overlays.Pause Width = 1f, Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour - } - } + Colour = backgroundColour, + }, + }, }, glowContainer = new Container { @@ -156,23 +184,23 @@ namespace osu.Game.Overlays.Pause RelativeSizeAxes = Axes.Both, Origin = Anchor.TopLeft, Anchor = Anchor.TopLeft, - Width = 0.125f + Width = 0.125f, }, centerGlow = new Box { RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Width = 0.75f + Width = 0.75f, }, rightGlow = new Box { RelativeSizeAxes = Axes.Both, Origin = Anchor.TopRight, Anchor = Anchor.TopRight, - Width = 0.125f - } - } + Width = 0.125f, + }, + }, }, new Container { @@ -194,7 +222,7 @@ namespace osu.Game.Overlays.Pause { Type = EdgeEffectType.Shadow, Colour = Color4.Black.Opacity(0.2f), - Radius = 5 + Radius = 5, }, Colour = ButtonColour, Shear = new Vector2(0.2f, 0), @@ -203,7 +231,7 @@ namespace osu.Game.Overlays.Pause new Box { EdgeSmoothness = new Vector2(2, 0), - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, }, new Container { @@ -217,13 +245,13 @@ namespace osu.Game.Overlays.Pause RelativeSizeAxes = Axes.Both, TriangleScale = 4, ColourDark = OsuColour.Gray(0.88f), - Shear = new Vector2(-0.2f, 0) - } - } + Shear = new Vector2(-0.2f, 0), + }, + }, }, - } - } - } + }, + }, + }, }, spriteText = new OsuSpriteText { @@ -234,8 +262,8 @@ namespace osu.Game.Overlays.Pause Font = "Exo2.0-Bold", Shadow = true, ShadowColour = new Color4(0, 0, 0, 0.1f), - Colour = Color4.White - } + Colour = Color4.White, + }, }; updateGlow(); diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index f657ffe330..2f53d00c7e 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -1,4 +1,7 @@ -using OpenTK.Graphics; +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; using OpenTK.Input; using osu.Framework.Input; using System; diff --git a/osu.Game/Graphics/UserInterface/OsuDropDownMenuItem.cs b/osu.Game/Graphics/UserInterface/OsuDropDownMenuItem.cs index c2eb1fbc69..d654299279 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropDownMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropDownMenuItem.cs @@ -19,9 +19,9 @@ namespace osu.Game.Graphics.UserInterface Children = new[] { - new FlowContainer + new FillFlowContainer { - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Children = new Drawable[] diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 85b1a1a338..df48694249 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -14,6 +14,8 @@ namespace osu.Game.Graphics.UserInterface { protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize); + public override bool AllowClipboardExport => false; + public class PasswordMaskChar : Container { private CircularContainer circle; diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index b9d1dca65d..10ffcffa7f 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -68,10 +68,10 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - stars = new FlowContainer + stars = new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, Spacing = new Vector2(star_spacing), } }; diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index bd3574b625..25bc7037eb 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -57,10 +57,7 @@ namespace osu.Game.Graphics.UserInterface c1.Origin = c1.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopLeft : Anchor.TopRight; c2.Origin = c2.Anchor = (value & Anchor.x2) > 0 ? Anchor.TopRight : Anchor.TopLeft; - Margin = new MarginPadding - { - Right = (value & Anchor.x2) > 0 ? -SIZE_RETRACTED.X * shear * 0.5f : 0 - }; + X = (value & Anchor.x2) > 0 ? SIZE_RETRACTED.X * shear * 0.5f : 0; c1.Depth = (value & Anchor.x2) > 0 ? 0 : 1; c2.Depth = (value & Anchor.x2) > 0 ? 1 : 0; diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs index 07a8017294..04ba2fcd2f 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs @@ -33,7 +33,7 @@ namespace osu.Game.Graphics.UserInterface.Volume Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.BottomRight, diff --git a/osu.Game/Modes/Mod.cs b/osu.Game/Modes/Mod.cs new file mode 100644 index 0000000000..e8d36b0aef --- /dev/null +++ b/osu.Game/Modes/Mod.cs @@ -0,0 +1,272 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.ComponentModel; +using osu.Game.Graphics; + +namespace osu.Game.Modes +{ + /// + /// The base class for gameplay modifiers. + /// + public abstract class Mod + { + /// + /// The name of this mod. + /// + public abstract Mods Name { get; } + + /// + /// The icon of this mod. + /// + public abstract FontAwesome Icon { get; } + + /// + /// The user readable description of this mod. + /// + public abstract string Description { get; } + + /// + /// The score multiplier of this mod. + /// + public abstract double ScoreMultiplier { get; } + + /// + /// Returns if this mod is ranked. + /// + public abstract bool Ranked { get; } + + /// + /// The mods this mod cannot be enabled with. + /// + public abstract Mods[] DisablesMods { get; } + } + + public class MultiMod : Mod + { + public override Mods Name => Modes.Mods.None; + public override FontAwesome Icon => FontAwesome.fa_close; + public override string Description => @""; + public override double ScoreMultiplier => 0.0; + public override bool Ranked => false; + public override Mods[] DisablesMods => new Mods[] { }; + + public Mod[] Mods; + } + + public abstract class ModNoFail : Mod + { + public override Mods Name => Mods.NoFail; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail; + public override string Description => @"You can't fail, no matter what."; + public override double ScoreMultiplier => 0.5; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.Relax, Mods.Autopilot, Mods.SuddenDeath, Mods.Perfect }; + } + + public abstract class ModEasy : Mod + { + public override Mods Name => Mods.Easy; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy; + public override string Description => @"Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."; + public override double ScoreMultiplier => 0.5; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.HardRock }; + } + + public abstract class ModHidden : Mod + { + public override Mods Name => Mods.Hidden; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; + public override bool Ranked => true; + } + + public abstract class ModHardRock : Mod + { + public override Mods Name => Mods.HardRock; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; + public override string Description => @"Everything just got a bit harder..."; + public override Mods[] DisablesMods => new Mods[] { Mods.Easy }; + } + + public abstract class ModSuddenDeath : Mod + { + public override Mods Name => Mods.SuddenDeath; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath; + public override string Description => @"Miss a note and fail."; + public override double ScoreMultiplier => 1; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.NoFail, Mods.Relax, Mods.Autopilot, Mods.Autoplay, Mods.Cinema }; + } + + public abstract class ModDoubleTime : Mod + { + public override Mods Name => Mods.DoubleTime; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime; + public override string Description => @"Zoooooooooom"; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.HalfTime }; + } + + public abstract class ModRelax : Mod + { + public override Mods Name => Mods.Relax; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax; + public override double ScoreMultiplier => 0; + public override bool Ranked => false; + public override Mods[] DisablesMods => new Mods[] { Mods.Autopilot, Mods.Autoplay, Mods.Cinema, Mods.NoFail, Mods.SuddenDeath, Mods.Perfect }; + } + + public abstract class ModHalfTime : Mod + { + public override Mods Name => Mods.HalfTime; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime; + public override string Description => @"Less zoom"; + public override bool Ranked => true; + public override Mods[] DisablesMods => new Mods[] { Mods.DoubleTime, Mods.Nightcore }; + } + + public abstract class ModNightcore : ModDoubleTime + { + public override Mods Name => Mods.Nightcore; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore; + public override string Description => @"uguuuuuuuu"; + } + + public abstract class ModFlashlight : Mod + { + public override Mods Name => Mods.Flashlight; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight; + public override string Description => @"Restricted view area."; + public override bool Ranked => true; + } + + public class ModAutoplay : Mod + { + public override Mods Name => Mods.Autoplay; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto; + public override string Description => @"Watch a perfect automated play through the song"; + public override double ScoreMultiplier => 0; + public override bool Ranked => false; + public override Mods[] DisablesMods => new Mods[] { Mods.Relax, Mods.Autopilot, Mods.SpunOut, Mods.SuddenDeath, Mods.Perfect }; + } + + public abstract class ModPerfect : ModSuddenDeath + { + public override Mods Name => Mods.Perfect; + public override FontAwesome Icon => FontAwesome.fa_close; + public override string Description => @"SS or quit."; + } + + public class ModCinema : ModAutoplay + { + public override Mods Name => Mods.Cinema; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; + } + + [Flags] + public enum Mods + { + None = 0, + + [Description(@"No Fail")] + NoFail = 1 << 0, + + [Description(@"Easy")] + Easy = 1 << 1, + + //NoVideo = 1 << 2, + + [Description(@"Hidden")] + Hidden = 1 << 3, + + [Description(@"Hard Rock")] + HardRock = 1 << 4, + + [Description(@"Sudden Death")] + SuddenDeath = 1 << 5, + + [Description(@"Double Time")] + DoubleTime = 1 << 6, + + [Description(@"Relax")] + Relax = 1 << 7, + + [Description(@"Halftime")] + HalfTime = 1 << 8, + + [Description(@"Nightcore")] + Nightcore = 1 << 9, + + [Description(@"Flashlight")] + Flashlight = 1 << 10, + + [Description(@"Auto")] + Autoplay = 1 << 11, + + [Description(@"Spun Out")] + SpunOut = 1 << 12, + + [Description(@"Autopilot")] + Autopilot = 1 << 13, + + [Description(@"Perfect")] + Perfect = 1 << 14, + + [Description(@"4K")] + Key4 = 1 << 15, + + [Description(@"5K")] + Key5 = 1 << 16, + + [Description(@"6K")] + Key6 = 1 << 17, + + [Description(@"7K")] + Key7 = 1 << 18, + + [Description(@"8K")] + Key8 = 1 << 19, + + [Description(@"Fade In")] + FadeIn = 1 << 20, + + [Description(@"Random")] + Random = 1 << 21, + + [Description(@"Cinema")] + Cinema = 1 << 22, + + [Description(@"Target Practice")] + Target = 1 << 23, + + [Description(@"9K")] + Key9 = 1 << 24, + + [Description(@"Co-Op")] + KeyCoop = 1 << 25, + + [Description(@"1K")] + Key1 = 1 << 26, + + [Description(@"3K")] + Key3 = 1 << 27, + + [Description(@"2K")] + Key2 = 1 << 28, + + LastMod = 1 << 29, + + KeyMod = Key1 | Key2 | Key3 | Key4 | Key5 | Key6 | Key7 | Key8 | Key9 | KeyCoop, + FreeModAllowed = NoFail | Easy | Hidden | HardRock | SuddenDeath | Flashlight | FadeIn | Relax | Autopilot | SpunOut | KeyMod, + ScoreIncreaseMods = Hidden | HardRock | DoubleTime | Flashlight | FadeIn + } + + public enum ModType + { + DifficultyReduction, + DifficultyIncrease, + Special, + } +} diff --git a/osu.Game/Modes/Objects/HitObjectParser.cs b/osu.Game/Modes/Objects/HitObjectParser.cs index 4020c1c069..5aa9f08589 100644 --- a/osu.Game/Modes/Objects/HitObjectParser.cs +++ b/osu.Game/Modes/Objects/HitObjectParser.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Modes.Objects { public abstract class HitObjectParser diff --git a/osu.Game/Modes/Objects/NullHitObjectParser.cs b/osu.Game/Modes/Objects/NullHitObjectParser.cs index 4f06d5ab26..fdec989632 100644 --- a/osu.Game/Modes/Objects/NullHitObjectParser.cs +++ b/osu.Game/Modes/Objects/NullHitObjectParser.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Modes.Objects { /// diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 9bff12d0ef..2b665dff17 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -9,6 +9,7 @@ using System.Collections.Concurrent; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Overlays.Mods; namespace osu.Game.Modes { @@ -27,6 +28,8 @@ namespace osu.Game.Modes public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; + public abstract IEnumerable GetModsFor(ModType type); + public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount); public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap, InputManager input = null); diff --git a/osu.Game/Modes/Score.cs b/osu.Game/Modes/Score.cs index df1b65872f..8da09cb974 100644 --- a/osu.Game/Modes/Score.cs +++ b/osu.Game/Modes/Score.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Modes { public class Score diff --git a/osu.Game/Modes/UI/HealthDisplay.cs b/osu.Game/Modes/UI/HealthDisplay.cs index 5201a58104..c8d45bac36 100644 --- a/osu.Game/Modes/UI/HealthDisplay.cs +++ b/osu.Game/Modes/UI/HealthDisplay.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using osu.Framework.Allocation; diff --git a/osu.Game/Modes/UI/ModIcon.cs b/osu.Game/Modes/UI/ModIcon.cs new file mode 100644 index 0000000000..232c12bfc6 --- /dev/null +++ b/osu.Game/Modes/UI/ModIcon.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2007-2016 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; + +namespace osu.Game.Modes.UI +{ + public class ModIcon : Container + { + private TextAwesome modIcon, background; + + private float iconSize = 80; + public float IconSize + { + get + { + return iconSize; + } + set + { + iconSize = value; + reapplySize(); + } + } + + private Color4 backgroundColour; + new public Color4 Colour + { + get + { + return backgroundColour; + } + set + { + backgroundColour = value; + background.Colour = value; + } + } + + private FontAwesome icon; + public FontAwesome Icon + { + get + { + return icon; + } + set + { + icon = value; + modIcon.Icon = value; + } + } + + private void reapplySize() + { + background.TextSize = iconSize; + modIcon.TextSize = iconSize - 35; + } + + public ModIcon() + { + Children = new Drawable[] + { + background = new TextAwesome + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Icon = FontAwesome.fa_osu_mod_bg, + Shadow = true, + }, + modIcon = new TextAwesome + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = OsuColour.Gray(84), + }, + }; + + reapplySize(); + } + } +} \ No newline at end of file diff --git a/osu.Game/Modes/UI/ScoreOverlay.cs b/osu.Game/Modes/UI/ScoreOverlay.cs index e43009adda..d9283c52cb 100644 --- a/osu.Game/Modes/UI/ScoreOverlay.cs +++ b/osu.Game/Modes/UI/ScoreOverlay.cs @@ -9,6 +9,9 @@ using osu.Game.Modes.Objects; using OpenTK; using osu.Framework.Graphics.Primitives; using osu.Game.Screens.Play; +using osu.Framework.Allocation; +using osu.Game.Configuration; +using osu.Framework.Configuration; namespace osu.Game.Modes.UI { @@ -21,6 +24,8 @@ namespace osu.Game.Modes.UI public HealthDisplay HealthDisplay; public Score Score { get; set; } + private Bindable showKeyCounter; + protected abstract KeyCounterCollection CreateKeyCounter(); protected abstract ComboCounter CreateComboCounter(); protected abstract PercentageCounter CreateAccuracyCounter(); @@ -58,6 +63,22 @@ namespace osu.Game.Modes.UI }; } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + showKeyCounter = config.GetBindable(OsuConfig.KeyOverlay); + showKeyCounter.ValueChanged += visibilityChanged; + showKeyCounter.TriggerChange(); + } + + private void visibilityChanged(object sender, EventArgs e) + { + if (showKeyCounter) + KeyCounter.Show(); + else + KeyCounter.Hide(); + } + public void BindProcessor(ScoreProcessor processor) { //bind processor bindables to combocounter, score display etc. diff --git a/osu.Game/Online/API/IOnlineComponent.cs b/osu.Game/Online/API/IOnlineComponent.cs index a4d25613b8..d213f5a0fc 100644 --- a/osu.Game/Online/API/IOnlineComponent.cs +++ b/osu.Game/Online/API/IOnlineComponent.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Online.API { public interface IOnlineComponent diff --git a/osu.Game/Online/Chat/Drawables/DrawableChannel.cs b/osu.Game/Online/Chat/Drawables/DrawableChannel.cs index 84d15967e4..b8eb1e466a 100644 --- a/osu.Game/Online/Chat/Drawables/DrawableChannel.cs +++ b/osu.Game/Online/Chat/Drawables/DrawableChannel.cs @@ -15,7 +15,7 @@ namespace osu.Game.Online.Chat.Drawables public class DrawableChannel : Container { private readonly Channel channel; - private FlowContainer flow; + private FillFlowContainer flow; private ScrollContainer scroll; public DrawableChannel(Channel channel) @@ -39,9 +39,9 @@ namespace osu.Game.Online.Chat.Drawables RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - flow = new FlowContainer + flow = new FillFlowContainer { - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Left = 20, Right = 20 } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bfb506fd4f..9f7e3e04dc 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -42,6 +42,8 @@ namespace osu.Game private NotificationManager notificationManager; + private DialogOverlay dialogOverlay; + private Intro intro { get @@ -142,6 +144,11 @@ namespace osu.Game Origin = Anchor.TopRight, }).LoadAsync(this, overlayContent.Add); + (dialogOverlay = new DialogOverlay + { + Depth = -4, + }).LoadAsync(this, overlayContent.Add); + Logger.NewEntry += entry => { if (entry.Level < LogLevel.Important) return; @@ -155,6 +162,7 @@ namespace osu.Game Dependencies.Cache(options); Dependencies.Cache(musicController); Dependencies.Cache(notificationManager); + Dependencies.Cache(dialogOverlay); (Toolbar = new Toolbar { diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs new file mode 100644 index 0000000000..e307b44bb1 --- /dev/null +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -0,0 +1,246 @@ +// Copyright (c) 2007-2017 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.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialog : FocusedOverlayContainer + { + public static readonly float ENTER_DURATION = 500; + public static readonly float EXIT_DURATION = 200; + private readonly Vector2 ringSize = new Vector2(100f); + private readonly Vector2 ringMinifiedSize = new Vector2(20f); + private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); + + private Container content, ring; + private FillFlowContainer buttonsContainer; + private TextAwesome iconText; + private SpriteText header, body; + + public FontAwesome Icon + { + get { return iconText.Icon; } + set { iconText.Icon = value; } + } + + public string HeaderText + { + get { return header.Text; } + set { header.Text = value; } + } + + public string BodyText + { + get { return body.Text; } + set { body.Text = value; } + } + + public IEnumerable Buttons + { + get { return buttonsContainer.Children; } + set + { + buttonsContainer.Children = value; + foreach (PopupDialogButton b in value) + { + var action = b.Action; + b.Action = () => + { + Hide(); + action?.Invoke(); + }; + } + } + } + + private void pressButtonAtIndex(int index) + { + if (index < Buttons.Count()) + Buttons.Skip(index).First().TriggerClick(); + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) return false; + + if (args.Key == Key.Enter) + { + Buttons.OfType().FirstOrDefault()?.TriggerClick(); + return true; + } + + // press button at number if 1-9 on number row or keypad are pressed + var k = args.Key; + if (k >= Key.Number1 && k <= Key.Number9) + { + pressButtonAtIndex(k - Key.Number1); + return true; + } + + if (k >= Key.Keypad1 && k <= Key.Keypad9) + { + pressButtonAtIndex(k - Key.Keypad1); + return true; + } + + return base.OnKeyDown(state, args); + } + + protected override void PopIn() + { + base.PopIn(); + + // Reset various animations but only if the dialog animation fully completed + if (content.Alpha == 0) + { + buttonsContainer.TransformSpacingTo(buttonsEnterSpacing); + buttonsContainer.MoveToY(buttonsEnterSpacing.Y); + ring.ResizeTo(ringMinifiedSize); + } + + content.FadeIn(ENTER_DURATION, EasingTypes.OutQuint); + ring.ResizeTo(ringSize, ENTER_DURATION, EasingTypes.OutQuint); + buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, EasingTypes.OutQuint); + buttonsContainer.MoveToY(0, ENTER_DURATION, EasingTypes.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + + content.FadeOut(EXIT_DURATION, EasingTypes.InSine); + } + + public PopupDialog() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Width = 0.4f, + Alpha = 0f, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.5f), + Radius = 8, + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"221a21"), + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourLight = OsuColour.FromHex(@"271e26"), + ColourDark = OsuColour.FromHex(@"1e171e"), + TriangleScale = 4, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Position = new Vector2(0f, -50f), + Direction = FillDirection.Down, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + new Container + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Size = ringSize, + Margin = new MarginPadding + { + Bottom = 30, + }, + Children = new Drawable[] + { + ring = new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + BorderColour = Color4.White, + BorderThickness = 5f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0), + }, + iconText = new TextAwesome + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Icon = FontAwesome.fa_close, + TextSize = 50, + }, + }, + }, + }, + }, + header = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Text = @"Header", + TextSize = 25, + Shadow = true, + }, + body = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Text = @"Body", + TextSize = 18, + Shadow = true, + }, + }, + }, + buttonsContainer = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Down, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialogButton.cs b/osu.Game/Overlays/Dialog/PopupDialogButton.cs new file mode 100644 index 0000000000..fe86423d8f --- /dev/null +++ b/osu.Game/Overlays/Dialog/PopupDialogButton.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogButton : DialogButton + { + public PopupDialogButton() + { + Height = 50; + BackgroundColour = OsuColour.FromHex(@"150e14"); + TextSize = 18; + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs b/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs new file mode 100644 index 0000000000..1449577b21 --- /dev/null +++ b/osu.Game/Overlays/Dialog/PopupDialogCancelButton.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogCancelButton : PopupDialogButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + ButtonColour = colours.Blue; + SampleHover = audio.Sample.Get(@"Menu/menuclick"); + SampleClick = audio.Sample.Get(@"Menu/menuback"); + } + } +} diff --git a/osu.Game/Overlays/Dialog/PopupDialogOKButton.cs b/osu.Game/Overlays/Dialog/PopupDialogOKButton.cs new file mode 100644 index 0000000000..46bf3debc4 --- /dev/null +++ b/osu.Game/Overlays/Dialog/PopupDialogOKButton.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogOkButton : PopupDialogButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + ButtonColour = colours.Pink; + SampleHover = audio.Sample.Get(@"Menu/menuclick"); + SampleClick = audio.Sample.Get(@"Menu/menu-play-click"); + } + } +} diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs new file mode 100644 index 0000000000..95f08e9e24 --- /dev/null +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; +using OpenTK.Graphics; + +namespace osu.Game.Overlays +{ + public class DialogOverlay : FocusedOverlayContainer + { + private Container dialogContainer; + private PopupDialog currentDialog; + + public void Push(PopupDialog dialog) + { + if (dialog == currentDialog) return; + + currentDialog?.Hide(); + currentDialog = dialog; + + dialogContainer.Add(currentDialog); + + currentDialog.Show(); + currentDialog.StateChanged += onDialogOnStateChanged; + State = Visibility.Visible; + } + + private void onDialogOnStateChanged(OverlayContainer dialog, Visibility v) + { + if (v != Visibility.Hidden) return; + + //handle the dialog being dismissed. + dialog.Delay(PopupDialog.EXIT_DURATION); + dialog.Expire(); + + if (dialog == currentDialog) + State = Visibility.Hidden; + } + + protected override void PopIn() + { + base.PopIn(); + FadeIn(PopupDialog.ENTER_DURATION, EasingTypes.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + FadeOut(PopupDialog.EXIT_DURATION, EasingTypes.InSine); + } + + public DialogOverlay() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + }, + }, + dialogContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Mods/AssistedSection.cs b/osu.Game/Overlays/Mods/AssistedSection.cs new file mode 100644 index 0000000000..86065fb7e7 --- /dev/null +++ b/osu.Game/Overlays/Mods/AssistedSection.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Mods +{ + public class AssistedSection : ModSection + { + protected override Key[] ToggleKeys => new Key[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Blue; + SelectedColour = colours.BlueLight; + } + + public AssistedSection() + { + Header = @"Assisted"; + } + } +} diff --git a/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs b/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs new file mode 100644 index 0000000000..62237812c4 --- /dev/null +++ b/osu.Game/Overlays/Mods/DifficultyIncreaseSection.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Mods +{ + public class DifficultyIncreaseSection : ModSection + { + protected override Key[] ToggleKeys => new Key[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Yellow; + SelectedColour = colours.YellowLight; + } + + public DifficultyIncreaseSection() + { + Header = @"Gameplay Difficulty Increase"; + } + } +} diff --git a/osu.Game/Overlays/Mods/DifficultyReductionSection.cs b/osu.Game/Overlays/Mods/DifficultyReductionSection.cs new file mode 100644 index 0000000000..f130617701 --- /dev/null +++ b/osu.Game/Overlays/Mods/DifficultyReductionSection.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Mods +{ + public class DifficultyReductionSection : ModSection + { + protected override Key[] ToggleKeys => new Key[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = colours.Green; + SelectedColour = colours.GreenLight; + } + + public DifficultyReductionSection() + { + Header = @"Gameplay Difficulty Reduction"; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs new file mode 100644 index 0000000000..811e9606fe --- /dev/null +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -0,0 +1,271 @@ +// Copyright (c) 2007-2016 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using osu.Game.Modes; +using osu.Game.Modes.UI; + +namespace osu.Game.Overlays.Mods +{ + public class ModButton : FillFlowContainer + { + private ModIcon[] icons; + private ModIcon displayIcon => icons[icons.Length - 1]; + private SpriteText text; + private Container iconsContainer; + private SampleChannel sampleOn, sampleOff; + + public Action Action; // Passed the selected mod or null if none + + private int _selectedMod = -1; + private int selectedMod + { + get + { + return _selectedMod; + } + set + { + if (value == _selectedMod) return; + _selectedMod = value; + + if (value >= Mods.Length) + { + _selectedMod = -1; + } + else if (value <= -2) + { + _selectedMod = Mods.Length - 1; + } + + iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic); + iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic); + for (int i = 0; i < icons.Length; i++) + { + if (Selected && i == icons.Length - 1) icons[i].Colour = SelectedColour; + else icons[i].Colour = Colour; + } + + displaySelectedMod(); + } + } + + public bool Selected => selectedMod != -1; + + private Color4 backgroundColour; + public new Color4 Colour + { + get + { + return backgroundColour; + } + set + { + if (value == backgroundColour) return; + backgroundColour = value; + foreach (ModIcon icon in icons) + { + icon.Colour = value; + } + } + } + + private Color4 selectedColour; + public Color4 SelectedColour + { + get + { + return selectedColour; + } + set + { + if (value == selectedColour) return; + selectedColour = value; + if (Selected) icons[0].Colour = value; + } + } + + private Mod mod; + public Mod Mod + { + get + { + return mod; + } + set + { + if (mod == value) return; + mod = value; + + if (mod is MultiMod) + { + mods = ((MultiMod)mod).Mods; + } + else + { + mods = new Mod[] { mod }; + } + + createIcons(); + if (mods.Length > 0) + { + displayMod(mods[0]); + } + } + } + + private Mod[] mods; + public Mod[] Mods => mods; // the mods from Mod, only multiple if Mod is a MultiMod + + public Mod SelectedMod => Mods.ElementAtOrDefault(selectedMod); + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleOn = audio.Sample.Get(@"Checkbox/check-on"); + sampleOff = audio.Sample.Get(@"Checkbox/check-off"); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + (args.Button == MouseButton.Right ? (Action)SelectPrevious : SelectNext)(); + return true; + } + + public void SelectNext() + { + selectedMod++; + if (selectedMod == -1) + { + sampleOff.Play(); + } + else + { + sampleOn.Play(); + } + + Action?.Invoke(SelectedMod); + } + + public void SelectPrevious() + { + selectedMod--; + if (selectedMod == -1) + { + sampleOff.Play(); + } + else + { + sampleOn.Play(); + } + + Action?.Invoke(SelectedMod); + } + + public void Deselect() + { + selectedMod = -1; + } + + private void displayMod(Mod mod) + { + displayIcon.Icon = mod.Icon; + text.Text = mod.Name.GetDescription(); + } + + private void displaySelectedMod() + { + var modIndex = selectedMod; + if (modIndex <= -1) + { + modIndex = 0; + } + + displayMod(Mods[modIndex]); + } + + private void createIcons() + { + if (Mods.Length > 1) + { + iconsContainer.Add(icons = new ModIcon[] + { + new ModIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Position = new Vector2(1.5f), + }, + new ModIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Position = new Vector2(-1.5f), + }, + }); + } + else + { + iconsContainer.Add(icons = new ModIcon[] + { + new ModIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + AutoSizeAxes = Axes.Both, + }, + }); + } + } + + public ModButton(Mod m) + { + Direction = FillDirection.Down; + Spacing = new Vector2(0f, -5f); + Size = new Vector2(100f); + + Children = new Drawable[] + { + new Container + { + Size = new Vector2(77f, 80f), + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + iconsContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + } + }, + text = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + TextSize = 18, + }, + }; + + Mod = m; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs new file mode 100644 index 0000000000..3bd91e2c80 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -0,0 +1,176 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; +using osu.Game.Modes; + +namespace osu.Game.Overlays.Mods +{ + class AlwaysPresentFlowContainer : FillFlowContainer + { + public override bool IsPresent => true; + } + + public class ModSection : Container + { + private OsuSpriteText headerLabel; + + private AlwaysPresentFlowContainer buttonsContainer; + public FillFlowContainer ButtonsContainer => buttonsContainer; + + public Action Action; + protected virtual Key[] ToggleKeys => new Key[] { }; + + public Mod[] SelectedMods + { + get + { + List selectedMods = new List(); + + foreach (ModButton button in Buttons) + { + Mod selectedMod = button.SelectedMod; + if (selectedMod != null) + selectedMods.Add(selectedMod); + } + + return selectedMods.ToArray(); + } + } + + private string header; + public string Header + { + get + { + return header; + } + set + { + header = value; + headerLabel.Text = value; + } + } + + private ModButton[] buttons = {}; + public ModButton[] Buttons + { + get + { + return buttons; + } + set + { + if (value == buttons) return; + buttons = value; + + foreach (ModButton button in value) + { + button.Colour = Colour; + button.SelectedColour = selectedColour; + button.Action = buttonPressed; + } + + buttonsContainer.Add(value); + } + } + + private Color4 colour = Color4.White; + new public Color4 Colour + { + get + { + return colour; + } + set + { + if (value == colour) return; + colour = value; + + foreach (ModButton button in buttons) + { + button.Colour = value; + } + } + } + + private Color4 selectedColour = Color4.White; + public Color4 SelectedColour + { + get + { + return selectedColour; + } + set + { + if (value == selectedColour) return; + selectedColour = value; + + foreach (ModButton button in buttons) + { + button.SelectedColour = value; + } + } + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + var index = Array.IndexOf(ToggleKeys, args.Key); + if (index > -1 && index < Buttons.Length) + Buttons[index].SelectNext(); + + return base.OnKeyDown(state, args); + } + + public void DeselectAll() + { + foreach (ModButton button in buttons) + { + button.Deselect(); + } + } + + private void buttonPressed(Mod mod) + { + Action?.Invoke(mod); + } + + public ModSection() + { + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + headerLabel = new OsuSpriteText + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + Position = new Vector2(0f, 0f), + Font = @"Exo2.0-Bold", + Text = Header, + }, + buttonsContainer = new AlwaysPresentFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Spacing = new Vector2(50f, 0f), + Margin = new MarginPadding + { + Top = 6, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs new file mode 100644 index 0000000000..a969d71347 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -0,0 +1,365 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Modes; + +namespace osu.Game.Overlays.Mods +{ + public class ModSelectOverlay : WaveOverlayContainer + { + private const int button_duration = 700; + private const int ranked_multiplier_duration = 700; + private const float content_width = 0.8f; + + private Color4 lowMultiplierColour, highMultiplierColour; + + private OsuSpriteText rankedLabel, multiplierLabel; + private FillFlowContainer rankedMultiplerContainer; + + private FillFlowContainer modSectionsContainer; + + public readonly Bindable SelectedMods = new Bindable(); + + public readonly Bindable PlayMode = new Bindable(); + + private void modeChanged(object sender, EventArgs eventArgs) + { + var ruleset = Ruleset.GetRuleset(PlayMode); + + modSectionsContainer.Children = new ModSection[] + { + new DifficultyReductionSection + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Action = modButtonPressed, + Buttons = ruleset.GetModsFor(ModType.DifficultyReduction).Select(m => new ModButton(m)).ToArray(), + }, + new DifficultyIncreaseSection + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Action = modButtonPressed, + Buttons = ruleset.GetModsFor(ModType.DifficultyIncrease).Select(m => new ModButton(m)).ToArray(), + }, + new AssistedSection + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Action = modButtonPressed, + Buttons = ruleset.GetModsFor(ModType.Special).Select(m => new ModButton(m)).ToArray(), + }, + }; + } + + [BackgroundDependencyLoader(permitNulls:true)] + private void load(OsuColour colours, OsuGame osu) + { + lowMultiplierColour = colours.Red; + highMultiplierColour = colours.Green; + + if (osu != null) + PlayMode.BindTo(osu.PlayMode); + PlayMode.ValueChanged += modeChanged; + PlayMode.TriggerChange(); + } + + protected override void PopOut() + { + base.PopOut(); + + rankedMultiplerContainer.MoveToX(rankedMultiplerContainer.DrawSize.X, APPEAR_DURATION, EasingTypes.InSine); + rankedMultiplerContainer.FadeOut(APPEAR_DURATION, EasingTypes.InSine); + + foreach (ModSection section in modSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), APPEAR_DURATION, EasingTypes.InSine); + section.ButtonsContainer.MoveToX(100f, APPEAR_DURATION, EasingTypes.InSine); + section.ButtonsContainer.FadeOut(APPEAR_DURATION, EasingTypes.InSine); + } + } + + protected override void PopIn() + { + base.PopIn(); + + rankedMultiplerContainer.MoveToX(0, ranked_multiplier_duration, EasingTypes.OutQuint); + rankedMultiplerContainer.FadeIn(ranked_multiplier_duration, EasingTypes.OutQuint); + + foreach (ModSection section in modSectionsContainer.Children) + { + section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), button_duration, EasingTypes.OutQuint); + section.ButtonsContainer.MoveToX(0, button_duration, EasingTypes.OutQuint); + section.ButtonsContainer.FadeIn(button_duration, EasingTypes.OutQuint); + } + } + + public void DeselectAll() + { + foreach (ModSection section in modSectionsContainer.Children) + { + foreach (ModButton button in section.Buttons) + { + button.Deselect(); + } + } + } + + public void DeselectMod(Modes.Mods modName) + { + foreach (ModSection section in modSectionsContainer.Children) + { + foreach (ModButton button in section.Buttons) + { + foreach (Mod mod in button.Mods) + { + if (mod.Name == modName) + { + button.Deselect(); + return; + } + } + } + } + } + + private void modButtonPressed(Mod selectedMod) + { + if (selectedMod != null) + { + foreach (Modes.Mods disableMod in selectedMod.DisablesMods) + { + DeselectMod(disableMod); + } + } + + refreshSelectedMods(); + + double multiplier = 1.0; + bool ranked = true; + + foreach (Mod mod in SelectedMods.Value) + { + multiplier *= mod.ScoreMultiplier; + + if (ranked) + ranked = mod.Ranked; + } + + // 1.00x + // 1.05x + // 1.20x + + multiplierLabel.Text = string.Format("{0:N2}x", multiplier); + string rankedString = ranked ? "Ranked" : "Unranked"; + rankedLabel.Text = $@"{rankedString}, Score Multiplier: "; + + if (multiplier > 1.0) + { + multiplierLabel.FadeColour(highMultiplierColour, 200); + } + else if (multiplier < 1.0) + { + multiplierLabel.FadeColour(lowMultiplierColour, 200); + } + else + { + multiplierLabel.FadeColour(Color4.White, 200); + } + } + + private void refreshSelectedMods() + { + List selectedMods = new List(); + + foreach (ModSection section in modSectionsContainer.Children) + { + foreach (Mod mod in section.SelectedMods) + { + selectedMods.Add(mod); + } + } + + SelectedMods.Value = selectedMods.ToArray(); + } + + public ModSelectOverlay() + { + FirstWaveColour = OsuColour.FromHex(@"19b0e2"); + SecondWaveColour = OsuColour.FromHex(@"2280a2"); + ThirdWaveColour = OsuColour.FromHex(@"005774"); + FourthWaveColour = OsuColour.FromHex(@"003a4e"); + + Height = 510; + Content.RelativeSizeAxes = Axes.X; + Content.AutoSizeAxes = Axes.Y; + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(36, 50, 68, 255) + }, + new Triangles + { + TriangleScale = 5, + RelativeSizeAxes = Axes.X, + Height = Height, //set the height from the start to ensure correct triangle density. + ColourLight = new Color4(53, 66, 82, 255), + ColourDark = new Color4(41, 54, 70, 255), + }, + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Direction = FillDirection.Down, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + // Header + new Container + { + RelativeSizeAxes = Axes.X, + Height = 82, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(10).Opacity(100), + }, + new FillFlowContainer + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Down, + Width = content_width, + Padding = new MarginPadding + { + Top = 10, + Bottom = 10, + }, + Children = new Drawable[] + { + new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Text = @"Gameplay Mods", + TextSize = 22, + Shadow = true, + Margin = new MarginPadding + { + Bottom = 4, + }, + }, + new OsuSpriteText + { + Text = @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.", + TextSize = 18, + Shadow = true, + }, + new OsuSpriteText + { + Text = @"Others are just for fun", + TextSize = 18, + Shadow = true, + }, + }, + }, + }, + }, + // Body + modSectionsContainer = new FillFlowContainer + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 10f), + Width = content_width, + }, + // Footer + new Container + { + RelativeSizeAxes = Axes.X, + Height = 70, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(172, 20, 116, 255), + Alpha = 0.5f, + }, + rankedMultiplerContainer = new FillFlowContainer + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Width = content_width, + Direction = FillDirection.Right, + Padding = new MarginPadding + { + Top = 20, + Bottom = 20, + }, + Children = new Drawable[] + { + rankedLabel = new OsuSpriteText + { + Text = @"Ranked, Score Multiplier: ", + TextSize = 30, + Shadow = true, + }, + multiplierLabel = new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Text = @"1.00x", + TextSize = 30, + Shadow = true, + }, + }, + }, + }, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/NotificationManager.cs b/osu.Game/Overlays/NotificationManager.cs index 77198a7fbe..5c666da8ae 100644 --- a/osu.Game/Overlays/NotificationManager.cs +++ b/osu.Game/Overlays/NotificationManager.cs @@ -44,9 +44,9 @@ namespace osu.Game.Overlays Margin = new MarginPadding { Top = Toolbar.Toolbar.HEIGHT }, Children = new[] { - sections = new FlowContainer + sections = new FillFlowContainer { - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Children = new [] diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index e5a597e387..91c89d41be 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -16,7 +16,7 @@ using OpenTK; namespace osu.Game.Overlays.Notifications { - public class NotificationSection : FlowContainer + public class NotificationSection : FillFlowContainer { private OsuSpriteText titleText; private OsuSpriteText countText; @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Notifications { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Direction = FlowDirections.Vertical; + Direction = FillDirection.Down; Padding = new MarginPadding { @@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Notifications Origin = Anchor.TopRight, Action = clearAll }, - new FlowContainer + new FillFlowContainer { Margin = new MarginPadding { @@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Notifications }, }, }, - notifications = new FlowContainer + notifications = new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Options/OptionDropDown.cs b/osu.Game/Overlays/Options/OptionDropDown.cs index 72a5160ba0..7e2cab2a61 100644 --- a/osu.Game/Overlays/Options/OptionDropDown.cs +++ b/osu.Game/Overlays/Options/OptionDropDown.cs @@ -14,7 +14,7 @@ using System.Collections.Generic; namespace osu.Game.Overlays.Options { - public class OptionDropDown : FlowContainer + public class OptionDropDown : FillFlowContainer { private DropDownMenu dropdown; private SpriteText text; @@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Options { Items = new KeyValuePair[0]; - Direction = FlowDirections.Vertical; + Direction = FillDirection.Down; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Children = new Drawable[] diff --git a/osu.Game/Overlays/Options/OptionSlider.cs b/osu.Game/Overlays/Options/OptionSlider.cs index 0e5d9f915c..2f09c51655 100644 --- a/osu.Game/Overlays/Options/OptionSlider.cs +++ b/osu.Game/Overlays/Options/OptionSlider.cs @@ -12,7 +12,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Options { - public class OptionSlider : FlowContainer where T : struct + public class OptionSlider : FillFlowContainer where T : struct { private SliderBar slider; private SpriteText text; @@ -40,7 +40,6 @@ namespace osu.Game.Overlays.Options public OptionSlider() { - Direction = FlowDirections.Vertical; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Padding = new MarginPadding { Right = 5 }; diff --git a/osu.Game/Overlays/Options/OptionsSection.cs b/osu.Game/Overlays/Options/OptionsSection.cs index 61cfa868ec..f87e69e307 100644 --- a/osu.Game/Overlays/Options/OptionsSection.cs +++ b/osu.Game/Overlays/Options/OptionsSection.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Options { public abstract class OptionsSection : Container { - protected FlowContainer FlowContent; + protected FillFlowContainer FlowContent; protected override Container Content => FlowContent; public abstract FontAwesome Icon { get; } @@ -58,10 +58,10 @@ namespace osu.Game.Overlays.Options TextSize = header_size, Text = Header, }, - FlowContent = new FlowContainer + FlowContent = new FillFlowContainer { Margin = new MarginPadding { Top = header_size + header_margin }, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Spacing = new Vector2(0, 30), AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Options/OptionsSubsection.cs b/osu.Game/Overlays/Options/OptionsSubsection.cs index 4d8be559a7..ef10e15abf 100644 --- a/osu.Game/Overlays/Options/OptionsSubsection.cs +++ b/osu.Game/Overlays/Options/OptionsSubsection.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Options { - public abstract class OptionsSubsection : FlowContainer + public abstract class OptionsSubsection : FillFlowContainer { private Container content; protected override Container Content => content; @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Options { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Direction = FlowDirections.Vertical; + Direction = FillDirection.Down; AddInternal(new Drawable[] { new OsuSpriteText @@ -29,12 +29,12 @@ namespace osu.Game.Overlays.Options Margin = new MarginPadding { Bottom = 10 }, Font = @"Exo2.0-Black", }, - content = new FlowContainer + content = new FillFlowContainer { - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, + Spacing = new Vector2(0, 5), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Spacing = new Vector2(0, 5), }, }); } diff --git a/osu.Game/Overlays/Options/Sections/EditorSection.cs b/osu.Game/Overlays/Options/Sections/EditorSection.cs index 513d89c601..11d3f6b642 100644 --- a/osu.Game/Overlays/Options/Sections/EditorSection.cs +++ b/osu.Game/Overlays/Options/Sections/EditorSection.cs @@ -7,6 +7,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; +using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Options.Sections { diff --git a/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs b/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs index 34f42aac43..db799ccae2 100644 --- a/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs +++ b/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Options.Sections.General } } - class LoginForm : FlowContainer + class LoginForm : FillFlowContainer { private TextBox username; private TextBox password; @@ -100,10 +100,10 @@ namespace osu.Game.Overlays.Options.Sections.General private void load(APIAccess api, OsuConfigManager config) { this.api = api; - Direction = FlowDirections.Vertical; + Direction = FillDirection.Down; + Spacing = new Vector2(0, 5); AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; - Spacing = new Vector2(0, 5); Children = new Drawable[] { username = new OsuTextBox @@ -117,7 +117,8 @@ namespace osu.Game.Overlays.Options.Sections.General { PlaceholderText = "Password", RelativeSizeAxes = Axes.X, - TabbableContentContainer = this + TabbableContentContainer = this, + OnCommit = (TextBox sender, bool newText) => performLogin() }, new OsuCheckbox { diff --git a/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs index b69e085e6b..5393c42843 100644 --- a/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; +using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Options.Sections { diff --git a/osu.Game/Overlays/Options/Sections/SkinSection.cs b/osu.Game/Overlays/Options/Sections/SkinSection.cs index 56dd9ca318..71c460760e 100644 --- a/osu.Game/Overlays/Options/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Options/Sections/SkinSection.cs @@ -8,6 +8,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; +using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Options.Sections { diff --git a/osu.Game/Overlays/Options/Sidebar.cs b/osu.Game/Overlays/Options/Sidebar.cs index 362c448807..5a1128749c 100644 --- a/osu.Game/Overlays/Options/Sidebar.cs +++ b/osu.Game/Overlays/Options/Sidebar.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Options { public class Sidebar : Container { - private FlowContainer content; + private FillFlowContainer content; internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH; internal const int EXPANDED_WIDTH = 200; protected override Container Content => content; @@ -34,13 +34,13 @@ namespace osu.Game.Overlays.Options { Children = new [] { - content = new FlowContainer + content = new FillFlowContainer { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Direction = FlowDirections.Vertical + Direction = FillDirection.Down, } } }, diff --git a/osu.Game/Overlays/OptionsOverlay.cs b/osu.Game/Overlays/OptionsOverlay.cs index 6d47e628ea..968914c6c0 100644 --- a/osu.Game/Overlays/OptionsOverlay.cs +++ b/osu.Game/Overlays/OptionsOverlay.cs @@ -73,11 +73,11 @@ namespace osu.Game.Overlays Margin = new MarginPadding { Left = SIDEBAR_WIDTH }, Children = new[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Children = new Drawable[] { @@ -94,11 +94,11 @@ namespace osu.Game.Overlays TextSize = 18, Margin = new MarginPadding { Left = CONTENT_MARGINS, Bottom = 30 }, }, - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Children = sections, } } diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index caed663c04..948b9c96fc 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -41,9 +41,9 @@ namespace osu.Game.Overlays.Toolbar Children = new Drawable[] { new ToolbarBackground(), - new FlowContainer + new FillFlowContainer { - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Children = new Drawable[] @@ -55,7 +55,10 @@ namespace osu.Game.Overlays.Toolbar }, modeSelector = new ToolbarModeSelector { - OnPlayModeChange = OnPlayModeChange + OnPlayModeChange = (PlayMode mode) => + { + OnPlayModeChange?.Invoke(mode); + } } } }, @@ -63,7 +66,7 @@ namespace osu.Game.Overlays.Toolbar { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Children = new Drawable[] @@ -141,7 +144,7 @@ namespace osu.Game.Overlays.Toolbar FadeOut(transition_time); } - class PassThroughFlowContainer : FlowContainer + class PassThroughFlowContainer : FillFlowContainer { //needed to get input to the login overlay. public override bool Contains(Vector2 screenSpacePos) => true; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 9c3c1095a3..0e8165283e 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -62,10 +62,10 @@ namespace osu.Game.Overlays.Toolbar protected TextAwesome DrawableIcon; protected SpriteText DrawableText; protected Box HoverBackground; - private FlowContainer tooltipContainer; + private FillFlowContainer tooltipContainer; private SpriteText tooltip1; private SpriteText tooltip2; - protected FlowContainer Flow; + protected FillFlowContainer Flow; private SampleChannel sampleClick; public ToolbarButton() @@ -82,21 +82,21 @@ namespace osu.Game.Overlays.Toolbar BlendingMode = BlendingMode.Additive, Alpha = 0, }, - Flow = new FlowContainer + Flow = new FillFlowContainer { - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, + Spacing = new Vector2(5), Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Padding = new MarginPadding { Left = Toolbar.HEIGHT / 2, Right = Toolbar.HEIGHT / 2 }, - Spacing = new Vector2(5), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Children = new Drawable[] { DrawableIcon = new TextAwesome { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, DrawableText = new OsuSpriteText { @@ -105,9 +105,9 @@ namespace osu.Game.Overlays.Toolbar }, }, }, - tooltipContainer = new FlowContainer + tooltipContainer = new FillFlowContainer { - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize Anchor = (TooltipAnchor & Anchor.x0) > 0 ? Anchor.BottomLeft : Anchor.BottomRight, Origin = TooltipAnchor, diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index b3d1d29ae9..31b8500155 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Toolbar { const float padding = 10; - private FlowContainer modeButtons; + private FillFlowContainer modeButtons; private Drawable modeButtonLine; private ToolbarModeButton activeButton; @@ -32,11 +32,11 @@ namespace osu.Game.Overlays.Toolbar Children = new Drawable[] { new OpaqueBackground(), - modeButtons = new FlowContainer + modeButtons = new FillFlowContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Padding = new MarginPadding { Left = padding, Right = padding }, diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs new file mode 100644 index 0000000000..fba6244d15 --- /dev/null +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -0,0 +1,197 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework; +using OpenTK.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using OpenTK; +using osu.Framework.Graphics.Transforms; + +namespace osu.Game.Overlays +{ + public abstract class WaveOverlayContainer : FocusedOverlayContainer + { + protected const float APPEAR_DURATION = 800; + protected const float DISAPPEAR_DURATION = 500; + + private const EasingTypes easing_show = EasingTypes.OutSine; + private const EasingTypes easing_hide = EasingTypes.InSine; + + private Wave firstWave, secondWave, thirdWave, fourthWave; + + private Container wavesContainer; + + private readonly Container contentContainer; + + protected override Container Content => contentContainer; + + protected Color4 FirstWaveColour + { + get + { + return firstWave.Colour; + } + set + { + if (firstWave.Colour == value) return; + firstWave.Colour = value; + } + } + + protected Color4 SecondWaveColour + { + get + { + return secondWave.Colour; + } + set + { + if (secondWave.Colour == value) return; + secondWave.Colour = value; + } + } + + protected Color4 ThirdWaveColour + { + get + { + return thirdWave.Colour; + } + set + { + if (thirdWave.Colour == value) return; + thirdWave.Colour = value; + } + } + + protected Color4 FourthWaveColour + { + get + { + return fourthWave.Colour; + } + set + { + if (fourthWave.Colour == value) return; + fourthWave.Colour = value; + } + } + + protected WaveOverlayContainer() + { + Masking = true; + + AddInternal(wavesContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Children = new[] + { + firstWave = new Wave + { + Rotation = 13, + FinalPosition = -930, + }, + secondWave = new Wave + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Rotation = -7, + FinalPosition = -560, + }, + thirdWave = new Wave + { + Rotation = 4, + FinalPosition = -390, + }, + fourthWave = new Wave + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Rotation = -2, + FinalPosition = -220, + }, + }, + }); + + AddInternal(contentContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }); + } + + protected override void PopIn() + { + base.PopIn(); + + foreach (var w in wavesContainer.Children) + w.State = Visibility.Visible; + + contentContainer.FadeIn(APPEAR_DURATION, EasingTypes.OutQuint); + contentContainer.MoveToY(0, APPEAR_DURATION, EasingTypes.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + + contentContainer.FadeOut(DISAPPEAR_DURATION, EasingTypes.In); + contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, EasingTypes.In); + + foreach (var w in wavesContainer.Children) + w.State = Visibility.Hidden; + } + + private class Wave : Container, IStateful + { + public float FinalPosition; + + public Wave() + { + RelativeSizeAxes = Axes.Both; + Size = new Vector2(1.5f); + Masking = true; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(50), + Radius = 20f, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + }, + }; + } + + private Visibility state; + public Visibility State + { + get { return state; } + set + { + state = value; + + switch (value) + { + case Visibility.Hidden: + MoveToY(DrawHeight / Height, DISAPPEAR_DURATION, easing_hide); + break; + case Visibility.Visible: + MoveToY(FinalPosition, APPEAR_DURATION, easing_show); + break; + } + } + } + } + } +} diff --git a/osu.Game/Screens/GameScreenWhiteBox.cs b/osu.Game/Screens/GameScreenWhiteBox.cs index a172726e67..623b411e35 100644 --- a/osu.Game/Screens/GameScreenWhiteBox.cs +++ b/osu.Game/Screens/GameScreenWhiteBox.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens protected virtual IEnumerable PossibleChildren => null; - private FlowContainer childModeButtons; + private FillFlowContainer childModeButtons; private Container textContainer; private Box box; @@ -124,9 +124,9 @@ namespace osu.Game.Screens Exit(); } }, - childModeButtons = new FlowContainer + childModeButtons = new FillFlowContainer { - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index b1e39c8a35..e7b933388d 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Menu new OsuSpriteText { Shadow = true, - Direction = FlowDirections.Horizontal, + AllowMultiline = false, Anchor = Anchor.Centre, Origin = Anchor.Centre, TextSize = 16, @@ -257,7 +257,8 @@ namespace osu.Game.Screens.Menu protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - if (args.Repeat) return false; + if (args.Repeat || state.Keyboard.ControlPressed || state.Keyboard.ShiftPressed || state.Keyboard.AltPressed) + return false; if (triggerKey == args.Key && triggerKey != Key.Unknown) { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index c4d1abd931..f26fded19d 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -78,16 +78,16 @@ namespace osu.Game.Screens.Menu }, buttonFlow = new FlowContainerWithOrigin { - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, + Spacing = new Vector2(-WEDGE_WIDTH, 0), Anchor = Anchor.Centre, AutoSizeAxes = Axes.Both, - Spacing = new Vector2(-WEDGE_WIDTH, 0), Children = new[] { settingsButton = new Button(@"settings", @"options", FontAwesome.fa_gear, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), backButton = new Button(@"back", @"back", FontAwesome.fa_osu_left_o, new Color4(51, 58, 94, 255), onBack, -WEDGE_WIDTH), iconFacade = new Container //need a container to make the osu! icon flow properly. - { + { Size = new Vector2(0, BUTTON_AREA_HEIGHT) } }, diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 56074dbdfd..deb9a4dbf8 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -27,12 +27,12 @@ namespace osu.Game.Screens.Menu Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Spacing = new Vector2(0, 2), Children = new Drawable[] { diff --git a/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs b/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs index f8b92dc795..d0972ea84b 100644 --- a/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs +++ b/osu.Game/Screens/Menu/FlowContainerWithOrigin.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Menu /// /// A flow container with an origin based on one of its contained drawables. /// - public class FlowContainerWithOrigin : FlowContainer + public class FlowContainerWithOrigin : FillFlowContainer { /// /// A target drawable which this flowcontainer should be centered around. @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Menu protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); - protected override IEnumerable SortedChildren => base.SortedChildren.Reverse(); + protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); public override Anchor Origin => Anchor.Custom; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 3ef8eff457..ec9f3bc7cf 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -13,6 +13,9 @@ using osu.Game.Screens.Direct; using osu.Game.Screens.Multiplayer; using OpenTK; using osu.Game.Screens.Select; +using osu.Game.Screens.Tournament; +using osu.Framework.Input; +using OpenTK.Input; namespace osu.Game.Screens.Menu { @@ -97,5 +100,16 @@ namespace osu.Game.Screens.Menu Content.FadeOut(3000); return base.OnExiting(next); } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!args.Repeat && state.Keyboard.ControlPressed && state.Keyboard.ShiftPressed && args.Key == Key.D) + { + Push(new Drawings()); + return true; + } + + return base.OnKeyDown(state, args); + } } } diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index 8e7f7558d7..0a93032a20 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -8,11 +8,11 @@ using OpenTK.Graphics; namespace osu.Game.Screens.Play { - public class KeyCounterCollection : FlowContainer + public class KeyCounterCollection : FillFlowContainer { public KeyCounterCollection() { - Direction = FlowDirections.Horizontal; + Direction = FillDirection.Right; AutoSizeAxes = Axes.Both; } diff --git a/osu.Game/Screens/Play/ModSelect.cs b/osu.Game/Screens/Play/ModSelect.cs deleted file mode 100644 index 9d3db0221d..0000000000 --- a/osu.Game/Screens/Play/ModSelect.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Screens; -using osu.Game.Screens.Backgrounds; -using OpenTK.Graphics; - -namespace osu.Game.Screens.Play -{ - class ModSelect : ScreenWhiteBox - { - protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); - - protected override void OnEntering(Screen last) - { - base.OnEntering(last); - Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500)); - } - - protected override bool OnExiting(Screen next) - { - Background.Schedule(() => Background.FadeColour(Color4.White, 500)); - return base.OnExiting(next); - } - } -} diff --git a/osu.Game/Overlays/Pause/PauseProgressBar.cs b/osu.Game/Screens/Play/Pause/PauseProgressBar.cs similarity index 96% rename from osu.Game/Overlays/Pause/PauseProgressBar.cs rename to osu.Game/Screens/Play/Pause/PauseProgressBar.cs index bd32147e35..8d124706b1 100644 --- a/osu.Game/Overlays/Pause/PauseProgressBar.cs +++ b/osu.Game/Screens/Play/Pause/PauseProgressBar.cs @@ -1,15 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using OpenTK.Graphics; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Screens.Play.Pause { public class PauseProgressBar : Container { diff --git a/osu.Game/Overlays/Pause/PauseProgressGraph.cs b/osu.Game/Screens/Play/Pause/PauseProgressGraph.cs similarity index 86% rename from osu.Game/Overlays/Pause/PauseProgressGraph.cs rename to osu.Game/Screens/Play/Pause/PauseProgressGraph.cs index faee80a863..89b9b42b86 100644 --- a/osu.Game/Overlays/Pause/PauseProgressGraph.cs +++ b/osu.Game/Screens/Play/Pause/PauseProgressGraph.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics.Containers; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Screens.Play.Pause { public class PauseProgressGraph : Container { diff --git a/osu.Game/Overlays/Pause/QuitButton.cs b/osu.Game/Screens/Play/Pause/QuitButton.cs similarity index 82% rename from osu.Game/Overlays/Pause/QuitButton.cs rename to osu.Game/Screens/Play/Pause/QuitButton.cs index dc99669111..7b71b4b2fc 100644 --- a/osu.Game/Overlays/Pause/QuitButton.cs +++ b/osu.Game/Screens/Play/Pause/QuitButton.cs @@ -1,14 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using OpenTK.Graphics; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Screens.Play.Pause { - public class QuitButton : PauseButton + public class QuitButton : DialogButton { [BackgroundDependencyLoader] private void load(AudioManager audio) diff --git a/osu.Game/Overlays/Pause/ResumeButton.cs b/osu.Game/Screens/Play/Pause/ResumeButton.cs similarity index 80% rename from osu.Game/Overlays/Pause/ResumeButton.cs rename to osu.Game/Screens/Play/Pause/ResumeButton.cs index 1aef384b0b..d4f7555d3c 100644 --- a/osu.Game/Overlays/Pause/ResumeButton.cs +++ b/osu.Game/Screens/Play/Pause/ResumeButton.cs @@ -4,10 +4,11 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Screens.Play.Pause { - public class ResumeButton : PauseButton + public class ResumeButton : DialogButton { [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) diff --git a/osu.Game/Overlays/Pause/RetryButton.cs b/osu.Game/Screens/Play/Pause/RetryButton.cs similarity index 81% rename from osu.Game/Overlays/Pause/RetryButton.cs rename to osu.Game/Screens/Play/Pause/RetryButton.cs index 003d31dfbb..8f660525c3 100644 --- a/osu.Game/Overlays/Pause/RetryButton.cs +++ b/osu.Game/Screens/Play/Pause/RetryButton.cs @@ -4,10 +4,11 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Screens.Play.Pause { - public class RetryButton : PauseButton + public class RetryButton : DialogButton { [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) diff --git a/osu.Game/Overlays/Pause/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs similarity index 89% rename from osu.Game/Overlays/Pause/PauseOverlay.cs rename to osu.Game/Screens/Play/PauseOverlay.cs index 12a193c0be..8004f967ad 100644 --- a/osu.Game/Overlays/Pause/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -2,19 +2,20 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using OpenTK; -using OpenTK.Input; -using OpenTK.Graphics; -using osu.Game.Graphics; -using osu.Framework.Input; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Screens.Play.Pause; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Input; -namespace osu.Game.Overlays.Pause +namespace osu.Game.Screens.Play { public class PauseOverlay : OverlayContainer { @@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Pause } } - private FlowContainer retryCounterContainer; + private FillFlowContainer retryCounterContainer; public override bool Contains(Vector2 screenSpacePos) => true; public override bool HandleInput => State == Visibility.Visible; @@ -100,21 +101,21 @@ namespace osu.Game.Overlays.Pause Colour = Color4.Black, Alpha = background_alpha, }, - new FlowContainer + new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FlowDirections.Vertical, - Spacing = new Vector2(0f, 50f), + Direction = FillDirection.Down, + Spacing = new Vector2(0, 50), Origin = Anchor.Centre, Anchor = Anchor.Centre, Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Vertical, - Spacing = new Vector2(0f, 20f), + Direction = FillDirection.Down, + Spacing = new Vector2(0, 20), Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Children = new Drawable[] @@ -141,7 +142,7 @@ namespace osu.Game.Overlays.Pause } } }, - new FlowContainer + new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -156,7 +157,6 @@ namespace osu.Game.Overlays.Pause { new ResumeButton { - RelativeSizeAxes = Axes.X, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Height = button_height, @@ -164,7 +164,6 @@ namespace osu.Game.Overlays.Pause }, new RetryButton { - RelativeSizeAxes = Axes.X, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Height = button_height, @@ -176,7 +175,6 @@ namespace osu.Game.Overlays.Pause }, new QuitButton { - RelativeSizeAxes = Axes.X, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, Height = button_height, @@ -188,7 +186,7 @@ namespace osu.Game.Overlays.Pause } } }, - retryCounterContainer = new FlowContainer + retryCounterContainer = new FillFlowContainer { AutoSizeAxes = Axes.Both, Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 98344e7017..0aab0f9de7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -16,7 +16,6 @@ using osu.Framework.Screens; using osu.Game.Modes.UI; using osu.Game.Screens.Ranking; using osu.Game.Configuration; -using osu.Game.Overlays.Pause; using osu.Framework.Configuration; using System; using System.Collections.Generic; diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c5c9e6722f..bc04dbb2f7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -131,12 +131,12 @@ namespace osu.Game.Screens.Play AutoSizeAxes = Axes.Both; Children = new Drawable[] { - new FlowContainer() + new FillFlowContainer() { AutoSizeAxes = Axes.Both, Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 33c680e539..1c8972e4be 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -65,10 +65,10 @@ namespace osu.Game.Screens.Ranking Children = new Drawable[] { - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Children = new Drawable[] { new OsuSpriteText diff --git a/osu.Game/Screens/Select/BeatmapDeleteDialog.cs b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs new file mode 100644 index 0000000000..49f864d4e0 --- /dev/null +++ b/osu.Game/Screens/Select/BeatmapDeleteDialog.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Screens.Select +{ + public class BeatmapDeleteDialog : PopupDialog + { + private BeatmapDatabase database; + + [BackgroundDependencyLoader] + private void load(BeatmapDatabase beatmapDatabase) + { + database = beatmapDatabase; + } + + public BeatmapDeleteDialog(WorkingBeatmap beatmap) + { + Icon = FontAwesome.fa_trash_o; + HeaderText = @"Confirm deletion of"; + BodyText = $@"{beatmap?.Beatmap?.Metadata?.Artist} - {beatmap?.Beatmap?.Metadata?.Title}"; + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Yes. Totally. Delete it.", + Action = () => + { + beatmap.Dispose(); + database.Delete(beatmap.BeatmapSetInfo); + }, + }, + new PopupDialogCancelButton + { + Text = @"Firetruck, I didn't mean to!", + }, + }; + } + } +} diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 4a3b9b7fcd..a3493971cb 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -121,11 +121,11 @@ namespace osu.Game.Screens.Select }, }, // Text for beatmap info - new FlowContainer + new FillFlowContainer { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 }, AutoSizeAxes = Axes.Both, Children = new Drawable[] @@ -144,10 +144,10 @@ namespace osu.Game.Screens.Select TextSize = 17, Shadow = true, }, - new FlowContainer + new FillFlowContainer { Margin = new MarginPadding { Top = 10 }, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, AutoSizeAxes = Axes.Both, Children = new [] { @@ -167,10 +167,10 @@ namespace osu.Game.Screens.Select }, } }, - new FlowContainer + new FillFlowContainer { Margin = new MarginPadding { Top = 20 }, - Spacing = new Vector2(40,0), + Spacing = new Vector2(40, 0), AutoSizeAxes = Axes.Both, Children = labels }, diff --git a/osu.Game/Screens/Select/CarouselContainer.cs b/osu.Game/Screens/Select/CarouselContainer.cs index 9d8f489755..5d8c11d223 100644 --- a/osu.Game/Screens/Select/CarouselContainer.cs +++ b/osu.Game/Screens/Select/CarouselContainer.cs @@ -183,6 +183,7 @@ namespace osu.Game.Screens.Select if (SelectedGroup != null && SelectedGroup != group && SelectedGroup.State != BeatmapGroupState.Hidden) SelectedGroup.State = BeatmapGroupState.Collapsed; + group.State = BeatmapGroupState.Expanded; SelectedGroup = group; panel.State = PanelSelectedState.Selected; SelectedPanel = panel; @@ -191,6 +192,62 @@ namespace osu.Game.Screens.Select ScrollTo(selectedY, animated); } + public void Sort(FilterControl.SortMode mode) + { + switch (mode) + { + case FilterControl.SortMode.Artist: + groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist)); + break; + case FilterControl.SortMode.Title: + groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title)); + break; + case FilterControl.SortMode.Author: + groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author)); + break; + case FilterControl.SortMode.Difficulty: + groups.Sort((x, y) => + { + float xAverage = 0, yAverage = 0; + int counter = 0; + foreach (BeatmapInfo set in x.BeatmapSet.Beatmaps) + { + xAverage += set.StarDifficulty; + counter++; + } + xAverage /= counter; + counter = 0; + foreach (BeatmapInfo set in y.BeatmapSet.Beatmaps) + { + yAverage += set.StarDifficulty; + counter++; + } + yAverage /= counter; + if (xAverage > yAverage) + return 1; + else + return -1; + }); + break; + default: + throw new NotImplementedException(); + } + scrollableContent.Clear(false); + lifetime.Clear(); + foreach (BeatmapGroup group in groups) + { + group.Header.Depth = -scrollableContent.Children.Count(); + scrollableContent.Add(group.Header); + + foreach (BeatmapPanel panel in group.BeatmapPanels) + { + panel.Depth = -scrollableContent.Children.Count(); + scrollableContent.Add(panel); + } + } + + } + private static float offsetX(float dist, float halfHeight) { // The radius of the circle the carousel moves on. @@ -291,7 +348,7 @@ namespace osu.Game.Screens.Select public void SelectNext(int direction = 1, bool skipDifficulties = true) { - if (!skipDifficulties) + if (!skipDifficulties && SelectedGroup != null) { int i = SelectedGroup.BeatmapPanels.IndexOf(SelectedPanel) + direction; diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 23a61459ea..fd7992ba16 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -21,7 +21,18 @@ namespace osu.Game.Screens.Select public Action FilterChanged; public string Search => searchTextBox.Text; - public SortMode Sort { get; private set; } = SortMode.Title; + private SortMode sort = SortMode.Title; + public SortMode Sort { + get { return sort; } + set { + if (sort != value) + { + sort = value; + FilterChanged?.Invoke(); + } + } + } + public Action Exit; private SearchTextBox searchTextBox; @@ -36,7 +47,7 @@ namespace osu.Game.Screens.Select Alpha = 0.8f, RelativeSizeAxes = Axes.Both, }, - new FlowContainer + new FillFlowContainer { Padding = new MarginPadding(20), AutoSizeAxes = Axes.Y, @@ -44,7 +55,7 @@ namespace osu.Game.Screens.Select Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Width = 0.4f, // TODO: InnerWidth property or something - Direction = FlowDirections.Vertical, + Direction = FillDirection.Down, Children = new Drawable[] { searchTextBox = new SearchTextBox { @@ -174,10 +185,10 @@ namespace osu.Game.Screens.Select Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, }, - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, Spacing = new Vector2(10, 0), Children = new Drawable[] { @@ -206,10 +217,10 @@ namespace osu.Game.Screens.Select } } }, - new FlowContainer + new FillFlowContainer { AutoSizeAxes = Axes.Both, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, Spacing = new Vector2(10, 0), Origin = Anchor.TopRight, Anchor = Anchor.TopRight, @@ -251,9 +262,9 @@ namespace osu.Game.Screens.Select public enum SortMode { - Arist, + Artist, BPM, - Creator, + Author, DateAdded, Difficulty, Length, @@ -264,9 +275,9 @@ namespace osu.Game.Screens.Select public enum GroupMode { NoGrouping, - Arist, + Artist, BPM, - Creator, + Author, DateAdded, Difficulty, Length, diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 05ed670673..2b62e8d452 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Select public Action OnBack; public Action OnStart; - private FlowContainer buttons; + private FillFlowContainer buttons; public OsuLogo StartButton; @@ -91,21 +91,20 @@ namespace osu.Game.Screens.Select Origin = Anchor.BottomLeft, Action = () => OnBack?.Invoke(), }, - new FlowContainer + new FillFlowContainer { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Position = new Vector2(BackButton.SIZE_EXTENDED.X + padding, 0), RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, Spacing = new Vector2(padding, 0), Children = new Drawable[] { - - buttons = new FlowContainer + buttons = new FillFlowContainer { - Direction = FlowDirections.Horizontal, + Direction = FillDirection.Right, Spacing = new Vector2(0.2f, 0), AutoSizeAxes = Axes.Both, } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs new file mode 100644 index 0000000000..6e68b7917b --- /dev/null +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -0,0 +1,152 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Screens.Select.Options +{ + public class BeatmapOptionsButton : ClickableContainer + { + private static readonly float width = 130; + + private Box background, flash; + private TextAwesome iconText; + private OsuSpriteText firstLine, secondLine; + private Container box; + + public Color4 ButtonColour + { + get { return background.Colour; } + set { background.Colour = value; } + } + + public FontAwesome Icon + { + get { return iconText.Icon; } + set { iconText.Icon = value; } + } + + public string FirstLineText + { + get { return firstLine.Text; } + set { firstLine.Text = value; } + } + + public string SecondLineText + { + get { return secondLine.Text; } + set { secondLine.Text = value; } + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + flash.FadeTo(0.1f, 1000, EasingTypes.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + flash.FadeTo(0, 1000, EasingTypes.OutQuint); + return base.OnMouseUp(state, args); + } + + protected override bool OnClick(InputState state) + { + flash.ClearTransforms(); + flash.Alpha = 0.9f; + flash.FadeOut(800, EasingTypes.OutExpo); + + return base.OnClick(state); + } + + public override bool Contains(Vector2 screenSpacePos) => box.Contains(screenSpacePos); + + public BeatmapOptionsButton() + { + Width = width; + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + box = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(0.2f, 0f), + Masking = true, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Roundness = 5, + Radius = 8, + }, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = new Vector2(1.5f, 0), + Colour = Color4.Black, + }, + flash = new Box + { + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = new Vector2(1.5f, 0), + BlendingMode = BlendingMode.Additive, + Colour = Color4.White, + Alpha = 0, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Down, + Children = new Drawable[] + { + iconText = new TextAwesome + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + TextSize = 30, + Shadow = true, + Icon = FontAwesome.fa_close, + Margin = new MarginPadding + { + Bottom = 5, + }, + }, + firstLine = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Font = @"Exo2.0-Bold", + Text = @"", + }, + secondLine = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Font = @"Exo2.0-Bold", + Text = @"", + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsClearLocalScoresButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsClearLocalScoresButton.cs new file mode 100644 index 0000000000..66a6dac358 --- /dev/null +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsClearLocalScoresButton.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Select.Options +{ + public class BeatmapOptionsClearLocalScoresButton : BeatmapOptionsButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + ButtonColour = colour.Purple; + } + + public BeatmapOptionsClearLocalScoresButton() + { + Icon = FontAwesome.fa_eraser; + FirstLineText = @"Clear"; + SecondLineText = @"local scores"; + } + } +} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsDeleteButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsDeleteButton.cs new file mode 100644 index 0000000000..563cf41a87 --- /dev/null +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsDeleteButton.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Select.Options +{ + public class BeatmapOptionsDeleteButton : BeatmapOptionsButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + ButtonColour = colour.Pink; + } + + public BeatmapOptionsDeleteButton() + { + Icon = FontAwesome.fa_trash; + FirstLineText = @"Delete"; + SecondLineText = @"Beatmap"; + } + } +} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsEditButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsEditButton.cs new file mode 100644 index 0000000000..f88c0df168 --- /dev/null +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsEditButton.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Select.Options +{ + public class BeatmapOptionsEditButton : BeatmapOptionsButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + ButtonColour = colour.Yellow; + } + + public BeatmapOptionsEditButton() + { + Icon = FontAwesome.fa_pencil; + FirstLineText = @"Edit"; + SecondLineText = @"Beatmap"; + } + } +} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs new file mode 100644 index 0000000000..47152dc165 --- /dev/null +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -0,0 +1,139 @@ +// Copyright (c) 2007-2017 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 OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Select.Options +{ + public class BeatmapOptionsOverlay : FocusedOverlayContainer + { + private const float transition_duration = 500; + private const float x_position = 290; + + private const float height = 100; + + private Box holder; + private FillFlowContainer buttonsContainer; + + public Action OnRemoveFromUnplayed; + public Action OnClearLocalScores; + public Action OnEdit; + public Action OnDelete; + + protected override void PopIn() + { + base.PopIn(); + + if (buttonsContainer.Position.X >= DrawWidth || buttonsContainer.Alpha <= 0) + buttonsContainer.MoveToX(-buttonsContainer.DrawWidth); + + buttonsContainer.Alpha = 1; + + holder.ScaleTo(new Vector2(1, 1), transition_duration / 2, EasingTypes.OutQuint); + + buttonsContainer.MoveToX(x_position, transition_duration, EasingTypes.OutQuint); + buttonsContainer.TransformSpacingTo(Vector2.Zero, transition_duration, EasingTypes.OutQuint); + } + + protected override void PopOut() + { + base.PopOut(); + + holder.ScaleTo(new Vector2(1, 0), transition_duration / 2, EasingTypes.InSine); + + buttonsContainer.MoveToX(DrawWidth, transition_duration, EasingTypes.InSine); + buttonsContainer.TransformSpacingTo(new Vector2(200f, 0f), transition_duration, EasingTypes.InSine); + + Delay(transition_duration); + Schedule(() => + { + if (State == Visibility.Hidden) + buttonsContainer.Alpha = 0; + }); + } + + public BeatmapOptionsOverlay() + { + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + Anchor = Anchor.BottomLeft; + Origin = Anchor.BottomLeft; + + Children = new Drawable[] + { + holder = new Box + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Scale = new Vector2(1, 0), + Colour = Color4.Black.Opacity(0.5f), + }, + buttonsContainer = new ButtonFlow + { + Height = height, + AutoSizeAxes = Axes.X, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Children = new BeatmapOptionsButton[] + { + new BeatmapOptionsRemoveFromUnplayedButton + { + Action = () => + { + Hide(); + OnRemoveFromUnplayed?.Invoke(); + }, + }, + new BeatmapOptionsClearLocalScoresButton + { + Action = () => + { + Hide(); + OnClearLocalScores?.Invoke(); + }, + }, + new BeatmapOptionsEditButton + { + Action = () => + { + Hide(); + OnEdit?.Invoke(); + }, + }, + new BeatmapOptionsDeleteButton + { + Action = () => + { + Hide(); + OnDelete?.Invoke(); + }, + }, + }, + }, + }; + } + + class ButtonFlow : FillFlowContainer + { + protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); + protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); + + public ButtonFlow() + { + Direction = FillDirection.Right; + } + } + } +} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsRemoveFromUnplayedButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsRemoveFromUnplayedButton.cs new file mode 100644 index 0000000000..eeab78ef02 --- /dev/null +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsRemoveFromUnplayedButton.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Select.Options +{ + public class BeatmapOptionsRemoveFromUnplayedButton : BeatmapOptionsButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + ButtonColour = colour.Purple; + } + + public BeatmapOptionsRemoveFromUnplayedButton() + { + Icon = FontAwesome.fa_times_circle_o; + FirstLineText = @"Remove"; + SecondLineText = @"from Unplayed"; + } + } +} diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 3011b5bf23..68783f33db 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -27,21 +27,27 @@ using osu.Framework.Input; using OpenTK.Input; using System.Collections.Generic; using osu.Framework.Threading; +using osu.Game.Overlays.Mods; +using osu.Game.Overlays; +using osu.Game.Screens.Select.Options; namespace osu.Game.Screens.Select { public class PlaySongSelect : OsuScreen { - private Bindable playMode; + private Bindable playMode = new Bindable(); private BeatmapDatabase database; protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); private CarouselContainer carousel; private TrackManager trackManager; + private DialogOverlay dialogOverlay; private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 225); private BeatmapInfoWedge beatmapInfoWedge; + private ModSelectOverlay modSelect; + private static readonly Vector2 background_blur = new Vector2(20); private CancellationTokenSource initialAddSetsTask; @@ -50,15 +56,31 @@ namespace osu.Game.Screens.Select private List beatmapGroups; + private BeatmapOptionsOverlay beatmapOptions; private Footer footer; OsuScreen player; - FilterControl filter; + private FilterControl filter; + public FilterControl Filter + { + get + { + return filter; + } + private set + { + if (filter != value) + { + filter = value; + filterChanged(); + } + } + } [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapDatabase beatmaps, AudioManager audio, Framework.Game game, - OsuGame osuGame, OsuColour colours) + private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, Framework.Game game, + OsuGame osu, OsuColour colours) { const float carousel_width = 640; const float filter_height = 100; @@ -94,7 +116,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.X, Height = filter_height, - FilterChanged = filterChanged, + FilterChanged = () => filterChanged(), Exit = Exit, }, beatmapInfoWedge = new BeatmapInfoWedge @@ -108,6 +130,27 @@ namespace osu.Game.Screens.Select Right = 20, }, }, + beatmapOptions = new BeatmapOptionsOverlay + { + OnRemoveFromUnplayed = null, + OnClearLocalScores = null, + OnEdit = null, + OnDelete = promptDelete, + Margin = new MarginPadding + { + Bottom = 50, + }, + }, + modSelect = new ModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Margin = new MarginPadding + { + Bottom = 50, + }, + }, footer = new Footer { OnBack = Exit, @@ -122,18 +165,16 @@ namespace osu.Game.Screens.Select PreferredPlayMode = playMode.Value })).LoadAsync(Game, l => Push(player)); } - } + }, }; - footer.AddButton(@"mods", colours.Yellow, null); + footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility); footer.AddButton(@"random", colours.Green, carousel.SelectRandom); - footer.AddButton(@"options", colours.Blue, null); + footer.AddButton(@"options", colours.Blue, beatmapOptions.ToggleVisibility); - if (osuGame != null) - { - playMode = osuGame.PlayMode; - playMode.ValueChanged += playMode_ValueChanged; - } + if (osu != null) + playMode.BindTo(osu.PlayMode); + playMode.ValueChanged += playMode_ValueChanged; if (database == null) database = beatmaps; @@ -142,6 +183,7 @@ namespace osu.Game.Screens.Select database.BeatmapSetRemoved += onBeatmapSetRemoved; trackManager = audio.Track; + dialogOverlay = dialog; sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand"); @@ -153,7 +195,7 @@ namespace osu.Game.Screens.Select private ScheduledDelegate filterTask; - private void filterChanged() + private void filterChanged(bool debounce = true, bool eagerSelection = true) { filterTask?.Cancel(); filterTask = Scheduler.AddDelayed(() => @@ -161,28 +203,46 @@ namespace osu.Game.Screens.Select filterTask = null; var search = filter.Search; BeatmapGroup newSelection = null; + carousel.Sort(filter.Sort); foreach (var beatmapGroup in carousel) { var set = beatmapGroup.BeatmapSet; - bool match = string.IsNullOrEmpty(search) + + bool hasCurrentMode = set.Beatmaps.Any(bm => bm.Mode == playMode); + + bool match = hasCurrentMode; + + match &= string.IsNullOrEmpty(search) || (set.Metadata.Artist ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 || (set.Metadata.ArtistUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 || (set.Metadata.Title ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 || (set.Metadata.TitleUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1; + if (match) { - beatmapGroup.State = BeatmapGroupState.Collapsed; if (newSelection == null || beatmapGroup.BeatmapSet.OnlineBeatmapSetID == Beatmap.BeatmapSetInfo.OnlineBeatmapSetID) + { + if (newSelection != null) + newSelection.State = BeatmapGroupState.Collapsed; newSelection = beatmapGroup; + } + else + beatmapGroup.State = BeatmapGroupState.Collapsed; } else { beatmapGroup.State = BeatmapGroupState.Hidden; } } + if (newSelection != null) - carousel.SelectBeatmap(newSelection.BeatmapSet.Beatmaps[0], false); - }, 250); + { + if (newSelection.BeatmapPanels.Any(b => b.Beatmap.ID == Beatmap.BeatmapInfo.ID)) + carousel.SelectBeatmap(Beatmap.BeatmapInfo, false); + else if (eagerSelection) + carousel.SelectBeatmap(newSelection.BeatmapSet.Beatmaps[0], false); + } + }, debounce ? 250 : 0); } private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => addBeatmapSet(s, Game, true)); @@ -243,8 +303,6 @@ namespace osu.Game.Screens.Select protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - if (playMode != null) - playMode.ValueChanged -= playMode_ValueChanged; database.BeatmapSetAdded -= onBeatmapSetAdded; database.BeatmapSetRemoved -= onBeatmapSetRemoved; @@ -254,6 +312,7 @@ namespace osu.Game.Screens.Select private void playMode_ValueChanged(object sender, EventArgs e) { + filterChanged(false); } private void changeBackground(WorkingBeatmap beatmap) @@ -339,6 +398,8 @@ namespace osu.Game.Screens.Select carousel.AddGroup(group); + filterChanged(false, false); + if (Beatmap == null || select) carousel.SelectBeatmap(beatmapSet.Beatmaps.First()); else @@ -374,6 +435,12 @@ namespace osu.Game.Screens.Select } } + private void promptDelete() + { + if (Beatmap != null) + dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap)); + } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { switch (args.Key) @@ -382,12 +449,12 @@ namespace osu.Game.Screens.Select footer.StartButton.TriggerClick(); return true; case Key.Delete: - if (Beatmap != null) + if (state.Keyboard.ShiftPressed) { - Beatmap.Dispose(); - database.Delete(Beatmap.BeatmapSetInfo); + promptDelete(); + return true; } - return true; + break; } return base.OnKeyDown(state, args); diff --git a/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs b/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs new file mode 100644 index 0000000000..e5d91a99fa --- /dev/null +++ b/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Platform; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Screens.Tournament.Components +{ + public class DrawingsConfigManager : ConfigManager + { + public override string Filename => @"drawings.ini"; + + protected override void InitialiseDefaults() + { + Set(DrawingsConfig.Groups, 8, 1, 8); + Set(DrawingsConfig.TeamsPerGroup, 8, 1, 8); + } + + public DrawingsConfigManager(Storage storage) + : base(storage) + { + } + } + + public enum DrawingsConfig + { + Groups, + TeamsPerGroup + } +} diff --git a/osu.Game/Screens/Tournament/Components/VisualiserContainer.cs b/osu.Game/Screens/Tournament/Components/VisualiserContainer.cs new file mode 100644 index 0000000000..ca15e833f7 --- /dev/null +++ b/osu.Game/Screens/Tournament/Components/VisualiserContainer.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.OpenGL; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Screens.Tournament.Components +{ + class VisualiserContainer : Container + { + /// + /// Number of lines in the visualiser. + /// + public int Lines + { + get { return allLines.Count; } + set + { + while (value > allLines.Count) + addLine(); + + while (value < allLines.Count) + removeLine(); + } + } + + private List allLines = new List(); + + private float offset; + + private void addLine() + { + VisualiserLine newLine = new VisualiserLine() + { + RelativeSizeAxes = Axes.Both, + + Offset = offset, + CycleTime = RNG.Next(10000, 12000), + }; + + allLines.Add(newLine); + Add(newLine); + + offset += RNG.Next(100, 5000); + } + + private void removeLine() + { + if (allLines.Count == 0) + return; + + Remove(allLines.First()); + allLines.Remove(allLines.First()); + } + + class VisualiserLine : Container + { + /// + /// Time offset. + /// + public float Offset; + + public double CycleTime; + + private float leftPos => -(float)((Time.Current + Offset) / CycleTime) + expiredCount; + + private Texture texture; + + private int expiredCount = 0; + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + texture = textures.Get("Drawings/visualiser-line"); + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + while (Children.Count() < 3) + addLine(); + + float pos = leftPos; + + foreach (var c in Children) + { + if (c.Position.X < -1) + { + c.ClearTransforms(); + c.Expire(); + expiredCount++; + } + else + c.MoveToX(pos, 100); + + pos += 1; + } + } + + private void addLine() + { + Add(new Sprite() + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + + RelativePositionAxes = Axes.Both, + RelativeSizeAxes = Axes.Both, + + Texture = texture, + + X = leftPos + 1 + }); + } + } + } +} diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs new file mode 100644 index 0000000000..53ebe2606f --- /dev/null +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -0,0 +1,336 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Backgrounds; +using osu.Game.Screens.Tournament.Components; +using osu.Game.Screens.Tournament.Teams; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Tournament +{ + public class Drawings : OsuScreen + { + private const string results_filename = "drawings_results.txt"; + + internal override bool ShowOverlays => false; + + protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); + + private ScrollingTeamContainer teamsContainer; + private GroupContainer groupsContainer; + private OsuSpriteText fullTeamNameText; + + private List allTeams = new List(); + + private DrawingsConfigManager drawingsConfig; + + private Task writeOp; + + private Storage storage; + + public ITeamList TeamList; + + [BackgroundDependencyLoader] + private void load(TextureStore textures, Storage storage) + { + this.storage = storage; + + if (TeamList == null) + TeamList = new StorageBackedTeamList(storage); + + if (!TeamList.Teams.Any()) + { + Exit(); + return; + } + + drawingsConfig = new DrawingsConfigManager(storage); + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(77, 77, 77, 255) + }, + new Sprite + { + FillMode = FillMode.Fill, + Texture = textures.Get(@"Backgrounds/Drawings/background.png") + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Right, + + Children = new Drawable[] + { + // Main container + new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.85f, + + Children = new Drawable[] + { + // Visualiser + new VisualiserContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + RelativeSizeAxes = Axes.X, + Size = new Vector2(1, 10), + + Colour = new Color4(255, 204, 34, 255), + + Lines = 6 + }, + // Groups + groupsContainer = new GroupContainer(drawingsConfig.Get(DrawingsConfig.Groups), drawingsConfig.Get(DrawingsConfig.TeamsPerGroup)) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + + Padding = new MarginPadding + { + Top = 35f, + Bottom = 35f + } + }, + // Scrolling teams + teamsContainer = new ScrollingTeamContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + RelativeSizeAxes = Axes.X, + }, + // Scrolling team name + fullTeamNameText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + + Position = new Vector2(0, 45f), + + Alpha = 0, + + Font = "Exo2.0-Light", + TextSize = 42f + } + } + }, + // Control panel container + new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.15f, + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(54, 54, 54, 255) + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Text = "Control Panel", + TextSize = 22f, + Font = "Exo2.0-Boldd" + }, + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.75f, + + Position = new Vector2(0, 35f), + + Direction = FillDirection.Down, + Spacing = new Vector2(0, 5f), + + Children = new Drawable[] + { + new OsuButton + { + RelativeSizeAxes = Axes.X, + + Text = "Begin random", + Action = teamsContainer.StartScrolling, + }, + new OsuButton + { + RelativeSizeAxes = Axes.X, + + Text = "Stop random", + Action = teamsContainer.StopScrolling, + }, + new OsuButton + { + RelativeSizeAxes = Axes.X, + + Text = "Reload", + Action = reloadTeams + } + } + }, + new FillFlowContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.75f, + + Position = new Vector2(0, -5f), + + Direction = FillDirection.Down, + Spacing = new Vector2(0, 5f), + + Children = new Drawable[] + { + new OsuButton + { + RelativeSizeAxes = Axes.X, + + Text = "Reset", + Action = () => reset(false) + } + } + } + } + } + } + } + }; + + teamsContainer.OnSelected += onTeamSelected; + teamsContainer.OnScrollStarted += () => fullTeamNameText.FadeOut(200); + + reset(true); + } + + private void onTeamSelected(Team team) + { + groupsContainer.AddTeam(team); + + fullTeamNameText.Text = team.FullName; + fullTeamNameText.FadeIn(200); + + writeResults(groupsContainer.GetStringRepresentation()); + } + + private void writeResults(string text) + { + Action writeAction = () => + { + try + { + // Write to drawings_results + using (Stream stream = storage.GetStream(results_filename, FileAccess.Write, FileMode.Create)) + using (StreamWriter sw = new StreamWriter(stream)) + { + sw.Write(text); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Failed to write results."); + } + }; + + if (writeOp == null) + writeOp = Task.Run(writeAction); + else + writeOp = writeOp.ContinueWith(t => { writeAction(); }); + } + + private void reloadTeams() + { + teamsContainer.ClearTeams(); + allTeams.Clear(); + + foreach (Team t in TeamList.Teams) + { + if (groupsContainer.ContainsTeam(t.FullName)) + continue; + + allTeams.Add(t); + teamsContainer.AddTeam(t); + } + } + + private void reset(bool loadLastResults = false) + { + groupsContainer.ClearTeams(); + + reloadTeams(); + + if (loadLastResults) + { + try + { + // Read from drawings_results + using (Stream stream = storage.GetStream(results_filename, FileAccess.Read, FileMode.Open)) + using (StreamReader sr = new StreamReader(stream)) + { + string line; + while ((line = sr.ReadLine()?.Trim()) != null) + { + if (string.IsNullOrEmpty(line)) + continue; + + if (line.ToUpper().StartsWith("GROUP")) + continue; + + Team teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line); + + if (teamToAdd == null) + continue; + + groupsContainer.AddTeam(teamToAdd); + teamsContainer.RemoveTeam(teamToAdd); + } + } + } + catch (Exception ex) + { + Logger.Error(ex, "Failed to read last drawings results."); + } + + } + else + { + writeResults(string.Empty); + } + } + } +} diff --git a/osu.Game/Screens/Tournament/Group.cs b/osu.Game/Screens/Tournament/Group.cs new file mode 100644 index 0000000000..c3a911a791 --- /dev/null +++ b/osu.Game/Screens/Tournament/Group.cs @@ -0,0 +1,187 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics.Sprites; +using osu.Game.Screens.Tournament.Teams; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Tournament +{ + public class Group : Container + { + public readonly string GroupName; + + public int TeamsCount { get; private set; } + + private FlowContainer teams; + + private List allTeams = new List(); + + public Group(string name) + { + GroupName = name; + + Size = new Vector2(176, 128); + + Masking = true; + CornerRadius = 4; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(54, 54, 54, 255) + }, + // Group name + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Position = new Vector2(0, 7f), + + Text = $"GROUP {name.ToUpper()}", + TextSize = 8f, + Font = @"Exo2.0-Bold", + Colour = new Color4(255, 204, 34, 255), + }, + teams = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + + Spacing = new Vector2(6f, 22), + + Margin = new MarginPadding + { + Top = 21f, + Bottom = 7f, + Left = 7f, + Right = 7f + } + } + }; + } + + public void AddTeam(Team team) + { + GroupTeam gt = new GroupTeam(team); + + if (TeamsCount < 8) + { + teams.Add(gt); + allTeams.Add(gt); + + TeamsCount++; + } + } + + public bool ContainsTeam(string fullName) + { + return allTeams.Any(t => t.Team.FullName == fullName); + } + + public bool RemoveTeam(Team team) + { + allTeams.RemoveAll(gt => gt.Team == team); + + if (teams.RemoveAll(gt => gt.Team == team) > 0) + { + TeamsCount--; + return true; + } + + return false; + } + + public void ClearTeams() + { + allTeams.Clear(); + teams.Clear(); + + TeamsCount = 0; + } + + public string GetStringRepresentation() + { + StringBuilder sb = new StringBuilder(); + foreach (GroupTeam gt in allTeams) + sb.AppendLine(gt.Team.FullName); + return sb.ToString(); + } + + class GroupTeam : Container + { + public Team Team; + + private FillFlowContainer innerContainer; + private Sprite flagSprite; + + public GroupTeam(Team team) + { + Team = team; + + Width = 36; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + innerContainer = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + + Direction = FillDirection.Down, + Spacing = new Vector2(0, 5f), + + Children = new Drawable[] + { + flagSprite = new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + FillMode = FillMode.Fit + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Text = team.Acronym.ToUpper(), + TextSize = 10f, + Font = @"Exo2.0-Bold" + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + innerContainer.ScaleTo(1.5f); + innerContainer.ScaleTo(1f, 200); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}"); + } + } + } +} diff --git a/osu.Game/Screens/Tournament/GroupContainer.cs b/osu.Game/Screens/Tournament/GroupContainer.cs new file mode 100644 index 0000000000..936881b47c --- /dev/null +++ b/osu.Game/Screens/Tournament/GroupContainer.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Screens.Tournament.Teams; +using OpenTK; + +namespace osu.Game.Screens.Tournament +{ + public class GroupContainer : Container + { + private List groups = new List(); + + private int maxTeams; + private int currentGroup; + + public GroupContainer(int numGroups, int teamsPerGroup) + { + FlowContainer bottomGroups; + FlowContainer topGroups; + + maxTeams = teamsPerGroup; + + char nextGroupName = 'A'; + + Children = new[] + { + topGroups = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + AutoSizeAxes = Axes.Both, + + Spacing = new Vector2(7f, 0) + }, + bottomGroups = new FillFlowContainer + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + + AutoSizeAxes = Axes.Both, + + Spacing = new Vector2(7f, 0) + } + }; + + for (int i = 0; i < numGroups; i++) + { + Group g = new Group(nextGroupName.ToString()); + + groups.Add(g); + nextGroupName++; + + if (i < (int)Math.Ceiling(numGroups / 2f)) + topGroups.Add(g); + else + bottomGroups.Add(g); + } + } + + public void AddTeam(Team team) + { + if (groups[currentGroup].TeamsCount == maxTeams) + return; + + groups[currentGroup].AddTeam(team); + + currentGroup = (currentGroup + 1) % groups.Count; + } + + public bool ContainsTeam(string fullName) + { + return groups.Any(g => g.ContainsTeam(fullName)); + } + + public void ClearTeams() + { + foreach (Group g in groups) + g.ClearTeams(); + + currentGroup = 0; + } + + public string GetStringRepresentation() + { + StringBuilder sb = new StringBuilder(); + + foreach (Group g in groups) + { + if (g != groups.First()) + sb.AppendLine(); + sb.AppendLine($"Group {g.GroupName}"); + sb.Append(g.GetStringRepresentation()); + } + + return sb.ToString(); + } + } +} diff --git a/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs new file mode 100644 index 0000000000..35369247ff --- /dev/null +++ b/osu.Game/Screens/Tournament/ScrollingTeamContainer.cs @@ -0,0 +1,379 @@ +// Copyright (c) 2007-2017 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.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Threading; +using osu.Game.Screens.Tournament.Teams; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Screens.Tournament +{ + public class ScrollingTeamContainer : Container + { + public event Action OnScrollStarted; + public event Action OnSelected; + + private readonly List availableTeams = new List(); + + private Container tracker; + + private float speed; + private int expiredCount; + + private float offset; + private float timeOffset; + private float leftPos => offset + timeOffset + expiredCount * ScrollingTeam.WIDTH; + + private double lastTime; + + private ScheduledDelegate delayedStateChangeDelegate; + + public ScrollingTeamContainer() + { + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + tracker = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + AutoSizeAxes = Axes.Both, + + Masking = true, + CornerRadius = 10f, + Alpha = 0, + + Children = new[] + { + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.BottomCentre, + Size = new Vector2(2, 55), + + ColourInfo = ColourInfo.GradientVertical(Color4.Transparent, Color4.White) + }, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Size = new Vector2(2, 55), + + ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.Transparent) + } + } + } + }; + } + + private ScrollState _scrollState; + private ScrollState scrollState + { + get { return _scrollState; } + set + { + if (_scrollState == value) + return; + + _scrollState = value; + + delayedStateChangeDelegate?.Cancel(); + + switch (value) + { + case ScrollState.Scrolling: + resetSelected(); + + OnScrollStarted?.Invoke(); + + speedTo(1000f, 200); + tracker.FadeOut(100); + break; + case ScrollState.Stopping: + speedTo(0f, 2000); + tracker.FadeIn(200); + + delayedStateChangeDelegate = Delay(2300).Schedule(() => scrollState = ScrollState.Stopped); + break; + case ScrollState.Stopped: + // Find closest to center + Drawable closest = null; + foreach (var c in Children) + { + if (!(c is ScrollingTeam)) + continue; + + if (closest == null) + { + closest = c; + continue; + } + + float offset = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f); + float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f); + + if (offset < lastOffset) + closest = c; + } + + offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f); + + ScrollingTeam st = closest as ScrollingTeam; + + availableTeams.RemoveAll(at => at == st.Team); + + st.Selected = true; + OnSelected?.Invoke(st.Team); + + delayedStateChangeDelegate = Delay(10000).Schedule(() => scrollState = ScrollState.Idle); + break; + case ScrollState.Idle: + resetSelected(); + + OnScrollStarted?.Invoke(); + + speedTo(40f, 200); + tracker.FadeOut(100); + break; + } + } + } + + public void AddTeam(Team team) + { + if (availableTeams.Contains(team)) + return; + + availableTeams.Add(team); + + RemoveAll(c => c is ScrollingTeam); + scrollState = ScrollState.Idle; + } + + public void AddTeams(IEnumerable teams) + { + if (teams == null) + return; + + foreach (Team t in teams) + AddTeam(t); + } + + public void ClearTeams() + { + availableTeams.Clear(); + RemoveAll(c => c is ScrollingTeam); + scrollState = ScrollState.Idle; + } + + public void RemoveTeam(Team team) + { + availableTeams.Remove(team); + + foreach (var c in Children) + { + ScrollingTeam st = c as ScrollingTeam; + + if (st == null) + continue; + + if (st.Team == team) + { + st.FadeOut(200); + st.Expire(); + } + } + } + + public void StartScrolling() + { + if (availableTeams.Count == 0) + return; + + scrollState = ScrollState.Scrolling; + } + + public void StopScrolling() + { + if (availableTeams.Count == 0) + return; + + switch (scrollState) + { + case ScrollState.Stopped: + case ScrollState.Idle: + return; + } + + scrollState = ScrollState.Stopping; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + scrollState = ScrollState.Idle; + } + + protected override void UpdateAfterChildren() + { + timeOffset -= (float)(Time.Current - lastTime) / 1000 * speed; + lastTime = Time.Current; + + if (availableTeams.Count > 0) + { + // Fill more than required to account for transformation + scrolling speed + while (Children.Count(c => c is ScrollingTeam) < DrawWidth * 2 / ScrollingTeam.WIDTH) + addFlags(); + } + + float pos = leftPos; + + foreach (var c in Children) + { + if (!(c is ScrollingTeam)) + continue; + + if (c.Position.X + c.DrawWidth < 0) + { + c.ClearTransforms(); + c.Expire(); + expiredCount++; + } + else + { + c.MoveToX(pos, 100); + c.FadeTo(1.0f - Math.Abs(pos - DrawWidth / 2f) / (DrawWidth / 2.5f), 100); + } + + pos += ScrollingTeam.WIDTH; + } + } + + private void addFlags() + { + for (int i = 0; i < availableTeams.Count; i++) + { + Add(new ScrollingTeam(availableTeams[i]) + { + X = leftPos + DrawWidth + }); + } + } + + private void resetSelected() + { + foreach (var c in Children) + { + ScrollingTeam st = c as ScrollingTeam; + if (st == null) + continue; + + if (st.Selected) + { + st.Selected = false; + RemoveTeam(st.Team); + } + } + } + + private void speedTo(float value, double duration = 0, EasingTypes easing = EasingTypes.None) + { + DelayReset(); + + UpdateTransformsOfType(typeof(TransformScrollSpeed)); + TransformFloatTo(speed, value, duration, easing, new TransformScrollSpeed()); + } + + enum ScrollState + { + None, + Idle, + Stopping, + Stopped, + Scrolling + } + + public class TransformScrollSpeed : TransformFloat + { + public override void Apply(Drawable d) + { + base.Apply(d); + (d as ScrollingTeamContainer).speed = CurrentValue; + } + } + + public class ScrollingTeam : Container + { + public const float WIDTH = 58; + public const float HEIGHT = 41; + + public Team Team; + + private Sprite flagSprite; + private Box outline; + + private bool selected; + public bool Selected + { + get { return selected; } + set + { + selected = value; + + if (selected) + outline.FadeIn(100); + else + outline.FadeOut(100); + } + } + + public ScrollingTeam(Team team) + { + Team = team; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + Size = new Vector2(WIDTH, HEIGHT); + Masking = true; + CornerRadius = 8f; + + Alpha = 0; + + Children = new Drawable[] + { + outline = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0 + }, + flagSprite = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + Size = new Vector2(WIDTH, HEIGHT) - new Vector2(8) + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + flagSprite.Texture = textures.Get($@"Flags/{Team.FlagName}"); + } + } + } +} diff --git a/osu.Game/Screens/Tournament/Teams/ITeamList.cs b/osu.Game/Screens/Tournament/Teams/ITeamList.cs new file mode 100644 index 0000000000..a4476b64c6 --- /dev/null +++ b/osu.Game/Screens/Tournament/Teams/ITeamList.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; + +namespace osu.Game.Screens.Tournament.Teams +{ + public interface ITeamList + { + IEnumerable Teams { get; } + } +} diff --git a/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs b/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs new file mode 100644 index 0000000000..7de48dd55b --- /dev/null +++ b/osu.Game/Screens/Tournament/Teams/StorageBackedTeamList.cs @@ -0,0 +1,73 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Framework.Logging; +using osu.Framework.Platform; + +namespace osu.Game.Screens.Tournament.Teams +{ + public class StorageBackedTeamList : ITeamList + { + private const string teams_filename = "drawings.txt"; + + private Storage storage; + + public StorageBackedTeamList(Storage storage) + { + this.storage = storage; + } + + public IEnumerable Teams + { + get + { + var teams = new List(); + + try + { + using (Stream stream = storage.GetStream(teams_filename, FileAccess.Read, FileMode.Open)) + using (StreamReader sr = new StreamReader(stream)) + { + while (sr.Peek() != -1) + { + string line = sr.ReadLine().Trim(); + + if (string.IsNullOrEmpty(line)) + continue; + + string[] split = line.Split(':'); + + if (split.Length < 2) + { + Logger.Log($"Invalid team definition: {line}. Expected \"flag_name : team_name : team_acronym\"."); + continue; + } + + string flagName = split[0].Trim(); + string teamName = split[1].Trim(); + + string acronym = split.Length >= 3 ? split[2].Trim() : teamName; + acronym = acronym.Substring(0, Math.Min(3, acronym.Length)); + + teams.Add(new Team() + { + FlagName = flagName, + FullName = teamName, + Acronym = acronym + }); + } + } + } + catch (Exception ex) + { + Logger.Error(ex, "Failed to read teams."); + } + + return teams; + } + } + } +} diff --git a/osu.Game/Screens/Tournament/Teams/Team.cs b/osu.Game/Screens/Tournament/Teams/Team.cs new file mode 100644 index 0000000000..226f35ec1d --- /dev/null +++ b/osu.Game/Screens/Tournament/Teams/Team.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Tournament.Teams +{ + public class Team + { + /// + /// The name of this team. + /// + public string FullName; + + /// + /// Short acronym which appears in the group boxes post-selection. + /// + public string Acronym; + + /// + /// Two-letter flag acronym (ISO 3166 standard) + /// + public string FlagName; + } +} diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 5928d6106c..540bfdbbf9 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - namespace osu.Game.Users { public class User diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 71f0fc92a8..23ca656199 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -160,7 +160,6 @@ - @@ -215,6 +214,15 @@ + + + + + + + + + @@ -278,13 +286,34 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -305,6 +334,7 @@ +