1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 17:43:05 +08:00

Merge branch 'current-star-rating' into player-loader-star-rating

This commit is contained in:
Salman Ahmed 2021-05-11 09:52:39 +03:00
commit 004ce95f33
23 changed files with 182 additions and 297 deletions

View File

@ -1,28 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.EmptyFreeform.Objects;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.EmptyFreeform.Replays
{
public class EmptyFreeformAutoGenerator : AutoGenerator
public class EmptyFreeformAutoGenerator : AutoGenerator<EmptyFreeformReplayFrame>
{
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
public new Beatmap<EmptyFreeformHitObject> Beatmap => (Beatmap<EmptyFreeformHitObject>)base.Beatmap;
public EmptyFreeformAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
}
public override Replay Generate()
protected override void GenerateFrames()
{
Frames.Add(new EmptyFreeformReplayFrame());
@ -35,8 +29,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Replays
// todo: add required inputs and extra frames.
});
}
return Replay;
}
}
}

View File

@ -1,28 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Pippidon.Objects;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Pippidon.Replays
{
public class PippidonAutoGenerator : AutoGenerator
public class PippidonAutoGenerator : AutoGenerator<PippidonReplayFrame>
{
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
public new Beatmap<PippidonHitObject> Beatmap => (Beatmap<PippidonHitObject>)base.Beatmap;
public PippidonAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
}
public override Replay Generate()
protected override void GenerateFrames()
{
Frames.Add(new PippidonReplayFrame());
@ -34,8 +28,6 @@ namespace osu.Game.Rulesets.Pippidon.Replays
Position = hitObject.Position,
});
}
return Replay;
}
}
}

View File

@ -1,28 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.EmptyScrolling.Objects;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.EmptyScrolling.Replays
{
public class EmptyScrollingAutoGenerator : AutoGenerator
public class EmptyScrollingAutoGenerator : AutoGenerator<EmptyScrollingReplayFrame>
{
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
public new Beatmap<EmptyScrollingHitObject> Beatmap => (Beatmap<EmptyScrollingHitObject>)base.Beatmap;
public EmptyScrollingAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
}
public override Replay Generate()
protected override void GenerateFrames()
{
Frames.Add(new EmptyScrollingReplayFrame());
@ -34,8 +28,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Replays
// todo: add required inputs and extra frames.
});
}
return Replay;
}
}
}

View File

@ -2,29 +2,23 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Pippidon.Objects;
using osu.Game.Rulesets.Pippidon.UI;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Pippidon.Replays
{
public class PippidonAutoGenerator : AutoGenerator
public class PippidonAutoGenerator : AutoGenerator<PippidonReplayFrame>
{
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
public new Beatmap<PippidonHitObject> Beatmap => (Beatmap<PippidonHitObject>)base.Beatmap;
public PippidonAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
}
public override Replay Generate()
protected override void GenerateFrames()
{
int currentLane = 0;
@ -55,8 +49,6 @@ namespace osu.Game.Rulesets.Pippidon.Replays
currentLane = hitObject.Lane;
}
return Replay;
}
private void addFrame(double time, PippidonAction direction)

View File

@ -5,7 +5,6 @@ using System;
using System.Linq;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
@ -13,26 +12,19 @@ using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Catch.Replays
{
internal class CatchAutoGenerator : AutoGenerator
internal class CatchAutoGenerator : AutoGenerator<CatchReplayFrame>
{
public const double RELEASE_DELAY = 20;
public new CatchBeatmap Beatmap => (CatchBeatmap)base.Beatmap;
public CatchAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
}
protected Replay Replay;
private CatchReplayFrame currentFrame;
public override Replay Generate()
protected override void GenerateFrames()
{
if (Beatmap.HitObjects.Count == 0)
return Replay;
return;
// todo: add support for HT DT
const double dash_speed = Catcher.BASE_SPEED;
@ -119,15 +111,11 @@ namespace osu.Game.Rulesets.Catch.Replays
}
}
}
return Replay;
}
private void addFrame(double time, float? position = null, bool dashing = false)
{
var last = currentFrame;
currentFrame = new CatchReplayFrame(time, position, dashing, last);
Replay.Frames.Add(currentFrame);
Frames.Add(new CatchReplayFrame(time, position, dashing, LastFrame));
}
}
}

