1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-05 17:02:58 +08:00

Merge branch 'master' into osu-selection-flip-rotate-buttons

This commit is contained in:
Dan Balasescu 2020-10-05 19:17:33 +09:00 committed by GitHub
commit 6d50a1ed7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
184 changed files with 2320 additions and 1055 deletions

View File

@ -52,6 +52,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.925.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.1004.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -9,7 +9,7 @@ using osu.Framework.Android;
namespace osu.Android namespace osu.Android
{ {
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullSensor, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)] [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)]
public class OsuGameActivity : AndroidGameActivity public class OsuGameActivity : AndroidGameActivity
{ {
protected override Framework.Game CreateGame() => new OsuGameAndroid(); protected override Framework.Game CreateGame() => new OsuGameAndroid();

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
public void TestDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Droplet { StartTime = 1000 }), shouldMiss); public void TestDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Droplet { StartTime = 1000 }), shouldMiss);
// We only care about testing misses, hits are tested via JuiceStream // We only care about testing misses, hits are tested via JuiceStream
[TestCase(false)] [TestCase(true)]
public void TestTinyDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new TinyDroplet { StartTime = 1000 }), shouldMiss); public void TestTinyDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new TinyDroplet { StartTime = 1000 }), shouldMiss);
} }
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[Test] [Test]
public void TestCatchComboCounter() public void TestCatchComboCounter()
{ {
AddRepeatStep("perform hit", () => performJudgement(HitResult.Perfect), 20); AddRepeatStep("perform hit", () => performJudgement(HitResult.Great), 20);
AddStep("perform miss", () => performJudgement(HitResult.Miss)); AddStep("perform miss", () => performJudgement(HitResult.Miss));
AddStep("randomize judged object colour", () => AddStep("randomize judged object colour", () =>

View File

@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{ {
mods = Score.Mods; mods = Score.Mods;
fruitsHit = Score.Statistics.GetOrDefault(HitResult.Perfect); fruitsHit = Score.Statistics.GetOrDefault(HitResult.Great);
ticksHit = Score.Statistics.GetOrDefault(HitResult.LargeTickHit); ticksHit = Score.Statistics.GetOrDefault(HitResult.LargeTickHit);
tinyTicksHit = Score.Statistics.GetOrDefault(HitResult.SmallTickHit); tinyTicksHit = Score.Statistics.GetOrDefault(HitResult.SmallTickHit);
tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss); tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss);

View File

@ -8,31 +8,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
public class CatchBananaJudgement : CatchJudgement public class CatchBananaJudgement : CatchJudgement
{ {
public override bool AffectsCombo => false; public override HitResult MaxResult => HitResult.LargeBonus;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return 1100;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return DEFAULT_MAX_HEALTH_INCREASE * 0.75;
}
}
public override bool ShouldExplodeFor(JudgementResult result) => true; public override bool ShouldExplodeFor(JudgementResult result) => true;
} }

View File

@ -7,16 +7,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
public class CatchDropletJudgement : CatchJudgement public class CatchDropletJudgement : CatchJudgement
{ {
protected override int NumericResultFor(HitResult result) public override HitResult MaxResult => HitResult.LargeTickHit;
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return 30;
}
}
} }
} }

View File

@ -9,19 +9,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
public class CatchJudgement : Judgement public class CatchJudgement : Judgement
{ {
public override HitResult MaxResult => HitResult.Perfect; public override HitResult MaxResult => HitResult.Great;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return 300;
}
}
/// <summary> /// <summary>
/// Whether fruit on the platter should explode or drop. /// Whether fruit on the platter should explode or drop.

View File

@ -7,30 +7,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
public class CatchTinyDropletJudgement : CatchJudgement public class CatchTinyDropletJudgement : CatchJudgement
{ {
public override bool AffectsCombo => false; public override HitResult MaxResult => HitResult.SmallTickHit;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return 10;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return 0.02;
}
}
} }
} }

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -86,7 +85,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
if (CheckPosition == null) return; if (CheckPosition == null) return;
if (timeOffset >= 0 && Result != null) if (timeOffset >= 0 && Result != null)
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss); ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
{ {
switch (result) switch (result)
{ {
case HitResult.Perfect: case HitResult.Great:
case HitResult.Miss: case HitResult.Miss:
return true; return true;
} }

View File

@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Scoring
{ {
public class CatchScoreProcessor : ScoreProcessor public class CatchScoreProcessor : ScoreProcessor
{ {
public override HitWindows CreateHitWindows() => new CatchHitWindows();
} }
} }

View File

@ -33,10 +33,10 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnNewResult(DrawableCatchHitObject judgedObject, JudgementResult result) public void OnNewResult(DrawableCatchHitObject judgedObject, JudgementResult result)
{ {
if (!result.Judgement.AffectsCombo || !result.HasResult) if (!result.Type.AffectsCombo() || !result.HasResult)
return; return;
if (result.Type == HitResult.Miss) if (!result.IsHit)
{ {
updateCombo(0, null); updateCombo(0, null);
return; return;
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnRevertResult(DrawableCatchHitObject judgedObject, JudgementResult result) public void OnRevertResult(DrawableCatchHitObject judgedObject, JudgementResult result)
{ {
if (!result.Judgement.AffectsCombo || !result.HasResult) if (!result.Type.AffectsCombo() || !result.HasResult)
return; return;
updateCombo(result.ComboAtJudgement, judgedObject.AccentColour.Value); updateCombo(result.ComboAtJudgement, judgedObject.AccentColour.Value);

View File

@ -11,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osuTK; using osuTK;
@ -52,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnNewResult(DrawableCatchHitObject fruit, JudgementResult result) public void OnNewResult(DrawableCatchHitObject fruit, JudgementResult result)
{ {
if (result.Judgement is IgnoreJudgement) if (!result.Type.IsScorable())
return; return;
void runAfterLoaded(Action action) void runAfterLoaded(Action action)

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq; using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -13,7 +14,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
public class TestSceneHoldNote : ManiaHitObjectTestScene public class TestSceneHoldNote : ManiaHitObjectTestScene
{ {
public TestSceneHoldNote() [Test]
public void TestHoldNote()
{ {
AddToggleStep("toggle hitting", v => AddToggleStep("toggle hitting", v =>
{ {

View File

@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Miss); assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
assertNoteJudgement(HitResult.Perfect); assertNoteJudgement(HitResult.IgnoreHit);
} }
/// <summary> /// <summary>
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Miss); assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
} }
@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Miss); assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
} }
@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Perfect); assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect); assertTickJudgement(HitResult.LargeTickHit);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
} }
@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Perfect); assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect); assertTickJudgement(HitResult.LargeTickHit);
assertTailJudgement(HitResult.Perfect); assertTailJudgement(HitResult.Perfect);
} }
@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Perfect); assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
} }
@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Perfect); assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect); assertTickJudgement(HitResult.LargeTickHit);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
} }
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Perfect); assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect); assertTickJudgement(HitResult.LargeTickHit);
assertTailJudgement(HitResult.Meh); assertTailJudgement(HitResult.Meh);
} }
@ -199,7 +199,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Miss); assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Perfect); assertTickJudgement(HitResult.LargeTickHit);
assertTailJudgement(HitResult.Miss); assertTailJudgement(HitResult.Miss);
} }
@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Miss); assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Perfect); assertTickJudgement(HitResult.LargeTickHit);
assertTailJudgement(HitResult.Meh); assertTailJudgement(HitResult.Meh);
} }
@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}); });
assertHeadJudgement(HitResult.Miss); assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss);
assertTailJudgement(HitResult.Meh); assertTailJudgement(HitResult.Meh);
} }
@ -280,10 +280,10 @@ namespace osu.Game.Rulesets.Mania.Tests
}, beatmap); }, beatmap);
AddAssert("first hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject)) AddAssert("first hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject))
.All(j => j.Type == HitResult.Miss)); .All(j => !j.Type.IsHit()));
AddAssert("second hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[1].NestedHitObjects.Contains(j.HitObject)) AddAssert("second hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[1].NestedHitObjects.Contains(j.HitObject))
.All(j => j.Type == HitResult.Perfect)); .All(j => j.Type.IsHit()));
} }
private void assertHeadJudgement(HitResult result) private void assertHeadJudgement(HitResult result)

View File

@ -28,8 +28,15 @@ namespace osu.Game.Rulesets.Mania.Tests
[TestFixture] [TestFixture]
public class TestSceneNotes : OsuTestScene public class TestSceneNotes : OsuTestScene
{ {
[BackgroundDependencyLoader] [Test]
private void load() public void TestVariousNotes()
{
DrawableNote note1 = null;
DrawableNote note2 = null;
DrawableHoldNote holdNote1 = null;
DrawableHoldNote holdNote2 = null;
AddStep("create notes", () =>
{ {
Child = new FillFlowContainer Child = new FillFlowContainer
{ {
@ -41,12 +48,13 @@ namespace osu.Game.Rulesets.Mania.Tests
Spacing = new Vector2(20), Spacing = new Vector2(20),
Children = new[] Children = new[]
{ {
createNoteDisplay(ScrollingDirection.Down, 1, out var note1), createNoteDisplay(ScrollingDirection.Down, 1, out note1),
createNoteDisplay(ScrollingDirection.Up, 2, out var note2), createNoteDisplay(ScrollingDirection.Up, 2, out note2),
createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1), createHoldNoteDisplay(ScrollingDirection.Down, 1, out holdNote1),
createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2), createHoldNoteDisplay(ScrollingDirection.Up, 2, out holdNote2),
} }
}; };
});
AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2)); AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0)); AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));

View File

@ -7,18 +7,6 @@ namespace osu.Game.Rulesets.Mania.Judgements
{ {
public class HoldNoteTickJudgement : ManiaJudgement public class HoldNoteTickJudgement : ManiaJudgement
{ {
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 20 : 0; public override HitResult MaxResult => HitResult.LargeTickHit;
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Perfect:
return 0.01;
}
}
} }
} }

View File

