2019-01-24 16:43:03 +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.
2018-04-13 17:19:50 +08:00
2017-05-23 14:20:32 +08:00
using System.Collections.Generic ;
2020-04-28 18:23:33 +08:00
using System.Linq ;
2017-06-09 21:03:28 +08:00
using osu.Framework.Allocation ;
2020-04-28 18:23:33 +08:00
using osu.Framework.Audio.Track ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2017-06-09 21:03:28 +08:00
using osu.Framework.Extensions.IEnumerableExtensions ;
2024-02-02 08:56:38 +08:00
using osu.Framework.Extensions.ObjectExtensions ;
2017-08-21 11:31:21 +08:00
using osu.Framework.Input ;
2024-07-10 23:24:03 +08:00
using osu.Framework.Platform ;
2024-02-02 08:56:38 +08:00
using osu.Framework.Threading ;
2024-07-10 23:24:03 +08:00
using osu.Framework.Utils ;
2017-03-10 14:08:53 +08:00
using osu.Game.Beatmaps ;
2020-04-03 12:16:01 +08:00
using osu.Game.Beatmaps.ControlPoints ;
2018-02-28 15:34:47 +08:00
using osu.Game.Input.Handlers ;
2018-11-28 16:20:37 +08:00
using osu.Game.Replays ;
2017-04-18 15:05:58 +08:00
using osu.Game.Rulesets.Mania.Beatmaps ;
2018-01-18 16:00:23 +08:00
using osu.Game.Rulesets.Mania.Configuration ;
2017-04-18 15:05:58 +08:00
using osu.Game.Rulesets.Mania.Objects ;
2017-09-12 14:52:18 +08:00
using osu.Game.Rulesets.Mania.Replays ;
2023-12-26 11:46:21 +08:00
using osu.Game.Rulesets.Mania.Skinning ;
2019-04-08 17:32:05 +08:00
using osu.Game.Rulesets.Mods ;
2019-09-10 12:29:50 +08:00
using osu.Game.Rulesets.Objects ;
2017-04-18 15:05:58 +08:00
using osu.Game.Rulesets.Objects.Drawables ;
using osu.Game.Rulesets.UI ;
2018-01-04 18:22:15 +08:00
using osu.Game.Rulesets.UI.Scrolling ;
2020-12-14 15:52:14 +08:00
using osu.Game.Scoring ;
2024-02-08 01:16:08 +08:00
using osu.Game.Screens.Play ;
2023-12-26 11:46:21 +08:00
using osu.Game.Skinning ;
2018-04-13 17:19:50 +08:00
2017-04-18 15:05:58 +08:00
namespace osu.Game.Rulesets.Mania.UI
2016-09-02 19:30:27 +08:00
{
2024-05-06 15:24:32 +08:00
[Cached]
2019-03-20 10:29:16 +08:00
public partial class DrawableManiaRuleset : DrawableScrollingRuleset < ManiaHitObject >
2016-09-02 19:30:27 +08:00
{
2020-04-03 17:25:01 +08:00
/// <summary>
2023-05-29 20:14:03 +08:00
/// The minimum time range. This occurs at a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 40.
2020-04-03 17:25:01 +08:00
/// </summary>
2021-07-16 17:22:34 +08:00
public const double MIN_TIME_RANGE = 290 ;
2020-04-03 17:25:01 +08:00
/// <summary>
2023-05-29 20:14:03 +08:00
/// The maximum time range. This occurs with a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 1.
2020-04-03 17:25:01 +08:00
/// </summary>
2021-07-16 17:22:34 +08:00
public const double MAX_TIME_RANGE = 11485 ;
2020-04-03 17:25:01 +08:00
2024-05-06 15:24:32 +08:00
public new ManiaPlayfield Playfield = > ( ManiaPlayfield ) base . Playfield ;
2019-03-20 10:29:16 +08:00
2018-01-03 17:44:25 +08:00
public new ManiaBeatmap Beatmap = > ( ManiaBeatmap ) base . Beatmap ;
2018-04-13 17:19:50 +08:00
2017-12-28 21:40:23 +08:00
public IEnumerable < BarLine > BarLines ;
2018-04-13 17:19:50 +08:00
2019-08-26 11:51:23 +08:00
protected override bool RelativeScaleBeatLengths = > true ;
2019-01-25 18:14:37 +08:00
protected new ManiaRulesetConfigManager Config = > ( ManiaRulesetConfigManager ) base . Config ;
2018-06-08 19:13:24 +08:00
2018-11-06 14:46:36 +08:00
private readonly Bindable < ManiaScrollingDirection > configDirection = new Bindable < ManiaScrollingDirection > ( ) ;
2023-05-29 20:14:03 +08:00
private readonly BindableInt configScrollSpeed = new BindableInt ( ) ;
2024-07-10 23:24:03 +08:00
private double currentTimeRange ;
protected double TargetTimeRange ;
2020-04-28 18:23:33 +08:00
// Stores the current speed adjustment active in gameplay.
2020-04-29 13:27:21 +08:00
private readonly Track speedAdjustmentTrack = new TrackVirtual ( 0 ) ;
2018-11-06 14:46:36 +08:00
2024-02-05 21:52:08 +08:00
private ISkinSource currentSkin = null ! ;
2023-12-26 11:46:21 +08:00
2024-07-10 23:24:03 +08:00
[Resolved]
private GameHost gameHost { get ; set ; } = null ! ;
2024-02-05 21:52:08 +08:00
public DrawableManiaRuleset ( Ruleset ruleset , IBeatmap beatmap , IReadOnlyList < Mod > ? mods = null )
2019-04-08 17:32:05 +08:00
: base ( ruleset , beatmap , mods )
2016-09-02 19:30:27 +08:00
{
2019-09-25 05:03:55 +08:00
BarLines = new BarLineGenerator < BarLine > ( Beatmap ) . BarLines ;
2023-05-29 20:14:03 +08:00
TimeRange . MinValue = 1 ;
TimeRange . MaxValue = MAX_TIME_RANGE ;
2017-05-10 13:56:39 +08:00
}
2018-04-13 17:19:50 +08:00
2017-06-09 21:03:28 +08:00
[BackgroundDependencyLoader]
2024-02-02 08:56:38 +08:00
private void load ( ISkinSource source )
2017-06-09 21:03:28 +08:00
{
2024-02-02 08:56:38 +08:00
currentSkin = source ;
currentSkin . SourceChanged + = onSkinChange ;
skinChanged ( ) ;
2020-04-28 18:23:33 +08:00
foreach ( var mod in Mods . OfType < IApplicableToTrack > ( ) )
mod . ApplyToTrack ( speedAdjustmentTrack ) ;
2020-04-03 12:16:01 +08:00
bool isForCurrentRuleset = Beatmap . BeatmapInfo . Ruleset . Equals ( Ruleset . RulesetInfo ) ;
foreach ( var p in ControlPoints )
{
// Mania doesn't care about global velocity
p . Velocity = 1 ;
2021-10-02 11:34:29 +08:00
p . BaseBeatLength * = Beatmap . Difficulty . SliderMultiplier ;
2020-04-03 12:16:01 +08:00
// For non-mania beatmap, speed changes should only happen through timing points
if ( ! isForCurrentRuleset )
2021-09-01 17:19:25 +08:00
p . EffectPoint = new EffectControlPoint ( ) ;
2020-04-03 12:16:01 +08:00
}
2017-08-04 22:07:08 +08:00
BarLines . ForEach ( Playfield . Add ) ;
2018-07-17 15:55:50 +08:00
2019-01-25 18:14:37 +08:00
Config . BindWith ( ManiaRulesetSetting . ScrollDirection , configDirection ) ;
2019-02-22 19:13:38 +08:00
configDirection . BindValueChanged ( direction = > Direction . Value = ( ScrollingDirection ) direction . NewValue , true ) ;
2018-11-07 16:24:05 +08:00
2023-05-29 20:14:03 +08:00
Config . BindWith ( ManiaRulesetSetting . ScrollSpeed , configScrollSpeed ) ;
2024-07-10 23:24:03 +08:00
configScrollSpeed . BindValueChanged ( speed = > TargetTimeRange = ComputeScrollTime ( speed . NewValue ) ) ;
2018-04-13 17:19:50 +08:00
2024-07-10 23:24:03 +08:00
TimeRange . Value = TargetTimeRange = currentTimeRange = ComputeScrollTime ( configScrollSpeed . Value ) ;
2024-05-06 15:24:32 +08:00
2024-05-14 19:28:14 +08:00
KeyBindingInputManager . Add ( new ManiaTouchInputArea ( ) ) ;
2020-04-03 17:25:01 +08:00
}
2023-05-29 20:14:03 +08:00
protected override void AdjustScrollSpeed ( int amount ) = > configScrollSpeed . Value + = amount ;
2020-04-03 17:25:01 +08:00
2020-04-28 18:23:33 +08:00
protected override void Update ( )
{
base . Update ( ) ;
updateTimeRange ( ) ;
}
2024-02-05 21:52:08 +08:00
private ScheduledDelegate ? pendingSkinChange ;
2024-02-02 08:56:38 +08:00
private float hitPosition ;
private void onSkinChange ( )
2023-12-26 11:46:21 +08:00
{
2024-02-02 08:56:38 +08:00
// schedule required to avoid calls after disposed.
// note that this has the side-effect of components only performing a skin change when they are alive.
pendingSkinChange ? . Cancel ( ) ;
pendingSkinChange = Scheduler . Add ( skinChanged ) ;
}
private void skinChanged ( )
{
hitPosition = currentSkin . GetConfig < ManiaSkinConfigurationLookup , float > (
new ManiaSkinConfigurationLookup ( LegacyManiaSkinConfigurationLookups . HitPosition ) ) ? . Value
? ? Stage . HIT_TARGET_POSITION ;
pendingSkinChange = null ;
}
2023-12-26 11:46:21 +08:00
2024-02-02 08:56:38 +08:00
private void updateTimeRange ( )
{
2023-12-26 11:46:21 +08:00
const float length_to_default_hit_position = 768 - LegacyManiaSkinConfiguration . DEFAULT_HIT_POSITION ;
float lengthToHitPosition = 768 - hitPosition ;
// This scaling factor preserves the scroll speed as the scroll length varies from changes to the hit position.
float scale = lengthToHitPosition / length_to_default_hit_position ;
2024-07-10 23:24:03 +08:00
// we're intentionally using the game host's update clock here to decouple the time range tween from the gameplay clock (which can be arbitrarily paused, or even rewinding)
currentTimeRange = Interpolation . DampContinuously ( currentTimeRange , TargetTimeRange , 50 , gameHost . UpdateThread . Clock . ElapsedFrameTime ) ;
TimeRange . Value = currentTimeRange * speedAdjustmentTrack . AggregateTempo . Value * speedAdjustmentTrack . AggregateFrequency . Value * scale ;
2023-12-26 11:46:21 +08:00
}
2023-05-29 20:14:03 +08:00
/// <summary>
/// Computes a scroll time (in milliseconds) from a scroll speed in the range of 1-40.
/// </summary>
/// <param name="scrollSpeed">The scroll speed.</param>
/// <returns>The scroll time.</returns>
public static double ComputeScrollTime ( int scrollSpeed ) = > MAX_TIME_RANGE / scrollSpeed ;
2020-04-28 18:23:33 +08:00
2019-03-31 00:29:37 +08:00
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer ( ) = > new ManiaPlayfieldAdjustmentContainer ( ) ;
2019-03-26 12:31:49 +08:00
protected override Playfield CreatePlayfield ( ) = > new ManiaPlayfield ( Beatmap . Stages ) ;
2018-04-13 17:19:50 +08:00
2019-02-28 18:07:43 +08:00
public override int Variant = > ( int ) ( Beatmap . Stages . Count = = 1 ? PlayfieldType . Single : PlayfieldType . Dual ) + Beatmap . TotalColumns ;
2018-04-13 17:19:50 +08:00
2019-03-19 19:21:31 +08:00
protected override PassThroughInputManager CreateInputManager ( ) = > new ManiaInputManager ( Ruleset . RulesetInfo , Variant ) ;
2018-04-13 17:19:50 +08:00
2024-02-05 21:52:08 +08:00
public override DrawableHitObject < ManiaHitObject > ? CreateDrawableRepresentation ( ManiaHitObject h ) = > null ;
2018-04-13 17:19:50 +08:00
2018-02-28 15:34:47 +08:00
protected override ReplayInputHandler CreateReplayInputHandler ( Replay replay ) = > new ManiaFramedReplayInputHandler ( replay ) ;
2020-03-24 13:55:49 +08:00
2020-12-14 15:52:14 +08:00
protected override ReplayRecorder CreateReplayRecorder ( Score score ) = > new ManiaReplayRecorder ( score ) ;
2024-02-02 08:56:38 +08:00
2024-02-08 01:16:08 +08:00
protected override ResumeOverlay CreateResumeOverlay ( ) = > new DelayedResumeOverlay ( ) ;
2024-02-02 08:56:38 +08:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
if ( currentSkin . IsNotNull ( ) )
currentSkin . SourceChanged - = onSkinChange ;
}
2016-09-02 19:30:27 +08:00
}
}