View File

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
@ -11,7 +10,7 @@ using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaAutoGenerator : AutoGenerator
internal class ManiaAutoGenerator : AutoGenerator<ManiaReplayFrame>
{
public const double RELEASE_DELAY = 20;
@ -22,8 +21,6 @@ namespace osu.Game.Rulesets.Mania.Replays
public ManiaAutoGenerator(ManiaBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
columnActions = new ManiaAction[Beatmap.TotalColumns];
var normalAction = ManiaAction.Key1;
@ -43,12 +40,10 @@ namespace osu.Game.Rulesets.Mania.Replays
}
}
protected Replay Replay;
public override Replay Generate()
protected override void GenerateFrames()
{
if (Beatmap.HitObjects.Count == 0)
return Replay;
return;
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
@ -70,10 +65,8 @@ namespace osu.Game.Rulesets.Mania.Replays
}
}
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
}
return Replay;
}
private IEnumerable<IActionPoint> generateActionPoints()

View File

@ -2,10 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Taiko.Beatmaps;
@ -13,7 +11,7 @@ using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Replays
{
public class TaikoAutoGenerator : AutoGenerator
public class TaikoAutoGenerator : AutoGenerator<TaikoReplayFrame>
{
public new TaikoBeatmap Beatmap => (TaikoBeatmap)base.Beatmap;
@ -22,16 +20,12 @@ namespace osu.Game.Rulesets.Taiko.Replays
public TaikoAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
Replay = new Replay();
}
protected Replay Replay;
protected List<ReplayFrame> Frames => Replay.Frames;
public override Replay Generate()
protected override void GenerateFrames()
{
if (Beatmap.HitObjects.Count == 0)
return Replay;
return;
bool hitButton = true;
@ -128,8 +122,6 @@ namespace osu.Game.Rulesets.Taiko.Replays
hitButton = !hitButton;
}
return Replay;
}
}
}

View File

@ -4,7 +4,8 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
@ -20,24 +21,28 @@ namespace osu.Game.Tests.Visual.Gameplay
[Resolved]
private OsuConfigManager config { get; set; }
[SetUpSteps]
public void SetUpSteps()
private void create(HealthProcessor healthProcessor)
{
AddStep("create layer", () =>
{
Child = layer = new FailingLayer();
layer.BindHealthProcessor(new DrainingHealthProcessor(1));
Child = new HealthProcessorContainer(healthProcessor)
{
RelativeSizeAxes = Axes.Both,
Child = layer = new FailingLayer()
};
layer.ShowHealth.BindTo(showHealth);
});
AddStep("show health", () => showHealth.Value = true);
AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer is visible", () => layer.IsPresent);
}
[Test]
public void TestLayerFading()
{
create(new DrainingHealthProcessor(0));
AddSliderStep("current health", 0.0, 1.0, 1.0, val =>
{
if (layer != null)
@ -53,6 +58,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLayerDisabledViaConfig()
{
create(new DrainingHealthProcessor(0));
AddUntilStep("layer is visible", () => layer.IsPresent);
AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddUntilStep("layer is not visible", () => !layer.IsPresent);
@ -61,7 +68,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLayerVisibilityWithAccumulatingProcessor()
{
AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new AccumulatingHealthProcessor(1)));
create(new AccumulatingHealthProcessor(1));
AddUntilStep("layer is not visible", () => !layer.IsPresent);
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddUntilStep("layer is not visible", () => !layer.IsPresent);
}
@ -69,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLayerVisibilityWithDrainingProcessor()
{
AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new DrainingHealthProcessor(1)));
create(new DrainingHealthProcessor(0));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddWaitStep("wait for potential fade", 10);
AddAssert("layer is still visible", () => layer.IsPresent);
@ -78,6 +86,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLayerVisibilityWithDifferentOptions()
{
create(new DrainingHealthProcessor(0));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddStep("don't show health", () => showHealth.Value = false);
@ -96,5 +106,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer fade is visible", () => layer.IsPresent);
}
private class HealthProcessorContainer : Container
{
[Cached(typeof(HealthProcessor))]
private readonly HealthProcessor healthProcessor;
public HealthProcessorContainer(HealthProcessor healthProcessor)
{
this.healthProcessor = healthProcessor;
}
}
}
}

View File

@ -23,6 +23,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
// best way to check without exposing.
private Drawable hideTarget => hudOverlay.KeyCounter;
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
@ -143,7 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create overlay", () =>
{
hudOverlay = new HUDOverlay(scoreProcessor, null, null, Array.Empty<Mod>());
hudOverlay = new HUDOverlay(scoreProcessor, null, Array.Empty<Mod>());
// Add any key just to display the key counter visually.
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));

View File