@ -8,27 +8,33 @@ namespace osu.Game.Rulesets.Mania.Judgements
{ {
public class ManiaJudgement : Judgement public class ManiaJudgement : Judgement
{ {
protected override int NumericResultFor(HitResult result) protected override double HealthIncreaseFor(HitResult result)
{ {
switch (result) switch (result)
{ {
default: case HitResult.LargeTickHit:
return 0; return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.LargeTickMiss:
return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.Meh: case HitResult.Meh:
return 50; return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
case HitResult.Ok: case HitResult.Ok:
return 100; return -DEFAULT_MAX_HEALTH_INCREASE * 0.3;
case HitResult.Good: case HitResult.Good:
return 200; return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.Great: case HitResult.Great:
return 300; return DEFAULT_MAX_HEALTH_INCREASE * 0.8;
case HitResult.Perfect: case HitResult.Perfect:
return 350; return DEFAULT_MAX_HEALTH_INCREASE;
default:
return base.HealthIncreaseFor(result);
} }
} }
} }

View File

@ -239,11 +239,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
if (Tail.AllJudged) if (Tail.AllJudged)
{ {
ApplyResult(r => r.Type = HitResult.Perfect); ApplyResult(r => r.Type = r.Judgement.MaxResult);
endHold(); endHold();
} }
if (Tail.Result.Type == HitResult.Miss) if (Tail.Judged && !Tail.IsHit)
HasBroken = true; HasBroken = true;
} }

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
@ -17,6 +16,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary> /// </summary>
public class DrawableHoldNoteTick : DrawableManiaHitObject<HoldNoteTick> public class DrawableHoldNoteTick : DrawableManiaHitObject<HoldNoteTick>
{ {
public override bool DisplayResult => false;
/// <summary> /// <summary>
/// References the time at which the user started holding the hold note. /// References the time at which the user started holding the hold note.
/// </summary> /// </summary>
@ -73,9 +74,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
var startTime = HoldStartTime?.Invoke(); var startTime = HoldStartTime?.Invoke();
if (startTime == null || startTime > HitObject.StartTime) if (startTime == null || startTime > HitObject.StartTime)
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
else else
ApplyResult(r => r.Type = HitResult.Perfect); ApplyResult(r => r.Type = r.Judgement.MaxResult);
} }
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
@ -136,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary> /// <summary>
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>. /// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary> /// </summary>
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss); public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
} }
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject

View File

@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }

View File

@ -10,7 +10,5 @@ namespace osu.Game.Rulesets.Mania.Scoring
protected override double DefaultAccuracyPortion => 0.95; protected override double DefaultAccuracyPortion => 0.95;
protected override double DefaultComboPortion => 0.05; protected override double DefaultComboPortion => 0.05;
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
} }
} }

View File

@ -130,6 +130,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
private Drawable getResult(HitResult result) private Drawable getResult(HitResult result)
{ {
if (!hitresult_mapping.ContainsKey(result))
return null;
string filename = this.GetManiaSkinConfig<string>(hitresult_mapping[result])?.Value string filename = this.GetManiaSkinConfig<string>(hitresult_mapping[result])?.Value
?? default_hitresult_skin_filenames[result]; ?? default_hitresult_skin_filenames[result];

View File

@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.UI
if (result.IsHit) if (result.IsHit)
hitPolicy.HandleHit(judgedObject); hitPolicy.HandleHit(judgedObject);
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value) if (!result.IsHit || !DisplayJudgements.Value)
return; return;
HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result))); HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result)));

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -20,7 +20,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
private int depthIndex; private int depthIndex;
public TestSceneHitCircle() [Test]
public void TestVariousHitCircles()
{ {
AddStep("Miss Big Single", () => SetContents(() => testSingle(2))); AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5))); AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } } HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
}, },
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
}); });
} }
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
Autoplay = false, Autoplay = false,
Beatmap = beatmap, Beatmap = beatmap,
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
}); });
} }

View File

@ -209,9 +209,9 @@ namespace osu.Game.Rulesets.Osu.Tests
}); });
addJudgementAssert(hitObjects[0], HitResult.Great); addJudgementAssert(hitObjects[0], HitResult.Great);
addJudgementAssert(hitObjects[1], HitResult.Great); addJudgementAssert(hitObjects[1], HitResult.IgnoreHit);
addJudgementAssert("slider head", () => ((Slider)hitObjects[1]).HeadCircle, HitResult.Miss); addJudgementAssert("slider head", () => ((Slider)hitObjects[1]).HeadCircle, HitResult.Miss);
addJudgementAssert("slider tick", () => ((Slider)hitObjects[1]).NestedHitObjects[1] as SliderTick, HitResult.Great); addJudgementAssert("slider tick", () => ((Slider)hitObjects[1]).NestedHitObjects[1] as SliderTick, HitResult.LargeTickHit);
} }
/// <summary> /// <summary>
@ -252,9 +252,9 @@ namespace osu.Game.Rulesets.Osu.Tests
}); });
addJudgementAssert(hitObjects[0], HitResult.Great); addJudgementAssert(hitObjects[0], HitResult.Great);
addJudgementAssert(hitObjects[1], HitResult.Great); addJudgementAssert(hitObjects[1], HitResult.IgnoreHit);
addJudgementAssert("slider head", () => ((Slider)hitObjects[1]).HeadCircle, HitResult.Great); addJudgementAssert("slider head", () => ((Slider)hitObjects[1]).HeadCircle, HitResult.Great);
addJudgementAssert("slider tick", () => ((Slider)hitObjects[1]).NestedHitObjects[1] as SliderTick, HitResult.Great); addJudgementAssert("slider tick", () => ((Slider)hitObjects[1]).NestedHitObjects[1] as SliderTick, HitResult.LargeTickHit);
} }
/// <summary> /// <summary>
@ -331,7 +331,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}); });
addJudgementAssert(hitObjects[0], HitResult.Great); addJudgementAssert(hitObjects[0], HitResult.Great);
addJudgementAssert(hitObjects[1], HitResult.Great); addJudgementAssert(hitObjects[1], HitResult.IgnoreHit);
} }
private void addJudgementAssert(OsuHitObject hitObject, HitResult result) private void addJudgementAssert(OsuHitObject hitObject, HitResult result)

View File

@ -27,7 +27,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
private int depthIndex; private int depthIndex;
public TestSceneSlider() [Test]
public void TestVariousSliders()
{ {
AddStep("Big Single", () => SetContents(() => testSimpleBig())); AddStep("Big Single", () => SetContents(() => testSimpleBig()));
AddStep("Medium Single", () => SetContents(() => testSimpleMedium())); AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
@ -164,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(239, 176), Position = new Vector2(239, 176),
Path = new SliderPath(PathType.PerfectCurve, new[] Path = new SliderPath(PathType.PerfectCurve, new[]
{ {
@ -185,22 +186,26 @@ namespace osu.Game.Rulesets.Osu.Tests
private Drawable testSlowSpeed() => createSlider(speedMultiplier: 0.5); private Drawable testSlowSpeed() => createSlider(speedMultiplier: 0.5);
private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5); private Drawable testShortSlowSpeed(int repeats = 0) => createSlider(distance: max_length / 4, repeats: repeats, speedMultiplier: 0.5);
private Drawable testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15); private Drawable testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); private Drawable testShortHighSpeed(int repeats = 0) => createSlider(distance: max_length / 4, repeats: repeats, speedMultiplier: 15);
private Drawable createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) private const double time_offset = 1500;
private const float max_length = 200;
private Drawable createSlider(float circleSize = 2, float distance = max_length, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0)
{ {
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(-(distance / 2), 0), Position = new Vector2(0, -(distance / 2)),
Path = new SliderPath(PathType.PerfectCurve, new[] Path = new SliderPath(PathType.PerfectCurve, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(distance, 0), new Vector2(0, distance),
}, distance), }, distance),
RepeatCount = repeats, RepeatCount = repeats,
StackHeight = stackHeight StackHeight = stackHeight
@ -213,14 +218,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(-200, 0), Position = new Vector2(-max_length / 2, 0),
Path = new SliderPath(PathType.PerfectCurve, new[] Path = new SliderPath(PathType.PerfectCurve, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(200, 200), new Vector2(max_length / 2, max_length / 2),
new Vector2(400, 0) new Vector2(max_length, 0)
}, 600), }, max_length * 1.5f),
RepeatCount = repeats, RepeatCount = repeats,
}; };
@ -233,16 +238,16 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(-200, 0), Position = new Vector2(-max_length / 2, 0),
Path = new SliderPath(PathType.Linear, new[] Path = new SliderPath(PathType.Linear, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(150, 75), new Vector2(max_length * 0.375f, max_length * 0.18f),
new Vector2(200, 0), new Vector2(max_length / 2, 0),
new Vector2(300, -200), new Vector2(max_length * 0.75f, -max_length / 2),
new Vector2(400, 0), new Vector2(max_length * 0.95f, 0),
new Vector2(430, 0) new Vector2(max_length, 0)
}), }),
RepeatCount = repeats, RepeatCount = repeats,
}; };
@ -256,15 +261,15 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(-200, 0), Position = new Vector2(-max_length / 2, 0),
Path = new SliderPath(PathType.Bezier, new[] Path = new SliderPath(PathType.Bezier, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(150, 75), new Vector2(max_length * 0.375f, max_length * 0.18f),
new Vector2(200, 100), new Vector2(max_length / 2, max_length / 4),
new Vector2(300, -200), new Vector2(max_length * 0.75f, -max_length / 2),
new Vector2(430, 0) new Vector2(max_length, 0)
}), }),
RepeatCount = repeats, RepeatCount = repeats,
}; };
@ -278,16 +283,16 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(0, 0), Position = new Vector2(0, 0),
Path = new SliderPath(PathType.Linear, new[] Path = new SliderPath(PathType.Linear, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(-200, 0), new Vector2(-max_length / 2, 0),
new Vector2(0, 0), new Vector2(0, 0),
new Vector2(0, -200), new Vector2(0, -max_length / 2),
new Vector2(-200, -200), new Vector2(-max_length / 2, -max_length / 2),
new Vector2(0, -200) new Vector2(0, -max_length / 2)
}), }),
RepeatCount = repeats, RepeatCount = repeats,
}; };
@ -305,14 +310,14 @@ namespace osu.Game.Rulesets.Osu.Tests
var slider = new Slider var slider = new Slider
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + time_offset,
Position = new Vector2(-100, 0), Position = new Vector2(-max_length / 4, 0),
Path = new SliderPath(PathType.Catmull, new[] Path = new SliderPath(PathType.Catmull, new[]
{ {
Vector2.Zero, Vector2.Zero,
new Vector2(50, -50), new Vector2(max_length * 0.125f, max_length * 0.125f),
new Vector2(150, 50), new Vector2(max_length * 0.375f, max_length * 0.125f),
new Vector2(200, 0) new Vector2(max_length / 2, 0)
}), }),
RepeatCount = repeats, RepeatCount = repeats,
NodeSamples = repeatSamples NodeSamples = repeatSamples

