2022-07-13 06:07:10 +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.
using System ;
using System.Collections.Generic ;
using System.Linq ;
using osu.Framework.Graphics ;
using osu.Framework.Input.Bindings ;
using osu.Framework.Input.Events ;
using osu.Game.Beatmaps.Timing ;
using osu.Game.Rulesets.Mods ;
using osu.Game.Rulesets.Objects ;
using osu.Game.Rulesets.Osu.Objects ;
using osu.Game.Rulesets.Scoring ;
using osu.Game.Rulesets.UI ;
using osu.Game.Screens.Play ;
using osu.Game.Utils ;
namespace osu.Game.Rulesets.Osu.Mods
{
public abstract class InputBlockingMod : Mod , IApplicableToDrawableRuleset < OsuHitObject >
{
public override double ScoreMultiplier = > 1.0 ;
2022-07-13 14:49:08 +08:00
public override Type [ ] IncompatibleMods = > new [ ] { typeof ( ModAutoplay ) , typeof ( ModRelax ) , typeof ( OsuModCinema ) } ;
2022-07-13 06:07:10 +08:00
public override ModType Type = > ModType . Conversion ;
protected const double FLASH_DURATION = 1000 ;
/// <summary>
/// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods).
/// </summary>
/// <remarks>
/// This is different from <see cref="Player.IsBreakTime"/> in that the periods here end strictly at the first object after the break, rather than the break's end time.
/// </remarks>
2022-07-13 21:02:46 +08:00
protected PeriodTracker NonGameplayPeriods = null ! ;
2022-07-13 06:07:10 +08:00
2022-07-13 21:02:46 +08:00
protected DrawableRuleset < OsuHitObject > Ruleset = null ! ;
2022-07-13 06:07:10 +08:00
2022-07-13 21:02:46 +08:00
protected IFrameStableClock GameplayClock = null ! ;
protected OsuAction ? LastActionPressed ;
2022-07-13 06:07:10 +08:00
public void ApplyToDrawableRuleset ( DrawableRuleset < OsuHitObject > drawableRuleset )
{
Ruleset = drawableRuleset ;
drawableRuleset . KeyBindingInputManager . Add ( new InputInterceptor ( this ) ) ;
var periods = new List < Period > ( ) ;
if ( drawableRuleset . Objects . Any ( ) )
{
periods . Add ( new Period ( int . MinValue , getValidJudgementTime ( Ruleset . Objects . First ( ) ) - 1 ) ) ;
foreach ( BreakPeriod b in drawableRuleset . Beatmap . Breaks )
periods . Add ( new Period ( b . StartTime , getValidJudgementTime ( Ruleset . Objects . First ( h = > h . StartTime > = b . EndTime ) ) - 1 ) ) ;
static double getValidJudgementTime ( HitObject hitObject ) = > hitObject . StartTime - hitObject . HitWindows . WindowFor ( HitResult . Meh ) ;
}
NonGameplayPeriods = new PeriodTracker ( periods ) ;
GameplayClock = drawableRuleset . FrameStableClock ;
}
protected virtual bool CheckCorrectAction ( OsuAction action )
{
if ( NonGameplayPeriods . IsInAny ( GameplayClock . CurrentTime ) )
{
LastActionPressed = null ;
return true ;
}
switch ( action )
{
case OsuAction . LeftButton :
case OsuAction . RightButton :
break ;
// Any action which is not left or right button should be ignored.
default :
return true ;
}
return false ;
}
private class InputInterceptor : Component , IKeyBindingHandler < OsuAction >
{
private readonly InputBlockingMod mod ;
public InputInterceptor ( InputBlockingMod mod )
{
this . mod = mod ;
}
public bool OnPressed ( KeyBindingPressEvent < OsuAction > e )
// if the pressed action is incorrect, block it from reaching gameplay.
= > ! mod . CheckCorrectAction ( e . Action ) ;
public void OnReleased ( KeyBindingReleaseEvent < OsuAction > e )
{
}
}
}
}