@ -21,6 +21,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached]
private readonly ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
[SetUpSteps]
public void SetUpSteps()
{
@ -34,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay
var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap);
var hudOverlay = new HUDOverlay(scoreProcessor, null, drawableRuleset, Array.Empty<Mod>())
var hudOverlay = new HUDOverlay(scoreProcessor, drawableRuleset, Array.Empty<Mod>())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -27,6 +27,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>();
// best way to check without exposing.
@ -76,7 +79,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
SetContents(() =>
{
hudOverlay = new HUDOverlay(scoreProcessor, null, null, Array.Empty<Mod>());
hudOverlay = new HUDOverlay(scoreProcessor, null, Array.Empty<Mod>());
// Add any key just to display the key counter visually.
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));

View File

@ -4,11 +4,14 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.Gameplay
@ -19,6 +22,9 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
[SetUpSteps]
public void SetUpSteps()
{
@ -28,8 +34,7 @@ namespace osu.Game.Tests.Visual.Gameplay
});
AddStep(@"Reset all", delegate
{
foreach (var s in healthDisplays)
s.Current.Value = 1;
healthProcessor.Health.Value = 1;
});
}
@ -38,23 +43,21 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddRepeatStep(@"decrease hp", delegate
{
foreach (var healthDisplay in healthDisplays)
healthDisplay.Current.Value -= 0.08f;
healthProcessor.Health.Value -= 0.08f;
}, 10);
AddRepeatStep(@"increase hp without flash", delegate
{
foreach (var healthDisplay in healthDisplays)
healthDisplay.Current.Value += 0.1f;
healthProcessor.Health.Value += 0.1f;
}, 3);
AddRepeatStep(@"increase hp with flash", delegate
{
foreach (var healthDisplay in healthDisplays)
healthProcessor.Health.Value += 0.1f;
healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement())
{
healthDisplay.Current.Value += 0.1f;
healthDisplay.Flash(new JudgementResult(null, new OsuJudgement()));
}
Type = HitResult.Perfect
});
}, 3);
}
}

View File

@ -1,40 +1,36 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Replays
{
public abstract class AutoGenerator : IAutoGenerator
public abstract class AutoGenerator
{
/// <summary>
/// Creates the auto replay and returns it.
/// Every subclass of OsuAutoGeneratorBase should implement this!
/// The default duration of a key press in milliseconds.
/// </summary>
public abstract Replay Generate();
#region Parameters
public const double KEY_UP_DELAY = 50;
/// <summary>
/// The beatmap we're making.
/// The beatmap the autoplay is generated for.
/// </summary>
protected IBeatmap Beatmap;
#endregion
protected IBeatmap Beatmap { get; }
protected AutoGenerator(IBeatmap beatmap)
{
Beatmap = beatmap;
}
#region Constants
// Shared amongst all modes
public const double KEY_UP_DELAY = 50;
#endregion
/// <summary>
/// Generate the replay of the autoplay.
/// </summary>
public abstract Replay Generate();
protected virtual HitObject GetNextObject(int currentIndex)
{
@ -44,4 +40,37 @@ namespace osu.Game.Rulesets.Replays
return Beatmap.HitObjects[currentIndex + 1];
}
}
public abstract class AutoGenerator<TFrame> : AutoGenerator
where TFrame : ReplayFrame
{
/// <summary>
/// The replay frames of the autoplay.
/// </summary>
protected readonly List<TFrame> Frames = new List<TFrame>();
[CanBeNull]
protected TFrame LastFrame => Frames.Count == 0 ? null : Frames[^1];
protected AutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
}
public sealed override Replay Generate()
{
Frames.Clear();
GenerateFrames();
return new Replay
{
Frames = Frames.OrderBy(frame => frame.Time).Cast<ReplayFrame>().ToList()
};
}
/// <summary>
/// Generate the replay frames of the autoplay and populate <see cref="Frames"/>.
/// </summary>
protected abstract void GenerateFrames();
}
}

View File

@ -1,12 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Replays;
namespace osu.Game.Rulesets.Replays
{
public interface IAutoGenerator
{
Replay Generate();
}
}

View File

