1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 15:33:21 +08:00

Merge branch 'master' into osu-selection-scaling

This commit is contained in:
Dean Herbert 2020-10-01 21:09:52 +09:00 committed by GitHub
commit 94996f2cc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
132 changed files with 1429 additions and 797 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.1001.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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,7 +33,7 @@ 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.Type == HitResult.Miss)
@ -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

@ -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

@ -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

@ -2,34 +2,10 @@
// 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.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements namespace osu.Game.Rulesets.Mania.Judgements
{ {
public class ManiaJudgement : Judgement public class ManiaJudgement : Judgement
{ {
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.Meh:
return 50;
case HitResult.Ok:
return 100;
case HitResult.Good:
return 200;
case HitResult.Great:
return 300;
case HitResult.Perfect:
return 350;
}
}
} }
} }

View File

@ -239,7 +239,7 @@ 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();
} }

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

@ -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)));

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

@ -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.IgnoreHit && judgementResults.First().Type == HitResult.Miss;
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.Great; private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.IgnoreHit;
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.Miss; private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.IgnoreMiss;
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

@ -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

@ -87,7 +87,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 +103,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();

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics;
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 osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -50,7 +49,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()

View File

@ -3,7 +3,6 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -46,7 +45,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 (!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; private void updatePosition() => Position = HitObject.Position - slider.Position;

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);
@ -217,7 +220,7 @@ 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)

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

@ -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

@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects
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

@ -29,9 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public class SliderTailJudgement : OsuJudgement public class SliderTailJudgement : OsuJudgement
{ {
protected override int NumericResultFor(HitResult result) => 0; public override HitResult MaxResult => HitResult.IgnoreHit;
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

@ -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

@ -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

@ -105,7 +105,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 +113,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 +122,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 +130,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,19 +7,7 @@ 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)
{ {

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

@ -129,7 +129,7 @@ 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 = HitResult.Miss);
@ -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

@ -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,10 +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; var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : HitResult.Miss;
ApplyResult(r => r.Type = hitResult); 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

@ -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,7 +42,7 @@ 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.Type > HitResult.Miss;

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

@ -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

@ -215,16 +215,12 @@ namespace osu.Game.Rulesets.Taiko.UI
private void addDrumRollHit(DrawableDrumRollTick drawableTick) => private void addDrumRollHit(DrawableDrumRollTick drawableTick) =>
drumRollHitContainer.Add(new DrawableFlyingHit(drawableTick)); drumRollHitContainer.Add(new DrawableFlyingHit(drawableTick));
/// <remarks> private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type)
/// As legacy skins have different explosions for singular and double strong hits,
/// explosion addition is scheduled to ensure that both hits are processed if they occur on the same frame.
/// </remarks>
private void addExplosion(DrawableHitObject drawableObject, HitResult result, HitType type) => Schedule(() =>
{ {
hitExplosionContainer.Add(new HitExplosion(drawableObject, result)); hitExplosionContainer.Add(new HitExplosion(drawableObject, result));
if (drawableObject.HitObject.Kiai) if (drawableObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
}); }
private class ProxyContainer : LifetimeManagementContainer private class ProxyContainer : LifetimeManagementContainer
{ {

View File

@ -170,7 +170,7 @@ namespace osu.Game.Tests.Gameplay
beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = 0 }); beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = 0 });
for (double time = 0; time < 5000; time += 100) for (double time = 0; time < 5000; time += 100)
beatmap.HitObjects.Add(new JudgeableHitObject(false) { StartTime = time }); beatmap.HitObjects.Add(new JudgeableHitObject(HitResult.LargeBonus) { StartTime = time });
beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = 5000 }); beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = 5000 });
createProcessor(beatmap); createProcessor(beatmap);
@ -236,23 +236,23 @@ namespace osu.Game.Tests.Gameplay
private class JudgeableHitObject : HitObject private class JudgeableHitObject : HitObject
{ {
private readonly bool affectsCombo; private readonly HitResult maxResult;
public JudgeableHitObject(bool affectsCombo = true) public JudgeableHitObject(HitResult maxResult = HitResult.Perfect)
{ {
this.affectsCombo = affectsCombo; this.maxResult = maxResult;
} }
public override Judgement CreateJudgement() => new TestJudgement(affectsCombo); public override Judgement CreateJudgement() => new TestJudgement(maxResult);
protected override HitWindows CreateHitWindows() => new HitWindows(); protected override HitWindows CreateHitWindows() => new HitWindows();
private class TestJudgement : Judgement private class TestJudgement : Judgement
{ {
public override bool AffectsCombo { get; } public override HitResult MaxResult { get; }
public TestJudgement(bool affectsCombo) public TestJudgement(HitResult maxResult)
{ {
AffectsCombo = affectsCombo; MaxResult = maxResult;
} }
} }
} }
@ -263,7 +263,7 @@ namespace osu.Game.Tests.Gameplay
public double Duration { get; set; } = 5000; public double Duration { get; set; } = 5000;
public JudgeableLongHitObject() public JudgeableLongHitObject()
: base(false) : base(HitResult.LargeBonus)
{ {
} }

View File

@ -17,13 +17,13 @@ namespace osu.Game.Tests.Gameplay
[Test] [Test]
public void TestNoScoreIncreaseFromMiss() public void TestNoScoreIncreaseFromMiss()
{ {
var beatmap = new Beatmap<TestHitObject> { HitObjects = { new TestHitObject() } }; var beatmap = new Beatmap<HitObject> { HitObjects = { new HitObject() } };
var scoreProcessor = new ScoreProcessor(); var scoreProcessor = new ScoreProcessor();
scoreProcessor.ApplyBeatmap(beatmap); scoreProcessor.ApplyBeatmap(beatmap);
// Apply a miss judgement // Apply a miss judgement
scoreProcessor.ApplyResult(new JudgementResult(new TestHitObject(), new TestJudgement()) { Type = HitResult.Miss }); scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement()) { Type = HitResult.Miss });
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(0.0)); Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(0.0));
} }
@ -31,37 +31,25 @@ namespace osu.Game.Tests.Gameplay
[Test] [Test]
public void TestOnlyBonusScore() public void TestOnlyBonusScore()
{ {
var beatmap = new Beatmap<TestBonusHitObject> { HitObjects = { new TestBonusHitObject() } }; var beatmap = new Beatmap<HitObject> { HitObjects = { new HitObject() } };
var scoreProcessor = new ScoreProcessor(); var scoreProcessor = new ScoreProcessor();
scoreProcessor.ApplyBeatmap(beatmap); scoreProcessor.ApplyBeatmap(beatmap);
// Apply a judgement // Apply a judgement
scoreProcessor.ApplyResult(new JudgementResult(new TestBonusHitObject(), new TestBonusJudgement()) { Type = HitResult.Perfect }); scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement(HitResult.LargeBonus)) { Type = HitResult.LargeBonus });
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(100)); Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
}
private class TestHitObject : HitObject
{
public override Judgement CreateJudgement() => new TestJudgement();
} }
private class TestJudgement : Judgement private class TestJudgement : Judgement
{ {
protected override int NumericResultFor(HitResult result) => 100; public override HitResult MaxResult { get; }
}
private class TestBonusHitObject : HitObject public TestJudgement(HitResult maxResult = HitResult.Perfect)
{ {
public override Judgement CreateJudgement() => new TestBonusJudgement(); MaxResult = maxResult;
} }
private class TestBonusJudgement : Judgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 100;
} }
} }
} }

