Merge https://github.com/ppy/osu into online-beatmap-set-overlay

This commit is contained in:
DrabWeb
2017-09-12 21:26:06 -03:00
113 changed files with 2251 additions and 1194 deletions

View File

@ -87,7 +87,9 @@ namespace osu.Game.Beatmaps
{
if (track != null) return track;
track = GetTrack();
// we want to ensure that we always have a track, even if it's a fake one.
track = GetTrack() ?? new TrackVirtual();
applyRateAdjustments();
return track;
}
@ -107,10 +109,17 @@ namespace osu.Game.Beatmaps
public virtual void Dispose()
{
track?.Dispose();
track = null;
background?.Dispose();
background = null;
}
public void DisposeTrack()
{
lock (trackLock)
{
track?.Dispose();
track = null;
}
}
}
}

View File

@ -131,7 +131,7 @@ namespace osu.Game.Graphics.Cursor
},
AdditiveLayer = new Sprite
{
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = colour.Pink,
Alpha = 0,
Texture = textures.Get(@"Cursor/menu-cursor-additive"),

View File

@ -57,6 +57,12 @@ namespace osu.Game.Graphics
private void load(FontStore store)
{
this.store = store;
}
protected override void LoadComplete()
{
base.LoadComplete();
updateTexture();
}

View File

@ -99,7 +99,8 @@ namespace osu.Game.Graphics.UserInterface
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
flash();
this.Delay(click_duration).Schedule(delegate {
this.Delay(click_duration).Schedule(delegate
{
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
spriteText.Spacing = Vector2.Zero;
glowContainer.FadeOut();
@ -140,7 +141,7 @@ namespace osu.Game.Graphics.UserInterface
colourContainer.Add(flash);
flash.Colour = ButtonColour;
flash.BlendingMode = BlendingMode.Additive;
flash.Blending = BlendingMode.Additive;
flash.Alpha = 0.3f;
flash.FadeOutFromOne(click_duration);
flash.Expire();

View File

@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface
hover = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White.Opacity(0.1f),
Alpha = 0,
},

View File

@ -152,10 +152,19 @@ namespace osu.Game
Beatmap.ValueChanged += b =>
{
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
if (lastBeatmap?.Track != b.Track)
if (!trackLoaded || lastBeatmap?.Track != b.Track)
{
lastBeatmap?.Track?.Dispose();
if (trackLoaded)
{
Debug.Assert(lastBeatmap != null);
Debug.Assert(lastBeatmap.Track != null);
lastBeatmap.DisposeTrack();
}
Audio.Track.AddItem(b.Track);
}

View File

@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Settings
backgroundBox = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = OsuColour.Gray(60),
Alpha = 0,
},

View File

@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Toolbar
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(80).Opacity(180),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0,
},
Flow = new FillFlowContainer

View File

@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Toolbar
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(150).Opacity(180),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Depth = 2,
Alpha = 0,
});

View File

@ -16,11 +16,9 @@ namespace osu.Game.Rulesets.Judgements
/// <summary>
/// A drawable object which visualises the hit result of a <see cref="Judgements.Judgement"/>.
/// </summary>
/// <typeparam name="TJudgement">The type of judgement to visualise.</typeparam>
public class DrawableJudgement<TJudgement> : Container
where TJudgement : Judgement
public class DrawableJudgement : Container
{
protected readonly TJudgement Judgement;
protected readonly Judgement Judgement;
protected readonly SpriteText JudgementText;
@ -28,21 +26,19 @@ namespace osu.Game.Rulesets.Judgements
/// Creates a drawable which visualises a <see cref="Judgements.Judgement"/>.
/// </summary>
/// <param name="judgement">The judgement to visualise.</param>
public DrawableJudgement(TJudgement judgement)
public DrawableJudgement(Judgement judgement)
{
Judgement = judgement;
AutoSizeAxes = Axes.Both;
string resultString = judgement.Result == HitResult.Hit ? judgement.ResultString : judgement.Result.GetDescription();
Children = new[]
{
JudgementText = new OsuSpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Text = resultString.ToUpper(),
Text = judgement.Result.GetDescription().ToUpper(),
Font = @"Venera",
TextSize = 16
}
@ -68,6 +64,8 @@ namespace osu.Game.Rulesets.Judgements
switch (Judgement.Result)
{
case HitResult.None:
break;
case HitResult.Miss:
this.ScaleTo(1.6f);
this.ScaleTo(1, 100, Easing.In);
@ -77,7 +75,7 @@ namespace osu.Game.Rulesets.Judgements
this.Delay(600).FadeOut(200);
break;
case HitResult.Hit:
default:
this.ScaleTo(0.9f);
this.ScaleTo(1, 500, Easing.OutElastic);

View File

@ -1,19 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Judgements
{
/// <summary>
/// Inidicates that the judgement this is attached to is a partial judgement and the scoring value may change.
/// </summary>
public interface IPartialJudgement
{
/// <summary>
/// Indicates that this partial judgement has changed and requires reprocessing.
/// <para>
/// This is set to false once the judgement has been re-processed.
/// </para>
/// </summary>
bool Changed { get; set; }
}
}

View File

@ -5,7 +5,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Judgements
{
public abstract class Judgement
public class Judgement
{
/// <summary>
/// Whether this judgement is the result of a hit or a miss.
@ -13,20 +13,35 @@ namespace osu.Game.Rulesets.Judgements
public HitResult Result;
/// <summary>
/// The offset at which this judgement occurred.
/// The maximum <see cref="HitResult"/> that can be achieved.
/// </summary>
public double TimeOffset;
public virtual HitResult MaxResult => HitResult.Perfect;
public bool IsHit => Result > HitResult.Miss;
/// <summary>
/// The offset from a perfect hit at which this judgement occurred.
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>.
/// </summary>
public double TimeOffset { get; internal set; }
public virtual bool AffectsCombo => true;
/// <summary>
/// The string representation for the result achieved.
/// The numeric representation for the result achieved.
/// </summary>
public abstract string ResultString { get; }
public int NumericResult => NumericResultFor(Result);
/// <summary>
/// The string representation for the max result achievable.
/// The numeric representation for the maximum achievable result.
/// </summary>
public abstract string MaxResultString { get; }
public int MaxNumericResult => NumericResultFor(MaxResult);
/// <summary>
/// Convert a <see cref="HitResult"/> to a numeric score representation.
/// </summary>
/// <param name="result">The value to convert.</param>
/// <returns>The number.</returns>
protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0;
}
}
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
{
protected abstract Score CreateReplayScore(Beatmap<T> beatmap);
public void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer)
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer)
{
rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay);
}

View File

@ -12,17 +12,18 @@ using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Audio;
using System.Linq;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables
{
public abstract class DrawableHitObject : Container
public abstract class DrawableHitObject : Container, IHasAccentColour
{
public readonly HitObject HitObject;
/// <summary>
/// The colour used for various elements of this DrawableHitObject.
/// </summary>
public virtual Color4 AccentColour { get; set; }
public virtual Color4 AccentColour { get; set; } = Color4.Gray;
protected DrawableHitObject(HitObject hitObject)
{
@ -33,32 +34,29 @@ namespace osu.Game.Rulesets.Objects.Drawables
public abstract class DrawableHitObject<TObject> : DrawableHitObject
where TObject : HitObject
{
public event Action<DrawableHitObject, Judgement> OnJudgement;
public new readonly TObject HitObject;
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
}
}
public abstract class DrawableHitObject<TObject, TJudgement> : DrawableHitObject<TObject>
where TObject : HitObject
where TJudgement : Judgement
{
public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
public override bool HandleInput => Interactive;
public bool Interactive = true;
public TJudgement Judgement;
/// <summary>
/// Whether this <see cref="DrawableHitObject"/> can be judged.
/// </summary>
protected virtual bool ProvidesJudgement => true;
private readonly List<Judgement> judgements = new List<Judgement>();
public IReadOnlyList<Judgement> Judgements => judgements;
protected List<SampleChannel> Samples = new List<SampleChannel>();
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
Depth = (float)hitObject.StartTime;
}
private ArmedState state;
@ -95,69 +93,80 @@ namespace osu.Game.Rulesets.Objects.Drawables
UpdateState(State);
}
/// <summary>
/// Whether this hit object and all of its nested hit objects have been judged.
/// </summary>
public bool Judged => (Judgement?.Result ?? HitResult.None) != HitResult.None && (NestedHitObjects?.All(h => h.Judged) ?? true);
private bool hasJudgementResult;
private bool judgementOccurred;
/// <summary>
/// Process a hit of this hitobject. Carries out judgement.
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
/// </summary>
/// <returns>Whether a hit was processed.</returns>
protected bool UpdateJudgement(bool userTriggered)
public virtual bool AllJudged => (!ProvidesJudgement || hasJudgementResult) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
/// <summary>
/// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>.
/// </summary>
/// <param name="judgement">The <see cref="Judgement"/>.</param>
protected void AddJudgement(Judgement judgement)
{
if (Judgement == null)
return false;
hasJudgementResult = judgement.Result >= HitResult.Miss;
judgementOccurred = true;
var partial = Judgement as IPartialJudgement;
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
judgement.TimeOffset = Time.Current - endTime;
// Never re-process non-partial hits
if (Judgement.Result != HitResult.None && partial == null)
return false;
judgements.Add(judgement);
// Update the judgement state
double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
Judgement.TimeOffset = Time.Current - endTime;
// Update the judgement state
bool hadResult = Judgement.Result != HitResult.None;
CheckJudgement(userTriggered);
// Don't process judgements with no result
if (Judgement.Result == HitResult.None)
return false;
// Don't process judgements that previously had results but the results were unchanged
if (hadResult && partial?.Changed != true)
return false;
switch (Judgement.Result)
switch (judgement.Result)
{
default:
State = ArmedState.Hit;
case HitResult.None:
break;
case HitResult.Miss:
State = ArmedState.Miss;
break;
default:
State = ArmedState.Hit;
break;
}
OnJudgement?.Invoke(this);
if (partial != null)
partial.Changed = false;
return true;
OnJudgement?.Invoke(this, judgement);
}
protected virtual void CheckJudgement(bool userTriggered)
/// <summary>
/// Processes this <see cref="DrawableHitObject"/>, checking if any judgements have occurred.
/// </summary>
/// <param name="userTriggered">Whether the user triggered this process.</param>
/// <returns>Whether a judgement has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>s.</returns>
protected bool UpdateJudgement(bool userTriggered)
{
judgementOccurred = false;
if (AllJudged || State != ArmedState.Idle)
return false;
if (NestedHitObjects != null)
{
foreach (var d in NestedHitObjects)
d.CheckJudgement(userTriggered);
judgementOccurred |= d.UpdateJudgement(userTriggered);
}
if (!ProvidesJudgement || hasJudgementResult || judgementOccurred)
return judgementOccurred;
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
CheckForJudgements(userTriggered, Time.Current - endTime);
return judgementOccurred;
}
/// <summary>
/// Checks if any judgements have occurred for this <see cref="DrawableHitObject"/>. This method must construct
/// all <see cref="Judgement"/>s and notify of them through <see cref="AddJudgement"/>.
/// </summary>
/// <param name="userTriggered">Whether the user triggered this check.</param>
/// <param name="timeOffset">The offset from the <see cref="HitObject"/> end time at which this check occurred. A <paramref name="timeOffset"/> &gt; 0
/// implies that this check occurred after the end time of <see cref="HitObject"/>. </param>
protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { }
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@ -178,25 +187,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
channel.Volume.Value = sample.Volume;
Samples.Add(channel);
}
//we may be setting a custom judgement in test cases or what not.
if (Judgement == null)
Judgement = CreateJudgement();
}
private List<DrawableHitObject<TObject, TJudgement>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<TObject, TJudgement>> NestedHitObjects => nestedHitObjects;
private List<DrawableHitObject<TObject>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
protected virtual void AddNested(DrawableHitObject<TObject, TJudgement> h)
protected virtual void AddNested(DrawableHitObject<TObject> h)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject<TObject, TJudgement>>();
nestedHitObjects = new List<DrawableHitObject<TObject>>();
h.OnJudgement += d => OnJudgement?.Invoke(d);
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
nestedHitObjects.Add(h);
}
protected abstract TJudgement CreateJudgement();
protected abstract void UpdateState(ArmedState state);
}
}