View File

@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 }, new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 },
}); });
AddAssert("Tracking retained", assertGreatJudge); AddAssert("Tracking retained", assertMaxJudge);
} }
/// <summary> /// <summary>
@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 }, new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
}); });
AddAssert("Tracking retained", assertGreatJudge); AddAssert("Tracking retained", assertMaxJudge);
} }
/// <summary> /// <summary>
@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 }, new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_1 },
}); });
AddAssert("Tracking retained", assertGreatJudge); AddAssert("Tracking retained", assertMaxJudge);
} }
/// <summary> /// <summary>
@ -288,7 +288,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.199f), Actions = { OsuAction.LeftButton }, Time = time_slider_end }, new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.199f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
}); });
AddAssert("Tracking kept", assertGreatJudge); AddAssert("Tracking kept", assertMaxJudge);
} }
/// <summary> /// <summary>
@ -312,13 +312,13 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking dropped", assertMidSliderJudgementFail); AddAssert("Tracking dropped", assertMidSliderJudgementFail);
} }
private bool assertGreatJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == HitResult.Great); private bool assertMaxJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == t.Judgement.MaxResult);
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss; private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.SmallTickHit && !judgementResults.First().IsHit;
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.Great; private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.SmallTickHit;
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.Miss; private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.SmallTickMiss;
private ScoreAccessibleReplayPlayer currentPlayer; private ScoreAccessibleReplayPlayer currentPlayer;

View File

@ -7,7 +7,6 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Timing; using osu.Framework.Timing;
@ -146,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
// multipled by 2 to nullify the score multiplier. (autoplay mod selected) // multipled by 2 to nullify the score multiplier. (autoplay mod selected)
var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2;
return totalScore == (int)(drawableSpinner.RotationTracker.RateAdjustedRotation / 360) * SpinnerTick.SCORE_PER_TICK; return totalScore == (int)(drawableSpinner.RotationTracker.RateAdjustedRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult;
}); });
addSeekStep(0); addSeekStep(0);
@ -194,13 +193,7 @@ namespace osu.Game.Rulesets.Osu.Tests
addSeekStep(0); addSeekStep(0);
AddStep("adjust track rate", () => MusicController.CurrentTrack.AddAdjustment(AdjustableProperty.Tempo, new BindableDouble(rate))); AddStep("adjust track rate", () => Player.GameplayClockContainer.UserPlaybackRate.Value = rate);
// autoplay replay frames use track time;
// if a spin takes 1000ms in track time and we're playing with a 2x rate adjustment, the spin will take 500ms of *real* time.
// therefore we need to apply the rate adjustment to the replay itself to change from track time to real time,
// as real time is what we care about for spinners
// (so we're making the spin take 1000ms in real time *always*, regardless of the track clock's rate).
transformReplay(replay => applyRateAdjustment(replay, rate));
addSeekStep(1000); addSeekStep(1000);
AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05)); AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05));

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private double accuracy; private double accuracy;
private int scoreMaxCombo; private int scoreMaxCombo;
private int countGreat; private int countGreat;
private int countGood; private int countOk;
private int countMeh; private int countMeh;
private int countMiss; private int countMiss;
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
accuracy = Score.Accuracy; accuracy = Score.Accuracy;
scoreMaxCombo = Score.MaxCombo; scoreMaxCombo = Score.MaxCombo;
countGreat = Score.Statistics.GetOrDefault(HitResult.Great); countGreat = Score.Statistics.GetOrDefault(HitResult.Great);
countGood = Score.Statistics.GetOrDefault(HitResult.Good); countOk = Score.Statistics.GetOrDefault(HitResult.Ok);
countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); countMeh = Score.Statistics.GetOrDefault(HitResult.Meh);
countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); countMiss = Score.Statistics.GetOrDefault(HitResult.Miss);
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
int amountHitObjectsWithAccuracy = countHitCircles; int amountHitObjectsWithAccuracy = countHitCircles;
if (amountHitObjectsWithAccuracy > 0) if (amountHitObjectsWithAccuracy > 0)
betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countGood * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6); betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6);
else else
betterAccuracyPercentage = 0; betterAccuracyPercentage = 0;
@ -204,7 +204,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
return accuracyValue; return accuracyValue;
} }
private int totalHits => countGreat + countGood + countMeh + countMiss; private int totalHits => countGreat + countOk + countMeh + countMiss;
private int totalSuccessfulHits => countGreat + countGood + countMeh; private int totalSuccessfulHits => countGreat + countOk + countMeh;
} }
} }

View File

@ -222,8 +222,8 @@ namespace osu.Game.Rulesets.Osu.Edit
point.Y -= origin.Y; point.Y -= origin.Y;
Vector2 ret; Vector2 ret;
ret.X = (float)(point.X * Math.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * Math.Sin(angle / 180f * Math.PI)); ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle));
ret.Y = (float)(point.X * -Math.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * Math.Cos(angle / 180f * Math.PI)); ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle));
ret.X += origin.X; ret.X += origin.X;
ret.Y += origin.Y; ret.Y += origin.Y;

View File

@ -7,10 +7,6 @@ namespace osu.Game.Rulesets.Osu.Judgements
{ {
public class OsuIgnoreJudgement : OsuJudgement public class OsuIgnoreJudgement : OsuJudgement
{ {
public override bool AffectsCombo => false; public override HitResult MaxResult => HitResult.IgnoreHit;
protected override int NumericResultFor(HitResult result) => 0;
protected override double HealthIncreaseFor(HitResult result) => 0;
} }
} }

View File

@ -9,23 +9,5 @@ namespace osu.Game.Rulesets.Osu.Judgements
public class OsuJudgement : Judgement public class OsuJudgement : Judgement
{ {
public override HitResult MaxResult => HitResult.Great; public override HitResult MaxResult => HitResult.Great;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Meh:
return 50;
case HitResult.Good:
return 100;
case HitResult.Great:
return 300;
}
}
} }
} }

View File

@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.Osu.Mods
base.ApplyToDrawableHitObjects(drawables); base.ApplyToDrawableHitObjects(drawables);
} }
private double lastSliderHeadFadeOutStartTime;
private double lastSliderHeadFadeOutDuration;
protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
{ {
if (!(drawable is DrawableOsuHitObject d)) if (!(drawable is DrawableOsuHitObject d))
@ -54,7 +57,35 @@ namespace osu.Game.Rulesets.Osu.Mods
switch (drawable) switch (drawable)
{ {
case DrawableSliderTail sliderTail:
// use stored values from head circle to achieve same fade sequence.
fadeOutDuration = lastSliderHeadFadeOutDuration;
fadeOutStartTime = lastSliderHeadFadeOutStartTime;
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
sliderTail.FadeOut(fadeOutDuration);
break;
case DrawableSliderRepeat sliderRepeat:
// use stored values from head circle to achieve same fade sequence.
fadeOutDuration = lastSliderHeadFadeOutDuration;
fadeOutStartTime = lastSliderHeadFadeOutStartTime;
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
// only apply to circle piece reverse arrow is not affected by hidden.
sliderRepeat.CirclePiece.FadeOut(fadeOutDuration);
break;
case DrawableHitCircle circle: case DrawableHitCircle circle:
if (circle is DrawableSliderHead)
{
lastSliderHeadFadeOutDuration = fadeOutDuration;
lastSliderHeadFadeOutStartTime = fadeOutStartTime;
}
// we don't want to see the approach circle // we don't want to see the approach circle
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
circle.ApproachCircle.Hide(); circle.ApproachCircle.Hide();

View File

@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }
@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var circleResult = (OsuHitCircleJudgementResult)r; var circleResult = (OsuHitCircleJudgementResult)r;
// 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. // 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.
if (result != HitResult.Miss) if (result.IsHit())
{ {
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position); var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2); circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// <summary> /// <summary>
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>. /// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary> /// </summary>
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss); public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement); protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
} }

View File

@ -8,7 +8,6 @@ using osu.Game.Configuration;
using osuTK; using osuTK;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK.Graphics; using osuTK.Graphics;
@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (JudgedObject != null) if (JudgedObject != null)
{ {
lightingColour = JudgedObject.AccentColour.GetBoundCopy(); lightingColour = JudgedObject.AccentColour.GetBoundCopy();
lightingColour.BindValueChanged(colour => Lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true); lightingColour.BindValueChanged(colour => Lighting.Colour = Result.IsHit ? colour.NewValue : Color4.Transparent, true);
} }
else else
{ {

View File

@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -52,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling), Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both }, tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
Ball = new SliderBall(s, this) Ball = new SliderBall(s, this)
@ -63,7 +63,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0 Alpha = 0
}, },
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both }, headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
}; };
} }
@ -87,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Tracking.BindValueChanged(updateSlidingSample); Tracking.BindValueChanged(updateSlidingSample);
} }
private SkinnableSound slidingSample; private PausableSkinnableSound slidingSample;
protected override void LoadSamples() protected override void LoadSamples()
{ {
@ -103,19 +102,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample); var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
clone.Name = "sliderslide"; clone.Name = "sliderslide";
AddInternal(slidingSample = new SkinnableSound(clone) AddInternal(slidingSample = new PausableSkinnableSound(clone)
{ {
Looping = true Looping = true
}); });
} }
} }
public override void StopAllSamples()
{
base.StopAllSamples();
slidingSample?.Stop();
}
private void updateSlidingSample(ValueChangedEvent<bool> tracking) private void updateSlidingSample(ValueChangedEvent<bool> tracking)
{ {
// note that samples will not start playing if exiting a seek operation in the middle of a slider. if (tracking.NewValue)
// may be something we want to address at a later point, but not so easy to make happen right now
// (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update).
if (tracking.NewValue && ShouldPlaySamples)
slidingSample?.Play(); slidingSample?.Play();
else else
slidingSample?.Stop(); slidingSample?.Stop();
@ -247,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
// rather than doing it this way, we should probably attach the sample to the tail circle. // rather than doing it this way, we should probably attach the sample to the tail circle.
// this can only be done after we stop using LegacyLastTick. // this can only be done after we stop using LegacyLastTick.
if (TailCircle.Result.Type != HitResult.Miss) if (TailCircle.IsHit)
base.PlaySamples(); base.PlaySamples();
} }