View File

@ -4,10 +4,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Audio;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Audio; using osu.Game.Audio;
@ -106,9 +108,14 @@ namespace osu.Game.Tests.Gameplay
Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, Audio); Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, Audio);
SelectedMods.Value = new[] { testedMod }; SelectedMods.Value = new[] { testedMod };
Add(gameplayContainer = new GameplayClockContainer(Beatmap.Value, 0)); var beatmapSkinSourceContainer = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
gameplayContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1)) Add(gameplayContainer = new GameplayClockContainer(Beatmap.Value, 0)
{
Child = beatmapSkinSourceContainer
});
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
{ {
Clock = gameplayContainer.GameplayClock Clock = gameplayContainer.GameplayClock
}); });
@ -116,7 +123,7 @@ namespace osu.Game.Tests.Gameplay
AddStep("start", () => gameplayContainer.Start()); AddStep("start", () => gameplayContainer.Start());
AddAssert("sample playback rate matches mod rates", () => sample.Channel.AggregateFrequency.Value == expectedRate); AddAssert("sample playback rate matches mod rates", () => sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value == expectedRate);
} }
private class TestSkin : LegacySkin private class TestSkin : LegacySkin
@ -168,8 +175,6 @@ namespace osu.Game.Tests.Gameplay
: base(sampleInfo) : base(sampleInfo)
{ {
} }
public new SampleChannel Channel => base.Channel;
} }
} }
} }

View File

@ -35,10 +35,10 @@ namespace osu.Game.Tests.Rulesets.Scoring
} }
[TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)] [TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)]
[TestCase(ScoringMode.Standardised, HitResult.Good, 800_000)] [TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)]
[TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)] [TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)]
[TestCase(ScoringMode.Classic, HitResult.Meh, 50)] [TestCase(ScoringMode.Classic, HitResult.Meh, 50)]
[TestCase(ScoringMode.Classic, HitResult.Good, 100)] [TestCase(ScoringMode.Classic, HitResult.Ok, 100)]
[TestCase(ScoringMode.Classic, HitResult.Great, 300)] [TestCase(ScoringMode.Classic, HitResult.Great, 300)]
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore) public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{ {

View File

@ -0,0 +1,47 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics.Audio;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Tests.Visual.Editing
{
public class TestSceneEditorSamplePlayback : EditorTestScene
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
[Test]
public void TestSlidingSampleStopsOnSeek()
{
DrawableSlider slider = null;
DrawableSample[] samples = null;
AddStep("get first slider", () =>
{
slider = Editor.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
samples = slider.ChildrenOfType<DrawableSample>().ToArray();
});
AddStep("start playback", () => EditorClock.Start());
AddUntilStep("wait for slider sliding then seek", () =>
{
if (!slider.Tracking.Value)
return false;
if (!samples.Any(s => s.Playing))
return false;
EditorClock.Seek(20000);
return true;
});
AddAssert("slider samples are not playing", () => samples.Length == 5 && samples.All(s => s.Played && !s.Playing));
}
}
}

View File

@ -4,7 +4,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Timing;
@ -17,16 +17,26 @@ namespace osu.Game.Tests.Visual.Editing
[Cached(typeof(IBeatSnapProvider))] [Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap; private readonly EditorBeatmap editorBeatmap;
protected override bool ScrollUsingMouseWheel => false;
public TestSceneTimingScreen() public TestSceneTimingScreen()
{ {
editorBeatmap = new EditorBeatmap(new OsuBeatmap()); editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
Beatmap.Disabled = true;
Child = new TimingScreen(); Child = new TimingScreen();
} }
protected override void Dispose(bool isDisposing)
{
Beatmap.Disabled = false;
base.Dispose(isDisposing);
}
} }
} }

View File

@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual.Gameplay
Children = new[] Children = new[]
{ {
new OsuSpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" }, new OsuSpriteText { Text = $@"Great: {hitWindows?.WindowFor(HitResult.Great)}" },
new OsuSpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Good)}" }, new OsuSpriteText { Text = $@"Good: {hitWindows?.WindowFor(HitResult.Ok)}" },
new OsuSpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" }, new OsuSpriteText { Text = $@"Meh: {hitWindows?.WindowFor(HitResult.Meh)}" },
} }
}); });

View File

@ -158,7 +158,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestQuickRetryFromFailedGameplay() public void TestQuickRetryFromFailedGameplay()
{ {
AddUntilStep("wait for fail", () => Player.HasFailed); AddUntilStep("wait for fail", () => Player.HasFailed);
AddStep("quick retry", () => Player.GameplayClockContainer.OfType<HotkeyRetryOverlay>().First().Action?.Invoke()); AddStep("quick retry", () => Player.GameplayClockContainer.ChildrenOfType<HotkeyRetryOverlay>().First().Action?.Invoke());
confirmExited(); confirmExited();
} }
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestQuickExitFromFailedGameplay() public void TestQuickExitFromFailedGameplay()
{ {
AddUntilStep("wait for fail", () => Player.HasFailed); AddUntilStep("wait for fail", () => Player.HasFailed);
AddStep("quick exit", () => Player.GameplayClockContainer.OfType<HotkeyExitOverlay>().First().Action?.Invoke()); AddStep("quick exit", () => Player.GameplayClockContainer.ChildrenOfType<HotkeyExitOverlay>().First().Action?.Invoke());
confirmExited(); confirmExited();
} }
@ -183,7 +183,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestQuickExitFromGameplay() public void TestQuickExitFromGameplay()
{ {
AddStep("quick exit", () => Player.GameplayClockContainer.OfType<HotkeyExitOverlay>().First().Action?.Invoke()); AddStep("quick exit", () => Player.GameplayClockContainer.ChildrenOfType<HotkeyExitOverlay>().First().Action?.Invoke());
confirmExited(); confirmExited();
} }

