2022-07-27 15:19:21 +08:00
// 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 osu.Framework.Allocation ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Timing ;
using osu.Game.Rulesets.Objects ;
using osu.Game.Rulesets.UI ;
using osu.Game.Skinning ;
namespace osu.Game.Screens.Play.HUD
{
public abstract class SongProgress : OverlayContainer , ISkinnableDrawable
{
public bool UsesFixedAnchor { get ; set ; }
2022-07-28 15:12:49 +08:00
[Resolved]
private GameplayClockContainer ? gameplayClockContainer { get ; set ; }
2022-07-27 15:19:21 +08:00
[Resolved(canBeNull: true)]
2022-07-28 15:12:49 +08:00
private DrawableRuleset ? drawableRuleset { get ; set ; }
2022-07-27 15:19:21 +08:00
2022-07-28 15:12:49 +08:00
private IClock ? referenceClock ;
private IEnumerable < HitObject > ? objects ;
2022-07-27 15:19:21 +08:00
public IEnumerable < HitObject > Objects
{
2022-07-28 15:25:12 +08:00
set
{
objects = value ;
2022-07-28 17:37:17 +08:00
FirstHitTime = objects . FirstOrDefault ( ) ? . StartTime ? ? 0 ;
//TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list).
LastHitTime = objects . LastOrDefault ( ) ? . GetEndTime ( ) ? ? 0 ;
2022-07-28 15:25:12 +08:00
UpdateObjects ( objects ) ;
}
2022-07-27 15:19:21 +08:00
}
2022-07-28 17:29:49 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
Show ( ) ;
}
2022-07-28 15:25:12 +08:00
protected double FirstHitTime { get ; private set ; }
2022-07-27 15:19:21 +08:00
2022-07-28 15:25:12 +08:00
protected double LastHitTime { get ; private set ; }
2022-07-27 15:19:21 +08:00
2022-07-28 15:12:49 +08:00
protected abstract void UpdateProgress ( double progress , bool isIntro ) ;
2022-07-28 17:37:17 +08:00
protected virtual void UpdateObjects ( IEnumerable < HitObject > objects ) { }
2022-07-27 15:19:21 +08:00
[BackgroundDependencyLoader]
private void load ( )
{
if ( drawableRuleset ! = null )
{
Objects = drawableRuleset . Objects ;
referenceClock = drawableRuleset . FrameStableClock ;
}
}
protected override void Update ( )
{
base . Update ( ) ;
if ( objects = = null )
return ;
2022-07-28 15:12:49 +08:00
// The reference clock is used to accurately tell the playfield's time. This is obtained from the drawable ruleset.
// However, if no drawable ruleset is available (i.e. used in tests), we fall back to either the gameplay clock container or this drawable's own clock.
2022-07-28 17:37:17 +08:00
double currentTime = referenceClock ? . CurrentTime ? ? gameplayClockContainer ? . GameplayClock . CurrentTime ? ? Time . Current ;
2022-07-27 15:19:21 +08:00
2022-07-28 17:37:17 +08:00
bool isInIntro = currentTime < FirstHitTime ;
if ( isInIntro )
2022-07-27 15:19:21 +08:00
{
2022-07-28 17:37:17 +08:00
double introStartTime = gameplayClockContainer ? . StartTime ? ? 0 ;
double introOffsetCurrent = currentTime - introStartTime ;
double introDuration = FirstHitTime - introStartTime ;
UpdateProgress ( introOffsetCurrent / introDuration , true ) ;
2022-07-27 15:19:21 +08:00
}
else
{
2022-07-28 17:37:17 +08:00
double objectOffsetCurrent = currentTime - FirstHitTime ;
double objectDuration = LastHitTime - FirstHitTime ;
UpdateProgress ( objectOffsetCurrent / objectDuration , false ) ;
2022-07-27 15:19:21 +08:00
}
}
}
}