View File

@ -6,10 +6,11 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -23,6 +24,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly Drawable scaleContainer; private readonly Drawable scaleContainer;
public readonly Drawable CirclePiece;
public override bool DisplayResult => false; public override bool DisplayResult => false;
public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider) public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
@ -35,7 +38,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Origin = Anchor.Centre; Origin = Anchor.Centre;
InternalChild = scaleContainer = new ReverseArrowPiece(); InternalChild = scaleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new[]
{
// no default for this; only visible in legacy skins.
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty()),
arrow = new ReverseArrowPiece(),
}
};
} }
private readonly IBindable<float> scaleBindable = new BindableFloat(); private readonly IBindable<float> scaleBindable = new BindableFloat();
@ -50,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (sliderRepeat.StartTime <= Time.Current) if (sliderRepeat.StartTime <= Time.Current)
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()
@ -86,6 +100,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private bool hasRotation; private bool hasRotation;
private readonly ReverseArrowPiece arrow;
public void UpdateSnakingPosition(Vector2 start, Vector2 end) public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{ {
// When the repeat is hit, the arrow should fade out on spot rather than following the slider // When the repeat is hit, the arrow should fade out on spot rather than following the slider
@ -115,18 +131,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X)); float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180) while (Math.Abs(aimRotation - arrow.Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360; aimRotation += aimRotation < arrow.Rotation ? 360 : -360;
if (!hasRotation) if (!hasRotation)
{ {
Rotation = aimRotation; arrow.Rotation = aimRotation;
hasRotation = true; hasRotation = true;
} }
else else
{ {
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly). // If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint); arrow.Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), arrow.Rotation, aimRotation, 0, 50, Easing.OutQuint);
} }
} }
} }

View File

@ -1,16 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking
{ {
private readonly Slider slider; private readonly SliderTailCircle tailCircle;
/// <summary> /// <summary>
/// The judgement text is provided by the <see cref="DrawableSlider"/>. /// The judgement text is provided by the <see cref="DrawableSlider"/>.
@ -19,36 +23,82 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; } public bool Tracking { get; set; }
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>(); private readonly IBindable<float> scaleBindable = new BindableFloat();
private readonly IBindable<int> pathVersion = new Bindable<int>();
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle) private readonly SkinnableDrawable circlePiece;
: base(hitCircle)
private readonly Container scaleContainer;
public DrawableSliderTail(Slider slider, SliderTailCircle tailCircle)
: base(tailCircle)
{ {
this.slider = slider; this.tailCircle = tailCircle;
Origin = Anchor.Centre; Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both; Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
FillMode = FillMode.Fit;
AlwaysPresent = true; InternalChildren = new Drawable[]
{
scaleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
// no default for this; only visible in legacy skins.
circlePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
}
},
};
}
positionBindable.BindTo(hitCircle.PositionBindable); [BackgroundDependencyLoader]
pathVersion.BindTo(slider.Path.Version); private void load()
{
scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
scaleBindable.BindTo(HitObject.ScaleBindable);
}
positionBindable.BindValueChanged(_ => updatePosition()); protected override void UpdateInitialTransforms()
pathVersion.BindValueChanged(_ => updatePosition(), true); {
base.UpdateInitialTransforms();
// TODO: This has no drawable content. Support for skins should be added. circlePiece.FadeInFromZero(HitObject.TimeFadeIn);
}
protected override void UpdateStateTransforms(ArmedState state)
{
base.UpdateStateTransforms(state);
Debug.Assert(HitObject.HitWindows != null);
switch (state)
{
case ArmedState.Idle:
this.Delay(HitObject.TimePreempt).FadeOut(500);
Expire(true);
break;
case ArmedState.Miss:
this.FadeOut(100);
break;
case ArmedState.Hit:
// todo: temporary / arbitrary
this.Delay(800).FadeOut();
break;
}
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (!userTriggered && timeOffset >= 0) if (!userTriggered && timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss); ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
private void updatePosition() => Position = HitObject.Position - slider.Position; public void UpdateSnakingPosition(Vector2 start, Vector2 end) =>
Position = tailCircle.RepeatIndex % 2 == 0 ? end : start;
} }
} }

View File

@ -10,7 +10,6 @@ using osuTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (timeOffset >= 0) if (timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss); ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()

View File

@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
isSpinning.BindValueChanged(updateSpinningSample); isSpinning.BindValueChanged(updateSpinningSample);
} }
private SkinnableSound spinningSample; private PausableSkinnableSound spinningSample;
private const float spinning_sample_initial_frequency = 1.0f; private const float spinning_sample_initial_frequency = 1.0f;
private const float spinning_sample_modulated_base_frequency = 0.5f; private const float spinning_sample_modulated_base_frequency = 0.5f;
@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample); var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
clone.Name = "spinnerspin"; clone.Name = "spinnerspin";
AddInternal(spinningSample = new SkinnableSound(clone) AddInternal(spinningSample = new PausableSkinnableSound(clone)
{ {
Volume = { Value = 0 }, Volume = { Value = 0 },
Looping = true, Looping = true,
@ -113,10 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private void updateSpinningSample(ValueChangedEvent<bool> tracking) private void updateSpinningSample(ValueChangedEvent<bool> tracking)
{ {
// note that samples will not start playing if exiting a seek operation in the middle of a spinner. if (tracking.NewValue)
// may be something we want to address at a later point, but not so easy to make happen right now
// (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update).
if (tracking.NewValue && ShouldPlaySamples)
{ {
spinningSample?.Play(); spinningSample?.Play();
spinningSample?.VolumeTo(1, 200); spinningSample?.VolumeTo(1, 200);
@ -127,6 +124,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
public override void StopAllSamples()
{
base.StopAllSamples();
spinningSample?.Stop();
}
protected override void AddNestedHitObject(DrawableHitObject hitObject) protected override void AddNestedHitObject(DrawableHitObject hitObject)
{ {
base.AddNestedHitObject(hitObject); base.AddNestedHitObject(hitObject);
@ -209,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return; return;
// Trigger a miss result for remaining ticks to avoid infinite gameplay. // Trigger a miss result for remaining ticks to avoid infinite gameplay.
foreach (var tick in ticks.Where(t => !t.IsHit)) foreach (var tick in ticks.Where(t => !t.Result.HasResult))
tick.TriggerResult(false); tick.TriggerResult(false);
ApplyResult(r => ApplyResult(r =>
@ -217,11 +220,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (Progress >= 1) if (Progress >= 1)
r.Type = HitResult.Great; r.Type = HitResult.Great;
else if (Progress > .9) else if (Progress > .9)
r.Type = HitResult.Good; r.Type = HitResult.Ok;
else if (Progress > .75) else if (Progress > .75)
r.Type = HitResult.Meh; r.Type = HitResult.Meh;
else if (Time.Current >= Spinner.EndTime) else if (Time.Current >= Spinner.EndTime)
r.Type = HitResult.Miss; r.Type = r.Judgement.MinResult;
}); });
} }
@ -265,7 +268,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
while (wholeSpins != spins) while (wholeSpins != spins)
{ {
var tick = ticks.FirstOrDefault(t => !t.IsHit); var tick = ticks.FirstOrDefault(t => !t.Result.HasResult);
// tick may be null if we've hit the spin limit. // tick may be null if we've hit the spin limit.
if (tick != null) if (tick != null)

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableSpinnerTick : DrawableOsuHitObject public class DrawableSpinnerTick : DrawableOsuHitObject
@ -18,6 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// Apply a judgement result. /// Apply a judgement result.
/// </summary> /// </summary>
/// <param name="hit">Whether this tick was reached.</param> /// <param name="hit">Whether this tick was reached.</param>
internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : HitResult.Miss); internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
} }

View File

@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(TextureStore textures) private void load(TextureStore textures, DrawableHitObject drawableHitObject)
{ {
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
@ -35,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Origin = Anchor.Centre, Origin = Anchor.Centre,
Texture = textures.Get(@"Gameplay/osu/disc"), Texture = textures.Get(@"Gameplay/osu/disc"),
}, },
new TrianglesPiece new TrianglesPiece((int)drawableHitObject.HitObject.StartTime)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,

View File

@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private Spinner spinner; private Spinner spinner;
private const float initial_scale = 1.3f;
private const float idle_alpha = 0.2f; private const float idle_alpha = 0.2f;
private const float tracking_alpha = 0.4f; private const float tracking_alpha = 0.4f;
@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// we are slightly bigger than our parent, to clip the top and bottom of the circle // we are slightly bigger than our parent, to clip the top and bottom of the circle
// this should probably be revisited when scaled spinners are a thing. // this should probably be revisited when scaled spinners are a thing.
Scale = new Vector2(1.3f); Scale = new Vector2(initial_scale);
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
@ -93,6 +94,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200)); drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
} }
protected override void Update() protected override void Update()
@ -115,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
fill.Alpha = (float)Interpolation.Damp(fill.Alpha, drawableSpinner.RotationTracker.Tracking ? tracking_alpha : idle_alpha, 0.98f, (float)Math.Abs(Clock.ElapsedFrameTime)); fill.Alpha = (float)Interpolation.Damp(fill.Alpha, drawableSpinner.RotationTracker.Tracking ? tracking_alpha : idle_alpha, 0.98f, (float)Math.Abs(Clock.ElapsedFrameTime));
} }
const float initial_scale = 0.2f; const float initial_fill_scale = 0.2f;
float targetScale = initial_scale + (1 - initial_scale) * drawableSpinner.Progress; float targetScale = initial_fill_scale + (1 - initial_fill_scale) * drawableSpinner.Progress;
fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1)));
mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation;
@ -124,14 +127,43 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{ {
centre.ScaleTo(0); if (!(drawableHitObject is DrawableSpinner))
mainContainer.ScaleTo(0); return;
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
{
this.ScaleTo(initial_scale);
this.RotateTo(0);
using (BeginDelayedSequence(spinner.TimePreempt / 2, true))
{ {
// constant ambient rotation to give the spinner "spinning" character. // constant ambient rotation to give the spinner "spinning" character.
this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration);
}
using (BeginDelayedSequence(spinner.TimePreempt + spinner.Duration + drawableHitObject.Result.TimeOffset, true))
{
switch (state)
{
case ArmedState.Hit:
this.ScaleTo(initial_scale * 1.2f, 320, Easing.Out);
this.RotateTo(mainContainer.Rotation + 180, 320);
break;
case ArmedState.Miss:
this.ScaleTo(initial_scale * 0.8f, 320, Easing.In);
break;
}
}
}
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
{
centre.ScaleTo(0);
mainContainer.ScaleTo(0);
using (BeginDelayedSequence(spinner.TimePreempt / 2, true))
{
centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint);
mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint);
@ -141,24 +173,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint);
} }
} }
}
// transforms we have from completing the spinner will be rolled back, so reapply immediately. // transforms we have from completing the spinner will be rolled back, so reapply immediately.
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
updateComplete(state == ArmedState.Hit, 0); updateComplete(state == ArmedState.Hit, 0);
using (BeginDelayedSequence(spinner.Duration, true))
{
switch (state)
{
case ArmedState.Hit:
this.ScaleTo(Scale * 1.2f, 320, Easing.Out);
this.RotateTo(mainContainer.Rotation + 180, 320);
break;
case ArmedState.Miss:
this.ScaleTo(Scale * 0.8f, 320, Easing.In);
break;
}
}
} }
private void updateComplete(bool complete, double duration) private void updateComplete(bool complete, double duration)