View File

@ -3,19 +3,17 @@
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Drawables
{
/// <summary>
/// A basic class that overrides <see cref="DrawableHitObject{TObject, TJudgement}"/> and implements <see cref="IScrollingHitObject"/>.
/// A basic class that overrides <see cref="DrawableHitObject{TObject}"/> and implements <see cref="IScrollingHitObject"/>.
/// This object does not need to have its <see cref="Drawable.RelativePositionAxes"/> set to be able to scroll, as this will
/// will be set by the scrolling container that contains it.
/// </summary>
public abstract class DrawableScrollingHitObject<TObject, TJudgement> : DrawableHitObject<TObject, TJudgement>, IScrollingHitObject
public abstract class DrawableScrollingHitObject<TObject> : DrawableHitObject<TObject>, IScrollingHitObject
where TObject : HitObject
where TJudgement : Judgement
{
public BindableDouble LifetimeOffset { get; } = new BindableDouble();
@ -57,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
set { lifetimeEnd = value; }
}
protected override void AddNested(DrawableHitObject<TObject, TJudgement> h)
protected override void AddNested(DrawableHitObject<TObject> h)
{
var scrollingHitObject = h as IScrollingHitObject;
scrollingHitObject?.LifetimeOffset.BindTo(LifetimeOffset);

View File

@ -10,17 +10,34 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary>
/// Indicates that the object has not been judged yet.
/// </summary>
[Description("")]
[Description(@"")]
None,
/// <summary>
/// Indicates that the object has been judged as a miss.
/// </summary>
[Description(@"Miss")]
Miss,
[Description(@"Meh")]
Meh,
/// <summary>
/// Indicates that the object has been judged as a hit.
/// Optional judgement.
/// </summary>
[Description(@"Hit")]
Hit,
[Description(@"OK")]
Ok,
[Description(@"Good")]
Good,
[Description(@"Great")]
Great,
/// <summary>
/// Optional judgement.
/// </summary>
[Description(@"Perfect")]
Perfect,
}
}
}

