2019-09-11 17:12:43 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2019-01-24 16:43:03 +08:00
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
2018-11-07 16:24:05 +08:00
using System.Linq ;
2018-11-20 15:51:59 +08:00
using osuTK.Graphics ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Game.Graphics ;
using osu.Game.Rulesets.Objects.Drawables ;
2018-07-02 11:31:41 +08:00
using osu.Framework.Allocation ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Input.Bindings ;
using osu.Game.Rulesets.Judgements ;
2019-09-11 17:20:41 +08:00
using osu.Game.Rulesets.Mania.Objects.Drawables ;
2018-06-07 19:49:06 +08:00
using osu.Game.Rulesets.Mania.UI.Components ;
2018-04-13 17:19:50 +08:00
using osu.Game.Rulesets.UI.Scrolling ;
2020-03-30 22:14:30 +08:00
using osu.Game.Skinning ;
2018-11-26 09:44:48 +08:00
using osuTK ;
2020-03-31 11:53:17 +08:00
using osu.Game.Rulesets.Mania.Beatmaps ;
2020-05-21 14:15:24 +08:00
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Rulesets.Mania.UI
{
2020-03-30 22:07:32 +08:00
[Cached]
2018-11-06 14:46:36 +08:00
public class Column : ScrollingPlayfield , IKeyBindingHandler < ManiaAction > , IHasAccentColour
2018-04-13 17:19:50 +08:00
{
2019-09-11 17:20:41 +08:00
public const float COLUMN_WIDTH = 80 ;
2020-04-01 12:38:03 +08:00
public const float SPECIAL_COLUMN_WIDTH = 70 ;
2018-04-13 17:19:50 +08:00
2018-11-12 17:24:18 +08:00
/// <summary>
/// The index of this column as part of the whole playfield.
/// </summary>
public readonly int Index ;
2018-07-02 11:31:41 +08:00
public readonly Bindable < ManiaAction > Action = new Bindable < ManiaAction > ( ) ;
2018-04-13 17:19:50 +08:00
2018-06-07 20:40:12 +08:00
private readonly ColumnHitObjectArea hitObjectArea ;
2018-04-13 17:19:50 +08:00
internal readonly Container TopLevelContainer ;
2020-05-18 16:47:47 +08:00
public Container UnderlayElements = > hitObjectArea . UnderlayElements ;
2018-11-12 17:24:18 +08:00
public Column ( int index )
2018-04-13 17:19:50 +08:00
{
2018-11-12 17:24:18 +08:00
Index = index ;
2018-04-13 17:19:50 +08:00
RelativeSizeAxes = Axes . Y ;
2020-04-08 17:23:24 +08:00
Width = COLUMN_WIDTH ;
2018-06-07 10:19:36 +08:00
2020-04-02 17:39:49 +08:00
Drawable background = new SkinnableDrawable ( new ManiaSkinComponent ( ManiaSkinComponents . ColumnBackground , Index ) , _ = > new DefaultColumnBackground ( ) )
2020-03-30 22:14:30 +08:00
{
RelativeSizeAxes = Axes . Both
} ;
2018-06-07 20:13:29 +08:00
InternalChildren = new [ ]
2018-04-13 17:19:50 +08:00
{
2018-06-07 20:13:29 +08:00
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
background . CreateProxy ( ) ,
2020-04-02 17:39:49 +08:00
hitObjectArea = new ColumnHitObjectArea ( Index , HitObjectContainer ) { RelativeSizeAxes = Axes . Both } ,
new SkinnableDrawable ( new ManiaSkinComponent ( ManiaSkinComponents . KeyArea , Index ) , _ = > new DefaultKeyArea ( ) )
2018-04-13 17:19:50 +08:00
{
2020-03-31 10:23:33 +08:00
RelativeSizeAxes = Axes . Both
2018-04-13 17:19:50 +08:00
} ,
2018-06-07 20:13:29 +08:00
background ,
2018-04-13 17:19:50 +08:00
TopLevelContainer = new Container { RelativeSizeAxes = Axes . Both }
} ;
2020-03-31 11:17:44 +08:00
TopLevelContainer . Add ( hitObjectArea . Explosions . CreateProxy ( ) ) ;
2018-04-13 17:19:50 +08:00
}
public override Axes RelativeSizeAxes = > Axes . Y ;
2020-04-01 12:38:03 +08:00
public ColumnType ColumnType { get ; set ; }
2019-02-28 12:31:40 +08:00
2020-04-01 12:38:03 +08:00
public bool IsSpecial = > ColumnType = = ColumnType . Special ;
2020-03-31 11:53:17 +08:00
2020-03-31 14:23:59 +08:00
public Color4 AccentColour { get ; set ; }
2018-04-13 17:19:50 +08:00
2018-07-11 16:07:14 +08:00
protected override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
2018-07-02 11:31:41 +08:00
{
2018-07-11 16:07:14 +08:00
var dependencies = new DependencyContainer ( base . CreateChildDependencies ( parent ) ) ;
2018-07-02 11:31:41 +08:00
dependencies . CacheAs < IBindable < ManiaAction > > ( Action ) ;
return dependencies ;
}
2018-04-13 17:19:50 +08:00
/// <summary>
/// Adds a DrawableHitObject to this Playfield.
/// </summary>
/// <param name="hitObject">The DrawableHitObject to add.</param>
public override void Add ( DrawableHitObject hitObject )
{
2019-07-22 13:45:25 +08:00
hitObject . AccentColour . Value = AccentColour ;
2018-08-06 09:54:16 +08:00
hitObject . OnNewResult + = OnNewResult ;
2018-04-13 17:19:50 +08:00
2018-11-07 16:24:05 +08:00
HitObjectContainer . Add ( hitObject ) ;
2018-04-13 17:19:50 +08:00
}
2018-11-19 15:20:21 +08:00
public override bool Remove ( DrawableHitObject h )
{
if ( ! base . Remove ( h ) )
return false ;
h . OnNewResult - = OnNewResult ;
return true ;
}
2018-08-06 09:54:16 +08:00
internal void OnNewResult ( DrawableHitObject judgedObject , JudgementResult result )
2018-04-13 17:19:50 +08:00
{
2019-02-21 17:56:34 +08:00
if ( ! result . IsHit | | ! judgedObject . DisplayResult | | ! DisplayJudgements . Value )
2018-04-13 17:19:50 +08:00
return ;
2020-04-02 17:39:49 +08:00
var explosion = new SkinnableDrawable ( new ManiaSkinComponent ( ManiaSkinComponents . HitExplosion , Index ) , _ = >
2020-04-01 18:19:32 +08:00
new DefaultHitExplosion ( judgedObject . AccentColour . Value , judgedObject is DrawableHoldNoteTick ) )
2018-06-08 14:23:47 +08:00
{
2020-04-01 18:19:32 +08:00
RelativeSizeAxes = Axes . Both
2020-04-02 14:57:02 +08:00
} ;
hitObjectArea . Explosions . Add ( explosion ) ;
explosion . Delay ( 200 ) . Expire ( true ) ;
2018-04-13 17:19:50 +08:00
}
public bool OnPressed ( ManiaAction action )
{
2019-02-21 17:56:34 +08:00
if ( action ! = Action . Value )
2018-04-13 17:19:50 +08:00
return false ;
2018-06-06 13:18:38 +08:00
var nextObject =
2018-11-07 16:24:05 +08:00
HitObjectContainer . AliveObjects . FirstOrDefault ( h = > h . HitObject . StartTime > Time . Current ) ? ?
2018-06-06 13:18:38 +08:00
// fallback to non-alive objects to find next off-screen object
2018-11-07 16:24:05 +08:00
HitObjectContainer . Objects . FirstOrDefault ( h = > h . HitObject . StartTime > Time . Current ) ? ?
HitObjectContainer . Objects . LastOrDefault ( ) ;
2018-06-06 13:18:38 +08:00
nextObject ? . PlaySamples ( ) ;
2018-04-13 17:19:50 +08:00
return true ;
}
2020-01-22 12:22:34 +08:00
public void OnReleased ( ManiaAction action )
{
}
2018-11-13 13:13:29 +08:00
public override bool ReceivePositionalInputAt ( Vector2 screenSpacePos )
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
2020-04-08 13:08:34 +08:00
= > DrawRectangle . Inflate ( new Vector2 ( Stage . COLUMN_SPACING / 2 , 0 ) ) . Contains ( ToLocalSpace ( screenSpacePos ) ) ;
2020-05-21 12:33:02 +08:00
2020-05-21 14:16:30 +08:00
/// <summary>
/// Given a time, return the screen space position within this column.
/// </summary>
public Vector2 ScreenSpacePositionAtTime ( double time )
2020-05-21 12:33:02 +08:00
{
var pos = ScrollingInfo . Algorithm . PositionAt ( time , Time . Current , ScrollingInfo . TimeRange . Value , HitObjectContainer . DrawHeight ) ;
2020-05-21 14:15:24 +08:00
switch ( ScrollingInfo . Direction . Value )
2020-05-21 12:33:02 +08:00
{
2020-05-21 14:15:24 +08:00
case ScrollingDirection . Down :
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
// so when scrolling downwards the coordinates need to be flipped.
pos = HitObjectContainer . DrawHeight - pos ;
// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction.
pos - = DefaultNotePiece . NOTE_HEIGHT / 2 ;
break ;
case ScrollingDirection . Up :
pos + = DefaultNotePiece . NOTE_HEIGHT / 2 ;
break ;
2020-05-21 12:33:02 +08:00
}
return HitObjectContainer . ToScreenSpace ( new Vector2 ( HitObjectContainer . DrawWidth / 2 , pos ) ) ;
}
2020-05-21 13:25:37 +08:00
2020-05-21 14:16:30 +08:00
/// <summary>
/// Given a position in screen space, return the time within this column.
/// </summary>
2020-05-21 13:25:37 +08:00
public double TimeAtScreenSpacePosition ( Vector2 screenSpacePosition )
{
// convert to local space of column so we can snap and fetch correct location.
Vector2 localPosition = HitObjectContainer . ToLocalSpace ( screenSpacePosition ) ;
2020-05-21 14:15:24 +08:00
switch ( ScrollingInfo . Direction . Value )
2020-05-21 13:25:37 +08:00
{
2020-05-21 14:15:24 +08:00
case ScrollingDirection . Down :
// as above
localPosition . Y = HitObjectContainer . DrawHeight - localPosition . Y ;
break ;
2020-05-21 13:25:37 +08:00
}
2020-05-21 14:15:24 +08:00
// offset for the fact that blueprints are centered, as above.
localPosition . Y - = DefaultNotePiece . NOTE_HEIGHT / 2 ;
2020-05-21 13:25:37 +08:00
return ScrollingInfo . Algorithm . TimeAt ( localPosition . Y , Time . Current , ScrollingInfo . TimeRange . Value , HitObjectContainer . DrawHeight ) ;
}
2018-04-13 17:19:50 +08:00
}
}