View File

@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
/// </summary> /// </summary>
public class SpinnerBonusDisplay : CompositeDrawable public class SpinnerBonusDisplay : CompositeDrawable
{ {
private static readonly int score_per_tick = new SpinnerBonusTick().CreateJudgement().MaxNumericResult;
private readonly OsuSpriteText bonusCounter; private readonly OsuSpriteText bonusCounter;
public SpinnerBonusDisplay() public SpinnerBonusDisplay()
@ -36,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return; return;
displayedCount = count; displayedCount = count;
bonusCounter.Text = $"{SpinnerBonusTick.SCORE_PER_TICK * count}"; bonusCounter.Text = $"{score_per_tick * count}";
bonusCounter.FadeOutFromOne(1500); bonusCounter.FadeOutFromOne(1500);
bonusCounter.ScaleTo(1.5f).Then().ScaleTo(1f, 1000, Easing.OutQuint); bonusCounter.ScaleTo(1.5f).Then().ScaleTo(1f, 1000, Easing.OutQuint);
} }

View File

@ -2,11 +2,13 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Screens.Play;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@ -77,6 +79,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private bool rotationTransferred; private bool rotationTransferred;
[Resolved(canBeNull: true)]
private GameplayClock gameplayClock { get; set; }
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -126,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
currentRotation += angle; currentRotation += angle;
// rate has to be applied each frame, because it's not guaranteed to be constant throughout playback // rate has to be applied each frame, because it's not guaranteed to be constant throughout playback
// (see: ModTimeRamp) // (see: ModTimeRamp)
RateAdjustedRotation += (float)(Math.Abs(angle) * Clock.Rate); RateAdjustedRotation += (float)(Math.Abs(angle) * (gameplayClock?.TrueGameplayRate ?? Clock.Rate));
} }
} }
} }

View File

@ -11,7 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
protected override bool CreateNewTriangles => false; protected override bool CreateNewTriangles => false;
protected override float SpawnRatio => 0.5f; protected override float SpawnRatio => 0.5f;
public TrianglesPiece() public TrianglesPiece(int? seed = null)
: base(seed)
{ {
TriangleScale = 1.2f; TriangleScale = 1.2f;
HideAlphaDiscrepancies = false; HideAlphaDiscrepancies = false;

View File

@ -176,6 +176,7 @@ namespace osu.Game.Rulesets.Osu.Objects
// if this is to change, we should revisit this. // if this is to change, we should revisit this.
AddNested(TailCircle = new SliderTailCircle(this) AddNested(TailCircle = new SliderTailCircle(this)
{ {
RepeatIndex = e.SpanIndex,
StartTime = e.Time, StartTime = e.Time,
Position = EndPosition, Position = EndPosition,
StackHeight = StackHeight StackHeight = StackHeight
@ -183,10 +184,9 @@ namespace osu.Game.Rulesets.Osu.Objects
break; break;
case SliderEventType.Repeat: case SliderEventType.Repeat:
AddNested(new SliderRepeat AddNested(new SliderRepeat(this)
{ {
RepeatIndex = e.SpanIndex, RepeatIndex = e.SpanIndex,
SpanDuration = SpanDuration,
StartTime = StartTime + (e.SpanIndex + 1) * SpanDuration, StartTime = StartTime + (e.SpanIndex + 1) * SpanDuration,
Position = Position + Path.PositionAt(e.PathProgress), Position = Position + Path.PositionAt(e.PathProgress),
StackHeight = StackHeight, StackHeight = StackHeight,

View File

@ -1,9 +0,0 @@
// 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.
namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderCircle : HitCircle
{
}
}

View File

@ -0,0 +1,50 @@
// 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.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
/// <summary>
/// A hit circle which is at the end of a slider path (either repeat or final tail).
/// </summary>
public abstract class SliderEndCircle : HitCircle
{
private readonly Slider slider;
protected SliderEndCircle(Slider slider)
{
this.slider = slider;
}
public int RepeatIndex { get; set; }
public double SpanDuration => slider.SpanDuration;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
if (RepeatIndex > 0)
{
// Repeat points after the first span should appear behind the still-visible one.
TimeFadeIn = 0;
// The next end circle should appear exactly after the previous circle (on the same end) is hit.
TimePreempt = SpanDuration * 2;
}
else
{
// taken from osu-stable
const float first_end_circle_preempt_adjust = 2 / 3f;
// The first end circle should fade in with the slider.
TimePreempt = (StartTime - slider.StartTime) + slider.TimePreempt * first_end_circle_preempt_adjust;
}
}
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -1,40 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
{ {
public class SliderRepeat : OsuHitObject public class SliderRepeat : SliderEndCircle
{ {
public int RepeatIndex { get; set; } public SliderRepeat(Slider slider)
public double SpanDuration { get; set; } : base(slider)
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{ {
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
// Out preempt should be one span early to give the user ample warning.
TimePreempt += SpanDuration;
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
if (RepeatIndex > 0)
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
} }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderRepeatJudgement(); public override Judgement CreateJudgement() => new SliderRepeatJudgement();
public class SliderRepeatJudgement : OsuJudgement public class SliderRepeatJudgement : OsuJudgement
{ {
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0; public override HitResult MaxResult => HitResult.LargeTickHit;
} }
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
@ -13,25 +12,18 @@ namespace osu.Game.Rulesets.Osu.Objects
/// Note that this should not be used for timing correctness. /// Note that this should not be used for timing correctness.
/// See <see cref="SliderEventType.LegacyLastTick"/> usage in <see cref="Slider"/> for more information. /// See <see cref="SliderEventType.LegacyLastTick"/> usage in <see cref="Slider"/> for more information.
/// </summary> /// </summary>
public class SliderTailCircle : SliderCircle public class SliderTailCircle : SliderEndCircle
{ {
private readonly IBindable<int> pathVersion = new Bindable<int>();
public SliderTailCircle(Slider slider) public SliderTailCircle(Slider slider)
: base(slider)
{ {
pathVersion.BindTo(slider.Path.Version);
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
} }
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderTailJudgement(); public override Judgement CreateJudgement() => new SliderTailJudgement();
public class SliderTailJudgement : OsuJudgement public class SliderTailJudgement : OsuJudgement
{ {
protected override int NumericResultFor(HitResult result) => 0; public override HitResult MaxResult => HitResult.SmallTickHit;
public override bool AffectsCombo => false;
} }
} }
} }

View File

@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public class SliderTickJudgement : OsuJudgement public class SliderTickJudgement : OsuJudgement
{ {
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0; public override HitResult MaxResult => HitResult.LargeTickHit;
} }
} }
} }

View File

@ -9,8 +9,6 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
public class SpinnerBonusTick : SpinnerTick public class SpinnerBonusTick : SpinnerTick
{ {
public new const int SCORE_PER_TICK = 50;
public SpinnerBonusTick() public SpinnerBonusTick()
{ {
Samples.Add(new HitSampleInfo { Name = "spinnerbonus" }); Samples.Add(new HitSampleInfo { Name = "spinnerbonus" });
@ -20,9 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public class OsuSpinnerBonusTickJudgement : OsuSpinnerTickJudgement public class OsuSpinnerBonusTickJudgement : OsuSpinnerTickJudgement
{ {
protected override int NumericResultFor(HitResult result) => result == MaxResult ? SCORE_PER_TICK : 0; public override HitResult MaxResult => HitResult.LargeBonus;
protected override double HealthIncreaseFor(HitResult result) => base.HealthIncreaseFor(result) * 2;
} }
} }
} }

View File

@ -9,19 +9,13 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
public class SpinnerTick : OsuHitObject public class SpinnerTick : OsuHitObject
{ {
public const int SCORE_PER_TICK = 10;
public override Judgement CreateJudgement() => new OsuSpinnerTickJudgement(); public override Judgement CreateJudgement() => new OsuSpinnerTickJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public class OsuSpinnerTickJudgement : OsuJudgement public class OsuSpinnerTickJudgement : OsuJudgement
{ {
public override bool AffectsCombo => false; public override HitResult MaxResult => HitResult.SmallBonus;
protected override int NumericResultFor(HitResult result) => result == MaxResult ? SCORE_PER_TICK : 0;
protected override double HealthIncreaseFor(HitResult result) => result == MaxResult ? 0.6 * base.HealthIncreaseFor(result) : 0;
} }
} }
} }

View File

@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu
ReverseArrow, ReverseArrow,
HitCircleText, HitCircleText,
SliderHeadHitCircle, SliderHeadHitCircle,
SliderTailHitCircle,
SliderFollowCircle, SliderFollowCircle,
SliderBall, SliderBall,
SliderBody, SliderBody,

View File

@ -137,13 +137,13 @@ namespace osu.Game.Rulesets.Osu.Replays
if (!(h is Spinner)) if (!(h is Spinner))
AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
else if (h.StartTime - hitWindows.WindowFor(HitResult.Good) > endTime + hitWindows.WindowFor(HitResult.Good) + 50) else if (h.StartTime - hitWindows.WindowFor(HitResult.Ok) > endTime + hitWindows.WindowFor(HitResult.Ok) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) if (!(prev is Spinner) && h.StartTime - endTime < 1000)
AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.WindowFor(HitResult.Ok), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner)) if (!(h is Spinner))
AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.WindowFor(HitResult.Ok), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
} }

View File

@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
private static readonly DifficultyRange[] osu_ranges = private static readonly DifficultyRange[] osu_ranges =
{ {
new DifficultyRange(HitResult.Great, 80, 50, 20), new DifficultyRange(HitResult.Great, 80, 50, 20),
new DifficultyRange(HitResult.Good, 140, 100, 60), new DifficultyRange(HitResult.Ok, 140, 100, 60),
new DifficultyRange(HitResult.Meh, 200, 150, 100), new DifficultyRange(HitResult.Meh, 200, 150, 100),
new DifficultyRange(HitResult.Miss, 400, 400, 400), new DifficultyRange(HitResult.Miss, 400, 400, 400),
}; };
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
switch (result) switch (result)
{ {
case HitResult.Great: case HitResult.Great:
case HitResult.Good: case HitResult.Ok:
case HitResult.Meh: case HitResult.Meh:
case HitResult.Miss: case HitResult.Miss:
return true; return true;

View File

@ -25,7 +25,5 @@ namespace osu.Game.Rulesets.Osu.Scoring
return new OsuJudgementResult(hitObject, judgement); return new OsuJudgementResult(hitObject, judgement);
} }
} }
public override HitWindows CreateHitWindows() => new OsuHitWindows();
} }
} }