View File

@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.Objects
/// </summary>
public SampleInfoList Samples = new SampleInfoList();
/// <summary>
/// Whether this <see cref="HitObject"/> is in Kiai time.
/// </summary>
public bool Kiai { get; private set; }
/// <summary>
/// Applies default values to this HitObject.
/// </summary>
@ -38,6 +43,9 @@ namespace osu.Game.Rulesets.Objects
public virtual void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
SoundControlPoint soundPoint = controlPointInfo.SoundPointAt(StartTime);
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
Kiai |= effectPoint.KiaiMode;
// Initialize first sample
Samples.ForEach(s => initializeSampleInfo(s, soundPoint));

View File

@ -135,14 +135,13 @@ namespace osu.Game.Rulesets.Scoring
}
}
public abstract class ScoreProcessor<TObject, TJudgement> : ScoreProcessor
public abstract class ScoreProcessor<TObject> : ScoreProcessor
where TObject : HitObject
where TJudgement : Judgement
{
/// <summary>
/// All judgements held by this ScoreProcessor.
/// </summary>
protected readonly List<TJudgement> Judgements = new List<TJudgement>();
protected readonly List<Judgement> Judgements = new List<Judgement>();
public override bool HasFailed => Health.Value == Health.MinValue;
@ -150,7 +149,7 @@ namespace osu.Game.Rulesets.Scoring
{
}
protected ScoreProcessor(RulesetContainer<TObject, TJudgement> rulesetContainer)
protected ScoreProcessor(RulesetContainer<TObject> rulesetContainer)
{
Judgements.Capacity = rulesetContainer.Beatmap.HitObjects.Count;
@ -171,7 +170,7 @@ namespace osu.Game.Rulesets.Scoring
/// Adds a judgement to this ScoreProcessor.
/// </summary>
/// <param name="judgement">The judgement to add.</param>
protected void AddJudgement(TJudgement judgement)
protected void AddJudgement(Judgement judgement)
{
bool exists = Judgements.Contains(judgement);
@ -181,10 +180,12 @@ namespace osu.Game.Rulesets.Scoring
{
switch (judgement.Result)
{
case HitResult.None:
break;
case HitResult.Miss:
Combo.Value = 0;
break;
case HitResult.Hit:
default:
Combo.Value++;
break;
}
@ -195,8 +196,6 @@ namespace osu.Game.Rulesets.Scoring
NotifyNewJudgement(judgement);
}
else
OnJudgementChanged(judgement);
UpdateFailed();
}
@ -210,20 +209,8 @@ namespace osu.Game.Rulesets.Scoring
/// <summary>
/// Updates any values that need post-processing. Invoked when a new judgement has occurred.
/// <para>
/// This is not triggered when existing judgements are changed - for that see <see cref="OnJudgementChanged(TJudgement)"/>.
/// </para>
/// </summary>
/// <param name="judgement">The judgement that triggered this calculation.</param>
protected abstract void OnNewJudgement(TJudgement judgement);
/// <summary>
/// Updates any values that need post-processing. Invoked when an existing judgement has changed.
/// <para>
/// This is not triggered when a new judgement has occurred - for that see <see cref="OnNewJudgement(TJudgement)"/>.
/// </para>
/// </summary>
/// <param name="judgement">The judgement that triggered this calculation.</param>
protected virtual void OnJudgementChanged(TJudgement judgement) { }
protected abstract void OnNewJudgement(Judgement judgement);
}
}

