2019-01-24 17:43:03 +09: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 18:19:50 +09:00
using System ;
2019-09-02 18:31:33 +09:00
using System.Diagnostics ;
2018-11-09 13:58:46 +09:00
using osu.Framework.Allocation ;
2019-02-21 19:04:31 +09:00
using osu.Framework.Bindables ;
2018-04-13 18:19:50 +09:00
using osu.Framework.Graphics ;
2019-02-14 18:47:05 +09:00
using osu.Framework.Graphics.Containers ;
2020-06-15 22:44:55 +09:00
using osu.Framework.Input ;
2019-07-24 18:50:57 +09:00
using osu.Framework.Input.Bindings ;
2020-06-15 22:44:55 +09:00
using osu.Game.Rulesets.Judgements ;
2018-04-13 18:19:50 +09:00
using osu.Game.Rulesets.Objects.Drawables ;
2020-06-15 22:44:55 +09:00
using osu.Game.Rulesets.Osu.Judgements ;
2018-04-13 18:19:50 +09:00
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces ;
using osu.Game.Rulesets.Scoring ;
2019-07-24 18:50:57 +09:00
using osu.Game.Skinning ;
2019-10-01 08:15:48 +03:00
using osuTK ;
2018-04-13 18:19:50 +09:00
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableHitCircle : DrawableOsuHitObject , IDrawableHitObjectWithProxiedApproach
{
2019-09-18 20:04:49 +09:00
public ApproachCircle ApproachCircle { get ; }
2018-04-13 18:19:50 +09:00
2018-11-09 13:58:46 +09:00
private readonly IBindable < Vector2 > positionBindable = new Bindable < Vector2 > ( ) ;
private readonly IBindable < int > stackHeightBindable = new Bindable < int > ( ) ;
2020-02-01 22:50:29 +01:00
private readonly IBindable < float > scaleBindable = new BindableFloat ( ) ;
2018-11-09 13:58:46 +09:00
2019-10-21 17:14:08 +09:00
public OsuAction ? HitAction = > HitArea . HitAction ;
2019-02-14 18:47:05 +09:00
2019-10-21 17:14:08 +09:00
public readonly HitReceptor HitArea ;
public readonly SkinnableDrawable CirclePiece ;
2019-02-14 18:47:05 +09:00
private readonly Container scaleContainer ;
2020-03-28 13:39:08 +09:00
protected virtual OsuSkinComponents CirclePieceComponent = > OsuSkinComponents . HitCircle ;
2020-06-15 22:44:55 +09:00
private InputManager inputManager ;
2018-04-13 18:19:50 +09:00
public DrawableHitCircle ( HitCircle h )
: base ( h )
{
Origin = Anchor . Centre ;
Position = HitObject . StackedPosition ;
InternalChildren = new Drawable [ ]
{
2019-02-14 18:47:05 +09:00
scaleContainer = new Container
2018-04-13 18:19:50 +09:00
{
2019-02-14 18:47:05 +09:00
RelativeSizeAxes = Axes . Both ,
Origin = Anchor . Centre ,
Anchor = Anchor . Centre ,
2019-07-24 18:50:57 +09:00
Children = new Drawable [ ]
2018-04-13 18:19:50 +09:00
{
2019-10-21 17:14:08 +09:00
HitArea = new HitReceptor
2019-02-14 18:47:05 +09:00
{
2019-07-24 18:50:57 +09:00
Hit = ( ) = >
2019-02-14 18:47:05 +09:00
{
2019-07-24 18:50:57 +09:00
if ( AllJudged )
return false ;
UpdateResult ( true ) ;
return true ;
2019-02-14 18:47:05 +09:00
} ,
2019-07-24 18:50:57 +09:00
} ,
2020-03-28 13:39:08 +09:00
CirclePiece = new SkinnableDrawable ( new OsuSkinComponent ( CirclePieceComponent ) , _ = > new MainCirclePiece ( ) ) ,
2019-07-24 18:50:57 +09:00
ApproachCircle = new ApproachCircle
{
Alpha = 0 ,
Scale = new Vector2 ( 4 ) ,
2019-02-14 18:47:05 +09:00
}
}
2018-04-13 18:19:50 +09:00
} ,
} ;
2019-10-21 17:14:08 +09:00
Size = HitArea . DrawSize ;
2018-11-09 13:58:46 +09:00
}
[BackgroundDependencyLoader]
private void load ( )
{
positionBindable . BindValueChanged ( _ = > Position = HitObject . StackedPosition ) ;
stackHeightBindable . BindValueChanged ( _ = > Position = HitObject . StackedPosition ) ;
2019-02-22 20:13:38 +09:00
scaleBindable . BindValueChanged ( scale = > scaleContainer . Scale = new Vector2 ( scale . NewValue ) , true ) ;
2018-04-13 18:19:50 +09:00
2018-11-09 13:58:46 +09:00
positionBindable . BindTo ( HitObject . PositionBindable ) ;
stackHeightBindable . BindTo ( HitObject . StackHeightBindable ) ;
scaleBindable . BindTo ( HitObject . ScaleBindable ) ;
2018-04-13 18:19:50 +09:00
2019-07-24 18:50:57 +09:00
AccentColour . BindValueChanged ( accent = > ApproachCircle . Colour = accent . NewValue , true ) ;
2018-04-13 18:19:50 +09:00
}
2020-06-15 22:44:55 +09:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
inputManager = GetContainingInputManager ( ) ;
}
2019-09-12 19:29:08 +09:00
public override double LifetimeStart
{
get = > base . LifetimeStart ;
set
{
base . LifetimeStart = value ;
ApproachCircle . LifetimeStart = value ;
}
}
public override double LifetimeEnd
{
get = > base . LifetimeEnd ;
set
{
base . LifetimeEnd = value ;
ApproachCircle . LifetimeEnd = value ;
}
}
2018-08-06 11:31:46 +09:00
protected override void CheckForResult ( bool userTriggered , double timeOffset )
2018-04-13 18:19:50 +09:00
{
2019-09-02 18:31:33 +09:00
Debug . Assert ( HitObject . HitWindows ! = null ) ;
2018-04-13 18:19:50 +09:00
if ( ! userTriggered )
{
if ( ! HitObject . HitWindows . CanBeHit ( timeOffset ) )
2018-08-03 15:38:48 +09:00
ApplyResult ( r = > r . Type = HitResult . Miss ) ;
2018-08-01 21:46:22 +09:00
2018-04-13 18:19:50 +09:00
return ;
}
var result = HitObject . HitWindows . ResultFor ( timeOffset ) ;
2019-04-01 12:16:05 +09:00
2020-04-10 02:02:09 +09:00
if ( result = = HitResult . None | | CheckHittable ? . Invoke ( this , Time . Current ) = = false )
2018-06-28 13:41:23 +02:00
{
2019-09-06 15:24:00 +09:00
Shake ( Math . Abs ( timeOffset ) - HitObject . HitWindows . WindowFor ( HitResult . Miss ) ) ;
2018-04-13 18:19:50 +09:00
return ;
2018-06-28 13:41:23 +02:00
}
2018-04-13 18:19:50 +09:00
2020-06-15 22:44:55 +09:00
ApplyResult ( r = >
{
var circleResult = ( OsuHitCircleJudgementResult ) r ;
2020-06-19 19:58:35 +09:00
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
2020-06-15 22:44:55 +09:00
if ( result ! = HitResult . Miss )
{
var localMousePosition = ToLocalSpace ( inputManager . CurrentState . Mouse . Position ) ;
2020-06-19 19:58:35 +09:00
circleResult . CursorPositionAtHit = HitObject . StackedPosition + ( localMousePosition - DrawSize / 2 ) ;
2020-06-15 22:44:55 +09:00
}
circleResult . Type = result ;
} ) ;
2018-04-13 18:19:50 +09:00
}
2019-07-22 15:33:12 +09:00
protected override void UpdateInitialTransforms ( )
2018-04-13 18:19:50 +09:00
{
2019-07-22 15:33:12 +09:00
base . UpdateInitialTransforms ( ) ;
2018-04-13 18:19:50 +09:00
2019-09-18 20:04:49 +09:00
CirclePiece . FadeInFromZero ( HitObject . TimeFadeIn ) ;
2019-08-28 18:10:58 +09:00
2018-07-05 11:32:09 +09:00
ApproachCircle . FadeIn ( Math . Min ( HitObject . TimeFadeIn * 2 , HitObject . TimePreempt ) ) ;
2019-07-24 19:32:24 +09:00
ApproachCircle . ScaleTo ( 1f , HitObject . TimePreempt ) ;
2018-12-13 14:55:28 +09:00
ApproachCircle . Expire ( true ) ;
2018-04-13 18:19:50 +09:00
}
2019-07-22 15:33:12 +09:00
protected override void UpdateStateTransforms ( ArmedState state )
2018-04-13 18:19:50 +09:00
{
2019-09-13 18:49:21 +09:00
base . UpdateStateTransforms ( state ) ;
2019-09-02 18:31:33 +09:00
Debug . Assert ( HitObject . HitWindows ! = null ) ;
2018-04-13 18:19:50 +09:00
switch ( state )
{
case ArmedState . Idle :
this . Delay ( HitObject . TimePreempt ) . FadeOut ( 500 ) ;
Expire ( true ) ;
2019-10-21 17:14:08 +09:00
HitArea . HitAction = null ;
2018-04-13 18:19:50 +09:00
break ;
2019-04-01 12:16:05 +09:00
2018-04-13 18:19:50 +09:00
case ArmedState . Miss :
ApproachCircle . FadeOut ( 50 ) ;
this . FadeOut ( 100 ) ;
break ;
2019-04-01 12:16:05 +09:00
2018-04-13 18:19:50 +09:00
case ArmedState . Hit :
ApproachCircle . FadeOut ( 50 ) ;
2019-07-24 18:50:57 +09:00
// todo: temporary / arbitrary
2019-09-12 19:29:08 +09:00
this . Delay ( 800 ) . FadeOut ( ) ;
2019-07-24 18:50:57 +09:00
break ;
}
}
2018-04-13 18:19:50 +09:00
2019-07-24 18:50:57 +09:00
public Drawable ProxiedLayer = > ApproachCircle ;
2018-04-13 18:19:50 +09:00
2020-06-15 22:44:55 +09:00
protected override JudgementResult CreateResult ( Judgement judgement ) = > new OsuHitCircleJudgementResult ( HitObject , judgement ) ;
2020-03-06 21:21:20 +01:00
public class HitReceptor : CompositeDrawable , IKeyBindingHandler < OsuAction >
2019-07-24 18:50:57 +09:00
{
// IsHovered is used
public override bool HandlePositionalInput = > true ;
2018-04-13 18:19:50 +09:00
2019-07-24 18:50:57 +09:00
public Func < bool > Hit ;
2018-04-13 18:19:50 +09:00
2019-07-24 18:50:57 +09:00
public OsuAction ? HitAction ;
2019-10-21 17:14:08 +09:00
public HitReceptor ( )
2019-07-24 18:50:57 +09:00
{
Size = new Vector2 ( OsuHitObject . OBJECT_RADIUS * 2 ) ;
Anchor = Anchor . Centre ;
Origin = Anchor . Centre ;
2020-03-06 21:21:20 +01:00
CornerRadius = OsuHitObject . OBJECT_RADIUS ;
CornerExponent = 2 ;
2018-04-13 18:19:50 +09:00
}
2019-07-24 18:50:57 +09:00
public bool OnPressed ( OsuAction action )
{
switch ( action )
{
case OsuAction . LeftButton :
case OsuAction . RightButton :
if ( IsHovered & & ( Hit ? . Invoke ( ) ? ? false ) )
{
HitAction = action ;
return true ;
}
break ;
}
return false ;
}
2020-01-22 13:22:34 +09:00
public void OnReleased ( OsuAction action )
{
}
2019-07-24 18:50:57 +09:00
}
2018-04-13 18:19:50 +09:00
}
}