View File

@ -22,11 +22,11 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneSkinnableSound : OsuTestScene public class TestSceneSkinnableSound : OsuTestScene
{ {
[Cached] [Cached(typeof(ISamplePlaybackDisabler))]
private GameplayClock gameplayClock = new GameplayClock(new FramedClock()); private GameplayClock gameplayClock = new GameplayClock(new FramedClock());
private TestSkinSourceContainer skinSource; private TestSkinSourceContainer skinSource;
private SkinnableSound skinnableSound; private PausableSkinnableSound skinnableSound;
[SetUp] [SetUp]
public void SetUp() => Schedule(() => public void SetUp() => Schedule(() =>
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
Clock = gameplayClock, Clock = gameplayClock,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = skinnableSound = new SkinnableSound(new SampleInfo("normal-sliderslide")) Child = skinnableSound = new PausableSkinnableSound(new SampleInfo("normal-sliderslide"))
}, },
}; };
}); });

View File

@ -28,7 +28,7 @@ namespace osu.Game.Tournament.Components
{ {
base.Bindable = new Bindable<string>(); base.Bindable = new Bindable<string>();
((OsuTextBox)Control).OnCommit = (sender, newText) => ((OsuTextBox)Control).OnCommit += (sender, newText) =>
{ {
try try
{ {

View File

@ -38,6 +38,7 @@ namespace osu.Game.Online.API.Requests.Responses
Rank = Rank, Rank = Rank,
Ruleset = ruleset, Ruleset = ruleset,
Mods = mods, Mods = mods,
IsLegacyScore = true
}; };
if (Statistics != null) if (Statistics != null)

View File

@ -59,12 +59,13 @@ namespace osu.Game.Online.Chat
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = textbox_height, Height = textbox_height,
PlaceholderText = "type your message", PlaceholderText = "type your message",
OnCommit = postMessage,
ReleaseFocusOnCommit = false, ReleaseFocusOnCommit = false,
HoldFocus = true, HoldFocus = true,
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
}); });
textbox.OnCommit += postMessage;
} }
Channel.BindValueChanged(channelChanged); Channel.BindValueChanged(channelChanged);

View File

@ -11,9 +11,11 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards; using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Users.Drawables; using osu.Game.Users.Drawables;
using osu.Game.Utils;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -55,6 +57,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
highAccuracyColour = colours.GreenLight; highAccuracyColour = colours.GreenLight;
} }
/// <summary>
/// The statistics that appear in the table, in order of appearance.
/// </summary>
private readonly List<HitResult> statisticResultTypes = new List<HitResult>();
private bool showPerformancePoints; private bool showPerformancePoints;
public void DisplayScores(IReadOnlyList<ScoreInfo> scores, bool showPerformanceColumn) public void DisplayScores(IReadOnlyList<ScoreInfo> scores, bool showPerformanceColumn)
@ -65,11 +72,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
return; return;
showPerformancePoints = showPerformanceColumn; showPerformancePoints = showPerformanceColumn;
statisticResultTypes.Clear();
for (int i = 0; i < scores.Count; i++) for (int i = 0; i < scores.Count; i++)
backgroundFlow.Add(new ScoreTableRowBackground(i, scores[i], row_height)); backgroundFlow.Add(new ScoreTableRowBackground(i, scores[i], row_height));
Columns = createHeaders(scores.FirstOrDefault()); Columns = createHeaders(scores);
Content = scores.Select((s, i) => createContent(i, s)).ToArray().ToRectangular(); Content = scores.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
} }
@ -79,7 +87,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
backgroundFlow.Clear(); backgroundFlow.Clear();
} }
private TableColumn[] createHeaders(ScoreInfo score) private TableColumn[] createHeaders(IReadOnlyList<ScoreInfo> scores)
{ {
var columns = new List<TableColumn> var columns = new List<TableColumn>
{ {
@ -92,10 +100,17 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120)) new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120))
}; };
foreach (var statistic in score.SortedStatistics.Take(score.SortedStatistics.Count() - 1)) // All statistics across all scores, unordered.
columns.Add(new TableColumn(statistic.Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60))); var allScoreStatistics = scores.SelectMany(s => s.GetStatisticsForDisplay().Select(stat => stat.result)).ToHashSet();
columns.Add(new TableColumn(score.SortedStatistics.LastOrDefault().Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 45, maxSize: 95))); foreach (var result in OrderAttributeUtils.GetValuesInOrder<HitResult>())
{
if (!allScoreStatistics.Contains(result))
continue;
columns.Add(new TableColumn(result.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60)));
statisticResultTypes.Add(result);
}
if (showPerformancePoints) if (showPerformancePoints)
columns.Add(new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30))); columns.Add(new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30)));
@ -148,13 +163,18 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
} }
}; };
foreach (var kvp in score.SortedStatistics) var availableStatistics = score.GetStatisticsForDisplay().ToDictionary(tuple => tuple.result);
foreach (var result in statisticResultTypes)
{ {
if (!availableStatistics.TryGetValue(result, out var stat))
stat = (result, 0, null);
content.Add(new OsuSpriteText content.Add(new OsuSpriteText
{ {
Text = $"{kvp.Value}", Text = stat.maxCount == null ? $"{stat.count}" : $"{stat.count}/{stat.maxCount}",
Font = OsuFont.GetFont(size: text_size), Font = OsuFont.GetFont(size: text_size),
Colour = kvp.Value == 0 ? Color4.Gray : Color4.White Colour = stat.count == 0 ? Color4.Gray : Color4.White
}); });
} }

View File

