mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 23:24:04 +09:00
Merge remote-tracking branch 'origin/master' into timingchange-improvements
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,13 +128,11 @@ namespace osu.Game.Graphics.Containers
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
ScrollbarVisible = false,
|
||||
Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() },
|
||||
Depth = float.MaxValue
|
||||
Children = new Drawable[] { scrollContentContainer = CreateScrollContentContainer() }
|
||||
});
|
||||
AddInternal(headerBackgroundContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Depth = float.MaxValue / 2
|
||||
RelativeSizeAxes = Axes.X
|
||||
});
|
||||
originalSectionsMargin = scrollContentContainer.Margin;
|
||||
}
|
||||
|
@ -20,14 +20,19 @@ namespace osu.Game.Graphics
|
||||
public static class AccentedColourExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Tweens the accent colour of a drawable to another colour.
|
||||
/// Smoothly adjusts <see cref="IHasAccentColour.AccentColour"/> over time.
|
||||
/// </summary>
|
||||
/// <param name="accentedDrawable">The drawable to apply the accent colour to.</param>
|
||||
/// <param name="newColour">The new accent colour.</param>
|
||||
/// <param name="duration">The tween duration.</param>
|
||||
/// <param name="easing">The tween easing.</param>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
|
||||
where T : IHasAccentColour
|
||||
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
|
||||
|
||||
/// <summary>
|
||||
/// Smoothly adjusts <see cref="IHasAccentColour.AccentColour"/> over time.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> FadeAccent<T>(this TransformSequence<T> t, Color4 newColour, double duration = 0, Easing easing = Easing.None)
|
||||
where T : Drawable, IHasAccentColour
|
||||
=> t.Append(o => o.FadeAccent(newColour, duration, easing));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -8,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.IO.Stores;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Caching;
|
||||
|
||||
namespace osu.Game.Graphics
|
||||
{
|
||||
@ -16,19 +18,28 @@ namespace osu.Game.Graphics
|
||||
private readonly Sprite spriteShadow;
|
||||
private readonly Sprite spriteMain;
|
||||
|
||||
private Cached layout = new Cached();
|
||||
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 +71,34 @@ 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 && Shadow)
|
||||
layout.Invalidate();
|
||||
return base.Invalidate(invalidation, source, shallPropagate);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (!layout.IsValid)
|
||||
{
|
||||
//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);
|
||||
|
||||
layout.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -169,13 +169,11 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
header.User = user;
|
||||
|
||||
for (int i = 0; i < user.ProfileOrder.Length; i++)
|
||||
foreach (string id in user.ProfileOrder)
|
||||
{
|
||||
string id = user.ProfileOrder[i];
|
||||
var sec = sections.FirstOrDefault(s => s.Identifier == id);
|
||||
if (sec != null)
|
||||
{
|
||||
sec.Depth = -i;
|
||||
sectionsContainer.Add(sec);
|
||||
tabs.AddItem(sec);
|
||||
}
|
||||
|
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 *= 1.3f; // CS uses a custom 1.3 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" />
|
||||
|
Reference in New Issue
Block a user