View File

@ -1,9 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -15,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
private bool disjointTrail; private bool disjointTrail;
private double lastTrailTime; private double lastTrailTime;
private IBindable<float> cursorSize;
public LegacyCursorTrail() public LegacyCursorTrail()
{ {
@ -22,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin) private void load(ISkinSource skin, OsuConfigManager config)
{ {
Texture = skin.GetTexture("cursortrail"); Texture = skin.GetTexture("cursortrail");
disjointTrail = skin.GetTexture("cursormiddle") == null; disjointTrail = skin.GetTexture("cursormiddle") == null;
@ -32,12 +36,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
// stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation. // stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation.
Texture.ScaleAdjust *= 1.6f; Texture.ScaleAdjust *= 1.6f;
} }
cursorSize = config.GetBindable<float>(OsuSetting.GameplayCursorSize).GetBoundCopy();
} }
protected override double FadeDuration => disjointTrail ? 150 : 500; protected override double FadeDuration => disjointTrail ? 150 : 500;
protected override bool InterpolateMovements => !disjointTrail; protected override bool InterpolateMovements => !disjointTrail;
protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1);
protected override bool OnMouseMove(MouseMoveEvent e) protected override bool OnMouseMove(MouseMoveEvent e)
{ {
if (!disjointTrail) if (!disjointTrail)

View File

@ -21,10 +21,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
public class LegacyMainCirclePiece : CompositeDrawable public class LegacyMainCirclePiece : CompositeDrawable
{ {
private readonly string priorityLookup; private readonly string priorityLookup;
private readonly bool hasNumber;
public LegacyMainCirclePiece(string priorityLookup = null) public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true)
{ {
this.priorityLookup = priorityLookup; this.priorityLookup = priorityLookup;
this.hasNumber = hasNumber;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
} }
@ -70,7 +72,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
} }
} }
}, },
hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText };
if (hasNumber)
{
AddInternal(hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
{ {
Font = OsuFont.Numeric.With(size: 40), Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false, UseFullGlyphHeight = false,
@ -78,8 +84,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}, });
}; }
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
@ -107,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
state.BindValueChanged(updateState, true); state.BindValueChanged(updateState, true);
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
if (hasNumber)
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
} }
@ -120,6 +127,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
circleSprites.FadeOut(legacy_fade_duration, Easing.Out); circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
if (hasNumber)
{
var legacyVersion = skin.GetConfig<LegacySetting, decimal>(LegacySetting.Version)?.Value; var legacyVersion = skin.GetConfig<LegacySetting, decimal>(LegacySetting.Version)?.Value;
if (legacyVersion >= 2.0m) if (legacyVersion >= 2.0m)
@ -131,6 +140,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
hitCircleText.FadeOut(legacy_fade_duration, Easing.Out); hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
} }
}
break; break;
} }

View File

@ -70,21 +70,31 @@ namespace osu.Game.Rulesets.Osu.Skinning
{ {
base.LoadComplete(); base.LoadComplete();
this.FadeOut();
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
} }
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{ {
if (!(drawableHitObject is DrawableSpinner))
return;
var spinner = (Spinner)drawableSpinner.HitObject; var spinner = (Spinner)drawableSpinner.HitObject;
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
this.FadeOut();
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true))
this.FadeInFromZero(spinner.TimeFadeIn / 2); this.FadeInFromZero(spinner.TimeFadeIn / 2);
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
{
fixedMiddle.FadeColour(Color4.White); fixedMiddle.FadeColour(Color4.White);
using (BeginAbsoluteSequence(spinner.StartTime, true))
using (BeginDelayedSequence(spinner.TimePreempt, true))
fixedMiddle.FadeColour(Color4.Red, spinner.Duration); fixedMiddle.FadeColour(Color4.Red, spinner.Duration);
} }
}
protected override void Update() protected override void Update()
{ {

View File

@ -24,12 +24,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
private Sprite metreSprite; private Sprite metreSprite;
private Container metre; private Container metre;
private bool spinnerBlink;
private const float sprite_scale = 1 / 1.6f; private const float sprite_scale = 1 / 1.6f;
private const float final_metre_height = 692 * sprite_scale; private const float final_metre_height = 692 * sprite_scale;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource source, DrawableHitObject drawableObject) private void load(ISkinSource source, DrawableHitObject drawableObject)
{ {
spinnerBlink = source.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true;
drawableSpinner = (DrawableSpinner)drawableObject; drawableSpinner = (DrawableSpinner)drawableObject;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
@ -84,14 +88,20 @@ namespace osu.Game.Rulesets.Osu.Skinning
{ {
base.LoadComplete(); base.LoadComplete();
this.FadeOut();
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
} }
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{ {
if (!(drawableHitObject is DrawableSpinner))
return;
var spinner = drawableSpinner.HitObject; var spinner = drawableSpinner.HitObject;
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
this.FadeOut();
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true)) using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true))
this.FadeInFromZero(spinner.TimeFadeIn / 2); this.FadeInFromZero(spinner.TimeFadeIn / 2);
} }
@ -116,12 +126,15 @@ namespace osu.Game.Rulesets.Osu.Skinning
private float getMetreHeight(float progress) private float getMetreHeight(float progress)
{ {
progress = Math.Min(99, progress * 100); progress *= 100;
// the spinner should still blink at 100% progress.
if (spinnerBlink)
progress = Math.Min(99, progress);
int barCount = (int)progress / 10; int barCount = (int)progress / 10;
// todo: add SpinnerNoBlink support if (spinnerBlink && RNG.NextBool(((int)progress % 10) / 10f))
if (RNG.NextBool(((int)progress % 10) / 10f))
barCount++; barCount++;
return (float)barCount / total_bars * final_metre_height; return (float)barCount / total_bars * final_metre_height;

View File

@ -66,6 +66,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
return null; return null;
case OsuSkinComponents.SliderTailHitCircle:
if (hasHitCircle.Value)
return new LegacyMainCirclePiece("sliderendcircle", false);
return null;
case OsuSkinComponents.SliderHeadHitCircle: case OsuSkinComponents.SliderHeadHitCircle:
if (hasHitCircle.Value) if (hasHitCircle.Value)
return new LegacyMainCirclePiece("sliderstartcircle"); return new LegacyMainCirclePiece("sliderstartcircle");

View File

@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
CursorRotate, CursorRotate,
HitCircleOverlayAboveNumber, HitCircleOverlayAboveNumber,
HitCircleOverlayAboveNumer, // Some old skins will have this typo HitCircleOverlayAboveNumer, // Some old skins will have this typo
SpinnerFrequencyModulate SpinnerFrequencyModulate,
SpinnerNoBlink
} }
} }

View File

@ -119,6 +119,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
/// </summary> /// </summary>
protected virtual bool InterpolateMovements => true; protected virtual bool InterpolateMovements => true;
protected virtual float IntervalMultiplier => 1.0f;
private Vector2? lastPosition; private Vector2? lastPosition;
private readonly InputResampler resampler = new InputResampler(); private readonly InputResampler resampler = new InputResampler();
@ -147,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
float distance = diff.Length; float distance = diff.Length;
Vector2 direction = diff / distance; Vector2 direction = diff / distance;
float interval = partSize.X / 2.5f; float interval = partSize.X / 2.5f * IntervalMultiplier;
for (float d = interval; d < distance; d += interval) for (float d = interval; d < distance; d += interval)
{ {

View File

@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
createDrawableRuleset(); createDrawableRuleset();
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
assertStateAfterResult(new JudgementResult(new StrongHitObject(), new TaikoStrongJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Idle); assertStateAfterResult(new JudgementResult(new StrongHitObject(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle);
} }
[Test] [Test]
@ -102,8 +102,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
createDrawableRuleset(); createDrawableRuleset();
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Kiai); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Ok }, TaikoMascotAnimationState.Kiai);
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoStrongJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Kiai); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Kiai);
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail);
} }
@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail);
assertStateAfterResult(new JudgementResult(new DrumRoll(), new TaikoDrumRollJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle); assertStateAfterResult(new JudgementResult(new DrumRoll(), new TaikoDrumRollJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Idle); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Ok }, TaikoMascotAnimationState.Idle);
} }
[TestCase(true)] [TestCase(true)]
@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
AddRepeatStep("reach 49 combo", () => applyNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }), 49); AddRepeatStep("reach 49 combo", () => applyNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }), 49);
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Good }, TaikoMascotAnimationState.Clear); assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Ok }, TaikoMascotAnimationState.Clear);
} }
[TestCase(true, TaikoMascotAnimationState.Kiai)] [TestCase(true, TaikoMascotAnimationState.Kiai)]

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
public void TestNormalHit() public void TestNormalHit()
{ {
AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great)))); AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great))));
AddStep("Good", () => SetContents(() => getContentFor(createHit(HitResult.Good)))); AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok))));
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss)))); AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
} }
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
public void TestStrongHit([Values(false, true)] bool hitBoth) public void TestStrongHit([Values(false, true)] bool hitBoth)
{ {
AddStep("Great", () => SetContents(() => getContentFor(createStrongHit(HitResult.Great, hitBoth)))); AddStep("Great", () => SetContents(() => getContentFor(createStrongHit(HitResult.Great, hitBoth))));
AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Good, hitBoth)))); AddStep("Good", () => SetContents(() => getContentFor(createStrongHit(HitResult.Ok, hitBoth))));
} }
private Drawable getContentFor(DrawableTestHit hit) private Drawable getContentFor(DrawableTestHit hit)

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
})); }));
AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value = AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value =
new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss })); new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Great : HitResult.Miss }));
AddToggleStep("toggle playback direction", reversed => this.reversed = reversed); AddToggleStep("toggle playback direction", reversed => this.reversed = reversed);
} }