@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play.HUD
GlowColour = colours.BlueDarker;
}
public override void Flash(JudgementResult result) => Scheduler.AddOnce(flash);
protected override void Flash(JudgementResult result) => Scheduler.AddOnce(flash);
private void flash()
{

View File

@ -39,7 +39,6 @@ namespace osu.Game.Screens.Play.HUD
private readonly Container boxes;
private Bindable<bool> fadePlayfieldWhenHealthLow;
private HealthProcessor healthProcessor;
public FailingLayer()
{
@ -88,18 +87,10 @@ namespace osu.Game.Screens.Play.HUD
updateState();
}
public override void BindHealthProcessor(HealthProcessor processor)
{
base.BindHealthProcessor(processor);
healthProcessor = processor;
updateState();
}
private void updateState()
{
// Don't display ever if the ruleset is not using a draining health display.
var showLayer = healthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value;
var showLayer = HealthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value;
this.FadeTo(showLayer ? 1 : 0, fade_time, Easing.OutQuint);
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
@ -11,26 +12,43 @@ namespace osu.Game.Screens.Play.HUD
{
/// <summary>
/// A container for components displaying the current player health.
/// Gets bound automatically to the <see cref="HealthProcessor"/> when inserted to <see cref="DrawableRuleset.Overlays"/> hierarchy.
/// Gets bound automatically to the <see cref="Rulesets.Scoring.HealthProcessor"/> when inserted to <see cref="DrawableRuleset.Overlays"/> hierarchy.
/// </summary>
public abstract class HealthDisplay : Container, IHealthDisplay
public abstract class HealthDisplay : Container
{
[Resolved]
protected HealthProcessor HealthProcessor { get; private set; }
public Bindable<double> Current { get; } = new BindableDouble(1)
{
MinValue = 0,
MaxValue = 1
};
public virtual void Flash(JudgementResult result)
protected virtual void Flash(JudgementResult result)
{
}
/// <summary>
/// Bind the tracked fields of <see cref="HealthProcessor"/> to this health display.
/// </summary>
public virtual void BindHealthProcessor(HealthProcessor processor)
[BackgroundDependencyLoader]
private void load()
{
Current.BindTo(processor.Health);
Current.BindTo(HealthProcessor.Health);
HealthProcessor.NewJudgement += onNewJudgement;
}
private void onNewJudgement(JudgementResult judgement)
{
if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit)
Flash(judgement);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (HealthProcessor != null)
HealthProcessor.NewJudgement -= onNewJudgement;
}
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Screens.Play.HUD
{
/// <summary>
/// An interface providing a set of methods to update a health display.
/// </summary>
public interface IHealthDisplay : IDrawable
{
/// <summary>
/// The current health to be displayed.
/// </summary>
Bindable<double> Current { get; }
/// <summary>
/// Flash the display for a specified result type.
/// </summary>
/// <param name="result">The result type.</param>
void Flash(JudgementResult result);
}
}

View File

@ -1,50 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
namespace osu.Game.Screens.Play.HUD
{
public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay
public class SkinnableHealthDisplay : SkinnableDrawable
{
public Bindable<double> Current { get; } = new BindableDouble(1)
{
MinValue = 0,
MaxValue = 1
};
public void Flash(JudgementResult result) => skinnedCounter?.Flash(result);
private HealthProcessor processor;
public void BindHealthProcessor(HealthProcessor processor)
{
if (this.processor != null)
throw new InvalidOperationException("Can't bind to a processor more than once");
this.processor = processor;
Current.BindTo(processor.Health);
}
public SkinnableHealthDisplay()
: base(new HUDSkinComponent(HUDSkinComponents.HealthDisplay), _ => new DefaultHealthDisplay())
{
CentreComponent = false;
}
private IHealthDisplay skinnedCounter;
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
skinnedCounter = Drawable as IHealthDisplay;
skinnedCounter?.Current.BindTo(Current);
}
}
}

View File

@ -47,7 +47,6 @@ namespace osu.Game.Screens.Play
public Bindable<bool> ShowHealthbar = new Bindable<bool>(true);
private readonly ScoreProcessor scoreProcessor;
private readonly HealthProcessor healthProcessor;
private readonly DrawableRuleset drawableRuleset;
private readonly IReadOnlyList<Mod> mods;
@ -75,10 +74,9 @@ namespace osu.Game.Screens.Play
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements };
public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
{
this.scoreProcessor = scoreProcessor;
this.healthProcessor = healthProcessor;
this.drawableRuleset = drawableRuleset;
this.mods = mods;
@ -158,12 +156,6 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader(true)]
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
{
if (scoreProcessor != null)
BindScoreProcessor(scoreProcessor);
if (healthProcessor != null)
BindHealthProcessor(healthProcessor);
if (drawableRuleset != null)
{
BindDrawableRuleset(drawableRuleset);
@ -318,24 +310,6 @@ namespace osu.Game.Screens.Play
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();
protected virtual void BindScoreProcessor(ScoreProcessor processor)
{
if (HealthDisplay is IHealthDisplay shd)
{
processor.NewJudgement += judgement =>
{
if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit)
shd.Flash(judgement);
};
}
}
protected virtual void BindHealthProcessor(HealthProcessor processor)
{
HealthDisplay?.BindHealthProcessor(processor);
FailingLayer?.BindHealthProcessor(processor);
}
public bool OnPressed(GlobalAction action)
{
switch (action)

View File

@ -208,6 +208,8 @@ namespace osu.Game.Screens.Play
HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
HealthProcessor.ApplyBeatmap(playableBeatmap);
dependencies.CacheAs(HealthProcessor);
if (!ScoreProcessor.Mode.Disabled)
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
@ -343,7 +345,7 @@ namespace osu.Game.Screens.Play
// display the cursor above some HUD elements.
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value)
HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value)
{
HoldToQuit =
{

View File

@ -13,7 +13,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
@ -25,23 +25,7 @@ namespace osu.Game.Screens.Ranking.Expanded
public class StarRatingDisplay : CompositeDrawable, IHasCurrentValue<StarDifficulty?>
{
private Box background;
private OsuSpriteText wholePart;
private OsuSpriteText fractionPart;
private double displayedStarRating;
protected double DisplayedStarRating
{
get => displayedStarRating;
set
{
displayedStarRating = value;
var starRatingParts = value.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
wholePart.Text = starRatingParts[0];
fractionPart.Text = starRatingParts[1];
}
}
private OsuTextFlowContainer textFlow;
[Resolved]
private OsuColour colours { get; set; }
@ -105,40 +89,13 @@ namespace osu.Game.Screens.Ranking.Expanded
Icon = FontAwesome.Solid.Star,
Colour = Color4.Black
},
new FillFlowContainer
textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
wholePart = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Colour = Color4.Black,
Font = OsuFont.Numeric.With(size: 14, weight: FontWeight.Black),
UseFullGlyphHeight = false,
},
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Colour = Color4.Black,
Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator,
Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black),
UseFullGlyphHeight = false,
},
fractionPart = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Colour = Color4.Black,
Font = OsuFont.Numeric.With(size: 7, weight: FontWeight.Black),
UseFullGlyphHeight = false,
}
}
TextAnchor = Anchor.BottomLeft,
}
}
}
@ -158,21 +115,39 @@ namespace osu.Game.Screens.Ranking.Expanded
const double duration = 400;
const Easing easing = Easing.OutQuint;
ColourInfo backgroundColour;
double stars = Current.Value?.Stars ?? 0.00f;
var starRatingParts = stars.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
string wholePart = starRatingParts[0];
string fractionPart = starRatingParts[1];
string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
if (Current.Value == null)
backgroundColour = Color4.SlateGray.Opacity(0.3f);
background.FadeColour(Color4.SlateGray.Opacity(0.3f));
else
{
var rating = Current.Value.Value.DifficultyRating;
backgroundColour = rating == DifficultyRating.ExpertPlus
background.FadeColour(rating == DifficultyRating.ExpertPlus
? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959"))
: (ColourInfo)colours.ForDifficultyRating(rating);
: (ColourInfo)colours.ForDifficultyRating(rating), duration, easing);
}
background.FadeColour(backgroundColour, duration, easing);
this.TransformTo(nameof(DisplayedStarRating), Current.Value?.Stars ?? 0.0f, duration, easing);
textFlow.Clear();
textFlow.AddText($"{wholePart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 14);
s.UseFullGlyphHeight = false;
});
textFlow.AddText($"{separator}{fractionPart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 7);
s.UseFullGlyphHeight = false;
});
}
}
}