@ -117,7 +117,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
ppColumn.Alpha = value.Beatmap?.Status == BeatmapSetOnlineStatus.Ranked ? 1 : 0; ppColumn.Alpha = value.Beatmap?.Status == BeatmapSetOnlineStatus.Ranked ? 1 : 0;
ppColumn.Text = $@"{value.PP:N0}"; ppColumn.Text = $@"{value.PP:N0}";
statisticsColumns.ChildrenEnumerable = value.SortedStatistics.Select(kvp => createStatisticsColumn(kvp.Key, kvp.Value)); statisticsColumns.ChildrenEnumerable = value.GetStatisticsForDisplay().Select(s => createStatisticsColumn(s.result, s.count, s.maxCount));
modsColumn.Mods = value.Mods; modsColumn.Mods = value.Mods;
if (scoreManager != null) if (scoreManager != null)
@ -125,9 +125,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
} }
} }
private TextColumn createStatisticsColumn(HitResult hitResult, int count) => new TextColumn(hitResult.GetDescription(), smallFont, bottom_columns_min_width) private TextColumn createStatisticsColumn(HitResult hitResult, int count, int? maxCount) => new TextColumn(hitResult.GetDescription(), smallFont, bottom_columns_min_width)
{ {
Text = count.ToString() Text = maxCount == null ? $"{count}" : $"{count}/{maxCount}"
}; };
private class InfoColumn : CompositeDrawable private class InfoColumn : CompositeDrawable

View File

@ -146,7 +146,6 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Height = 1, Height = 1,
PlaceholderText = "type your message", PlaceholderText = "type your message",
OnCommit = postMessage,
ReleaseFocusOnCommit = false, ReleaseFocusOnCommit = false,
HoldFocus = true, HoldFocus = true,
} }
@ -186,6 +185,8 @@ namespace osu.Game.Overlays
}, },
}; };
textbox.OnCommit += postMessage;
ChannelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue; ChannelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
ChannelTabControl.ChannelSelectorActive.ValueChanged += active => ChannelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden; ChannelTabControl.ChannelSelectorActive.ValueChanged += active => ChannelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden;
ChannelSelectionOverlay.State.ValueChanged += state => ChannelSelectionOverlay.State.ValueChanged += state =>

View File

@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music
}, },
}; };
filter.Search.OnCommit = (sender, newText) => filter.Search.OnCommit += (sender, newText) =>
{ {
BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();

View File

@ -236,7 +236,6 @@ namespace osu.Game.Overlays.Settings.Sections.General
PlaceholderText = "password", PlaceholderText = "password",
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
TabbableContentContainer = this, TabbableContentContainer = this,
OnCommit = (sender, newText) => performLogin()
}, },
new SettingsCheckbox new SettingsCheckbox
{ {
@ -276,6 +275,8 @@ namespace osu.Game.Overlays.Settings.Sections.General
} }
} }
}; };
password.OnCommit += (sender, newText) => performLogin();
} }
public override bool AcceptsFocus => true; public override bool AcceptsFocus => true;

View File

@ -7,10 +7,6 @@ namespace osu.Game.Rulesets.Judgements
{ {
public class IgnoreJudgement : Judgement public class IgnoreJudgement : Judgement
{ {
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

@ -1,6 +1,7 @@
// 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.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -11,31 +12,69 @@ namespace osu.Game.Rulesets.Judgements
/// </summary> /// </summary>
public class Judgement public class Judgement
{ {
/// <summary>
/// The score awarded for a small bonus.
/// </summary>
public const int SMALL_BONUS_SCORE = 10;
/// <summary>
/// The score awarded for a large bonus.
/// </summary>
public const int LARGE_BONUS_SCORE = 50;
/// <summary> /// <summary>
/// The default health increase for a maximum judgement, as a proportion of total health. /// The default health increase for a maximum judgement, as a proportion of total health.
/// By default, each maximum judgement restores 5% of total health. /// By default, each maximum judgement restores 5% of total health.
/// </summary> /// </summary>
protected const double DEFAULT_MAX_HEALTH_INCREASE = 0.05; protected const double DEFAULT_MAX_HEALTH_INCREASE = 0.05;
/// <summary>
/// Whether this <see cref="Judgement"/> should affect the current combo.
/// </summary>
[Obsolete("Has no effect. Use HitResult members instead (e.g. use small-tick or bonus to not affect combo).")] // Can be removed 20210328
public virtual bool AffectsCombo => true;
/// <summary>
/// Whether this <see cref="Judgement"/> should be counted as base (combo) or bonus score.
/// </summary>
[Obsolete("Has no effect. Use HitResult members instead (e.g. use small-tick or bonus to not affect combo).")] // Can be removed 20210328
public virtual bool IsBonus => !AffectsCombo;
/// <summary> /// <summary>
/// The maximum <see cref="HitResult"/> that can be achieved. /// The maximum <see cref="HitResult"/> that can be achieved.
/// </summary> /// </summary>
public virtual HitResult MaxResult => HitResult.Perfect; public virtual HitResult MaxResult => HitResult.Perfect;
/// <summary> /// <summary>
/// Whether this <see cref="Judgement"/> should affect the current combo. /// The minimum <see cref="HitResult"/> that can be achieved - the inverse of <see cref="MaxResult"/>.
/// </summary> /// </summary>
public virtual bool AffectsCombo => true; public HitResult MinResult
{
get
{
switch (MaxResult)
{
case HitResult.SmallBonus:
case HitResult.LargeBonus:
case HitResult.IgnoreHit:
return HitResult.IgnoreMiss;
/// <summary> case HitResult.SmallTickHit:
/// Whether this <see cref="Judgement"/> should be counted as base (combo) or bonus score. return HitResult.SmallTickMiss;
/// </summary>
public virtual bool IsBonus => !AffectsCombo; case HitResult.LargeTickHit:
return HitResult.LargeTickMiss;
default:
return HitResult.Miss;
}
}
}
/// <summary> /// <summary>
/// The numeric score representation for the maximum achievable result. /// The numeric score representation for the maximum achievable result.
/// </summary> /// </summary>
public int MaxNumericResult => NumericResultFor(MaxResult); public int MaxNumericResult => ToNumericResult(MaxResult);
/// <summary> /// <summary>
/// The health increase for the maximum achievable result. /// The health increase for the maximum achievable result.
@ -47,14 +86,15 @@ namespace osu.Game.Rulesets.Judgements
/// </summary> /// </summary>
/// <param name="result">The <see cref="HitResult"/> to find the numeric score representation for.</param> /// <param name="result">The <see cref="HitResult"/> to find the numeric score representation for.</param>
/// <returns>The numeric score representation of <paramref name="result"/>.</returns> /// <returns>The numeric score representation of <paramref name="result"/>.</returns>
protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0; [Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be made non-virtual 20210328
protected virtual int NumericResultFor(HitResult result) => ToNumericResult(result);
/// <summary> /// <summary>
/// Retrieves the numeric score representation of a <see cref="JudgementResult"/>. /// Retrieves the numeric score representation of a <see cref="JudgementResult"/>.
/// </summary> /// </summary>
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric score representation for.</param> /// <param name="result">The <see cref="JudgementResult"/> to find the numeric score representation for.</param>
/// <returns>The numeric score representation of <paramref name="result"/>.</returns> /// <returns>The numeric score representation of <paramref name="result"/>.</returns>
public int NumericResultFor(JudgementResult result) => NumericResultFor(result.Type); public int NumericResultFor(JudgementResult result) => ToNumericResult(result.Type);
/// <summary> /// <summary>
/// Retrieves the numeric health increase of a <see cref="HitResult"/>. /// Retrieves the numeric health increase of a <see cref="HitResult"/>.
@ -65,26 +105,44 @@ namespace osu.Game.Rulesets.Judgements
{ {
switch (result) switch (result)
{ {
default:
return 0;
case HitResult.SmallTickHit:
return DEFAULT_MAX_HEALTH_INCREASE * 0.05;
case HitResult.SmallTickMiss:
return -DEFAULT_MAX_HEALTH_INCREASE * 0.05;
case HitResult.LargeTickHit:
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.LargeTickMiss:
return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.Miss: case HitResult.Miss:
return -DEFAULT_MAX_HEALTH_INCREASE; return -DEFAULT_MAX_HEALTH_INCREASE;
case HitResult.Meh: case HitResult.Meh:
return -DEFAULT_MAX_HEALTH_INCREASE * 0.05; return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
case HitResult.Ok: case HitResult.Ok:
return -DEFAULT_MAX_HEALTH_INCREASE * 0.01; return -DEFAULT_MAX_HEALTH_INCREASE * 0.3;
case HitResult.Good: case HitResult.Good:
return DEFAULT_MAX_HEALTH_INCREASE * 0.5; return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.Great: case HitResult.Great:
return DEFAULT_MAX_HEALTH_INCREASE; return DEFAULT_MAX_HEALTH_INCREASE * 0.8;
case HitResult.Perfect: case HitResult.Perfect:
return DEFAULT_MAX_HEALTH_INCREASE * 1.05; return DEFAULT_MAX_HEALTH_INCREASE;
default: case HitResult.SmallBonus:
return 0; return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
case HitResult.LargeBonus:
return DEFAULT_MAX_HEALTH_INCREASE * 0.2;
} }
} }
@ -95,6 +153,42 @@ namespace osu.Game.Rulesets.Judgements
/// <returns>The numeric health increase of <paramref name="result"/>.</returns> /// <returns>The numeric health increase of <paramref name="result"/>.</returns>
public double HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type); public double HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
public override string ToString() => $"AffectsCombo:{AffectsCombo} MaxResult:{MaxResult} MaxScore:{MaxNumericResult}"; public override string ToString() => $"MaxResult:{MaxResult} MaxScore:{MaxNumericResult}";
public static int ToNumericResult(HitResult result)
{
switch (result)
{
default:
return 0;
case HitResult.SmallTickHit:
return 10;
case HitResult.LargeTickHit:
return 30;
case HitResult.Meh:
return 50;
case HitResult.Ok:
return 100;
case HitResult.Good:
return 200;
case HitResult.Great:
return 300;
case HitResult.Perfect:
return 350;
case HitResult.SmallBonus:
return SMALL_BONUS_SCORE;
case HitResult.LargeBonus:
return LARGE_BONUS_SCORE;
}
}
} }
} }

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Judgements
/// <summary> /// <summary>
/// Whether a successful hit occurred. /// Whether a successful hit occurred.
/// </summary> /// </summary>
public bool IsHit => Type > HitResult.Miss; public bool IsHit => Type.IsHit();
/// <summary> /// <summary>
/// Creates a new <see cref="JudgementResult"/>. /// Creates a new <see cref="JudgementResult"/>.

View File

@ -1,7 +1,7 @@
// 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.Audio.Sample; using osu.Framework.Graphics.Audio;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Mods
/// </summary> /// </summary>
public interface IApplicableToSample : IApplicableMod public interface IApplicableToSample : IApplicableMod
{ {
void ApplyToSample(SampleChannel sample); void ApplyToSample(DrawableSample sample);
} }
} }