View File

@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
DrawableDrumRollTick h; DrawableDrumRollTick h;
DrawableRuleset.Playfield.Add(h = new DrawableDrumRollTick(tick) { JudgementType = hitType }); DrawableRuleset.Playfield.Add(h = new DrawableDrumRollTick(tick) { JudgementType = hitType });
((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Perfect }); ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(tick, new TaikoDrumRollTickJudgement()) { Type = HitResult.Great });
} }
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -30,8 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
private readonly Random rng = new Random(1337); private readonly Random rng = new Random(1337);
[BackgroundDependencyLoader] [Test]
private void load() public void TestVariousHits()
{ {
AddStep("Hit", () => addHitJudgement(false)); AddStep("Hit", () => addHitJudgement(false));
AddStep("Strong hit", () => addStrongHitJudgement(false)); AddStep("Strong hit", () => addStrongHitJudgement(false));
@ -105,7 +104,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
private void addHitJudgement(bool kiai) private void addHitJudgement(bool kiai)
{ {
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great; HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great;
var cpi = new ControlPointInfo(); var cpi = new ControlPointInfo();
cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); cpi.Add(0, new EffectControlPoint { KiaiMode = kiai });
@ -113,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Hit hit = new Hit(); Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty()); hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) };
DrawableRuleset.Playfield.Add(h); DrawableRuleset.Playfield.Add(h);
@ -122,7 +121,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
private void addStrongHitJudgement(bool kiai) private void addStrongHitJudgement(bool kiai)
{ {
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great; HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great;
var cpi = new ControlPointInfo(); var cpi = new ControlPointInfo();
cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); cpi.Add(0, new EffectControlPoint { KiaiMode = kiai });
@ -130,7 +129,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Hit hit = new Hit(); Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty()); hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) };
DrawableRuleset.Playfield.Add(h); DrawableRuleset.Playfield.Add(h);

View File

@ -42,9 +42,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
} }
} }
private SkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd) private PausableSkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd)
{ {
var drawable = new SkinnableSound(hitSampleInfo) var drawable = new PausableSkinnableSound(hitSampleInfo)
{ {
LifetimeStart = lifetimeStart, LifetimeStart = lifetimeStart,
LifetimeEnd = lifetimeEnd LifetimeEnd = lifetimeEnd
@ -57,8 +57,8 @@ namespace osu.Game.Rulesets.Taiko.Audio
public class DrumSample public class DrumSample
{ {
public SkinnableSound Centre; public PausableSkinnableSound Centre;
public SkinnableSound Rim; public PausableSkinnableSound Rim;
} }
} }
} }

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private Mod[] mods; private Mod[] mods;
private int countGreat; private int countGreat;
private int countGood; private int countOk;
private int countMeh; private int countMeh;
private int countMiss; private int countMiss;
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
{ {
mods = Score.Mods; mods = Score.Mods;
countGreat = Score.Statistics.GetOrDefault(HitResult.Great); countGreat = Score.Statistics.GetOrDefault(HitResult.Great);
countGood = Score.Statistics.GetOrDefault(HitResult.Good); countOk = Score.Statistics.GetOrDefault(HitResult.Ok);
countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); countMeh = Score.Statistics.GetOrDefault(HitResult.Meh);
countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); countMiss = Score.Statistics.GetOrDefault(HitResult.Miss);
@ -102,6 +102,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3)); return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
} }
private int totalHits => countGreat + countGood + countMeh + countMiss; private int totalHits => countGreat + countOk + countMeh + countMiss;
} }
} }

View File

@ -7,25 +7,13 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
public class TaikoDrumRollTickJudgement : TaikoJudgement public class TaikoDrumRollTickJudgement : TaikoJudgement
{ {
public override bool AffectsCombo => false; public override HitResult MaxResult => HitResult.SmallTickHit;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
case HitResult.Great:
return 200;
default:
return 0;
}
}
protected override double HealthIncreaseFor(HitResult result) protected override double HealthIncreaseFor(HitResult result)
{ {
switch (result) switch (result)
{ {
case HitResult.Great: case HitResult.SmallTickHit:
return 0.15; return 0.15;
default: default:

View File

@ -10,21 +10,6 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
public override HitResult MaxResult => HitResult.Great; public override HitResult MaxResult => HitResult.Great;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
case HitResult.Good:
return 100;
case HitResult.Great:
return 300;
default:
return 0;
}
}
protected override double HealthIncreaseFor(HitResult result) protected override double HealthIncreaseFor(HitResult result)
{ {
switch (result) switch (result)
@ -32,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
case HitResult.Miss: case HitResult.Miss:
return -1.0; return -1.0;
case HitResult.Good: case HitResult.Ok:
return 1.1; return 1.1;
case HitResult.Great: case HitResult.Great:

View File

@ -7,9 +7,9 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
public class TaikoStrongJudgement : TaikoJudgement public class TaikoStrongJudgement : TaikoJudgement
{ {
public override HitResult MaxResult => HitResult.SmallBonus;
// MainObject already changes the HP // MainObject already changes the HP
protected override double HealthIncreaseFor(HitResult result) => 0; protected override double HealthIncreaseFor(HitResult result) => 0;
public override bool AffectsCombo => false;
} }
} }

View File

@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!(obj is DrawableDrumRollTick)) if (!(obj is DrawableDrumRollTick))
return; return;
if (result.Type > HitResult.Miss) if (result.IsHit)
rollingHits++; rollingHits++;
else else
rollingHits--; rollingHits--;
@ -129,10 +129,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (countHit >= HitObject.RequiredGoodHits) if (countHit >= HitObject.RequiredGoodHits)
{ {
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good); ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Ok);
} }
else else
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
} }
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!MainObject.Judged) if (!MainObject.Judged)
return; return;
ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = MainObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(TaikoAction action) => false;

View File

@ -4,7 +4,6 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -34,14 +33,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!userTriggered) if (!userTriggered)
{ {
if (timeOffset > HitObject.HitWindow) if (timeOffset > HitObject.HitWindow)
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }
if (Math.Abs(timeOffset) > HitObject.HitWindow) if (Math.Abs(timeOffset) > HitObject.HitWindow)
return; return;
ApplyResult(r => r.Type = HitResult.Great); ApplyResult(r => r.Type = r.Judgement.MaxResult);
} }
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
@ -74,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!MainObject.Judged) if (!MainObject.Judged)
return; return;
ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = MainObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(TaikoAction action) => false;

View File

@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }
@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return; return;
if (!validActionPressed) if (!validActionPressed)
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
else else
ApplyResult(r => r.Type = result); ApplyResult(r => r.Type = result);
} }
@ -257,19 +257,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (!MainObject.Result.IsHit) if (!MainObject.Result.IsHit)
{ {
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }
if (!userTriggered) if (!userTriggered)
{ {
if (timeOffset - MainObject.Result.TimeOffset > second_hit_window) if (timeOffset - MainObject.Result.TimeOffset > second_hit_window)
ApplyResult(r => r.Type = HitResult.Miss); ApplyResult(r => r.Type = r.Judgement.MinResult);
return; return;
} }
if (Math.Abs(timeOffset - MainObject.Result.TimeOffset) <= second_hit_window) if (Math.Abs(timeOffset - MainObject.Result.TimeOffset) <= second_hit_window)
ApplyResult(r => r.Type = MainObject.Result.Type); ApplyResult(r => r.Type = r.Judgement.MaxResult);
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action)

View File

@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
} }
nextTick?.TriggerResult(HitResult.Great); nextTick?.TriggerResult(true);
var numHits = ticks.Count(r => r.IsHit); var numHits = ticks.Count(r => r.IsHit);
@ -208,12 +208,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
continue; continue;
} }
tick.TriggerResult(HitResult.Miss); tick.TriggerResult(false);
} }
var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Good : HitResult.Miss; ApplyResult(r => r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : r.Judgement.MinResult);
ApplyResult(r => r.Type = hitResult);
} }
} }

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -19,10 +18,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateInitialTransforms() => this.FadeOut(); protected override void UpdateInitialTransforms() => this.FadeOut();
public void TriggerResult(HitResult type) public void TriggerResult(bool hit)
{ {
HitObject.StartTime = Time.Current; HitObject.StartTime = Time.Current;
ApplyResult(r => r.Type = type); ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private double hpMultiplier; private double hpMultiplier;
/// <summary> /// <summary>
/// HP multiplier for a <see cref="HitResult.Miss"/>. /// HP multiplier for a <see cref="HitResult"/> that does not satisfy <see cref="HitResultExtensions.IsHit"/>.
/// </summary> /// </summary>
private double hpMissMultiplier; private double hpMissMultiplier;
@ -45,6 +45,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
} }
protected override double GetHealthIncreaseFor(JudgementResult result) protected override double GetHealthIncreaseFor(JudgementResult result)
=> base.GetHealthIncreaseFor(result) * (result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier); => base.GetHealthIncreaseFor(result) * (result.IsHit ? hpMultiplier : hpMissMultiplier);
} }
} }

