osu/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs

195 lines
6.6 KiB
C#
Raw Normal View History

// 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
2019-02-21 10:04:31 +00:00
using osu.Framework.Bindables;
2018-04-13 09:19:50 +00:00
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
2018-04-13 09:19:50 +00:00
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
2018-04-13 09:19:50 +00:00
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// Visualises a <see cref="HoldNote"/> hit object.
/// </summary>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
{
public override bool DisplayResult => false;
public DrawableNote Head => headContainer.Child;
public DrawableNote Tail => tailContainer.Child;
private readonly Container<DrawableHoldNoteHead> headContainer;
private readonly Container<DrawableHoldNoteTail> tailContainer;
private readonly Container<DrawableHoldNoteTick> tickContainer;
2018-04-13 09:19:50 +00:00
private readonly BodyPiece bodyPiece;
/// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
/// </summary>
internal double? HoldStartTime;
2018-04-13 09:19:50 +00:00
/// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release.
/// </summary>
internal bool HasBroken;
2018-04-13 09:19:50 +00:00
public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
2018-04-13 09:19:50 +00:00
{
RelativeSizeAxes = Axes.X;
AddRangeInternal(new Drawable[]
2018-04-13 09:19:50 +00:00
{
bodyPiece = new BodyPiece { RelativeSizeAxes = Axes.X },
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
});
2018-04-13 09:19:50 +00:00
2019-07-22 05:45:25 +00:00
AccentColour.BindValueChanged(colour =>
{
bodyPiece.AccentColour = colour.NewValue;
}, true);
2018-04-13 09:19:50 +00:00
}
protected override void AddNestedHitObject(DrawableHitObject hitObject)
{
base.AddNestedHitObject(hitObject);
2019-10-17 03:53:54 +00:00
switch (hitObject)
{
case DrawableHoldNoteHead head:
headContainer.Child = head;
break;
case DrawableHoldNoteTail tail:
tailContainer.Child = tail;
break;
case DrawableHoldNoteTick tick:
tickContainer.Add(tick);
break;
}
}
protected override void ClearNestedHitObjects()
{
base.ClearNestedHitObjects();
headContainer.Clear();
tailContainer.Clear();
tickContainer.Clear();
}
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
{
switch (hitObject)
{
case TailNote _:
return new DrawableHoldNoteTail(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = { BindTarget = AccentColour }
};
case Note _:
return new DrawableHoldNoteHead(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = { BindTarget = AccentColour }
};
case HoldNoteTick tick:
return new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => HoldStartTime,
AccentColour = { BindTarget = AccentColour }
};
}
return base.CreateNestedHitObject(hitObject);
}
2019-02-21 09:56:34 +00:00
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
{
2019-02-21 09:56:34 +00:00
base.OnDirectionChanged(e);
2019-02-21 09:56:34 +00:00
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
2018-05-16 10:43:01 +00:00
{
2018-05-20 10:22:42 +00:00
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
2018-04-13 09:19:50 +00:00
}
protected override void Update()
{
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
2018-04-13 09:19:50 +00:00
}
protected override void UpdateStateTransforms(ArmedState state)
{
using (BeginDelayedSequence(HitObject.Duration, true))
base.UpdateStateTransforms(state);
}
internal void BeginHold()
{
HoldStartTime = Time.Current;
bodyPiece.Hitting = true;
}
protected void EndHold()
{
HoldStartTime = null;
bodyPiece.Hitting = false;
}
2018-04-13 09:19:50 +00:00
public bool OnPressed(ManiaAction action)
{
// Make sure the action happened within the body of the hold note
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
return false;
if (action != Action.Value)
2018-04-13 09:19:50 +00:00
return false;
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
// and within the limited range of the above if-statement. This state will be managed by the head note if the
// user has pressed during the hit windows of the head note.
BeginHold();
2018-04-13 09:19:50 +00:00
return true;
}
public bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!HoldStartTime.HasValue)
2018-04-13 09:19:50 +00:00
return false;
if (action != Action.Value)
2018-04-13 09:19:50 +00:00
return false;
EndHold();
2018-04-13 09:19:50 +00:00
// If the key has been released too early, the user should not receive full score for the release
2018-05-20 10:22:42 +00:00
if (!Tail.IsHit)
HasBroken = true;
2018-04-13 09:19:50 +00:00
return true;
}
}
}