mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 09:02:55 +08:00
Merge pull request #1083 from peppy/more-mods
Implement NoFail, HardRock, Easy mods
This commit is contained in:
commit
c7b59646c0
@ -1 +1 @@
|
||||
Subproject commit 9ae8979ef7ef0968c90cdbce40a04969d43633c7
|
||||
Subproject commit 107c5517670ca88dbe8c83a97e37e99ac5742ee6
|
@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -45,7 +46,7 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="original">The original beatmap to use the parameters of.</param>
|
||||
public Beatmap(Beatmap original = null)
|
||||
{
|
||||
BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo;
|
||||
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
|
||||
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
|
||||
Breaks = original?.Breaks ?? Breaks;
|
||||
ComboColors = original?.ComboColors ?? ComboColors;
|
||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
// Editor
|
||||
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
|
||||
public string StoredBookmarks { get; internal set; }
|
||||
public string StoredBookmarks { get; set; }
|
||||
|
||||
[Ignore]
|
||||
[JsonIgnore]
|
||||
|
@ -52,7 +52,14 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
lock (beatmapLock)
|
||||
{
|
||||
return beatmap ?? (beatmap = GetBeatmap());
|
||||
if (beatmap != null) return beatmap;
|
||||
|
||||
beatmap = GetBeatmap();
|
||||
|
||||
// use the database-backed info.
|
||||
beatmap.BeatmapInfo = BeatmapInfo;
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -16,19 +17,27 @@ namespace osu.Game.Graphics
|
||||
private readonly Sprite spriteShadow;
|
||||
private readonly Sprite spriteMain;
|
||||
|
||||
private readonly Container shadowVisibility;
|
||||
|
||||
public SpriteIcon()
|
||||
{
|
||||
InternalChildren = new[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
spriteShadow = new Sprite
|
||||
shadowVisibility = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Position = new Vector2(0, 0.06f),
|
||||
Colour = new Color4(0f, 0f, 0f, 0.2f),
|
||||
Alpha = 0
|
||||
Child = spriteShadow = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Y = 2,
|
||||
Colour = new Color4(0f, 0f, 0f, 0.2f),
|
||||
},
|
||||
Alpha = 0,
|
||||
},
|
||||
spriteMain = new Sprite
|
||||
{
|
||||
@ -60,10 +69,27 @@ namespace osu.Game.Graphics
|
||||
Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0);
|
||||
}
|
||||
|
||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||
{
|
||||
if ((invalidation & Invalidation.Colour) > 0)
|
||||
{
|
||||
//adjust shadow alpha based on highest component intensity to avoid muddy display of darker text.
|
||||
//squared result for quadratic fall-off seems to give the best result.
|
||||
var avgColour = (Color4)DrawInfo.Colour.AverageColour;
|
||||
|
||||
spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2);
|
||||
}
|
||||
|
||||
return base.Invalidate(invalidation, source, shallPropagate);
|
||||
}
|
||||
|
||||
public bool Shadow
|
||||
{
|
||||
get { return spriteShadow.IsPresent; }
|
||||
set { spriteShadow.Alpha = value ? 1 : 0; }
|
||||
set
|
||||
{
|
||||
shadowVisibility.Alpha = value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private FontAwesome icon;
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.IO.Serialization
|
||||
{
|
||||
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)
|
||||
|
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);
|
||||
}
|
||||
}
|
@ -45,5 +45,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// The mods this mod cannot be enabled with.
|
||||
/// </summary>
|
||||
public virtual Type[] IncompatibleMods => new Type[] { };
|
||||
|
||||
/// <summary>
|
||||
/// Whether we should allow failing at the current point in time.
|
||||
/// </summary>
|
||||
public virtual bool AllowFail => true;
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,12 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModEasy : Mod
|
||||
public abstract class ModEasy : Mod, IApplicableToDifficulty
|
||||
{
|
||||
public override string Name => "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 bool Ranked => true;
|
||||
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
|
||||
|
||||
using System;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModHardRock : Mod
|
||||
public abstract class ModHardRock : Mod, IApplicableToDifficulty
|
||||
{
|
||||
public override string Name => "Hard Rock";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override string Description => "Everything just got a bit harder...";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,5 +15,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override bool Ranked => true;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) };
|
||||
|
||||
/// <summary>
|
||||
/// We never fail, 'yo.
|
||||
/// </summary>
|
||||
public override bool AllowFail => false;
|
||||
}
|
||||
}
|
@ -16,8 +16,9 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when the ScoreProcessor is in a failed state.
|
||||
/// Return true if the fail was permitted.
|
||||
/// </summary>
|
||||
public event Action Failed;
|
||||
public event Func<bool> Failed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the <see cref="ScoreProcessor"/>.
|
||||
@ -106,8 +107,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (alreadyFailed || !HasFailed)
|
||||
return;
|
||||
|
||||
alreadyFailed = true;
|
||||
Failed?.Invoke();
|
||||
if (Failed?.Invoke() != false)
|
||||
alreadyFailed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -153,6 +153,10 @@ namespace osu.Game.Rulesets.UI
|
||||
// Convert the beatmap
|
||||
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
|
||||
foreach (var h in Beatmap.HitObjects)
|
||||
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
|
||||
@ -163,7 +167,7 @@ namespace osu.Game.Rulesets.UI
|
||||
ApplyBeatmap();
|
||||
|
||||
// Add mods, should always be the last thing applied to give full control to mods
|
||||
applyMods(beatmap.Mods.Value);
|
||||
applyMods(Mods);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -45,10 +45,11 @@ namespace osu.Game.Rulesets.UI
|
||||
},
|
||||
modIcon = new SpriteIcon
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Colour = OsuColour.Gray(84),
|
||||
Size = new Vector2(icon_size - 35),
|
||||
Y = 25,
|
||||
Icon = mod.Icon
|
||||
},
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
@ -77,23 +78,28 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Ruleset rulesetInstance;
|
||||
|
||||
WorkingBeatmap working = Beatmap.Value;
|
||||
Beatmap beatmap;
|
||||
|
||||
try
|
||||
{
|
||||
if (Beatmap.Value.Beatmap == null)
|
||||
beatmap = working.Beatmap;
|
||||
|
||||
if (beatmap == null)
|
||||
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();
|
||||
|
||||
try
|
||||
{
|
||||
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.Value.BeatmapInfo.Ruleset.ID);
|
||||
HitRenderer = rulesetInstance.CreateHitRendererWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
// 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.
|
||||
ruleset = Beatmap.Value.BeatmapInfo.Ruleset;
|
||||
ruleset = beatmap.BeatmapInfo.Ruleset;
|
||||
rulesetInstance = ruleset.CreateInstance();
|
||||
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true);
|
||||
}
|
||||
@ -110,11 +116,11 @@ namespace osu.Game.Screens.Play
|
||||
return;
|
||||
}
|
||||
|
||||
adjustableSourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
||||
adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
|
||||
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
|
||||
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();
|
||||
|
||||
offsetClock = new FramedOffsetClock(decoupledClock);
|
||||
@ -127,7 +133,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
adjustableSourceClock.Reset();
|
||||
|
||||
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>())
|
||||
foreach (var mod in working.Mods.Value.OfType<IApplicableToClock>())
|
||||
mod.ApplyToClock(adjustableSourceClock);
|
||||
|
||||
decoupledClock.ChangeSource(adjustableSourceClock);
|
||||
@ -195,7 +201,7 @@ namespace osu.Game.Screens.Play
|
||||
hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded;
|
||||
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)
|
||||
HitRenderer.OnAllJudged += onCompletion;
|
||||
@ -238,13 +244,17 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
}
|
||||
|
||||
private void onFail()
|
||||
private bool onFail()
|
||||
{
|
||||
if (Beatmap.Value.Mods.Value.Any(m => !m.AllowFail))
|
||||
return false;
|
||||
|
||||
decoupledClock.Stop();
|
||||
|
||||
HasFailed = true;
|
||||
failOverlay.Retries = RestartCount;
|
||||
failOverlay.Show();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
|
@ -117,6 +117,7 @@
|
||||
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
|
||||
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
|
||||
<Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" />
|
||||
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
|
||||
<Compile Include="Users\UserCoverBackground.cs" />
|
||||
<Compile Include="Overlays\UserProfileOverlay.cs" />
|
||||
<Compile Include="Overlays\Profile\ProfileHeader.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user