View File

@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private static readonly DifficultyRange[] taiko_ranges = private static readonly DifficultyRange[] taiko_ranges =
{ {
new DifficultyRange(HitResult.Great, 50, 35, 20), new DifficultyRange(HitResult.Great, 50, 35, 20),
new DifficultyRange(HitResult.Good, 120, 80, 50), new DifficultyRange(HitResult.Ok, 120, 80, 50),
new DifficultyRange(HitResult.Miss, 135, 95, 70), new DifficultyRange(HitResult.Miss, 135, 95, 70),
}; };
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
switch (result) switch (result)
{ {
case HitResult.Great: case HitResult.Great:
case HitResult.Good: case HitResult.Ok:
case HitResult.Miss: case HitResult.Miss:
return true; return true;
} }

View File

@ -10,7 +10,5 @@ namespace osu.Game.Rulesets.Taiko.Scoring
protected override double DefaultAccuracyPortion => 0.75; protected override double DefaultAccuracyPortion => 0.75;
protected override double DefaultComboPortion => 0.25; protected override double DefaultComboPortion => 0.25;
public override HitWindows CreateHitWindows() => new TaikoHitWindows();
} }
} }

View File

@ -1,21 +1,64 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Skinning namespace osu.Game.Rulesets.Taiko.Skinning
{ {
public class LegacyHitExplosion : CompositeDrawable public class LegacyHitExplosion : CompositeDrawable
{ {
public LegacyHitExplosion(Drawable sprite) private readonly Drawable sprite;
{ private readonly Drawable strongSprite;
InternalChild = sprite;
private DrawableStrongNestedHit nestedStrongHit;
private bool switchedToStrongSprite;
/// <summary>
/// Creates a new legacy hit explosion.
/// </summary>
/// <remarks>
/// Contrary to stable's, this implementation doesn't require a frame-perfect hit
/// for the strong sprite to be displayed.
/// </remarks>
/// <param name="sprite">The normal legacy explosion sprite.</param>
/// <param name="strongSprite">The strong legacy explosion sprite.</param>
public LegacyHitExplosion(Drawable sprite, Drawable strongSprite = null)
{
this.sprite = sprite;
this.strongSprite = strongSprite;
}
[BackgroundDependencyLoader]
private void load(DrawableHitObject judgedObject)
{
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
AddInternal(sprite.With(s =>
{
s.Anchor = Anchor.Centre;
s.Origin = Anchor.Centre;
}));
if (strongSprite != null)
{
AddInternal(strongSprite.With(s =>
{
s.Alpha = 0;
s.Anchor = Anchor.Centre;
s.Origin = Anchor.Centre;
}));
}
if (judgedObject is DrawableHit hit)
nestedStrongHit = hit.NestedHitObjects.SingleOrDefault() as DrawableStrongNestedHit;
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -33,5 +76,25 @@ namespace osu.Game.Rulesets.Taiko.Skinning
Expire(true); Expire(true);
} }
protected override void Update()
{
base.Update();
if (shouldSwitchToStrongSprite() && !switchedToStrongSprite)
{
sprite.FadeOut(50, Easing.OutQuint);
strongSprite.FadeIn(50, Easing.OutQuint);
switchedToStrongSprite = true;
}
}
private bool shouldSwitchToStrongSprite()
{
if (nestedStrongHit == null || strongSprite == null)
return false;
return nestedStrongHit.IsHit;
}
} }
} }

View File

@ -42,10 +42,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning
var r = result.NewValue; var r = result.NewValue;
// always ignore hitobjects that don't affect combo (drumroll ticks etc.) // always ignore hitobjects that don't affect combo (drumroll ticks etc.)
if (r?.Judgement.AffectsCombo == false) if (r?.Type.AffectsCombo() == false)
return; return;
passing = r == null || r.Type > HitResult.Miss; passing = r == null || r.IsHit;
foreach (var sprite in InternalChildren.OfType<ScrollerSprite>()) foreach (var sprite in InternalChildren.OfType<ScrollerSprite>())
sprite.Passing = passing; sprite.Passing = passing;

View File

@ -74,15 +74,23 @@ namespace osu.Game.Rulesets.Taiko.Skinning
return null; return null;
case TaikoSkinComponents.TaikoExplosionGood:
case TaikoSkinComponents.TaikoExplosionGoodStrong:
case TaikoSkinComponents.TaikoExplosionGreat:
case TaikoSkinComponents.TaikoExplosionGreatStrong:
case TaikoSkinComponents.TaikoExplosionMiss: case TaikoSkinComponents.TaikoExplosionMiss:
var sprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false); var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false);
if (sprite != null) if (missSprite != null)
return new LegacyHitExplosion(sprite); return new LegacyHitExplosion(missSprite);
return null;
case TaikoSkinComponents.TaikoExplosionOk:
case TaikoSkinComponents.TaikoExplosionGreat:
var hitName = getHitName(taikoComponent.Component);
var hitSprite = this.GetAnimation(hitName, true, false);
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
if (hitSprite != null)
return new LegacyHitExplosion(hitSprite, strongHitSprite);
return null; return null;
@ -106,20 +114,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning
case TaikoSkinComponents.TaikoExplosionMiss: case TaikoSkinComponents.TaikoExplosionMiss:
return "taiko-hit0"; return "taiko-hit0";
case TaikoSkinComponents.TaikoExplosionGood: case TaikoSkinComponents.TaikoExplosionOk:
return "taiko-hit100"; return "taiko-hit100";
case TaikoSkinComponents.TaikoExplosionGoodStrong:
return "taiko-hit100k";
case TaikoSkinComponents.TaikoExplosionGreat: case TaikoSkinComponents.TaikoExplosionGreat:
return "taiko-hit300"; return "taiko-hit300";
case TaikoSkinComponents.TaikoExplosionGreatStrong:
return "taiko-hit300k";
} }
throw new ArgumentOutOfRangeException(nameof(component), "Invalid result type"); throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
} }
public override SampleChannel GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo)); public override SampleChannel GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));

View File

@ -16,10 +16,8 @@ namespace osu.Game.Rulesets.Taiko
PlayfieldBackgroundRight, PlayfieldBackgroundRight,
BarLine, BarLine,
TaikoExplosionMiss, TaikoExplosionMiss,
TaikoExplosionGood, TaikoExplosionOk,
TaikoExplosionGoodStrong,
TaikoExplosionGreat, TaikoExplosionGreat,
TaikoExplosionGreatStrong,
Scroller, Scroller,
Mascot, Mascot,
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Alpha = 0.15f; Alpha = 0.15f;
Masking = true; Masking = true;
if (result == HitResult.Miss) if (!result.IsHit())
return; return;
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim; bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
switch (Result.Type) switch (Result.Type)
{ {
case HitResult.Good: case HitResult.Ok:
JudgementBody.Colour = colours.GreenLight; JudgementBody.Colour = colours.GreenLight;
break; break;

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -77,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.UI
lastObjectHit = true; lastObjectHit = true;
} }
if (!result.Judgement.AffectsCombo) if (!result.Type.AffectsCombo())
return; return;
lastObjectHit = result.IsHit; lastObjectHit = result.IsHit;
@ -115,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.UI
} }
private bool triggerComboClear(JudgementResult judgementResult) private bool triggerComboClear(JudgementResult judgementResult)
=> (judgementResult.ComboAtJudgement + 1) % 50 == 0 && judgementResult.Judgement.AffectsCombo && judgementResult.IsHit; => (judgementResult.ComboAtJudgement + 1) % 50 == 0 && judgementResult.Type.AffectsCombo() && judgementResult.IsHit;
private bool triggerSwellClear(JudgementResult judgementResult) private bool triggerSwellClear(JudgementResult judgementResult)
=> judgementResult.Judgement is TaikoSwellJudgement && judgementResult.IsHit; => judgementResult.Judgement is TaikoSwellJudgement && judgementResult.IsHit;

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using osuTK; using osuTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -10,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
@ -50,39 +48,24 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject)), _ => new DefaultHitExplosion(JudgedObject, result)); Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(result)), _ => new DefaultHitExplosion(JudgedObject, result));
} }
private TaikoSkinComponents getComponentName(DrawableHitObject judgedObject) private static TaikoSkinComponents getComponentName(HitResult result)
{ {
switch (result) switch (result)
{ {
case HitResult.Miss: case HitResult.Miss:
return TaikoSkinComponents.TaikoExplosionMiss; return TaikoSkinComponents.TaikoExplosionMiss;
case HitResult.Good: case HitResult.Ok:
return useStrongExplosion(judgedObject) return TaikoSkinComponents.TaikoExplosionOk;
? TaikoSkinComponents.TaikoExplosionGoodStrong
: TaikoSkinComponents.TaikoExplosionGood;
case HitResult.Great: case HitResult.Great:
return useStrongExplosion(judgedObject) return TaikoSkinComponents.TaikoExplosionGreat;
? TaikoSkinComponents.TaikoExplosionGreatStrong
: TaikoSkinComponents.TaikoExplosionGreat;
} }
throw new ArgumentOutOfRangeException(nameof(judgedObject), "Invalid result type"); throw new ArgumentOutOfRangeException(nameof(result), $"Invalid result type: {result}");
}
private bool useStrongExplosion(DrawableHitObject judgedObject)
{
if (!(judgedObject.HitObject is Hit))
return false;
if (!(judgedObject.NestedHitObjects.SingleOrDefault() is DrawableStrongNestedHit nestedHit))
return false;
return judgedObject.Result.Type == nestedHit.Result.Type;
} }
/// <summary> /// <summary>

View File

@ -163,7 +163,6 @@ namespace osu.Game.Rulesets.Taiko.UI
target = centreHit; target = centreHit;
back = centre; back = centre;
if (gameplayClock?.IsSeeking != true)
drumSample.Centre?.Play(); drumSample.Centre?.Play();
} }
else if (action == RimAction) else if (action == RimAction)
@ -171,7 +170,6 @@ namespace osu.Game.Rulesets.Taiko.UI
target = rimHit; target = rimHit;
back = rim; back = rim;
if (gameplayClock?.IsSeeking != true)
drumSample.Rim?.Play(); drumSample.Rim?.Play();
} }

Some files were not shown because too many files have changed in this diff Show More