Merge branch 'master' into custom-data-directory

This commit is contained in:
Dan Balasescu
2020-05-11 12:12:03 +09:00
committed by GitHub
25 changed files with 372 additions and 37 deletions

View File

@ -18,6 +18,7 @@ using osu.Game.Screens.Menu;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Development;
@ -97,6 +98,7 @@ namespace osu.Game
private MainMenu menuScreen;
[CanBeNull]
private IntroScreen introScreen;
private Bindable<int> configRuleset;
@ -914,7 +916,7 @@ namespace osu.Game
if (ScreenStack.CurrentScreen is Loader)
return false;
if (introScreen.DidLoadMenu && !(ScreenStack.CurrentScreen is IntroScreen))
if (introScreen?.DidLoadMenu == true && !(ScreenStack.CurrentScreen is IntroScreen))
{
Scheduler.Add(introScreen.MakeCurrent);
return true;

View File

@ -50,13 +50,6 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
[BackgroundDependencyLoader(true)]
private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig)
{
if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false)
{
button.Enabled.Value = false;
button.TooltipText = "this beatmap is currently not available for download.";
return;
}
noVideoSetting = osuConfig.GetBindable<bool>(OsuSetting.PreferNoVideo);
button.Action = () =>
@ -81,6 +74,26 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
break;
}
};
State.BindValueChanged(state =>
{
switch (state.NewValue)
{
case DownloadState.LocallyAvailable:
button.Enabled.Value = true;
button.TooltipText = string.Empty;
break;
default:
if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false)
{
button.Enabled.Value = false;
button.TooltipText = "this beatmap is currently not available for download.";
}
break;
}
}, true);
}
}
}

View File

@ -264,7 +264,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
if (BeatmapSet.Value == null) return;
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable)
{
downloadButtonsContainer.Clear();
return;

View File

@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
[Cached(typeof(DrawableHitObject))]
public abstract class DrawableHitObject : SkinReloadableDrawable
{
public event Action<DrawableHitObject> DefaultsApplied;
public readonly HitObject HitObject;
/// <summary>
@ -148,7 +150,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
samplesBindable.CollectionChanged += (_, __) => loadSamples();
updateState(ArmedState.Idle, true);
onDefaultsApplied();
apply(HitObject);
}
private void loadSamples()
@ -175,7 +177,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
AddInternal(Samples);
}
private void onDefaultsApplied() => apply(HitObject);
private void onDefaultsApplied(HitObject hitObject)
{
apply(hitObject);
DefaultsApplied?.Invoke(this);
}
private void apply(HitObject hitObject)
{

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Invoked after <see cref="ApplyDefaults"/> has completed on this <see cref="HitObject"/>.
/// </summary>
public event Action DefaultsApplied;
public event Action<HitObject> DefaultsApplied;
public readonly Bindable<double> StartTimeBindable = new BindableDouble();
@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Objects
foreach (var h in nestedHitObjects)
h.ApplyDefaults(controlPointInfo, difficulty);
DefaultsApplied?.Invoke();
DefaultsApplied?.Invoke(this);
}
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)

View File

@ -487,6 +487,11 @@ namespace osu.Game.Rulesets.UI
protected virtual ResumeOverlay CreateResumeOverlay() => null;
/// <summary>
/// Whether to display gameplay overlays, such as <see cref="HUDOverlay"/> and <see cref="BreakOverlay"/>.
/// </summary>
public virtual bool AllowGameplayOverlays => true;
/// <summary>
/// Sets a replay to be used, overriding local input.
/// </summary>

View File