View File

@ -52,10 +52,10 @@ namespace osu.Game.Rulesets.Mods
public class NightcoreBeatContainer : BeatSyncedContainer public class NightcoreBeatContainer : BeatSyncedContainer
{ {
private SkinnableSound hatSample; private PausableSkinnableSound hatSample;
private SkinnableSound clapSample; private PausableSkinnableSound clapSample;
private SkinnableSound kickSample; private PausableSkinnableSound kickSample;
private SkinnableSound finishSample; private PausableSkinnableSound finishSample;
private int? firstBeat; private int? firstBeat;
@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mods
{ {
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
hatSample = new SkinnableSound(new SampleInfo("nightcore-hat")), hatSample = new PausableSkinnableSound(new SampleInfo("nightcore-hat")),
clapSample = new SkinnableSound(new SampleInfo("nightcore-clap")), clapSample = new PausableSkinnableSound(new SampleInfo("nightcore-clap")),
kickSample = new SkinnableSound(new SampleInfo("nightcore-kick")), kickSample = new PausableSkinnableSound(new SampleInfo("nightcore-kick")),
finishSample = new SkinnableSound(new SampleInfo("nightcore-finish")), finishSample = new PausableSkinnableSound(new SampleInfo("nightcore-finish")),
}; };
} }

View File

@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Mods
public override string Description => "SS or quit."; public override string Description => "SS or quit.";
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
=> !(result.Judgement is IgnoreJudgement) => result.Type.AffectsAccuracy()
&& result.Judgement.AffectsCombo
&& result.Type != result.Judgement.MaxResult; && result.Type != result.Judgement.MaxResult;
} }
} }

View File

@ -2,9 +2,9 @@
// 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.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Audio;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods
track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange); track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
} }
public virtual void ApplyToSample(SampleChannel sample) public virtual void ApplyToSample(DrawableSample sample)
{ {
sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange);
} }

View File

@ -29,6 +29,8 @@ namespace osu.Game.Rulesets.Mods
healthProcessor.FailConditions += FailCondition; healthProcessor.FailConditions += FailCondition;
} }
protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => !result.IsHit && result.Judgement.AffectsCombo; protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
=> result.Type.AffectsCombo()
&& !result.IsHit;
} }
} }

View File

@ -6,11 +6,11 @@ using System.Linq;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Framework.Audio.Sample; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Mods
AdjustPitch.TriggerChange(); AdjustPitch.TriggerChange();
} }
public void ApplyToSample(SampleChannel sample) public void ApplyToSample(DrawableSample sample)
{ {
sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange);
} }

