Skip to content

Commit

Permalink
Separate head/tail notes from hold note class
Browse files Browse the repository at this point in the history
  • Loading branch information
smoogipoo committed Dec 23, 2019
1 parent 78b30a0 commit 42853b5
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 113 deletions.
129 changes: 16 additions & 113 deletions osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// 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.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
Expand All @@ -24,21 +23,21 @@ public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHan
public DrawableNote Head => headContainer.Child;
public DrawableNote Tail => tailContainer.Child;

private readonly Container<DrawableHeadNote> headContainer;
private readonly Container<DrawableTailNote> tailContainer;
private readonly Container<DrawableHoldNoteHead> headContainer;
private readonly Container<DrawableHoldNoteTail> tailContainer;
private readonly Container<DrawableHoldNoteTick> tickContainer;

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>
private double? holdStartTime;
internal double? HoldStartTime;

/// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release.
/// </summary>
private bool hasBroken;
internal bool HasBroken;

public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
Expand All @@ -49,8 +48,8 @@ public DrawableHoldNote(HoldNote hitObject)
{
bodyPiece = new BodyPiece { RelativeSizeAxes = Axes.X },
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHeadNote> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableTailNote> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
});

AccentColour.BindValueChanged(colour =>
Expand All @@ -65,11 +64,11 @@ protected override void AddNestedHitObject(DrawableHitObject hitObject)

switch (hitObject)
{
case DrawableHeadNote head:
case DrawableHoldNoteHead head:
headContainer.Child = head;
break;

case DrawableTailNote tail:
case DrawableHoldNoteTail tail:
tailContainer.Child = tail;
break;

Expand All @@ -92,15 +91,15 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
switch (hitObject)
{
case TailNote _:
return new DrawableTailNote(this)
return new DrawableHoldNoteTail(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = { BindTarget = AccentColour }
};

case Note _:
return new DrawableHeadNote(this)
return new DrawableHoldNoteHead(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Expand All @@ -110,7 +109,7 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
case HoldNoteTick tick:
return new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime,
HoldStartTime = () => HoldStartTime,
AccentColour = { BindTarget = AccentColour }
};
}
Expand Down Expand Up @@ -146,15 +145,15 @@ protected override void UpdateStateTransforms(ArmedState state)
base.UpdateStateTransforms(state);
}

protected void BeginHold()
internal void BeginHold()
{
holdStartTime = Time.Current;
HoldStartTime = Time.Current;
bodyPiece.Hitting = true;
}

protected void EndHold()
{
holdStartTime = null;
HoldStartTime = null;
bodyPiece.Hitting = false;
}

Expand All @@ -177,7 +176,7 @@ public bool OnPressed(ManiaAction action)
public bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdStartTime.HasValue)
if (!HoldStartTime.HasValue)
return false;

if (action != Action.Value)
Expand All @@ -187,105 +186,9 @@ public bool OnReleased(ManiaAction action)

// If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit)
hasBroken = true;
HasBroken = true;

return true;
}

/// <summary>
/// The head note of a hold.
/// </summary>
private class DrawableHeadNote : DrawableNote
{
private readonly DrawableHoldNote holdNote;

public DrawableHeadNote(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
this.holdNote = holdNote;
}

public override bool OnPressed(ManiaAction action)
{
if (!base.OnPressed(action))
return false;

// If the key has been released too early, the user should not receive full score for the release
if (Result.Type == HitResult.Miss)
holdNote.hasBroken = true;

// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
holdNote.BeginHold();

return true;
}
}

/// <summary>
/// The tail note of a hold.
/// </summary>
private class DrawableTailNote : DrawableNote
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
/// </summary>
private const double release_window_lenience = 1.5;

private readonly DrawableHoldNote holdNote;

public DrawableTailNote(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Tail)
{
this.holdNote = holdNote;
}

protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);

// Factor in the release lenience
timeOffset /= release_window_lenience;

if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);

return;
}

var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;

ApplyResult(r =>
{
if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
result = HitResult.Good;
r.Type = result;
});
}

public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down

public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.holdStartTime.HasValue)
return false;

if (action != Action.Value)
return false;

UpdateResult(true);

// Handled by the hold note, which will set holding = false
return false;
}
}
}
}
37 changes: 37 additions & 0 deletions osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 osu.Game.Rulesets.Scoring;

namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// The head of a <see cref="DrawableHoldNote"/>.
/// </summary>
public class DrawableHoldNoteHead : DrawableNote
{
private readonly DrawableHoldNote holdNote;

public DrawableHoldNoteHead(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
this.holdNote = holdNote;
}

public override bool OnPressed(ManiaAction action)
{
if (!base.OnPressed(action))
return false;

// If the key has been released too early, the user should not receive full score for the release
if (Result.Type == HitResult.Miss)
holdNote.HasBroken = true;

// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
holdNote.BeginHold();

return true;
}
}
}
74 changes: 74 additions & 0 deletions osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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.Diagnostics;
using osu.Game.Rulesets.Scoring;

namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// The tail of a <see cref="DrawableHoldNote"/>.
/// </summary>
public class DrawableHoldNoteTail : DrawableNote
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
/// </summary>
private const double release_window_lenience = 1.5;

private readonly DrawableHoldNote holdNote;

public DrawableHoldNoteTail(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Tail)
{
this.holdNote = holdNote;
}

protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);

// Factor in the release lenience
timeOffset /= release_window_lenience;

if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);

return;
}

var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;

ApplyResult(r =>
{
if (holdNote.HasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
result = HitResult.Good;
r.Type = result;
});
}

public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down

public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.HoldStartTime.HasValue)
return false;

if (action != Action.Value)
return false;

UpdateResult(true);

// Handled by the hold note, which will set holding = false
return false;
}
}
}

0 comments on commit 42853b5

Please sign in to comment.