mirror of
https://github.com/ppy/osu
synced 2025-01-11 16:49:39 +00:00
Add difficulty application mods
Also fixes circular references when using IJsonSerializable.
This commit is contained in:
parent
224de9cc1e
commit
e75f438c29
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
15
osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs
Normal file
15
osu.Game/Rulesets/Mods/IApplicableToDifficulty.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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" />
|
||||||
|
Loading…
Reference in New Issue
Block a user