View File

@ -10,6 +10,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Logging;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -17,7 +18,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Screens.Play;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables namespace osu.Game.Rulesets.Objects.Drawables
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary> /// </summary>
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>(Color4.Gray); public readonly Bindable<Color4> AccentColour = new Bindable<Color4>(Color4.Gray);
protected SkinnableSound Samples { get; private set; } protected PausableSkinnableSound Samples { get; private set; }
public virtual IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples; public virtual IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples;
@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
} }
Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s))); Samples = new PausableSkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
AddInternal(Samples); AddInternal(Samples);
} }
@ -359,9 +359,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
{ {
} }
[Resolved(canBeNull: true)]
private GameplayClock gameplayClock { get; set; }
/// <summary> /// <summary>
/// Calculate the position to be used for sample playback at a specified X position (0..1). /// Calculate the position to be used for sample playback at a specified X position (0..1).
/// </summary> /// </summary>
@ -374,24 +371,24 @@ namespace osu.Game.Rulesets.Objects.Drawables
return balance_adjust_amount * (userPositionalHitSounds.Value ? position - 0.5f : 0); return balance_adjust_amount * (userPositionalHitSounds.Value ? position - 0.5f : 0);
} }
/// <summary>
/// Whether samples should currently be playing. Will be false during seek operations.
/// </summary>
protected bool ShouldPlaySamples => gameplayClock?.IsSeeking != true;
/// <summary> /// <summary>
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>. /// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit. /// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
/// </summary> /// </summary>
public virtual void PlaySamples() public virtual void PlaySamples()
{ {
if (Samples != null && ShouldPlaySamples) if (Samples != null)
{ {
Samples.Balance.Value = CalculateSamplePlaybackBalance(SamplePlaybackPosition); Samples.Balance.Value = CalculateSamplePlaybackBalance(SamplePlaybackPosition);
Samples.Play(); Samples.Play();
} }
} }
/// <summary>
/// Stops playback of all samples. Automatically called when <see cref="DrawableHitObject{TObject}"/>'s lifetime has been exceeded.
/// </summary>
public virtual void StopAllSamples() => Samples?.Stop();
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -460,6 +457,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
foreach (var nested in NestedHitObjects) foreach (var nested in NestedHitObjects)
nested.OnKilled(); nested.OnKilled();
StopAllSamples();
UpdateResult(false); UpdateResult(false);
} }
@ -475,6 +474,30 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (!Result.HasResult) if (!Result.HasResult)
throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}."); throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
// Some (especially older) rulesets use scorable judgements instead of the newer ignorehit/ignoremiss judgements.
// Can be removed 20210328
if (Result.Judgement.MaxResult == HitResult.IgnoreHit)
{
HitResult originalType = Result.Type;
if (Result.Type == HitResult.Miss)
Result.Type = HitResult.IgnoreMiss;
else if (Result.Type >= HitResult.Meh && Result.Type <= HitResult.Perfect)
Result.Type = HitResult.IgnoreHit;
if (Result.Type != originalType)
{
Logger.Log($"{GetType().ReadableName()} applied an invalid hit result ({originalType}) when {nameof(HitResult.IgnoreMiss)} or {nameof(HitResult.IgnoreHit)} is expected.\n"
+ $"This has been automatically adjusted to {Result.Type}, and support will be removed from 2020-03-28 onwards.", level: LogLevel.Important);
}
}
if (!Result.Type.IsValidHitResult(Result.Judgement.MinResult, Result.Judgement.MaxResult))
{
throw new InvalidOperationException(
$"{GetType().ReadableName()} applied an invalid hit result (was: {Result.Type}, expected: [{Result.Judgement.MinResult} ... {Result.Judgement.MaxResult}]).");
}
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller // Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = HitObject.GetEndTime(); var endTime = HitObject.GetEndTime();

View File

@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Scoring
{ {
base.ApplyResultInternal(result); base.ApplyResultInternal(result);
if (!result.Judgement.IsBonus) if (!result.Type.IsBonus())
healthIncreases.Add((result.HitObject.GetEndTime() + result.TimeOffset, GetHealthIncreaseFor(result))); healthIncreases.Add((result.HitObject.GetEndTime() + result.TimeOffset, GetHealthIncreaseFor(result)));
} }

View File

