1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-12 17:57:21 +08:00

Create synthetic LifetimeEntry for a DHO when not supplied

Now, a DHO is always associated with a HitObjectLifetimeEntry while used.
Result is always stored in the entry, and not in the DHO.
This commit is contained in:
ekrctb 2021-04-19 20:37:06 +09:00
parent 5afdc3ff66
commit 2c487ddb70
2 changed files with 55 additions and 52 deletions

View File

@ -39,7 +39,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary> /// <summary>
/// The <see cref="HitObject"/> currently represented by this <see cref="DrawableHitObject"/>. /// The <see cref="HitObject"/> currently represented by this <see cref="DrawableHitObject"/>.
/// </summary> /// </summary>
public HitObject HitObject { get; private set; } public HitObject HitObject => lifetimeEntry?.HitObject ?? initialHitObject;
/// <summary>
/// The <see cref="HitObject"/> given in the constructor that will be applied when loaded.
/// </summary>
private HitObject initialHitObject;
/// <summary> /// <summary>
/// The parenting <see cref="DrawableHitObject"/>, if any. /// The parenting <see cref="DrawableHitObject"/>, if any.
@ -108,7 +113,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary> /// <summary>
/// The scoring result of this <see cref="DrawableHitObject"/>. /// The scoring result of this <see cref="DrawableHitObject"/>.
/// </summary> /// </summary>
public JudgementResult Result { get; private set; } public JudgementResult Result => lifetimeEntry?.Result;
/// <summary> /// <summary>
/// The relative X position of this hit object for sample playback balance adjustment. /// The relative X position of this hit object for sample playback balance adjustment.
@ -140,11 +145,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </remarks> /// </remarks>
public IBindable<ArmedState> State => state; public IBindable<ArmedState> State => state;
/// <summary>
/// Whether <see cref="HitObject"/> is currently applied.
/// </summary>
private bool hasHitObjectApplied;
/// <summary> /// <summary>
/// The <see cref="HitObjectLifetimeEntry"/> controlling the lifetime of the currently-attached <see cref="HitObject"/>. /// The <see cref="HitObjectLifetimeEntry"/> controlling the lifetime of the currently-attached <see cref="HitObject"/>.
/// </summary> /// </summary>
@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </param> /// </param>
protected DrawableHitObject([CanBeNull] HitObject initialHitObject = null) protected DrawableHitObject([CanBeNull] HitObject initialHitObject = null)
{ {
HitObject = initialHitObject; this.initialHitObject = initialHitObject;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -184,8 +184,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
{ {
base.LoadAsyncComplete(); base.LoadAsyncComplete();
if (HitObject != null) if (initialHitObject != null)
Apply(HitObject, lifetimeEntry); {
Apply(initialHitObject, null);
initialHitObject = null;
}
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -209,25 +212,35 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (lifetimeEntry != null) if (lifetimeEntry != null)
{ {
applyEntry(lifetimeEntry); if (lifetimeEntry.HitObject != hitObject)
throw new InvalidOperationException($"{nameof(HitObjectLifetimeEntry)} has different {nameof(HitObject)} from the specified one.");
apply(lifetimeEntry);
} }
else else
{ {
applyHitObject(hitObject); var unmanagedEntry = new UnmanagedHitObjectEntry(hitObject, this);
apply(unmanagedEntry);
// Set default lifetime for a non-pooled DHO // Set default lifetime for a non-pooled DHO
LifetimeStart = hitObject.StartTime - InitialLifetimeOffset; LifetimeStart = hitObject.StartTime - InitialLifetimeOffset;
} }
} }
private void applyHitObject([NotNull] HitObject hitObject) /// <summary>
/// Applies a new <see cref="HitObjectLifetimeEntry"/> to be represented by this <see cref="DrawableHitObject"/>.
/// </summary>
private void apply([NotNull] HitObjectLifetimeEntry entry)
{ {
freeHitObject(); free();
HitObject = hitObject; lifetimeEntry = entry;
LifetimeStart = entry.LifetimeStart;
LifetimeEnd = entry.LifetimeEnd;
// Ensure this DHO has a result. // Ensure this DHO has a result.
Result ??= CreateResult(HitObject.CreateJudgement()) entry.Result ??= CreateResult(HitObject.CreateJudgement())
?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); ?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
// Copy back the result to the entry for potential future retrieval. // Copy back the result to the entry for potential future retrieval.
@ -281,30 +294,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
else else
updateState(ArmedState.Idle, true); updateState(ArmedState.Idle, true);
} }
hasHitObjectApplied = true;
}
private void applyEntry([NotNull] HitObjectLifetimeEntry entry)
{
freeEntry();
setLifetime(entry.LifetimeStart, entry.LifetimeEnd);
lifetimeEntry = entry;
// Copy any existing result from the entry (required for rewind / judgement revert).
Result = entry.Result;
applyHitObject(entry.HitObject);
} }
/// <summary> /// <summary>
/// Removes the currently applied <see cref="HitObject"/> /// Removes the currently applied <see cref="lifetimeEntry"/>
/// </summary> /// </summary>
private void freeHitObject() private void free()
{ {
if (!hasHitObjectApplied) if (lifetimeEntry == null) return;
return;
StartTimeBindable.UnbindFrom(HitObject.StartTimeBindable); StartTimeBindable.UnbindFrom(HitObject.StartTimeBindable);
if (HitObject is IHasComboInformation combo) if (HitObject is IHasComboInformation combo)
@ -336,24 +333,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
OnFree(); OnFree();
HitObject = null;
ParentHitObject = null; ParentHitObject = null;
Result = null;
clearExistingStateTransforms();
hasHitObjectApplied = false;
}
private void freeEntry()
{
freeHitObject();
if (lifetimeEntry == null) return;
lifetimeEntry = null; lifetimeEntry = null;
setLifetime(double.MaxValue, double.MaxValue); clearExistingStateTransforms();
} }
protected sealed override void FreeAfterUse() protected sealed override void FreeAfterUse()
@ -364,7 +347,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (!IsInPool) if (!IsInPool)
return; return;
freeEntry(); free();
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,20 @@
// 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.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Objects
{
internal class UnmanagedHitObjectEntry : HitObjectLifetimeEntry
{
public readonly DrawableHitObject DrawableHitObject;
public UnmanagedHitObjectEntry(HitObject hitObject, DrawableHitObject drawableHitObject)
: base(hitObject)
{
DrawableHitObject = drawableHitObject;
LifetimeStart = DrawableHitObject.LifetimeStart;
LifetimeEnd = DrawableHitObject.LifetimeEnd;
}
}
}