1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 14:55:37 +08:00
osu-lazer/osu.Game/Rulesets/Objects/HitObjectLifetimeEntry.cs
ekrctb 812a4b412a Move judgement result revert logic to Playfield
Previously, some judgement results were not reverted
when the source DHO is not alive (e.g. frames skipped in editor).
Now, all results are reverted in the exact reverse order.
2023-01-19 19:57:13 +09:00

105 lines
4.1 KiB
C#

// 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.Framework.Graphics.Performance;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Objects
{
/// <summary>
/// A <see cref="LifetimeEntry"/> that stores the lifetime for a <see cref="HitObject"/>.
/// </summary>
public class HitObjectLifetimeEntry : LifetimeEntry
{
/// <summary>
/// The <see cref="HitObject"/>.
/// </summary>
public readonly HitObject HitObject;
/// <summary>
/// The result that <see cref="HitObject"/> was judged with.
/// This is set by the accompanying <see cref="DrawableHitObject"/>, and reused when required for rewinding.
/// </summary>
internal JudgementResult? Result;
private readonly IBindable<double> startTimeBindable = new BindableDouble();
internal event Action? RevertResult;
/// <summary>
/// Creates a new <see cref="HitObjectLifetimeEntry"/>.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to store the lifetime of.</param>
public HitObjectLifetimeEntry(HitObject hitObject)
{
HitObject = hitObject;
startTimeBindable.BindTo(HitObject.StartTimeBindable);
startTimeBindable.BindValueChanged(_ => SetInitialLifetime(), true);
// Subscribe to this event before the DrawableHitObject so that the local callback is invoked before the entry is re-applied as a result of DefaultsApplied.
// This way, the DrawableHitObject can use OnApply() to overwrite the LifetimeStart that was set inside setInitialLifetime().
HitObject.DefaultsApplied += _ => SetInitialLifetime();
}
// The lifetime, as set by the hitobject.
private double realLifetimeStart = double.MinValue;
private double realLifetimeEnd = double.MaxValue;
// This method is called even if `start == LifetimeStart` when `KeepAlive` is true (necessary to update `realLifetimeStart`).
protected override void SetLifetimeStart(double start)
{
realLifetimeStart = start;
if (!keepAlive)
base.SetLifetimeStart(start);
}
protected override void SetLifetimeEnd(double end)
{
realLifetimeEnd = end;
if (!keepAlive)
base.SetLifetimeEnd(end);
}
private bool keepAlive;
/// <summary>
/// Whether the <see cref="HitObject"/> should be kept always alive.
/// </summary>
internal bool KeepAlive
{
set
{
if (keepAlive == value)
return;
keepAlive = value;
if (keepAlive)
SetLifetime(double.MinValue, double.MaxValue);
else
SetLifetime(realLifetimeStart, realLifetimeEnd);
}
}
/// <summary>
/// A safe offset prior to the start time of <see cref="HitObject"/> at which it may begin displaying contents.
/// By default, <see cref="HitObject"/>s are assumed to display their contents within 10 seconds prior to their start time.
/// </summary>
/// <remarks>
/// This is only used as an optimisation to delay the initial application of the <see cref="HitObject"/> to a <see cref="DrawableHitObject"/>.
/// A more accurate <see cref="LifetimeEntry.LifetimeStart"/> should be set on the hit object application, for further optimisation.
/// </remarks>
protected virtual double InitialLifetimeOffset => 10000;
/// <summary>
/// Set <see cref="LifetimeEntry.LifetimeStart"/> using <see cref="InitialLifetimeOffset"/>.
/// </summary>
internal void SetInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
internal void OnRevertResult() => RevertResult?.Invoke();
}
}