@ -2,15 +2,19 @@
// 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.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using osu.Game.Utils;
namespace osu.Game.Rulesets.Scoring namespace osu.Game.Rulesets.Scoring
{ {
[HasOrderedElements]
public enum HitResult public enum HitResult
{ {
/// <summary> /// <summary>
/// Indicates that the object has not been judged yet. /// Indicates that the object has not been judged yet.
/// </summary> /// </summary>
[Description(@"")] [Description(@"")]
[Order(14)]
None, None,
/// <summary> /// <summary>
@ -21,47 +25,169 @@ namespace osu.Game.Rulesets.Scoring
/// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time). /// "too far in the future). It should also define when a forced miss should be triggered (as a result of no user input in time).
/// </remarks> /// </remarks>
[Description(@"Miss")] [Description(@"Miss")]
[Order(5)]
Miss, Miss,
[Description(@"Meh")] [Description(@"Meh")]
[Order(4)]
Meh, Meh,
/// <summary>
/// Optional judgement.
/// </summary>
[Description(@"OK")] [Description(@"OK")]
[Order(3)]
Ok, Ok,
[Description(@"Good")] [Description(@"Good")]
[Order(2)]
Good, Good,
[Description(@"Great")] [Description(@"Great")]
[Order(1)]
Great, Great,
/// <summary>
/// Optional judgement.
/// </summary>
[Description(@"Perfect")] [Description(@"Perfect")]
[Order(0)]
Perfect, Perfect,
/// <summary> /// <summary>
/// Indicates small tick miss. /// Indicates small tick miss.
/// </summary> /// </summary>
[Order(11)]
SmallTickMiss, SmallTickMiss,
/// <summary> /// <summary>
/// Indicates a small tick hit. /// Indicates a small tick hit.
/// </summary> /// </summary>
[Description(@"S Tick")]
[Order(7)]
SmallTickHit, SmallTickHit,
/// <summary> /// <summary>
/// Indicates a large tick miss. /// Indicates a large tick miss.
/// </summary> /// </summary>
[Order(10)]
LargeTickMiss, LargeTickMiss,
/// <summary> /// <summary>
/// Indicates a large tick hit. /// Indicates a large tick hit.
/// </summary> /// </summary>
LargeTickHit [Description(@"L Tick")]
[Order(6)]
LargeTickHit,
/// <summary>
/// Indicates a small bonus.
/// </summary>
[Description("S Bonus")]
[Order(9)]
SmallBonus,
/// <summary>
/// Indicates a large bonus.
/// </summary>
[Description("L Bonus")]
[Order(8)]
LargeBonus,
/// <summary>
/// Indicates a miss that should be ignored for scoring purposes.
/// </summary>
[Order(13)]
IgnoreMiss,
/// <summary>
/// Indicates a hit that should be ignored for scoring purposes.
/// </summary>
[Order(12)]
IgnoreHit,
}
public static class HitResultExtensions
{
/// <summary>
/// Whether a <see cref="HitResult"/> increases/decreases the combo, and affects the combo portion of the score.
/// </summary>
public static bool AffectsCombo(this HitResult result)
{
switch (result)
{
case HitResult.Miss:
case HitResult.Meh:
case HitResult.Ok:
case HitResult.Good:
case HitResult.Great:
case HitResult.Perfect:
case HitResult.LargeTickHit:
case HitResult.LargeTickMiss:
return true;
default:
return false;
}
}
/// <summary>
/// Whether a <see cref="HitResult"/> affects the accuracy portion of the score.
/// </summary>
public static bool AffectsAccuracy(this HitResult result)
=> IsScorable(result) && !IsBonus(result);
/// <summary>
/// Whether a <see cref="HitResult"/> should be counted as bonus score.
/// </summary>
public static bool IsBonus(this HitResult result)
{
switch (result)
{
case HitResult.SmallBonus:
case HitResult.LargeBonus:
return true;
default:
return false;
}
}
/// <summary>
/// Whether a <see cref="HitResult"/> represents a successful hit.
/// </summary>
public static bool IsHit(this HitResult result)
{
switch (result)
{
case HitResult.None:
case HitResult.IgnoreMiss:
case HitResult.Miss:
case HitResult.SmallTickMiss:
case HitResult.LargeTickMiss:
return false;
default:
return true;
}
}
/// <summary>
/// Whether a <see cref="HitResult"/> is scorable.
/// </summary>
public static bool IsScorable(this HitResult result) => result >= HitResult.Miss && result < HitResult.IgnoreMiss;
/// <summary>
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
/// </summary>
/// <param name="result">The <see cref="HitResult"/> to check.</param>
/// <param name="minResult">The minimum <see cref="HitResult"/>.</param>
/// <param name="maxResult">The maximum <see cref="HitResult"/>.</param>
/// <returns>Whether <see cref="HitResult"/> falls between <paramref name="minResult"/> and <paramref name="maxResult"/>.</returns>
public static bool IsValidHitResult(this HitResult result, HitResult minResult, HitResult maxResult)
{
if (result == HitResult.None)
return false;
if (result == minResult || result == maxResult)
return true;
Debug.Assert(minResult <= maxResult);
return result > minResult && result < maxResult;
}
} }
} }

View File