View File

@ -16,7 +16,7 @@ using osuTK.Graphics;
namespace osu.Game.Skinning
{
public class LegacyHealthDisplay : CompositeDrawable, IHealthDisplay, ISkinnableComponent
public class LegacyHealthDisplay : HealthDisplay
{
private const double epic_cutoff = 0.5;
@ -28,12 +28,6 @@ namespace osu.Game.Skinning
private bool isNewStyle;
public Bindable<double> Current { get; } = new BindableDouble(1)
{
MinValue = 0,
MaxValue = 1
};
public LegacyHealthDisplay(Skin skin)
{
this.skin = skin;
@ -83,7 +77,7 @@ namespace osu.Game.Skinning
marker.Position = fill.Position + new Vector2(fill.DrawWidth, isNewStyle ? fill.DrawHeight / 2 : 0);
}
public void Flash(JudgementResult result) => marker.Flash(result);
protected override void Flash(JudgementResult result) => marker.Flash(result);
private static Texture getTexture(Skin skin, string name) => skin.GetTexture($"scorebar-{name}");
@ -254,7 +248,7 @@ namespace osu.Game.Skinning
Main.ScaleTo(1.4f).Then().ScaleTo(1, 200, Easing.Out);
}
public class LegacyHealthPiece : CompositeDrawable, IHealthDisplay
public class LegacyHealthPiece : CompositeDrawable
{
public Bindable<double> Current { get; } = new Bindable<double>();