diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 894719ac6e..7c806a589a 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -8,6 +8,7 @@ using osu.Game.Database; using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; +using System.Linq; namespace osu.Game.Beatmaps { @@ -26,6 +27,18 @@ namespace osu.Game.Beatmaps BeatmapInfo = beatmapInfo; BeatmapSetInfo = beatmapSetInfo; WithStoryboard = withStoryboard; + + Mods.ValueChanged += mods => applyRateAdjustments(); + } + + private void applyRateAdjustments() + { + var t = track; + if (t == null) return; + + t.ResetRate(); + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToClock(t); } protected abstract Beatmap GetBeatmap(); @@ -66,7 +79,11 @@ namespace osu.Game.Beatmaps { lock (trackLock) { - return track ?? (track = GetTrack()); + if (track != null) return track; + + track = GetTrack(); + applyRateAdjustments(); + return track; } } } diff --git a/osu.Game/Rulesets/Mods/IApplicableMod.cs b/osu.Game/Rulesets/Mods/IApplicableMod.cs index 66f3fc5da6..18e1ae4b3d 100644 --- a/osu.Game/Rulesets/Mods/IApplicableMod.cs +++ b/osu.Game/Rulesets/Mods/IApplicableMod.cs @@ -17,6 +17,6 @@ namespace osu.Game.Rulesets.Mods /// Applies the mod to a HitRenderer. /// /// The HitRenderer to apply the mod to. - void Apply(HitRenderer hitRenderer); + void ApplyToHitRenderer(HitRenderer hitRenderer); } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToClock.cs new file mode 100644 index 0000000000..f0502cf346 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableToClock.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Timing; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that make adjustments to the track. + /// + public interface IApplicableToClock + { + void ApplyToClock(IAdjustableClock clock); + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index cfb4d8b8ab..a2846c1d2f 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -1,12 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; using System; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mods { @@ -45,131 +41,4 @@ namespace osu.Game.Rulesets.Mods /// public virtual Type[] IncompatibleMods => new Type[] { }; } - - public class MultiMod : Mod - { - public override string Name => string.Empty; - public override string Description => string.Empty; - public override double ScoreMultiplier => 0.0; - - public Mod[] Mods; - } - - public abstract class ModNoFail : Mod - { - public override string Name => "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 Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; - } - - public abstract class ModEasy : Mod - { - public override string Name => "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 Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; - } - - public abstract class ModHidden : Mod - { - public override string Name => "Hidden"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; - public override bool Ranked => true; - } - - public abstract class ModHardRock : Mod - { - public override string Name => "Hard Rock"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; - public override string Description => "Everything just got a bit harder..."; - public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; - } - - public abstract class ModSuddenDeath : Mod - { - public override string Name => "Sudden Death"; - 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 Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; - } - - public abstract class ModDoubleTime : Mod - { - public override string Name => "Double Time"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime; - public override string Description => "Zoooooooooom"; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) }; - } - - public abstract class ModRelax : Mod - { - public override string Name => "Relax"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax; - public override double ScoreMultiplier => 0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) }; - } - - public abstract class ModHalfTime : Mod - { - public override string Name => "Half Time"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime; - public override string Description => "Less zoom"; - public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) }; - } - - public abstract class ModNightcore : ModDoubleTime - { - public override string Name => "Nightcore"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore; - public override string Description => "uguuuuuuuu"; - } - - public abstract class ModFlashlight : Mod - { - public override string Name => "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 string Name => "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 Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; - } - - public abstract class ModAutoplay : ModAutoplay, IApplicableMod - where T : HitObject - { - protected abstract Score CreateReplayScore(Beatmap beatmap); - - public void Apply(HitRenderer hitRenderer) - { - hitRenderer.SetReplay(CreateReplayScore(hitRenderer.Beatmap)?.Replay); - } - } - - public abstract class ModPerfect : ModSuddenDeath - { - public override string Name => "Perfect"; - public override string Description => "SS or quit."; - } - - public class ModCinema : ModAutoplay - { - public override string Name => "Cinema"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; - } } diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs new file mode 100644 index 0000000000..1217bf835f --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModAutoplay : ModAutoplay, IApplicableMod + where T : HitObject + { + protected abstract Score CreateReplayScore(Beatmap beatmap); + + public void ApplyToHitRenderer(HitRenderer hitRenderer) + { + hitRenderer.SetReplay(CreateReplayScore(hitRenderer.Beatmap)?.Replay); + } + } + + public class ModAutoplay : Mod + { + public override string Name => "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 Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) }; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs new file mode 100644 index 0000000000..332bd2c5ac --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -0,0 +1,13 @@ +// 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.Rulesets.Mods +{ + public class ModCinema : ModAutoplay + { + public override string Name => "Cinema"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs new file mode 100644 index 0000000000..377a4c2180 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -0,0 +1,25 @@ +// 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.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public class ModDoubleTime : Mod, IApplicableToClock + { + public override string Name => "Double Time"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime; + public override string Description => "Zoooooooooom"; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) }; + + public override double ScoreMultiplier => 1.12; + + public virtual void ApplyToClock(IAdjustableClock clock) + { + clock.Rate = 1.5; + } + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs new file mode 100644 index 0000000000..bef3f04af3 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModEasy : Mod + { + public override string Name => "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 Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs new file mode 100644 index 0000000000..63c534dc7d --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -0,0 +1,15 @@ +// 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.Rulesets.Mods +{ + public abstract class ModFlashlight : Mod + { + public override string Name => "Flashlight"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight; + public override string Description => "Restricted view area."; + public override bool Ranked => true; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs new file mode 100644 index 0000000000..235fc7ad76 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -0,0 +1,25 @@ +// 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.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModHalfTime : Mod, IApplicableToClock + { + public override string Name => "Half Time"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime; + public override string Description => "Less zoom"; + public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) }; + + public override double ScoreMultiplier => 1.12; + + public void ApplyToClock(IAdjustableClock clock) + { + clock.Rate = 0.75; + } + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs new file mode 100644 index 0000000000..b729b5ae15 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModHardRock : Mod + { + public override string Name => "Hard Rock"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; + public override string Description => "Everything just got a bit harder..."; + public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs new file mode 100644 index 0000000000..12c788ce54 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -0,0 +1,14 @@ +// 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.Rulesets.Mods +{ + public abstract class ModHidden : Mod + { + public override string Name => "Hidden"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; + public override bool Ranked => true; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs new file mode 100644 index 0000000000..d04643fb8b --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Audio; +using osu.Framework.Timing; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModNightcore : ModDoubleTime + { + public override string Name => "Nightcore"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore; + public override string Description => "uguuuuuuuu"; + + public override void ApplyToClock(IAdjustableClock clock) + { + var pitchAdjust = clock as IHasPitchAdjust; + if (pitchAdjust != null) + pitchAdjust.PitchAdjust = 1.5; + else + base.ApplyToClock(clock); + } + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs new file mode 100644 index 0000000000..0c8726bbc1 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModNoFail : Mod + { + public override string Name => "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 Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) }; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs new file mode 100644 index 0000000000..35217c8305 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModPerfect : ModSuddenDeath + { + public override string Name => "Perfect"; + public override string Description => "SS or quit."; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs new file mode 100644 index 0000000000..5491f8fc58 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModRelax : Mod + { + public override string Name => "Relax"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax; + public override double ScoreMultiplier => 0; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) }; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs new file mode 100644 index 0000000000..a7dcbbc9ed --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModSuddenDeath : Mod + { + public override string Name => "Sudden Death"; + 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 Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Mods/MultiMod.cs b/osu.Game/Rulesets/Mods/MultiMod.cs new file mode 100644 index 0000000000..c5fac250d0 --- /dev/null +++ b/osu.Game/Rulesets/Mods/MultiMod.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mods +{ + public class MultiMod : Mod + { + public override string Name => string.Empty; + public override string Description => string.Empty; + public override double ScoreMultiplier => 0.0; + + public Mod[] Mods; + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/HitRenderer.cs index d2f2cc6021..a3a806b6a7 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/HitRenderer.cs @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.UI return; foreach (var mod in mods.OfType>()) - mod.Apply(this); + mod.ApplyToHitRenderer(this); } /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5502723c0d..5624cd75f9 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -20,6 +20,7 @@ using osu.Game.Screens.Backgrounds; using System; using System.Linq; using osu.Framework.Threading; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; @@ -120,6 +121,8 @@ namespace osu.Game.Screens.Play Schedule(() => { sourceClock.Reset(); + foreach (var mod in Beatmap.Mods.Value.OfType()) + mod.ApplyToClock(sourceClock); }); scoreProcessor = HitRenderer.CreateScoreProcessor(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6d7a905eed..49cc32a6b6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -109,6 +109,21 @@ + + + + + + + + + + + + + + +