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/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 2d59c4005e..569a97e496 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -195,6 +195,7 @@ + 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 fd778d1ce6..b1d0780739 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.Game.Graphics; using osu.Game.Modes.Catch.UI; using osu.Game.Modes.Objects; @@ -16,6 +17,61 @@ namespace osu.Game.Modes.Catch public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new CatchHitRenderer { Beatmap = beatmap }; + 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 bbf22086c0..df8a48262e 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.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.Game.Graphics; using osu.Game.Modes.Mania.UI; using osu.Game.Modes.Objects; @@ -16,6 +17,77 @@ namespace osu.Game.Modes.Mania public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new ManiaHitRenderer { Beatmap = beatmap }; + 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/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 d05c8193cc..21554154c1 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -34,6 +34,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/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 6141838880..71b747f895 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.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.Game.Graphics; using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.UI; @@ -16,6 +17,61 @@ namespace osu.Game.Modes.Taiko public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new TaikoHitRenderer { Beatmap = beatmap }; + 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/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/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/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 851e144408..11281a18d0 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Concurrent; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Overlays.Mods; namespace osu.Game.Modes { @@ -26,6 +27,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); 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/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..0a5d0e49e1 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -0,0 +1,369 @@ +// 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 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); + } + + TriggerFocusLost(); + } + + 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); + } + + Schedule(TriggerFocusContention); + } + + 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/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs new file mode 100644 index 0000000000..37a939005b --- /dev/null +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -0,0 +1,201 @@ +// 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, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White.Opacity(50), + }, + }, + }); + } + + protected override void 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() + { + 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/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/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index a60c39d61b..68783f33db 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -27,6 +27,7 @@ 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; @@ -45,6 +46,8 @@ namespace osu.Game.Screens.Select 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; @@ -138,6 +141,16 @@ namespace osu.Game.Screens.Select Bottom = 50, }, }, + modSelect = new ModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Margin = new MarginPadding + { + Bottom = 50, + }, + }, footer = new Footer { OnBack = Exit, @@ -155,7 +168,7 @@ namespace osu.Game.Screens.Select }, }; - 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, beatmapOptions.ToggleVisibility); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4cd9a49fcb..cf98450307 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -156,7 +156,6 @@ - @@ -289,6 +288,13 @@ + + + + + + + @@ -296,6 +302,8 @@ + +