2019-03-26 04:31:49 +00:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2019-01-24 08:43:03 +00:00
// See the LICENCE file in the repository root for full licence text.
2018-04-13 09:19:50 +00:00
2022-06-17 07:37:17 +00:00
#nullable disable
2017-03-10 06:08:53 +00:00
using System ;
using System.Collections.Generic ;
2017-03-11 16:19:51 +00:00
using System.Linq ;
2019-05-10 07:31:09 +00:00
using System.Threading ;
2019-09-03 04:05:03 +00:00
using JetBrains.Annotations ;
2021-07-30 07:47:07 +00:00
using osu.Framework.Allocation ;
2021-07-31 06:05:54 +00:00
using osu.Framework.Audio ;
2019-02-21 10:04:31 +00:00
using osu.Framework.Bindables ;
2021-07-30 07:47:07 +00:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2018-01-12 09:13:17 +00:00
using osu.Framework.Graphics.Cursor ;
2017-08-09 02:50:34 +00:00
using osu.Framework.Input ;
2019-03-05 09:06:24 +00:00
using osu.Framework.Input.Events ;
2021-07-30 07:47:07 +00:00
using osu.Game.Beatmaps ;
2018-01-24 08:35:37 +00:00
using osu.Game.Configuration ;
2019-03-05 09:06:24 +00:00
using osu.Game.Graphics.Cursor ;
2018-02-28 07:34:47 +00:00
using osu.Game.Input.Handlers ;
2018-01-18 08:00:23 +00:00
using osu.Game.Overlays ;
2018-11-28 08:20:37 +00:00
using osu.Game.Replays ;
2018-01-18 08:00:23 +00:00
using osu.Game.Rulesets.Configuration ;
2021-07-30 07:47:07 +00:00
using osu.Game.Rulesets.Judgements ;
using osu.Game.Rulesets.Mods ;
using osu.Game.Rulesets.Objects ;
using osu.Game.Rulesets.Objects.Drawables ;
2017-04-18 07:05:58 +00:00
using osu.Game.Rulesets.Scoring ;
2018-12-03 19:37:26 +00:00
using osu.Game.Scoring ;
2019-03-19 11:21:31 +00:00
using osu.Game.Screens.Play ;
2019-11-29 08:35:11 +00:00
using osuTK ;
2018-04-13 09:19:50 +00:00
2017-04-18 07:05:58 +00:00
namespace osu.Game.Rulesets.UI
2016-09-02 10:25:13 +00:00
{
2017-03-15 09:58:41 +00:00
/// <summary>
2019-03-20 02:31:03 +00:00
/// Displays an interactive ruleset gameplay instance.
2017-03-15 09:58:41 +00:00
/// </summary>
2019-03-19 14:44:15 +00:00
/// <typeparam name="TObject">The type of HitObject contained by this DrawableRuleset.</typeparam>
public abstract class DrawableRuleset < TObject > : DrawableRuleset , IProvideCursor , ICanAttachKeyCounter
2019-03-19 11:21:31 +00:00
where TObject : HitObject
2016-10-19 10:44:03 +00:00
{
2020-11-10 14:32:30 +00:00
public override event Action < JudgementResult > NewResult ;
public override event Action < JudgementResult > RevertResult ;
2019-12-11 08:25:06 +00:00
2018-01-25 14:41:03 +00:00
/// <summary>
/// The selected variant.
/// </summary>
public virtual int Variant = > 0 ;
2018-04-13 09:19:50 +00:00
2017-03-15 09:58:41 +00:00
/// <summary>
2019-03-19 14:44:15 +00:00
/// The key conversion input manager for this DrawableRuleset.
2017-03-15 09:58:41 +00:00
/// </summary>
2017-08-23 03:47:47 +00:00
public PassThroughInputManager KeyBindingInputManager ;
2018-04-13 09:19:50 +00:00
2020-04-28 05:47:45 +00:00
public override double GameplayStartTime = > Objects . FirstOrDefault ( ) ? . StartTime - 2000 ? ? 0 ;
2018-07-17 05:29:22 +00:00
2017-12-14 11:27:51 +00:00
private readonly Lazy < Playfield > playfield ;
2018-06-06 05:20:51 +00:00
2017-11-30 12:56:12 +00:00
/// <summary>
/// The playfield.
/// </summary>
2019-06-04 07:13:16 +00:00
public override Playfield Playfield = > playfield . Value ;
2018-04-13 09:19:50 +00:00
2020-03-26 06:28:56 +00:00
public override Container Overlays { get ; } = new Container { RelativeSizeAxes = Axes . Both } ;
2019-12-25 05:35:32 +00:00
2020-03-26 06:28:56 +00:00
public override Container FrameStableComponents { get ; } = new Container { RelativeSizeAxes = Axes . Both } ;
2018-08-05 11:12:31 +00:00
2022-08-15 09:53:10 +00:00
public override IFrameStableClock FrameStableClock = > frameStabilityContainer ;
2019-05-09 09:06:11 +00:00
2019-08-26 07:33:24 +00:00
private bool frameStablePlayback = true ;
2021-06-03 08:27:21 +00:00
internal override bool FrameStablePlayback
2019-08-15 09:25:31 +00:00
{
2019-08-26 07:33:24 +00:00
get = > frameStablePlayback ;
set
{
2020-11-11 09:50:38 +00:00
frameStablePlayback = value ;
2019-08-26 07:33:24 +00:00
if ( frameStabilityContainer ! = null )
frameStabilityContainer . FrameStablePlayback = value ;
}
2019-08-15 09:25:31 +00:00
}
2019-03-20 05:55:38 +00:00
/// <summary>
/// The beatmap.
/// </summary>
2021-05-01 12:32:45 +00:00
[Cached(typeof(IBeatmap))]
2019-12-12 06:58:11 +00:00
public readonly Beatmap < TObject > Beatmap ;
2018-04-13 09:19:50 +00:00
2019-03-20 05:55:38 +00:00
public override IEnumerable < HitObject > Objects = > Beatmap . HitObjects ;
2018-04-13 09:19:50 +00:00
2018-07-11 08:25:57 +00:00
protected IRulesetConfigManager Config { get ; private set ; }
2019-04-10 08:11:17 +00:00
[Cached(typeof(IReadOnlyList<Mod>))]
2021-02-11 08:47:29 +00:00
public sealed override IReadOnlyList < Mod > Mods { get ; }
2019-03-20 05:55:38 +00:00
private FrameStabilityContainer frameStabilityContainer ;
2018-01-18 08:40:05 +00:00
private OnScreenDisplay onScreenDisplay ;
2018-04-13 09:19:50 +00:00
2020-04-11 01:22:23 +00:00
private DrawableRulesetDependencies dependencies ;
2021-07-30 07:47:07 +00:00
/// <summary>
2021-07-31 06:05:54 +00:00
/// Audio adjustments which are applied to the playfield.
2021-07-30 07:47:07 +00:00
/// </summary>
/// <remarks>
/// Does not affect <see cref="Overlays"/>.
/// </remarks>
2021-07-31 06:05:54 +00:00
public IAdjustableAudioComponent Audio { get ; private set ; }
2021-07-30 07:47:07 +00:00
2017-08-09 04:04:11 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// Creates a ruleset visualisation for the provided ruleset and beatmap.
2017-08-09 04:04:11 +00:00
/// </summary>
2019-03-20 05:55:38 +00:00
/// <param name="ruleset">The ruleset being represented.</param>
2019-12-12 06:58:11 +00:00
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
2019-04-25 08:36:17 +00:00
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
2019-12-12 06:58:11 +00:00
protected DrawableRuleset ( Ruleset ruleset , IBeatmap beatmap , IReadOnlyList < Mod > mods = null )
2019-03-19 11:21:31 +00:00
: base ( ruleset )
2017-03-14 07:15:26 +00:00
{
2019-12-12 06:58:11 +00:00
if ( beatmap = = null )
throw new ArgumentNullException ( nameof ( beatmap ) , "Beatmap cannot be null." ) ;
2019-04-08 09:32:05 +00:00
2019-12-12 06:58:11 +00:00
if ( ! ( beatmap is Beatmap < TObject > tBeatmap ) )
throw new ArgumentException ( $"{GetType()} expected the beatmap to contain hitobjects of type {typeof(TObject)}." , nameof ( beatmap ) ) ;
2019-03-19 11:21:31 +00:00
2019-12-12 06:58:11 +00:00
Beatmap = tBeatmap ;
2020-04-28 10:23:33 +00:00
Mods = mods ? . ToArray ( ) ? ? Array . Empty < Mod > ( ) ;
2019-03-19 11:21:31 +00:00
2019-12-12 06:58:11 +00:00
RelativeSizeAxes = Axes . Both ;
2019-03-19 11:21:31 +00:00
KeyBindingInputManager = CreateInputManager ( ) ;
2020-11-10 14:32:30 +00:00
playfield = new Lazy < Playfield > ( ( ) = > CreatePlayfield ( ) . With ( p = >
{
p . NewResult + = ( _ , r ) = > NewResult ? . Invoke ( r ) ;
p . RevertResult + = ( _ , r ) = > RevertResult ? . Invoke ( r ) ;
} ) ) ;
2022-03-18 06:28:48 +00:00
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2018-04-13 09:19:50 +00:00
2019-02-22 08:51:39 +00:00
IsPaused . ValueChanged + = paused = >
2018-07-11 08:01:27 +00:00
{
2019-02-21 09:56:34 +00:00
if ( HasReplayLoaded . Value )
2018-07-11 08:01:27 +00:00
return ;
2019-02-22 08:51:39 +00:00
KeyBindingInputManager . UseParentInput = ! paused . NewValue ;
2018-07-11 08:01:27 +00:00
} ;
2017-03-14 07:15:26 +00:00
}
2018-05-06 10:57:52 +00:00
2018-07-11 08:07:14 +00:00
protected override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
2018-01-18 08:00:23 +00:00
{
2020-04-11 01:22:23 +00:00
dependencies = new DrawableRulesetDependencies ( Ruleset , base . CreateChildDependencies ( parent ) ) ;
2019-09-04 11:28:21 +00:00
2020-04-11 01:22:23 +00:00
Config = dependencies . RulesetConfigManager ;
2019-09-04 11:28:21 +00:00
2018-06-11 05:36:56 +00:00
onScreenDisplay = dependencies . Get < OnScreenDisplay > ( ) ;
2018-07-11 08:25:57 +00:00
if ( Config ! = null )
onScreenDisplay ? . BeginTracking ( this , Config ) ;
2018-04-13 09:19:50 +00:00
2018-06-11 05:36:56 +00:00
return dependencies ;
}
2018-06-11 03:57:26 +00:00
2019-03-30 16:29:37 +00:00
public virtual PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer ( ) = > new PlayfieldAdjustmentContainer ( ) ;
2019-03-26 04:31:49 +00:00
2020-09-28 05:15:54 +00:00
[Resolved]
private OsuConfigManager config { get ; set ; }
2019-03-20 05:55:38 +00:00
[BackgroundDependencyLoader]
2020-09-28 05:15:54 +00:00
private void load ( CancellationToken ? cancellationToken )
2019-03-20 05:55:38 +00:00
{
2021-07-31 06:05:54 +00:00
AudioContainer audioContainer ;
2021-07-30 07:47:07 +00:00
InternalChild = frameStabilityContainer = new FrameStabilityContainer ( GameplayStartTime )
2019-03-20 05:55:38 +00:00
{
2021-07-30 07:47:07 +00:00
FrameStablePlayback = FrameStablePlayback ,
Children = new Drawable [ ]
2019-03-20 05:55:38 +00:00
{
2021-07-30 07:47:07 +00:00
FrameStableComponents ,
2021-07-31 06:05:54 +00:00
audioContainer = new AudioContainer
2019-12-25 05:35:32 +00:00
{
2021-07-30 07:47:07 +00:00
RelativeSizeAxes = Axes . Both ,
Child = KeyBindingInputManager
2019-12-25 05:35:32 +00:00
. WithChild ( CreatePlayfieldAdjustmentContainer ( )
. WithChild ( Playfield )
) ,
2021-07-30 07:47:07 +00:00
} ,
Overlays ,
}
2019-03-20 05:55:38 +00:00
} ;
2021-07-31 06:05:54 +00:00
Audio = audioContainer ;
2019-03-25 10:21:25 +00:00
if ( ( ResumeOverlay = CreateResumeOverlay ( ) ) ! = null )
{
AddInternal ( CreateInputManager ( )
. WithChild ( CreatePlayfieldAdjustmentContainer ( )
. WithChild ( ResumeOverlay ) ) ) ;
}
2021-06-02 02:06:30 +00:00
applyRulesetMods ( Mods , config ) ;
2019-03-20 05:55:38 +00:00
2020-11-10 14:32:30 +00:00
loadObjects ( cancellationToken ? ? default ) ;
2019-03-19 11:21:31 +00:00
}
2018-04-13 09:19:50 +00:00
2019-03-18 05:40:53 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// Creates and adds drawable representations of hit objects to the play field.
2019-03-18 05:40:53 +00:00
/// </summary>
2020-11-10 14:32:30 +00:00
private void loadObjects ( CancellationToken cancellationToken )
2019-03-19 11:21:31 +00:00
{
foreach ( TObject h in Beatmap . HitObjects )
2019-05-10 07:31:09 +00:00
{
2020-11-10 14:32:30 +00:00
cancellationToken . ThrowIfCancellationRequested ( ) ;
AddHitObject ( h ) ;
2019-05-10 07:31:09 +00:00
}
2020-11-10 14:32:30 +00:00
cancellationToken . ThrowIfCancellationRequested ( ) ;
2018-04-13 09:19:50 +00:00
2019-03-19 11:21:31 +00:00
Playfield . PostProcess ( ) ;
2019-03-16 04:47:11 +00:00
2021-06-16 09:46:29 +00:00
foreach ( var mod in Mods . OfType < IApplicableToDrawableHitObject > ( ) )
{
foreach ( var drawableHitObject in Playfield . AllHitObjects )
mod . ApplyToDrawableHitObject ( drawableHitObject ) ;
}
2019-03-19 11:21:31 +00:00
}
2018-04-13 09:19:50 +00:00
2019-03-21 07:57:40 +00:00
public override void RequestResume ( Action continueResume )
{
2019-03-25 13:00:33 +00:00
if ( ResumeOverlay ! = null & & ( Cursor = = null | | ( Cursor . LastFrameState = = Visibility . Visible & & Contains ( Cursor . ActiveCursor . ScreenSpaceDrawQuad . Centre ) ) ) )
2019-03-21 07:57:40 +00:00
{
2019-03-25 10:21:25 +00:00
ResumeOverlay . GameplayCursor = Cursor ;
2019-03-21 07:57:40 +00:00
ResumeOverlay . ResumeAction = continueResume ;
ResumeOverlay . Show ( ) ;
}
else
continueResume ( ) ;
}
2019-10-25 19:57:49 +00:00
public override void CancelResume ( )
{
2019-10-26 19:29:52 +00:00
// called if the user pauses while the resume overlay is open
2019-10-25 21:49:18 +00:00
ResumeOverlay ? . Hide ( ) ;
2019-10-25 19:57:49 +00:00
}
2020-11-12 03:55:42 +00:00
/// <summary>
/// Adds a <see cref="HitObject"/> to this <see cref="DrawableRuleset"/>.
/// </summary>
/// <remarks>
/// This does not add the <see cref="HitObject"/> to the beatmap.
/// </remarks>
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
2020-11-10 14:32:30 +00:00
public void AddHitObject ( TObject hitObject )
2017-04-18 12:55:44 +00:00
{
2020-11-12 05:04:16 +00:00
var drawableRepresentation = CreateDrawableRepresentation ( hitObject ) ;
// If a drawable representation exists, use it, otherwise assume the hitobject is being pooled.
if ( drawableRepresentation ! = null )
Playfield . Add ( drawableRepresentation ) ;
2020-11-10 14:32:30 +00:00
else
2020-11-13 15:54:57 +00:00
Playfield . Add ( hitObject ) ;
2020-11-10 14:32:30 +00:00
}
2018-04-13 09:19:50 +00:00
2020-11-12 03:55:42 +00:00
/// <summary>
2020-11-12 05:04:16 +00:00
/// Removes a <see cref="HitObject"/> from this <see cref="DrawableRuleset"/>.
2020-11-12 03:55:42 +00:00
/// </summary>
/// <remarks>
/// This does not remove the <see cref="HitObject"/> from the beatmap.
/// </remarks>
/// <param name="hitObject">The <see cref="HitObject"/> to remove.</param>
2020-11-12 05:04:16 +00:00
public bool RemoveHitObject ( TObject hitObject )
2020-11-10 14:32:30 +00:00
{
2020-11-13 15:54:57 +00:00
if ( Playfield . Remove ( hitObject ) )
2020-11-12 05:04:16 +00:00
return true ;
2020-11-13 15:54:57 +00:00
// If the entry was not removed from the playfield, assume the hitobject is not being pooled and attempt a direct drawable removal.
2020-11-12 05:04:16 +00:00
var drawableObject = Playfield . AllHitObjects . SingleOrDefault ( d = > d . HitObject = = hitObject ) ;
if ( drawableObject ! = null )
return Playfield . Remove ( drawableObject ) ;
return false ;
2017-04-18 12:55:44 +00:00
}
2018-04-13 09:19:50 +00:00
2020-12-14 07:52:14 +00:00
public sealed override void SetRecordTarget ( Score score )
2020-03-23 10:31:43 +00:00
{
2020-03-24 06:39:01 +00:00
if ( ! ( KeyBindingInputManager is IHasRecordingHandler recordingInputManager ) )
2020-03-23 10:31:43 +00:00
throw new InvalidOperationException ( $"A {nameof(KeyBindingInputManager)} which supports recording is not available" ) ;
2021-06-03 16:59:56 +00:00
if ( score = = null )
{
recordingInputManager . Recorder = null ;
return ;
}
2020-12-14 07:52:14 +00:00
var recorder = CreateReplayRecorder ( score ) ;
2020-03-24 05:13:46 +00:00
if ( recorder = = null )
return ;
2020-03-23 10:31:43 +00:00
recorder . ScreenSpaceToGamefield = Playfield . ScreenSpaceToGamefield ;
2020-03-24 06:39:01 +00:00
recordingInputManager . Recorder = recorder ;
2020-03-23 10:31:43 +00:00
}
2019-03-20 05:55:38 +00:00
public override void SetReplayScore ( Score replayScore )
2018-01-18 08:00:23 +00:00
{
2019-03-20 05:55:38 +00:00
if ( ! ( KeyBindingInputManager is IHasReplayHandler replayInputManager ) )
throw new InvalidOperationException ( $"A {nameof(KeyBindingInputManager)} which supports replay loading is not available" ) ;
2018-04-13 09:19:50 +00:00
2019-03-20 05:55:38 +00:00
var handler = ( ReplayScore = replayScore ) ! = null ? CreateReplayInputHandler ( replayScore . Replay ) : null ;
2018-04-13 09:19:50 +00:00
2019-03-20 05:55:38 +00:00
replayInputManager . ReplayInputHandler = handler ;
frameStabilityContainer . ReplayInputHandler = handler ;
2018-08-06 01:54:16 +00:00
2019-03-20 05:55:38 +00:00
HasReplayLoaded . Value = replayInputManager . ReplayInputHandler ! = null ;
2018-04-13 09:19:50 +00:00
2019-03-20 05:55:38 +00:00
if ( replayInputManager . ReplayInputHandler ! = null )
replayInputManager . ReplayInputHandler . GamefieldToScreenSpace = Playfield . GamefieldToScreenSpace ;
2019-04-23 04:32:44 +00:00
if ( ! ProvidingUserCursor )
2019-04-23 04:45:51 +00:00
{
// The cursor is hidden by default (see Playfield.load()), but should be shown when there's a replay
2019-04-23 04:32:44 +00:00
Playfield . Cursor ? . Show ( ) ;
2019-04-23 04:45:51 +00:00
}
2019-03-20 05:55:38 +00:00
}
2018-04-13 09:19:50 +00:00
2017-06-12 06:20:34 +00:00
/// <summary>
2020-11-13 05:57:09 +00:00
/// Creates a <see cref="DrawableHitObject{TObject}"/> to represent a <see cref="HitObject"/>.
2017-06-12 06:20:34 +00:00
/// </summary>
2020-11-13 05:57:09 +00:00
/// <remarks>
2020-11-13 15:41:18 +00:00
/// If this method returns <c>null</c>, then this <see cref="DrawableRuleset"/> will assume the requested <see cref="HitObject"/> type is being pooled inside the <see cref="Playfield"/>,
/// and will instead attempt to retrieve the <see cref="DrawableHitObject"/>s at the point they should become alive via pools registered in the <see cref="Playfield"/>.
2020-11-13 05:57:09 +00:00
/// </remarks>
/// <param name="h">The <see cref="HitObject"/> to represent.</param>
/// <returns>The representing <see cref="DrawableHitObject{TObject}"/>.</returns>
public abstract DrawableHitObject < TObject > CreateDrawableRepresentation ( TObject h ) ;
2018-04-13 09:19:50 +00:00
2019-03-26 02:28:43 +00:00
public void Attach ( KeyCounterDisplay keyCounter ) = >
2019-03-19 11:21:31 +00:00
( KeyBindingInputManager as ICanAttachKeyCounter ) ? . Attach ( keyCounter ) ;
2018-04-13 09:19:50 +00:00
2017-05-19 06:57:32 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// Creates a key conversion input manager. An exception will be thrown if a valid <see cref="RulesetInputManager{T}"/> is not returned.
2017-08-22 04:01:51 +00:00
/// </summary>
2019-03-19 11:21:31 +00:00
/// <returns>The input manager.</returns>
protected abstract PassThroughInputManager CreateInputManager ( ) ;
2018-04-13 09:19:50 +00:00
2019-03-19 11:21:31 +00:00
protected virtual ReplayInputHandler CreateReplayInputHandler ( Replay replay ) = > null ;
2018-04-25 08:15:53 +00:00
2020-12-14 07:52:14 +00:00
protected virtual ReplayRecorder CreateReplayRecorder ( Score score ) = > null ;
2020-03-23 10:03:42 +00:00
2017-08-22 04:01:51 +00:00
/// <summary>
2017-12-01 15:26:02 +00:00
/// Creates a Playfield.
2017-05-19 06:57:32 +00:00
/// </summary>
2017-12-01 15:26:02 +00:00
/// <returns>The Playfield.</returns>
protected abstract Playfield CreatePlayfield ( ) ;
2018-05-21 06:59:33 +00:00
2017-09-06 09:05:51 +00:00
/// <summary>
2019-03-19 14:44:15 +00:00
/// Applies the active mods to this DrawableRuleset.
2017-09-06 09:05:51 +00:00
/// </summary>
2019-04-25 08:36:17 +00:00
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
/// <param name="config">The <see cref="OsuConfigManager"/> to apply.</param>
2019-04-10 08:13:12 +00:00
private void applyRulesetMods ( IReadOnlyList < Mod > mods , OsuConfigManager config )
2017-09-06 09:05:51 +00:00
{
if ( mods = = null )
return ;
2018-04-13 09:19:50 +00:00
2019-03-19 14:44:15 +00:00
foreach ( var mod in mods . OfType < IApplicableToDrawableRuleset < TObject > > ( ) )
mod . ApplyToDrawableRuleset ( this ) ;
2018-05-06 11:09:46 +00:00
2018-06-06 05:20:51 +00:00
foreach ( var mod in mods . OfType < IReadFromConfig > ( ) )
mod . ReadFromConfig ( config ) ;
2017-09-06 09:05:51 +00:00
}
2018-04-13 09:19:50 +00:00
2019-03-20 05:55:38 +00:00
#region IProvideCursor
protected override bool OnHover ( HoverEvent e ) = > true ; // required for IProvideCursor
2019-11-29 08:35:11 +00:00
// only show the cursor when within the playfield, by default.
public override bool ReceivePositionalInputAt ( Vector2 screenSpacePos ) = > Playfield . ReceivePositionalInputAt ( screenSpacePos ) ;
2022-07-26 05:11:52 +00:00
CursorContainer IProvideCursor . MenuCursor = > Playfield . Cursor ;
2019-03-26 04:39:19 +00:00
public override GameplayCursorContainer Cursor = > Playfield . Cursor ;
2019-03-20 05:55:38 +00:00
public bool ProvidingUserCursor = > Playfield . Cursor ! = null & & ! HasReplayLoaded . Value ;
#endregion
2019-03-19 11:21:31 +00:00
protected override void Dispose ( bool isDisposing )
2017-08-23 07:48:13 +00:00
{
2019-03-19 11:21:31 +00:00
base . Dispose ( isDisposing ) ;
2018-04-13 09:19:50 +00:00
2019-03-19 11:21:31 +00:00
if ( Config ! = null )
{
onScreenDisplay ? . StopTracking ( this , Config ) ;
Config = null ;
}
2020-04-11 01:22:23 +00:00
// Dispose the components created by this dependency container.
2020-04-11 23:24:36 +00:00
dependencies ? . Dispose ( ) ;
2016-10-13 01:10:15 +00:00
}
2019-03-19 11:21:31 +00:00
}
2018-04-13 09:19:50 +00:00
2019-03-19 11:21:31 +00:00
/// <summary>
2019-03-20 02:31:03 +00:00
/// Displays an interactive ruleset gameplay instance.
/// <remarks>
/// This type is required only for adding non-generic type to the draw hierarchy.
/// </remarks>
2019-03-19 11:21:31 +00:00
/// </summary>
2020-11-10 15:22:36 +00:00
[Cached(typeof(DrawableRuleset))]
2019-03-19 14:44:15 +00:00
public abstract class DrawableRuleset : CompositeDrawable
2019-03-19 11:21:31 +00:00
{
2019-12-11 08:25:06 +00:00
/// <summary>
/// Invoked when a <see cref="JudgementResult"/> has been applied by a <see cref="DrawableHitObject"/>.
/// </summary>
2020-11-10 14:32:30 +00:00
public abstract event Action < JudgementResult > NewResult ;
2019-12-11 08:25:06 +00:00
/// <summary>
/// Invoked when a <see cref="JudgementResult"/> is being reverted by a <see cref="DrawableHitObject"/>.
/// </summary>
2020-11-10 14:32:30 +00:00
public abstract event Action < JudgementResult > RevertResult ;
2019-12-11 08:25:06 +00:00
2017-06-15 10:25:54 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// Whether a replay is currently loaded.
2017-06-15 10:25:54 +00:00
/// </summary>
2019-03-19 11:21:31 +00:00
public readonly BindableBool HasReplayLoaded = new BindableBool ( ) ;
2018-04-13 09:19:50 +00:00
2019-03-19 11:21:31 +00:00
/// <summary>
/// Whether the game is paused. Used to block user input.
/// </summary>
public readonly BindableBool IsPaused = new BindableBool ( ) ;
2018-04-13 09:19:50 +00:00
2019-06-04 07:13:16 +00:00
/// <summary>
/// The playfield.
/// </summary>
public abstract Playfield Playfield { get ; }
2019-12-25 05:35:32 +00:00
/// <summary>
2020-03-26 03:50:00 +00:00
/// Content to be placed above hitobjects. Will be affected by frame stability.
2019-12-25 05:35:32 +00:00
/// </summary>
public abstract Container Overlays { get ; }
2020-03-26 06:28:56 +00:00
/// <summary>
/// Components to be run potentially multiple times in line with frame-stable gameplay.
/// </summary>
public abstract Container FrameStableComponents { get ; }
2019-05-09 09:06:11 +00:00
/// <summary>
/// The frame-stable clock which is being used for playfield display.
/// </summary>
2020-10-27 05:10:12 +00:00
public abstract IFrameStableClock FrameStableClock { get ; }
2019-05-09 09:06:11 +00:00
2021-06-03 09:32:05 +00:00
/// <summary>
/// Whether to enable frame-stable playback.
/// </summary>
2021-06-03 08:27:21 +00:00
internal abstract bool FrameStablePlayback { get ; set ; }
2020-11-06 13:09:54 +00:00
/// <summary>
/// The mods which are to be applied.
/// </summary>
2021-02-11 08:47:29 +00:00
public abstract IReadOnlyList < Mod > Mods { get ; }
2020-11-06 13:09:54 +00:00
2019-03-20 02:31:03 +00:00
/// <summary>~
2019-03-19 11:21:31 +00:00
/// The associated ruleset.
/// </summary>
public readonly Ruleset Ruleset ;
2018-04-13 09:19:50 +00:00
2018-10-03 06:36:14 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// Creates a ruleset visualisation for the provided ruleset.
2018-10-03 06:36:14 +00:00
/// </summary>
2019-03-19 11:21:31 +00:00
/// <param name="ruleset">The ruleset.</param>
2019-03-20 02:31:03 +00:00
internal DrawableRuleset ( Ruleset ruleset )
2018-10-03 06:36:14 +00:00
{
2019-03-19 11:21:31 +00:00
Ruleset = ruleset ;
}
2018-10-03 06:36:14 +00:00
2019-03-19 11:21:31 +00:00
/// <summary>
/// All the converted hit objects contained by this hit renderer.
/// </summary>
public abstract IEnumerable < HitObject > Objects { get ; }
2018-10-03 06:36:14 +00:00
2019-03-19 11:21:31 +00:00
/// <summary>
/// The point in time at which gameplay starts, including any required lead-in for display purposes.
/// Defaults to two seconds before the first <see cref="HitObject"/>. Override as necessary.
/// </summary>
public abstract double GameplayStartTime { get ; }
2018-10-03 06:36:14 +00:00
2019-03-19 11:21:31 +00:00
/// <summary>
/// The currently loaded replay. Usually null in the case of a local player.
/// </summary>
public Score ReplayScore { get ; protected set ; }
2018-10-03 06:36:14 +00:00
2017-03-15 09:58:41 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// The cursor being displayed by the <see cref="Playfield"/>. May be null if no cursor is provided.
2017-03-15 09:58:41 +00:00
/// </summary>
2019-03-26 04:39:19 +00:00
public abstract GameplayCursorContainer Cursor { get ; }
2018-04-13 09:19:50 +00:00
2019-09-02 02:20:50 +00:00
/// <summary>
/// An optional overlay used when resuming gameplay from a paused state.
/// </summary>
public ResumeOverlay ResumeOverlay { get ; protected set ; }
2019-09-03 04:05:03 +00:00
/// <summary>
/// Returns first available <see cref="HitWindows"/> provided by a <see cref="HitObject"/>.
/// </summary>
[CanBeNull]
public HitWindows FirstAvailableHitWindows
{
get
{
2021-07-04 21:24:17 +00:00
foreach ( var hitObject in Objects )
2019-09-03 04:05:03 +00:00
{
2021-07-04 21:24:17 +00:00
if ( hitObject . HitWindows . WindowFor ( HitResult . Miss ) > 0 )
return hitObject . HitWindows ;
2019-09-03 04:05:03 +00:00
2021-07-04 21:24:17 +00:00
foreach ( var nested in hitObject . NestedHitObjects )
2019-11-11 11:53:22 +00:00
{
2021-07-04 21:24:17 +00:00
if ( nested . HitWindows . WindowFor ( HitResult . Miss ) > 0 )
return nested . HitWindows ;
2019-11-11 11:53:22 +00:00
}
2019-09-03 04:05:03 +00:00
}
return null ;
}
}
2019-09-02 02:20:50 +00:00
protected virtual ResumeOverlay CreateResumeOverlay ( ) = > null ;
2020-05-07 06:52:36 +00:00
/// <summary>
2020-05-10 11:09:41 +00:00
/// Whether to display gameplay overlays, such as <see cref="HUDOverlay"/> and <see cref="BreakOverlay"/>.
2020-05-07 06:52:36 +00:00
/// </summary>
2020-05-08 07:37:50 +00:00
public virtual bool AllowGameplayOverlays = > true ;
2020-05-07 06:52:36 +00:00
2017-08-04 05:14:43 +00:00
/// <summary>
2019-03-19 11:21:31 +00:00
/// Sets a replay to be used, overriding local input.
/// </summary>
/// <param name="replayScore">The replay, null for local input.</param>
public abstract void SetReplayScore ( Score replayScore ) ;
2020-03-23 10:31:43 +00:00
/// <summary>
/// Sets a replay to be used to record gameplay.
/// </summary>
2020-12-14 07:52:14 +00:00
/// <param name="score">The target to be recorded to.</param>
2021-06-03 16:59:56 +00:00
public abstract void SetRecordTarget ( [ CanBeNull ] Score score ) ;
2020-03-23 10:31:43 +00:00
2019-03-20 06:27:06 +00:00
/// <summary>
/// Invoked when the interactive user requests resuming from a paused state.
/// Allows potentially delaying the resume process until an interaction is performed.
2017-08-04 05:14:43 +00:00
/// </summary>
2019-03-20 06:27:06 +00:00
/// <param name="continueResume">The action to run when resuming is to be completed.</param>
public abstract void RequestResume ( Action continueResume ) ;
2018-04-13 09:19:50 +00:00
2019-10-26 19:29:52 +00:00
/// <summary>
/// Invoked when the user requests to pause while the resume overlay is active.
/// </summary>
2019-10-25 19:57:49 +00:00
public abstract void CancelResume ( ) ;
2017-08-04 05:14:43 +00:00
}
2018-04-13 09:19:50 +00:00
2017-05-06 16:38:17 +00:00
public class BeatmapInvalidForRulesetException : ArgumentException
2017-04-17 06:44:46 +00:00
{
2017-04-20 03:11:03 +00:00
public BeatmapInvalidForRulesetException ( string text )
2017-04-17 06:44:46 +00:00
: base ( text )
{
}
}
2016-09-02 10:25:13 +00:00
}