View File

@ -4,7 +4,6 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Rulesets.Judgements;
@ -14,9 +13,7 @@ using System.Linq;
namespace osu.Game.Rulesets.UI
{
public abstract class Playfield<TObject, TJudgement> : Container
where TObject : HitObject
where TJudgement : Judgement
public abstract class Playfield : Container
{
/// <summary>
/// The HitObjects contained in this Playfield.
@ -70,7 +67,7 @@ namespace osu.Game.Rulesets.UI
public override Axes RelativeSizeAxes
{
get { return Axes.Both; }
set { throw new InvalidOperationException($@"{nameof(Playfield<TObject, TJudgement>)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
}
/// <summary>
@ -82,19 +79,20 @@ namespace osu.Game.Rulesets.UI
/// Adds a DrawableHitObject to this Playfield.
/// </summary>
/// <param name="h">The DrawableHitObject to add.</param>
public virtual void Add(DrawableHitObject<TObject, TJudgement> h) => HitObjects.Add(h);
public virtual void Add(DrawableHitObject h) => HitObjects.Add(h);
/// <summary>
/// Remove a DrawableHitObject from this Playfield.
/// </summary>
/// <param name="h">The DrawableHitObject to remove.</param>
public virtual void Remove(DrawableHitObject<TObject, TJudgement> h) => HitObjects.Remove(h);
public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h);
/// <summary>
/// Triggered when an object's Judgement is updated.
/// Triggered when a new <see cref="Judgement"/> occurs on a <see cref="DrawableHitObject"/>.
/// </summary>
/// <param name="judgedObject">The object that Judgement has been updated for.</param>
public virtual void OnJudgement(DrawableHitObject<TObject, TJudgement> judgedObject) { }
/// <param name="judgedObject">The object that <paramref name="judgement"/> occured for.</param>
/// <param name="judgement">The <see cref="Judgement"/> that occurred.</param>
public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { }
public class HitObjectContainer : CompositeDrawable
{

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.UI
/// <summary>
/// Base RulesetContainer. Doesn't hold objects.
/// <para>
/// Should not be derived - derive <see cref="RulesetContainer{TObject,TJudgement}"/> instead.
/// Should not be derived - derive <see cref="RulesetContainer{TObject}"/> instead.
/// </para>
/// </summary>
public abstract class RulesetContainer : Container
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.UI
public event Action OnAllJudged;
/// <summary>
/// Whether to apply adjustments to the child <see cref="Playfield{TObject,TJudgement}"/> based on our own size.
/// Whether to apply adjustments to the child <see cref="Playfield"/> based on our own size.
/// </summary>
public bool AspectAdjust = true;
@ -77,13 +77,6 @@ namespace osu.Game.Rulesets.UI
Ruleset = ruleset;
}
[BackgroundDependencyLoader]
private void load()
{
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
}
/// <summary>
/// Checks whether all HitObjects have been judged, and invokes OnAllJudged.
/// </summary>
@ -123,13 +116,15 @@ namespace osu.Game.Rulesets.UI
/// RulesetContainer that applies conversion to Beatmaps. Does not contain a Playfield
/// and does not load drawable hit objects.
/// <para>
/// Should not be derived - derive <see cref="RulesetContainer{TObject,TJudgement}"/> instead.
/// Should not be derived - derive <see cref="RulesetContainer{TObject}"/> instead.
/// </para>
/// </summary>
/// <typeparam name="TObject">The type of HitObject contained by this RulesetContainer.</typeparam>
public abstract class RulesetContainer<TObject> : RulesetContainer
where TObject : HitObject
{
public event Action<Judgement> OnJudgement;
/// <summary>
/// The Beatmap
/// </summary>
@ -155,6 +150,20 @@ namespace osu.Game.Rulesets.UI
/// </summary>
protected readonly bool IsForCurrentRuleset;
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
protected override bool AllObjectsJudged => drawableObjects.All(h => h.AllJudged);
/// <summary>
/// The playfield.
/// </summary>
public Playfield Playfield { get; private set; }
protected override Container<Drawable> Content => content;
private Container content;
private readonly List<DrawableHitObject<TObject>> drawableObjects = new List<DrawableHitObject<TObject>>();
/// <summary>
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
/// Creates a hit renderer for a beatmap.
@ -162,7 +171,7 @@ namespace osu.Game.Rulesets.UI
/// <param name="ruleset">The ruleset being repesented.</param>
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
internal RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
: base(ruleset)
{
Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
@ -194,78 +203,13 @@ namespace osu.Game.Rulesets.UI
// Post-process the beatmap
processor.PostProcess(Beatmap);
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
// Add mods, should always be the last thing applied to give full control to mods
applyMods(Mods);
}
/// <summary>
/// Applies the active mods to this RulesetContainer.
/// </summary>
/// <param name="mods"></param>
private void applyMods(IEnumerable<Mod> mods)
{
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
mod.ApplyToRulesetContainer(this);
}
/// <summary>
/// Creates a processor to perform post-processing operations
/// on HitObjects in converted Beatmaps.
/// </summary>
/// <returns>The Beatmap processor.</returns>
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
/// <summary>
/// Creates a converter to convert Beatmap to a specific mode.
/// </summary>
/// <returns>The Beatmap converter.</returns>
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
}
/// <summary>
/// A derivable RulesetContainer that manages the Playfield and HitObjects.
/// </summary>
/// <typeparam name="TObject">The type of HitObject contained by this RulesetContainer.</typeparam>
/// <typeparam name="TJudgement">The type of Judgement of DrawableHitObjects contained by this RulesetContainer.</typeparam>
public abstract class RulesetContainer<TObject, TJudgement> : RulesetContainer<TObject>
where TObject : HitObject
where TJudgement : Judgement
{
public event Action<TJudgement> OnJudgement;
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
/// <summary>
/// All the converted hit objects contained by this hit renderer.
/// </summary>
public new IEnumerable<TObject> Objects => Beatmap.HitObjects;
protected override bool AllObjectsJudged => drawableObjects.All(h => h.Judged);
/// <summary>
/// The playfield.
/// </summary>
public Playfield<TObject, TJudgement> Playfield { get; private set; }
protected override Container<Drawable> Content => content;
private Container content;
private readonly List<DrawableHitObject<TObject, TJudgement>> drawableObjects = new List<DrawableHitObject<TObject, TJudgement>>();
/// <summary>
/// Creates a hit renderer for a beatmap.
/// </summary>
/// <param name="ruleset">The ruleset being repesented.</param>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
{
}
[BackgroundDependencyLoader]
private void load()
{
@ -280,6 +224,19 @@ namespace osu.Game.Rulesets.UI
loadObjects();
}
/// <summary>
/// Applies the active mods to this RulesetContainer.
/// </summary>
/// <param name="mods"></param>
private void applyMods(IEnumerable<Mod> mods)
{
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
mod.ApplyToRulesetContainer(this);
}
public override void SetReplay(Replay replay)
{
base.SetReplay(replay);
@ -302,7 +259,12 @@ namespace osu.Game.Rulesets.UI
if (drawableObject == null)
continue;
drawableObject.OnJudgement += onJudgement;
drawableObject.OnJudgement += (d, j) =>
{
Playfield.OnJudgement(d, j);
OnJudgement?.Invoke(j);
CheckAllJudged();
};
drawableObjects.Add(drawableObject);
Playfield.Add(drawableObject);
@ -319,36 +281,36 @@ namespace osu.Game.Rulesets.UI
}
/// <summary>
/// In some cases we want to apply changes to the relative size of our contained <see cref="Playfield{TObject, TJudgement}"/> based on custom conditions.
/// Creates a processor to perform post-processing operations
/// on HitObjects in converted Beatmaps.
/// </summary>
/// <returns>The Beatmap processor.</returns>
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
/// <summary>
/// In some cases we want to apply changes to the relative size of our contained <see cref="Playfield"/> based on custom conditions.
/// </summary>
/// <returns></returns>
protected virtual Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); //a sane default
/// <summary>
/// Triggered when an object's Judgement is updated.
/// Creates a converter to convert Beatmap to a specific mode.
/// </summary>
/// <param name="judgedObject">The object that Judgement has been updated for.</param>
private void onJudgement(DrawableHitObject<TObject, TJudgement> judgedObject)
{
Playfield.OnJudgement(judgedObject);
OnJudgement?.Invoke(judgedObject.Judgement);
CheckAllJudged();
}
/// <returns>The Beatmap converter.</returns>
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
/// <summary>
/// Creates a DrawableHitObject from a HitObject.
/// </summary>
/// <param name="h">The HitObject to make drawable.</param>
/// <returns>The DrawableHitObject.</returns>
protected abstract DrawableHitObject<TObject, TJudgement> GetVisualRepresentation(TObject h);
protected abstract DrawableHitObject<TObject> GetVisualRepresentation(TObject h);
/// <summary>
/// Creates a Playfield.
/// </summary>
/// <returns>The Playfield.</returns>
protected abstract Playfield<TObject, TJudgement> CreatePlayfield();
protected abstract Playfield CreatePlayfield();
}
/// <summary>
@ -356,11 +318,9 @@ namespace osu.Game.Rulesets.UI
/// </summary>
/// <typeparam name="TPlayfield">The type of Playfield contained by this RulesetContainer.</typeparam>
/// <typeparam name="TObject">The type of HitObject contained by this RulesetContainer.</typeparam>
/// <typeparam name="TJudgement">The type of Judgement of DrawableHitObjects contained by this RulesetContainer.</typeparam>
public abstract class RulesetContainer<TPlayfield, TObject, TJudgement> : RulesetContainer<TObject, TJudgement>
public abstract class RulesetContainer<TPlayfield, TObject> : RulesetContainer<TObject>
where TObject : HitObject
where TJudgement : Judgement
where TPlayfield : Playfield<TObject, TJudgement>
where TPlayfield : Playfield
{
/// <summary>
/// The playfield.

View File

@ -11,19 +11,15 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.UI
{
/// <summary>
/// A type of <see cref="Playfield{TObject, TJudgement}"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
/// </summary>
public class ScrollingPlayfield<TObject, TJudgement> : Playfield<TObject, TJudgement>
where TObject : HitObject
where TJudgement : Judgement
public class ScrollingPlayfield : Playfield
{
/// <summary>
/// The default span of time visible by the length of the scrolling axes.
@ -65,7 +61,7 @@ namespace osu.Game.Rulesets.UI
public new readonly ScrollingHitObjectContainer HitObjects;
/// <summary>
/// Creates a new <see cref="ScrollingPlayfield{TObject, TJudgement}"/>.
/// Creates a new <see cref="ScrollingPlayfield"/>.
/// </summary>
/// <param name="scrollingAxes">The axes on which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width</param>
@ -77,21 +73,21 @@ namespace osu.Game.Rulesets.UI
HitObjects.Reversed.BindTo(Reversed);
}
private List<ScrollingPlayfield<TObject, TJudgement>> nestedPlayfields;
private List<ScrollingPlayfield> nestedPlayfields;
/// <summary>
/// All the <see cref="ScrollingPlayfield{TObject, TJudgement}"/>s nested inside this playfield.
/// All the <see cref="ScrollingPlayfield"/>s nested inside this playfield.
/// </summary>
public IEnumerable<ScrollingPlayfield<TObject, TJudgement>> NestedPlayfields => nestedPlayfields;
public IEnumerable<ScrollingPlayfield> NestedPlayfields => nestedPlayfields;
/// <summary>
/// Adds a <see cref="ScrollingPlayfield{TObject, TJudgement}"/> to this playfield. The nested <see cref="ScrollingPlayfield{TObject, TJudgement}"/>
/// Adds a <see cref="ScrollingPlayfield"/> to this playfield. The nested <see cref="ScrollingPlayfield"/>
/// will be given all of the same speed adjustments as this playfield.
/// </summary>
/// <param name="otherPlayfield">The <see cref="ScrollingPlayfield{TObject, TJudgement}"/> to add.</param>
protected void AddNested(ScrollingPlayfield<TObject, TJudgement> otherPlayfield)
/// <param name="otherPlayfield">The <see cref="ScrollingPlayfield"/> to add.</param>
protected void AddNested(ScrollingPlayfield otherPlayfield)
{
if (nestedPlayfields == null)
nestedPlayfields = new List<ScrollingPlayfield<TObject, TJudgement>>();
nestedPlayfields = new List<ScrollingPlayfield>();
nestedPlayfields.Add(otherPlayfield);
}
@ -119,7 +115,7 @@ namespace osu.Game.Rulesets.UI
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
}
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield<TObject, TJudgement>>
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
{
private double valueAt(double time)
{
@ -131,8 +127,8 @@ namespace osu.Game.Rulesets.UI
public override string TargetMember => "VisibleTimeRange.Value";
protected override void Apply(ScrollingPlayfield<TObject, TJudgement> d, double time) => d.VisibleTimeRange.Value = valueAt(time);
protected override void ReadIntoStartValue(ScrollingPlayfield<TObject, TJudgement> d) => StartValue = d.VisibleTimeRange.Value;
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
}
/// <summary>

View File

@ -9,7 +9,6 @@ using osu.Framework.Lists;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Timing;
@ -17,17 +16,16 @@ using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.UI
{
/// <summary>
/// A type of <see cref="RulesetContainer{TPlayfield,TObject,TJudgement}"/> that supports a <see cref="ScrollingPlayfield{TObject, TJudgement}"/>.
/// <see cref="HitObject"/>s inside this <see cref="RulesetContainer{TPlayfield,TObject,TJudgement}"/> will scroll within the playfield.
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
/// <see cref="HitObject"/>s inside this <see cref="RulesetContainer{TPlayfield,TObject}"/> will scroll within the playfield.
/// </summary>
public abstract class ScrollingRulesetContainer<TPlayfield, TObject, TJudgement> : RulesetContainer<TPlayfield, TObject, TJudgement>
public abstract class ScrollingRulesetContainer<TPlayfield, TObject> : RulesetContainer<TPlayfield, TObject>
where TObject : HitObject
where TJudgement : Judgement
where TPlayfield : ScrollingPlayfield<TObject, TJudgement>
where TPlayfield : ScrollingPlayfield
{
/// <summary>
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
/// inside this <see cref="RulesetContainer{TPlayfield,TObject,TJudgement}"/>.
/// inside this <see cref="RulesetContainer{TPlayfield,TObject}"/>.
/// </summary>
/// <returns></returns>
protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
@ -88,7 +86,7 @@ namespace osu.Game.Rulesets.UI
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
}
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield<TObject, TJudgement> playfield)
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
{
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));

View File

@ -83,7 +83,7 @@ namespace osu.Game.Screens.Menu
{
EdgeSmoothness = new Vector2(1.5f, 0),
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},

View File

@ -52,7 +52,7 @@ namespace osu.Game.Screens.Menu
{
Alpha = 0,
Triangles = false,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Interactive = false,
Colour = Color4.DarkGray,
Ripple = false

View File

@ -73,7 +73,7 @@ namespace osu.Game.Screens.Menu
{
texture = Texture.WhitePixel;
AccentColour = new Color4(1, 1, 1, 0.2f);
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
}
[BackgroundDependencyLoader]

View File

@ -50,7 +50,7 @@ namespace osu.Game.Screens.Menu
RelativeSizeAxes = Axes.Y,
Width = box_width,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
},
rightBox = new Box
{
@ -59,7 +59,7 @@ namespace osu.Game.Screens.Menu
RelativeSizeAxes = Axes.Y,
Width = box_width,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
}
};
}

View File

@ -108,7 +108,7 @@ namespace osu.Game.Screens.Menu
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0
}
}
@ -169,7 +169,7 @@ namespace osu.Game.Screens.Menu
flashLayer = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},
@ -272,14 +272,22 @@ namespace osu.Game.Screens.Menu
const float scale_adjust_cutoff = 0.4f;
const float velocity_adjust_cutoff = 0.98f;
const float paused_velocity = 0.5f;
var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value.Track?.CurrentAmplitudes.Maximum ?? 0 : 0;
logoAmplitudeContainer.ScaleTo(1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 75, Easing.OutQuint);
if (Beatmap.Value.Track.IsRunning)
{
var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value.Track.CurrentAmplitudes.Maximum : 0;
logoAmplitudeContainer.ScaleTo(1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 75, Easing.OutQuint);
if (maxAmplitude > velocity_adjust_cutoff)
triangles.Velocity = 1 + Math.Max(0, maxAmplitude - velocity_adjust_cutoff) * 50;
if (maxAmplitude > velocity_adjust_cutoff)
triangles.Velocity = 1 + Math.Max(0, maxAmplitude - velocity_adjust_cutoff) * 50;
else
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, 1, 0.995f, Time.Elapsed);
}
else
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, 1, 0.995f, Time.Elapsed);
{
triangles.Velocity = paused_velocity;
}
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)

View File

@ -100,7 +100,7 @@ namespace osu.Game.Screens
Colour = getColourFor(GetType()),
Alpha = 0.2f,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
},
textContainer = new FillFlowContainer
{

View File

@ -304,7 +304,7 @@ namespace osu.Game.Screens.Select.Leaderboards
BlurSigma = new Vector2(4),
CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Size = new Vector2(3f),
Children = new[]
{

View File

@ -119,7 +119,7 @@ namespace osu.Game.Screens.Select.Options
{
RelativeSizeAxes = Axes.Both,
EdgeSmoothness = new Vector2(1.5f, 0),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},

View File

@ -215,7 +215,6 @@
<Compile Include="Rulesets\Objects\Types\IHasYPosition.cs" />
<Compile Include="Rulesets\Replays\Replay.cs" />
<Compile Include="Rulesets\Judgements\DrawableJudgement.cs" />
<Compile Include="Rulesets\Judgements\IPartialJudgement.cs" />
<Compile Include="Rulesets\Replays\FramedReplayInputHandler.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\ModType.cs" />