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
2019-11-06 13:55:05 +08:00
using System ;
2018-04-13 17:19:50 +08:00
using System.Collections.Generic ;
2019-10-16 19:20:07 +08:00
using System.Linq ;
2018-04-13 17:19:50 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Rulesets.Edit ;
using osu.Game.Rulesets.Edit.Tools ;
2019-04-08 17:32:05 +08:00
using osu.Game.Rulesets.Mods ;
2019-10-16 19:20:07 +08:00
using osu.Game.Rulesets.Objects ;
2018-04-13 17:19:50 +08:00
using osu.Game.Rulesets.Objects.Drawables ;
2018-11-07 15:08:56 +08:00
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles ;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders ;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners ;
2018-10-17 17:01:38 +08:00
using osu.Game.Rulesets.Osu.Objects ;
2018-04-13 17:19:50 +08:00
using osu.Game.Rulesets.Osu.Objects.Drawables ;
2018-10-17 17:01:38 +08:00
using osu.Game.Rulesets.UI ;
2018-11-16 16:12:24 +08:00
using osu.Game.Screens.Edit.Compose.Components ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Rulesets.Osu.Edit
{
2018-10-17 17:01:38 +08:00
public class OsuHitObjectComposer : HitObjectComposer < OsuHitObject >
2018-04-13 17:19:50 +08:00
{
public OsuHitObjectComposer ( Ruleset ruleset )
: base ( ruleset )
{
}
2019-12-12 14:58:11 +08:00
protected override DrawableRuleset < OsuHitObject > CreateDrawableRuleset ( Ruleset ruleset , IBeatmap beatmap , IReadOnlyList < Mod > mods = null )
2019-04-08 17:32:05 +08:00
= > new DrawableOsuEditRuleset ( ruleset , beatmap , mods ) ;
2018-04-13 17:19:50 +08:00
2018-10-04 12:44:49 +08:00
protected override IReadOnlyList < HitObjectCompositionTool > CompositionTools = > new HitObjectCompositionTool [ ]
2018-04-13 17:19:50 +08:00
{
2018-10-03 15:27:26 +08:00
new HitCircleCompositionTool ( ) ,
2018-10-04 12:44:49 +08:00
new SliderCompositionTool ( ) ,
2018-10-29 17:35:46 +08:00
new SpinnerCompositionTool ( )
2018-04-13 17:19:50 +08:00
} ;
2018-11-19 15:58:11 +08:00
public override SelectionHandler CreateSelectionHandler ( ) = > new OsuSelectionHandler ( ) ;
2018-11-16 16:12:24 +08:00
2018-11-06 17:03:21 +08:00
public override SelectionBlueprint CreateBlueprintFor ( DrawableHitObject hitObject )
2018-04-13 17:19:50 +08:00
{
switch ( hitObject )
{
case DrawableHitCircle circle :
2018-11-06 16:56:04 +08:00
return new HitCircleSelectionBlueprint ( circle ) ;
2019-04-01 11:44:46 +08:00
2018-04-13 17:19:50 +08:00
case DrawableSlider slider :
2018-11-06 16:56:04 +08:00
return new SliderSelectionBlueprint ( slider ) ;
2019-04-01 11:44:46 +08:00
2018-10-29 17:23:23 +08:00
case DrawableSpinner spinner :
2018-11-06 16:56:04 +08:00
return new SpinnerSelectionBlueprint ( spinner ) ;
2018-04-13 17:19:50 +08:00
}
2018-11-06 17:03:21 +08:00
return base . CreateBlueprintFor ( hitObject ) ;
2018-04-13 17:19:50 +08:00
}
2019-10-16 19:20:07 +08:00
protected override DistanceSnapGrid CreateDistanceSnapGrid ( IEnumerable < HitObject > selectedHitObjects )
{
var objects = selectedHitObjects . ToList ( ) ;
if ( objects . Count = = 0 )
2019-11-06 13:55:05 +08:00
return createGrid ( h = > h . StartTime < = EditorClock . CurrentTime ) ;
double minTime = objects . Min ( h = > h . StartTime ) ;
2019-11-06 15:04:20 +08:00
return createGrid ( h = > h . StartTime < minTime , objects . Count + 1 ) ;
2019-11-06 13:55:05 +08:00
}
2019-11-06 15:04:20 +08:00
/// <summary>
/// Creates a grid from the last <see cref="HitObject"/> matching a predicate to a target <see cref="HitObject"/>.
/// </summary>
/// <param name="sourceSelector">A predicate that matches <see cref="HitObject"/>s where the grid can start from.
/// Only the last <see cref="HitObject"/> matching the predicate is used.</param>
/// <param name="targetOffset">An offset from the <see cref="HitObject"/> selected via <paramref name="sourceSelector"/> at which the grid should stop.</param>
/// <returns>The <see cref="OsuDistanceSnapGrid"/> from a selected <see cref="HitObject"/> to a target <see cref="HitObject"/>.</returns>
private OsuDistanceSnapGrid createGrid ( Func < HitObject , bool > sourceSelector , int targetOffset = 1 )
2019-11-06 13:55:05 +08:00
{
2019-11-06 15:04:20 +08:00
if ( targetOffset < 1 ) throw new ArgumentOutOfRangeException ( nameof ( targetOffset ) ) ;
int sourceIndex = - 1 ;
2019-11-06 13:55:05 +08:00
for ( int i = 0 ; i < EditorBeatmap . HitObjects . Count ; i + + )
2019-10-16 19:20:07 +08:00
{
2019-11-06 15:04:20 +08:00
if ( ! sourceSelector ( EditorBeatmap . HitObjects [ i ] ) )
2019-11-06 13:55:05 +08:00
break ;
2019-10-16 19:20:07 +08:00
2019-11-06 15:04:20 +08:00
sourceIndex = i ;
2019-10-16 19:20:07 +08:00
}
2019-11-06 15:04:20 +08:00
if ( sourceIndex = = - 1 )
2019-11-06 13:55:05 +08:00
return null ;
2019-10-16 19:20:07 +08:00
2019-12-27 18:39:30 +08:00
HitObject sourceObject = EditorBeatmap . HitObjects [ sourceIndex ] ;
2019-12-17 15:35:40 +08:00
int targetIndex = sourceIndex + targetOffset ;
2019-12-27 18:39:30 +08:00
HitObject targetObject = null ;
2019-12-17 15:35:40 +08:00
// Keep advancing the target object while its start time falls before the end time of the source object
while ( true )
{
if ( targetIndex > = EditorBeatmap . HitObjects . Count )
break ;
if ( EditorBeatmap . HitObjects [ targetIndex ] . StartTime > = sourceObject . GetEndTime ( ) )
{
targetObject = EditorBeatmap . HitObjects [ targetIndex ] ;
break ;
}
targetIndex + + ;
}
2019-10-16 19:20:07 +08:00
2019-12-27 18:39:30 +08:00
return new OsuDistanceSnapGrid ( ( OsuHitObject ) sourceObject , ( OsuHitObject ) targetObject ) ;
2019-10-16 19:20:07 +08:00
}
2018-04-13 17:19:50 +08:00
}
}