@ -16,17 +16,23 @@ namespace osu.Game.Rulesets.UI.Scrolling
{
private readonly IBindable<double> timeRange = new BindableDouble();
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
// Responds to changes in the layout. When the layout changes, all hit object states must be recomputed.
private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
// A combined cache across all hit object states to reduce per-update iterations.
// When invalidated, one or more (but not necessarily all) hitobject states must be re-validated.
private readonly Cached combinedObjCache = new Cached();
public ScrollingHitObjectContainer()
{
RelativeSizeAxes = Axes.Both;
AddLayout(initialStateCache);
AddLayout(layoutCache);
}
[BackgroundDependencyLoader]
@ -35,13 +41,14 @@ namespace osu.Game.Rulesets.UI.Scrolling
direction.BindTo(scrollingInfo.Direction);
timeRange.BindTo(scrollingInfo.TimeRange);
direction.ValueChanged += _ => initialStateCache.Invalidate();
timeRange.ValueChanged += _ => initialStateCache.Invalidate();
direction.ValueChanged += _ => layoutCache.Invalidate();
timeRange.ValueChanged += _ => layoutCache.Invalidate();
}
public override void Add(DrawableHitObject hitObject)
{
initialStateCache.Invalidate();
combinedObjCache.Invalidate();
hitObject.DefaultsApplied += onDefaultsApplied;
base.Add(hitObject);
}
@ -51,8 +58,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
if (result)
{
initialStateCache.Invalidate();
combinedObjCache.Invalidate();
hitObjectInitialStateCache.Remove(hitObject);
hitObject.DefaultsApplied -= onDefaultsApplied;
}
return result;
@ -60,23 +69,45 @@ namespace osu.Game.Rulesets.UI.Scrolling
public override void Clear(bool disposeChildren = true)
{
foreach (var h in Objects)
h.DefaultsApplied -= onDefaultsApplied;
base.Clear(disposeChildren);
initialStateCache.Invalidate();
combinedObjCache.Invalidate();
hitObjectInitialStateCache.Clear();
}
private void onDefaultsApplied(DrawableHitObject drawableObject)
{
// The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame).
// In such a case, combinedObjCache will take care of updating the hitobject.
if (hitObjectInitialStateCache.TryGetValue(drawableObject, out var objCache))
{
combinedObjCache.Invalidate();
objCache.Invalidate();
}
}
private float scrollLength;
protected override void Update()
{
base.Update();
if (!initialStateCache.IsValid)
if (!layoutCache.IsValid)
{
foreach (var cached in hitObjectInitialStateCache.Values)
cached.Invalidate();
combinedObjCache.Invalidate();
scrollingInfo.Algorithm.Reset();
layoutCache.Validate();
}
if (!combinedObjCache.IsValid)
{
switch (direction.Value)
{
case ScrollingDirection.Up:
@ -89,15 +120,21 @@ namespace osu.Game.Rulesets.UI.Scrolling
break;
}
scrollingInfo.Algorithm.Reset();
foreach (var obj in Objects)
{
if (!hitObjectInitialStateCache.TryGetValue(obj, out var objCache))
objCache = hitObjectInitialStateCache[obj] = new Cached();
if (objCache.IsValid)
continue;
computeLifetimeStartRecursive(obj);
computeInitialStateRecursive(obj);
objCache.Validate();
}
initialStateCache.Validate();
combinedObjCache.Validate();
}
}
@ -109,8 +146,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
computeLifetimeStartRecursive(obj);
}
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
{
float originAdjustment = 0.0f;
@ -142,12 +177,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
// Cant use AddOnce() since the delegate is re-constructed every invocation
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
{
if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached))
cached = hitObjectInitialStateCache[hitObject] = new Cached();
if (cached.IsValid)
return;
if (hitObject.HitObject is IHasEndTime e)
{
switch (direction.Value)
@ -171,8 +200,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
// Nested hitobjects don't need to scroll, but they do need accurate positions
updatePosition(obj, hitObject.HitObject.StartTime);
}
cached.Validate();
});
protected override void UpdateAfterChildrenLife()

View File

@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -201,6 +202,25 @@ namespace osu.Game.Screens.Edit
updateHitObject(null, true);
}
/// <summary>
/// Clears all <see cref="HitObjects"/> from this <see cref="EditorBeatmap"/>.
/// </summary>
public void Clear()
{
var removed = HitObjects.ToList();
mutableHitObjects.Clear();
foreach (var b in startTimeBindables)
b.Value.UnbindAll();
startTimeBindables.Clear();
foreach (var h in removed)
HitObjectRemoved?.Invoke(h);
updateHitObject(null, true);
}
private void trackStartTime(HitObject hitObject)
{
startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy();

View File

@ -2,10 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Screens.Play
@ -38,5 +40,11 @@ namespace osu.Game.Screens.Play
public IEnumerable<BeatmapStatistic> GetStatistics() => PlayableBeatmap.GetStatistics();
public IBeatmap Clone() => PlayableBeatmap.Clone();
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
public IBindable<JudgementResult> LastJudgementResult => lastJudgementResult;
public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result;
}
}

View File

@ -184,6 +184,13 @@ namespace osu.Game.Screens.Play
addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap);
addOverlayComponents(GameplayClockContainer, Beatmap.Value);
if (!DrawableRuleset.AllowGameplayOverlays)
{
HUDOverlay.ShowHud.Value = false;
HUDOverlay.ShowHud.Disabled = true;
BreakOverlay.Hide();
}
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
// bind clock into components that require it
@ -193,6 +200,7 @@ namespace osu.Game.Screens.Play
{
HealthProcessor.ApplyResult(r);
ScoreProcessor.ApplyResult(r);
gameplayBeatmap.ApplyResult(r);
};
DrawableRuleset.OnRevertResult += r =>