@ -71,7 +71,6 @@ namespace osu.Game.Rulesets.Scoring
private double maxBaseScore; private double maxBaseScore;
private double rollingMaxBaseScore; private double rollingMaxBaseScore;
private double baseScore; private double baseScore;
private double bonusScore;
private readonly List<HitEvent> hitEvents = new List<HitEvent>(); private readonly List<HitEvent> hitEvents = new List<HitEvent>();
private HitObject lastHitObject; private HitObject lastHitObject;
@ -116,14 +115,15 @@ namespace osu.Game.Rulesets.Scoring
if (result.FailedAtJudgement) if (result.FailedAtJudgement)
return; return;
if (result.Judgement.AffectsCombo) if (!result.Type.IsScorable())
return;
if (result.Type.AffectsCombo())
{ {
switch (result.Type) switch (result.Type)
{ {
case HitResult.None:
break;
case HitResult.Miss: case HitResult.Miss:
case HitResult.LargeTickMiss:
Combo.Value = 0; Combo.Value = 0;
break; break;
@ -133,22 +133,16 @@ namespace osu.Game.Rulesets.Scoring
} }
} }
double scoreIncrease = result.Type == HitResult.Miss ? 0 : result.Judgement.NumericResultFor(result); double scoreIncrease = result.Type.IsHit() ? result.Judgement.NumericResultFor(result) : 0;
if (result.Judgement.IsBonus) if (!result.Type.IsBonus())
{ {
if (result.IsHit)
bonusScore += scoreIncrease;
}
else
{
if (result.HasResult)
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
baseScore += scoreIncrease; baseScore += scoreIncrease;
rollingMaxBaseScore += result.Judgement.MaxNumericResult; rollingMaxBaseScore += result.Judgement.MaxNumericResult;
} }
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
hitEvents.Add(CreateHitEvent(result)); hitEvents.Add(CreateHitEvent(result));
lastHitObject = result.HitObject; lastHitObject = result.HitObject;
@ -171,22 +165,19 @@ namespace osu.Game.Rulesets.Scoring
if (result.FailedAtJudgement) if (result.FailedAtJudgement)
return; return;
double scoreIncrease = result.Type == HitResult.Miss ? 0 : result.Judgement.NumericResultFor(result); if (!result.Type.IsScorable())
return;
if (result.Judgement.IsBonus) double scoreIncrease = result.Type.IsHit() ? result.Judgement.NumericResultFor(result) : 0;
{
if (result.IsHit)
bonusScore -= scoreIncrease;
}
else
{
if (result.HasResult)
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
if (!result.Type.IsBonus())
{
baseScore -= scoreIncrease; baseScore -= scoreIncrease;
rollingMaxBaseScore -= result.Judgement.MaxNumericResult; rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
} }
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) - 1;
Debug.Assert(hitEvents.Count > 0); Debug.Assert(hitEvents.Count > 0);
lastHitObject = hitEvents[^1].LastHitObject; lastHitObject = hitEvents[^1].LastHitObject;
hitEvents.RemoveAt(hitEvents.Count - 1); hitEvents.RemoveAt(hitEvents.Count - 1);
@ -207,7 +198,7 @@ namespace osu.Game.Rulesets.Scoring
return GetScore(mode, maxHighestCombo, return GetScore(mode, maxHighestCombo,
maxBaseScore > 0 ? baseScore / maxBaseScore : 0, maxBaseScore > 0 ? baseScore / maxBaseScore : 0,
maxHighestCombo > 0 ? (double)HighestCombo.Value / maxHighestCombo : 0, maxHighestCombo > 0 ? (double)HighestCombo.Value / maxHighestCombo : 0,
bonusScore); scoreResultCounts);
} }
/// <summary> /// <summary>
@ -217,9 +208,9 @@ namespace osu.Game.Rulesets.Scoring
/// <param name="maxCombo">The maximum combo achievable in the beatmap.</param> /// <param name="maxCombo">The maximum combo achievable in the beatmap.</param>
/// <param name="accuracyRatio">The accuracy percentage achieved by the player.</param> /// <param name="accuracyRatio">The accuracy percentage achieved by the player.</param>
/// <param name="comboRatio">The proportion of <paramref name="maxCombo"/> achieved by the player.</param> /// <param name="comboRatio">The proportion of <paramref name="maxCombo"/> achieved by the player.</param>
/// <param name="bonusScore">Any bonus score to be added.</param> /// <param name="statistics">Any statistics to be factored in.</param>
/// <returns>The total score.</returns> /// <returns>The total score.</returns>
public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, double bonusScore) public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, Dictionary<HitResult, int> statistics)
{ {
switch (mode) switch (mode)
{ {
@ -228,14 +219,18 @@ namespace osu.Game.Rulesets.Scoring
double accuracyScore = accuracyPortion * accuracyRatio; double accuracyScore = accuracyPortion * accuracyRatio;
double comboScore = comboPortion * comboRatio; double comboScore = comboPortion * comboRatio;
return (max_score * (accuracyScore + comboScore) + bonusScore) * scoreMultiplier; return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier;
case ScoringMode.Classic: case ScoringMode.Classic:
// should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1)
return bonusScore + (accuracyRatio * maxCombo * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25); return getBonusScore(statistics) + (accuracyRatio * maxCombo * 300) * (1 + Math.Max(0, (comboRatio * maxCombo) - 1) * scoreMultiplier / 25);
} }
} }
private double getBonusScore(Dictionary<HitResult, int> statistics)
=> statistics.GetOrDefault(HitResult.SmallBonus) * Judgement.SMALL_BONUS_SCORE
+ statistics.GetOrDefault(HitResult.LargeBonus) * Judgement.LARGE_BONUS_SCORE;
private ScoreRank rankFrom(double acc) private ScoreRank rankFrom(double acc)
{ {
if (acc == 1) if (acc == 1)
@ -282,7 +277,6 @@ namespace osu.Game.Rulesets.Scoring
baseScore = 0; baseScore = 0;
rollingMaxBaseScore = 0; rollingMaxBaseScore = 0;
bonusScore = 0;
TotalScore.Value = 0; TotalScore.Value = 0;
Accuracy.Value = 1; Accuracy.Value = 1;
@ -309,9 +303,7 @@ namespace osu.Game.Rulesets.Scoring
score.Rank = Rank.Value; score.Rank = Rank.Value;
score.Date = DateTimeOffset.Now; score.Date = DateTimeOffset.Now;
var hitWindows = CreateHitWindows(); foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r.IsScorable()))
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
score.Statistics[result] = GetStatistic(result); score.Statistics[result] = GetStatistic(result);
score.HitEvents = hitEvents; score.HitEvents = hitEvents;
@ -320,6 +312,7 @@ namespace osu.Game.Rulesets.Scoring
/// <summary> /// <summary>
/// Create a <see cref="HitWindows"/> for this processor. /// Create a <see cref="HitWindows"/> for this processor.
/// </summary> /// </summary>
[Obsolete("Method is now unused.")] // Can be removed 20210328
public virtual HitWindows CreateHitWindows() => new HitWindows(); public virtual HitWindows CreateHitWindows() => new HitWindows();
} }

View File

@ -2,7 +2,10 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
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.Timing; using osu.Framework.Timing;
@ -59,7 +62,7 @@ namespace osu.Game.Rulesets.UI
{ {
if (clock != null) if (clock != null)
{ {
stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock; parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
GameplayClock.IsPaused.BindTo(clock.IsPaused); GameplayClock.IsPaused.BindTo(clock.IsPaused);
} }
} }
@ -215,7 +218,9 @@ namespace osu.Game.Rulesets.UI
private class StabilityGameplayClock : GameplayClock private class StabilityGameplayClock : GameplayClock
{ {
public IFrameBasedClock ParentGameplayClock; public GameplayClock ParentGameplayClock;
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<Bindable<double>>();
public StabilityGameplayClock(FramedClock underlyingClock) public StabilityGameplayClock(FramedClock underlyingClock)
: base(underlyingClock) : base(underlyingClock)

View File

@ -28,37 +28,9 @@ namespace osu.Game.Scoring.Legacy
} }
} }
public static int? GetCount300(this ScoreInfo scoreInfo) public static int? GetCount300(this ScoreInfo scoreInfo) => getCount(scoreInfo, HitResult.Great);
{
switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID)
{
case 0:
case 1:
case 3:
return getCount(scoreInfo, HitResult.Great);
case 2: public static void SetCount300(this ScoreInfo scoreInfo, int value) => scoreInfo.Statistics[HitResult.Great] = value;
return getCount(scoreInfo, HitResult.Perfect);
}
return null;
}
public static void SetCount300(this ScoreInfo scoreInfo, int value)
{
switch (scoreInfo.Ruleset?.ID ?? scoreInfo.RulesetID)
{
case 0:
case 1:
case 3:
scoreInfo.Statistics[HitResult.Great] = value;
break;
case 2:
scoreInfo.Statistics[HitResult.Perfect] = value;
break;
}
}
public static int? GetCountKatu(this ScoreInfo scoreInfo) public static int? GetCountKatu(this ScoreInfo scoreInfo)
{ {
@ -94,8 +66,6 @@ namespace osu.Game.Scoring.Legacy
{ {
case 0: case 0:
case 1: case 1:
return getCount(scoreInfo, HitResult.Good);
case 3: case 3:
return getCount(scoreInfo, HitResult.Ok); return getCount(scoreInfo, HitResult.Ok);
@ -112,9 +82,6 @@ namespace osu.Game.Scoring.Legacy
{ {
case 0: case 0:
case 1: case 1:
scoreInfo.Statistics[HitResult.Good] = value;
break;
case 3: case 3:
scoreInfo.Statistics[HitResult.Ok] = value; scoreInfo.Statistics[HitResult.Ok] = value;
break; break;

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