Add difficulty application mods

Also fixes circular references when using IJsonSerializable.
This commit is contained in:
Dean Herbert 2017-08-05 16:22:10 +09:00
parent 224de9cc1e
commit e75f438c29
9 changed files with 68 additions and 14 deletions

View File

@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -45,7 +46,7 @@ namespace osu.Game.Beatmaps
/// <param name="original">The original beatmap to use the parameters of.</param> /// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null) public Beatmap(Beatmap original = null)
{ {
BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo; BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks; Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors; ComboColors = original?.ComboColors ?? ComboColors;

View File

@ -52,7 +52,14 @@ namespace osu.Game.Beatmaps
{ {
lock (beatmapLock) lock (beatmapLock)
{ {
return beatmap ?? (beatmap = GetBeatmap()); if (beatmap != null) return beatmap;
beatmap = GetBeatmap();
// use the database-backed info.
beatmap.BeatmapInfo = BeatmapInfo;
return beatmap;
} }
} }
} }

View File

@ -13,7 +13,7 @@ namespace osu.Game.IO.Serialization
{ {
public static string Serialize(this IJsonSerializable obj) public static string Serialize(this IJsonSerializable obj)
{ {
return JsonConvert.SerializeObject(obj); return JsonConvert.SerializeObject(obj, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
} }
public static T Deserialize<T>(this string objString) public static T Deserialize<T>(this string objString)

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that make general adjustments to difficulty.
/// </summary>
public interface IApplicableToDifficulty
{
void ApplyToDifficulty(BeatmapDifficulty difficulty);
}
}

View File

@ -2,11 +2,12 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
public abstract class ModEasy : Mod public abstract class ModEasy : Mod, IApplicableToDifficulty
{ {
public override string Name => "Easy"; public override string Name => "Easy";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy; public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy;
@ -15,5 +16,14 @@ namespace osu.Game.Rulesets.Mods
public override double ScoreMultiplier => 0.5; public override double ScoreMultiplier => 0.5;
public override bool Ranked => true; public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
public void ApplyToDifficulty(BeatmapDifficulty difficulty)
{
const float ratio = 0.5f;
difficulty.CircleSize *= ratio;
difficulty.ApproachRate *= ratio;
difficulty.DrainRate *= ratio;
difficulty.OverallDifficulty *= ratio;
}
} }
} }

View File

@ -2,16 +2,26 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
public abstract class ModHardRock : Mod public abstract class ModHardRock : Mod, IApplicableToDifficulty
{ {
public override string Name => "Hard Rock"; public override string Name => "Hard Rock";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock; public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
public override ModType Type => ModType.DifficultyIncrease; public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder..."; public override string Description => "Everything just got a bit harder...";
public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; public override Type[] IncompatibleMods => new[] { typeof(ModEasy) };
public void ApplyToDifficulty(BeatmapDifficulty difficulty)
{
const float ratio = 1.4f;
difficulty.CircleSize *= ratio;
difficulty.ApproachRate *= ratio;
difficulty.DrainRate *= ratio;
difficulty.OverallDifficulty *= ratio;
}
} }
} }

View File

@ -153,6 +153,10 @@ namespace osu.Game.Rulesets.UI
// Convert the beatmap // Convert the beatmap
Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset); Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset);
// Apply difficulty adjustments from mods before using Difficulty.
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.Difficulty);
// Apply defaults // Apply defaults
foreach (var h in Beatmap.HitObjects) foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty); h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
@ -163,7 +167,7 @@ namespace osu.Game.Rulesets.UI
ApplyBeatmap(); ApplyBeatmap();
// Add mods, should always be the last thing applied to give full control to mods // Add mods, should always be the last thing applied to give full control to mods
applyMods(beatmap.Mods.Value); applyMods(Mods);
} }
/// <summary> /// <summary>

View File

@ -22,6 +22,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -77,23 +78,28 @@ namespace osu.Game.Screens.Play
Ruleset rulesetInstance; Ruleset rulesetInstance;
WorkingBeatmap working = Beatmap.Value;
Beatmap beatmap;
try try
{ {
if (Beatmap.Value.Beatmap == null) beatmap = working.Beatmap;
if (beatmap == null)
throw new InvalidOperationException("Beatmap was not loaded"); throw new InvalidOperationException("Beatmap was not loaded");
ruleset = osu?.Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; ruleset = osu?.Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance(); rulesetInstance = ruleset.CreateInstance();
try try
{ {
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.Value.BeatmapInfo.Ruleset.ID); HitRenderer = rulesetInstance.CreateHitRendererWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
} }
catch (BeatmapInvalidForRulesetException) catch (BeatmapInvalidForRulesetException)
{ {
// we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset
// let's try again forcing the beatmap's ruleset. // let's try again forcing the beatmap's ruleset.
ruleset = Beatmap.Value.BeatmapInfo.Ruleset; ruleset = beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance(); rulesetInstance = ruleset.CreateInstance();
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true);
} }
@ -110,11 +116,11 @@ namespace osu.Game.Screens.Play
return; return;
} }
adjustableSourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
var firstObjectTime = HitRenderer.Objects.First().StartTime; var firstObjectTime = HitRenderer.Objects.First().StartTime;
decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.Value.BeatmapInfo.AudioLeadIn))); decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn)));
decoupledClock.ProcessFrame(); decoupledClock.ProcessFrame();
offsetClock = new FramedOffsetClock(decoupledClock); offsetClock = new FramedOffsetClock(decoupledClock);
@ -127,7 +133,7 @@ namespace osu.Game.Screens.Play
{ {
adjustableSourceClock.Reset(); adjustableSourceClock.Reset();
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>()) foreach (var mod in working.Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(adjustableSourceClock); mod.ApplyToClock(adjustableSourceClock);
decoupledClock.ChangeSource(adjustableSourceClock); decoupledClock.ChangeSource(adjustableSourceClock);
@ -195,7 +201,7 @@ namespace osu.Game.Screens.Play
hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded; hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded;
hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos);
hudOverlay.ModDisplay.Current.BindTo(Beatmap.Value.Mods); hudOverlay.ModDisplay.Current.BindTo(working.Mods);
//bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
HitRenderer.OnAllJudged += onCompletion; HitRenderer.OnAllJudged += onCompletion;

View File

@ -117,6 +117,7 @@
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" /> <Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" /> <Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
<Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" /> <Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" />
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
<Compile Include="Users\UserCoverBackground.cs" /> <Compile Include="Users\UserCoverBackground.cs" />
<Compile Include="Overlays\UserProfileOverlay.cs" /> <Compile Include="Overlays\UserProfileOverlay.cs" />
<Compile Include="Overlays\Profile\ProfileHeader.cs" /> <Compile Include="Overlays\Profile\ProfileHeader.cs" />