2019-01-24 08:43:03 +00: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 09:19:50 +00:00
2017-05-23 06:20:32 +00:00
using System.Collections.Generic ;
2020-04-28 10:23:33 +00:00
using System.Linq ;
2017-06-09 13:03:28 +00:00
using osu.Framework.Allocation ;
2020-04-28 10:23:33 +00:00
using osu.Framework.Audio.Track ;
2019-02-21 10:04:31 +00:00
using osu.Framework.Bindables ;
2017-06-09 13:03:28 +00:00
using osu.Framework.Extensions.IEnumerableExtensions ;
2024-02-02 00:56:38 +00:00
using osu.Framework.Extensions.ObjectExtensions ;
2017-08-21 03:31:21 +00:00
using osu.Framework.Input ;
2024-07-10 15:24:03 +00:00
using osu.Framework.Platform ;
2024-02-02 00:56:38 +00:00
using osu.Framework.Threading ;
2024-07-10 15:24:03 +00:00
using osu.Framework.Utils ;
2017-03-10 06:08:53 +00:00
using osu.Game.Beatmaps ;
2020-04-03 04:16:01 +00:00
using osu.Game.Beatmaps.ControlPoints ;
2018-02-28 07:34:47 +00:00
using osu.Game.Input.Handlers ;
2018-11-28 08:20:37 +00:00
using osu.Game.Replays ;
2017-04-18 07:05:58 +00:00
using osu.Game.Rulesets.Mania.Beatmaps ;
2018-01-18 08:00:23 +00:00
using osu.Game.Rulesets.Mania.Configuration ;
2017-04-18 07:05:58 +00:00
using osu.Game.Rulesets.Mania.Objects ;
2017-09-12 06:52:18 +00:00
using osu.Game.Rulesets.Mania.Replays ;
2023-12-26 03:46:21 +00:00
using osu.Game.Rulesets.Mania.Skinning ;
2019-04-08 09:32:05 +00:00
using osu.Game.Rulesets.Mods ;
2019-09-10 04:29:50 +00:00
using osu.Game.Rulesets.Objects ;
2017-04-18 07:05:58 +00:00
using osu.Game.Rulesets.Objects.Drawables ;
using osu.Game.Rulesets.UI ;
2018-01-04 10:22:15 +00:00
using osu.Game.Rulesets.UI.Scrolling ;
2020-12-14 07:52:14 +00:00
using osu.Game.Scoring ;
2024-02-07 17:16:08 +00:00
using osu.Game.Screens.Play ;
2023-12-26 03:46:21 +00:00
using osu.Game.Skinning ;
2018-04-13 09:19:50 +00:00
2017-04-18 07:05:58 +00:00
namespace osu.Game.Rulesets.Mania.UI
2016-09-02 11:30:27 +00:00
{
2024-05-06 07:24:32 +00:00
[Cached]
2019-03-20 02:29:16 +00:00
public partial class DrawableManiaRuleset : DrawableScrollingRuleset < ManiaHitObject >
2016-09-02 11:30:27 +00:00
{
2020-04-03 09:25:01 +00:00
/// <summary>
2023-05-29 12:14:03 +00:00
/// The minimum time range. This occurs at a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 40.
2020-04-03 09:25:01 +00:00
/// </summary>
2021-07-16 09:22:34 +00:00
public const double MIN_TIME_RANGE = 290 ;
2020-04-03 09:25:01 +00:00
/// <summary>
2023-05-29 12:14:03 +00:00
/// The maximum time range. This occurs with a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 1.
2020-04-03 09:25:01 +00:00
/// </summary>
2021-07-16 09:22:34 +00:00
public const double MAX_TIME_RANGE = 11485 ;
2020-04-03 09:25:01 +00:00
2024-05-06 07:24:32 +00:00
public new ManiaPlayfield Playfield = > ( ManiaPlayfield ) base . Playfield ;
2019-03-20 02:29:16 +00:00
2018-01-03 09:44:25 +00:00
public new ManiaBeatmap Beatmap = > ( ManiaBeatmap ) base . Beatmap ;
2018-04-13 09:19:50 +00:00
2017-12-28 13:40:23 +00:00
public IEnumerable < BarLine > BarLines ;
2018-04-13 09:19:50 +00:00
2019-08-26 03:51:23 +00:00
protected override bool RelativeScaleBeatLengths = > true ;
2019-01-25 10:14:37 +00:00
protected new ManiaRulesetConfigManager Config = > ( ManiaRulesetConfigManager ) base . Config ;
2018-06-08 11:13:24 +00:00
2018-11-06 06:46:36 +00:00
private readonly Bindable < ManiaScrollingDirection > configDirection = new Bindable < ManiaScrollingDirection > ( ) ;
2023-05-29 12:14:03 +00:00
private readonly BindableInt configScrollSpeed = new BindableInt ( ) ;
2024-07-10 15:24:03 +00:00
private double currentTimeRange ;
protected double TargetTimeRange ;
2020-04-28 10:23:33 +00:00
// Stores the current speed adjustment active in gameplay.
2020-04-29 05:27:21 +00:00
private readonly Track speedAdjustmentTrack = new TrackVirtual ( 0 ) ;
2018-11-06 06:46:36 +00:00
2024-02-05 13:52:08 +00:00
private ISkinSource currentSkin = null ! ;
2023-12-26 03:46:21 +00:00
2024-07-10 15:24:03 +00:00
[Resolved]
private GameHost gameHost { get ; set ; } = null ! ;
2024-02-05 13:52:08 +00:00
public DrawableManiaRuleset ( Ruleset ruleset , IBeatmap beatmap , IReadOnlyList < Mod > ? mods = null )
2019-04-08 09:32:05 +00:00
: base ( ruleset , beatmap , mods )
2016-09-02 11:30:27 +00:00
{
2019-09-24 21:03:55 +00:00
BarLines = new BarLineGenerator < BarLine > ( Beatmap ) . BarLines ;
2023-05-29 12:14:03 +00:00
TimeRange . MinValue = 1 ;
TimeRange . MaxValue = MAX_TIME_RANGE ;
2017-05-10 05:56:39 +00:00
}
2018-04-13 09:19:50 +00:00
2017-06-09 13:03:28 +00:00
[BackgroundDependencyLoader]
2024-02-02 00:56:38 +00:00
private void load ( ISkinSource source )
2017-06-09 13:03:28 +00:00
{
2024-02-02 00:56:38 +00:00
currentSkin = source ;
currentSkin . SourceChanged + = onSkinChange ;
skinChanged ( ) ;
2020-04-28 10:23:33 +00:00
foreach ( var mod in Mods . OfType < IApplicableToTrack > ( ) )
mod . ApplyToTrack ( speedAdjustmentTrack ) ;
2020-04-03 04:16:01 +00: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 03:34:29 +00:00
p . BaseBeatLength * = Beatmap . Difficulty . SliderMultiplier ;
2020-04-03 04:16:01 +00:00
// For non-mania beatmap, speed changes should only happen through timing points
if ( ! isForCurrentRuleset )
2021-09-01 09:19:25 +00:00
p . EffectPoint = new EffectControlPoint ( ) ;
2020-04-03 04:16:01 +00:00
}
2017-08-04 14:07:08 +00:00
BarLines . ForEach ( Playfield . Add ) ;
2018-07-17 07:55:50 +00:00
2019-01-25 10:14:37 +00:00
Config . BindWith ( ManiaRulesetSetting . ScrollDirection , configDirection ) ;
2019-02-22 11:13:38 +00:00
configDirection . BindValueChanged ( direction = > Direction . Value = ( ScrollingDirection ) direction . NewValue , true ) ;
2018-11-07 08:24:05 +00:00
2023-05-29 12:14:03 +00:00
Config . BindWith ( ManiaRulesetSetting . ScrollSpeed , configScrollSpeed ) ;
2024-07-10 15:24:03 +00:00
configScrollSpeed . BindValueChanged ( speed = > TargetTimeRange = ComputeScrollTime ( speed . NewValue ) ) ;
2018-04-13 09:19:50 +00:00
2024-07-10 15:24:03 +00:00
TimeRange . Value = TargetTimeRange = currentTimeRange = ComputeScrollTime ( configScrollSpeed . Value ) ;
2024-05-06 07:24:32 +00:00
2024-05-14 11:28:14 +00:00
KeyBindingInputManager . Add ( new ManiaTouchInputArea ( ) ) ;
2020-04-03 09:25:01 +00:00
}
2023-05-29 12:14:03 +00:00
protected override void AdjustScrollSpeed ( int amount ) = > configScrollSpeed . Value + = amount ;
2020-04-03 09:25:01 +00:00
2020-04-28 10:23:33 +00:00
protected override void Update ( )
{
base . Update ( ) ;
updateTimeRange ( ) ;
}
2024-02-05 13:52:08 +00:00
private ScheduledDelegate ? pendingSkinChange ;
2024-02-02 00:56:38 +00:00
private float hitPosition ;
private void onSkinChange ( )
2023-12-26 03:46:21 +00:00
{
2024-02-02 00:56:38 +00: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 03:46:21 +00:00
2024-02-02 00:56:38 +00:00
private void updateTimeRange ( )
{
2023-12-26 03:46:21 +00: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 15:24:03 +00: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 03:46:21 +00:00
}
2023-05-29 12:14:03 +00: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 10:23:33 +00:00
2019-03-30 16:29:37 +00:00
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer ( ) = > new ManiaPlayfieldAdjustmentContainer ( ) ;
2019-03-26 04:31:49 +00:00
protected override Playfield CreatePlayfield ( ) = > new ManiaPlayfield ( Beatmap . Stages ) ;
2018-04-13 09:19:50 +00:00
2019-02-28 10:07:43 +00:00
public override int Variant = > ( int ) ( Beatmap . Stages . Count = = 1 ? PlayfieldType . Single : PlayfieldType . Dual ) + Beatmap . TotalColumns ;
2018-04-13 09:19:50 +00:00
2019-03-19 11:21:31 +00:00
protected override PassThroughInputManager CreateInputManager ( ) = > new ManiaInputManager ( Ruleset . RulesetInfo , Variant ) ;
2018-04-13 09:19:50 +00:00
2024-02-05 13:52:08 +00:00
public override DrawableHitObject < ManiaHitObject > ? CreateDrawableRepresentation ( ManiaHitObject h ) = > null ;
2018-04-13 09:19:50 +00:00
2018-02-28 07:34:47 +00:00
protected override ReplayInputHandler CreateReplayInputHandler ( Replay replay ) = > new ManiaFramedReplayInputHandler ( replay ) ;
2020-03-24 05:55:49 +00:00
2020-12-14 07:52:14 +00:00
protected override ReplayRecorder CreateReplayRecorder ( Score score ) = > new ManiaReplayRecorder ( score ) ;
2024-02-02 00:56:38 +00:00
2024-02-07 17:16:08 +00:00
protected override ResumeOverlay CreateResumeOverlay ( ) = > new DelayedResumeOverlay ( ) ;
2024-02-02 00:56:38 +00:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
if ( currentSkin . IsNotNull ( ) )
currentSkin . SourceChanged - = onSkinChange ;
}
2016-09-02 11:30:27 +00:00
}
}