mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 01:02:56 +08:00
Merge branch 'master' into adjust-hp-increases
This commit is contained in:
commit
1ec96e1bb1
@ -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.930.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -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", () =>
|
||||||
|
@ -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);
|
||||||
|
@ -9,7 +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;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether fruit on the platter should explode or drop.
|
/// Whether fruit on the platter should explode or drop.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
@ -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));
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,7 +103,7 @@ 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
|
||||||
});
|
});
|
||||||
@ -112,10 +112,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
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();
|
||||||
|
@ -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);
|
||||||
@ -217,7 +214,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)
|
||||||
|
@ -5,7 +5,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Judgements;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
@ -14,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()
|
||||||
@ -37,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
displayedCount = count;
|
displayedCount = count;
|
||||||
bonusCounter.Text = $"{Judgement.LARGE_BONUS_SCORE * 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);
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -102,7 +102,7 @@ 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.IgnoreMiss }, 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)]
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,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:
|
||||||
|
@ -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);
|
||||||
|
@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
tick.TriggerResult(false);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, 900_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, 200)]
|
[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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)}" },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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"))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
26
osu.Game/Extensions/EditorDisplayExtensions.cs
Normal file
26
osu.Game/Extensions/EditorDisplayExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Extensions
|
||||||
|
{
|
||||||
|
public static class EditorDisplayExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get an editor formatted string (mm:ss:mss)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="milliseconds">A time value in milliseconds.</param>
|
||||||
|
/// <returns>An editor formatted display string.</returns>
|
||||||
|
public static string ToEditorFormattedString(this double milliseconds) =>
|
||||||
|
ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get an editor formatted string (mm:ss:mss)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeSpan">A time value.</param>
|
||||||
|
/// <returns>An editor formatted display string.</returns>
|
||||||
|
public static string ToEditorFormattedString(this TimeSpan timeSpan) =>
|
||||||
|
$"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}";
|
||||||
|
}
|
||||||
|
}
|
@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The score awarded for a small bonus.
|
/// The score awarded for a small bonus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const double SMALL_BONUS_SCORE = 10;
|
public const int SMALL_BONUS_SCORE = 10;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The score awarded for a large bonus.
|
/// The score awarded for a large bonus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const double LARGE_BONUS_SCORE = 50;
|
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.
|
||||||
@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The numeric score representation for the maximum achievable result.
|
/// The numeric score representation for the maximum achievable result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double MaxNumericResult => ToNumericResult(MaxResult);
|
public int MaxNumericResult => ToNumericResult(MaxResult);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The health increase for the maximum achievable result.
|
/// The health increase for the maximum achievable result.
|
||||||
@ -82,19 +82,19 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
|
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the numeric score representation of a <see cref="JudgementResult"/>.
|
/// Retrieves the numeric score representation of a <see cref="HitResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="result">The <see cref="JudgementResult"/> 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>
|
||||||
[Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be removed 20210328
|
[Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be made non-virtual 20210328
|
||||||
protected virtual int NumericResultFor(HitResult result) => result == HitResult.Miss ? 0 : 1;
|
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 double NumericResultFor(JudgementResult result) => ToNumericResult(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"/>.
|
||||||
@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
|
|
||||||
public override string ToString() => $"MaxResult:{MaxResult} MaxScore:{MaxNumericResult}";
|
public override string ToString() => $"MaxResult:{MaxResult} MaxScore:{MaxNumericResult}";
|
||||||
|
|
||||||
public static double ToNumericResult(HitResult result)
|
public static int ToNumericResult(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
@ -163,25 +163,25 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case HitResult.SmallTickHit:
|
case HitResult.SmallTickHit:
|
||||||
return 1 / 30d;
|
return 10;
|
||||||
|
|
||||||
case HitResult.LargeTickHit:
|
case HitResult.LargeTickHit:
|
||||||
return 1 / 10d;
|
return 30;
|
||||||
|
|
||||||
case HitResult.Meh:
|
case HitResult.Meh:
|
||||||
return 1 / 6d;
|
return 50;
|
||||||
|
|
||||||
case HitResult.Ok:
|
case HitResult.Ok:
|
||||||
return 1 / 3d;
|
return 100;
|
||||||
|
|
||||||
case HitResult.Good:
|
case HitResult.Good:
|
||||||
return 2 / 3d;
|
return 200;
|
||||||
|
|
||||||
case HitResult.Great:
|
case HitResult.Great:
|
||||||
return 1d;
|
return 300;
|
||||||
|
|
||||||
case HitResult.Perfect:
|
case HitResult.Perfect:
|
||||||
return 7 / 6d;
|
return 350;
|
||||||
|
|
||||||
case HitResult.SmallBonus:
|
case HitResult.SmallBonus:
|
||||||
return SMALL_BONUS_SCORE;
|
return SMALL_BONUS_SCORE;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,18 +371,13 @@ 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();
|
||||||
@ -476,12 +468,21 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
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.
|
// Some (especially older) rulesets use scorable judgements instead of the newer ignorehit/ignoremiss judgements.
|
||||||
|
// Can be removed 20210328
|
||||||
if (Result.Judgement.MaxResult == HitResult.IgnoreHit)
|
if (Result.Judgement.MaxResult == HitResult.IgnoreHit)
|
||||||
{
|
{
|
||||||
|
HitResult originalType = Result.Type;
|
||||||
|
|
||||||
if (Result.Type == HitResult.Miss)
|
if (Result.Type == HitResult.Miss)
|
||||||
Result.Type = HitResult.IgnoreMiss;
|
Result.Type = HitResult.IgnoreMiss;
|
||||||
else if (Result.Type >= HitResult.Meh && Result.Type <= HitResult.Perfect)
|
else if (Result.Type >= HitResult.Meh && Result.Type <= HitResult.Perfect)
|
||||||
Result.Type = HitResult.IgnoreHit;
|
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))
|
if (!Result.Type.IsValidHitResult(Result.Judgement.MinResult, Result.Judgement.MaxResult))
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Description(@"")]
|
[Description(@"")]
|
||||||
[Order(14)]
|
[Order(14)]
|
||||||
None = 0,
|
None,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the object has been judged as a miss.
|
/// Indicates that the object has been judged as a miss.
|
||||||
@ -26,15 +26,12 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Description(@"Miss")]
|
[Description(@"Miss")]
|
||||||
[Order(5)]
|
[Order(5)]
|
||||||
Miss = 64,
|
Miss,
|
||||||
|
|
||||||
[Description(@"Meh")]
|
[Description(@"Meh")]
|
||||||
[Order(4)]
|
[Order(4)]
|
||||||
Meh,
|
Meh,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Optional judgement.
|
|
||||||
/// </summary>
|
|
||||||
[Description(@"OK")]
|
[Description(@"OK")]
|
||||||
[Order(3)]
|
[Order(3)]
|
||||||
Ok,
|
Ok,
|
||||||
@ -47,9 +44,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
[Order(1)]
|
[Order(1)]
|
||||||
Great,
|
Great,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Optional judgement.
|
|
||||||
/// </summary>
|
|
||||||
[Description(@"Perfect")]
|
[Description(@"Perfect")]
|
||||||
[Order(0)]
|
[Order(0)]
|
||||||
Perfect,
|
Perfect,
|
||||||
@ -58,7 +52,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// Indicates small tick miss.
|
/// Indicates small tick miss.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Order(11)]
|
[Order(11)]
|
||||||
SmallTickMiss = 128,
|
SmallTickMiss,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates a small tick hit.
|
/// Indicates a small tick hit.
|
||||||
@ -71,7 +65,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// Indicates a large tick miss.
|
/// Indicates a large tick miss.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Order(10)]
|
[Order(10)]
|
||||||
LargeTickMiss = 192,
|
LargeTickMiss,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates a large tick hit.
|
/// Indicates a large tick hit.
|
||||||
@ -85,20 +79,20 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("S Bonus")]
|
[Description("S Bonus")]
|
||||||
[Order(9)]
|
[Order(9)]
|
||||||
SmallBonus = 254,
|
SmallBonus,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates a large bonus.
|
/// Indicates a large bonus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("L Bonus")]
|
[Description("L Bonus")]
|
||||||
[Order(8)]
|
[Order(8)]
|
||||||
LargeBonus = 320,
|
LargeBonus,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates a miss that should be ignored for scoring purposes.
|
/// Indicates a miss that should be ignored for scoring purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Order(13)]
|
[Order(13)]
|
||||||
IgnoreMiss = 384,
|
IgnoreMiss,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates a hit that should be ignored for scoring purposes.
|
/// Indicates a hit that should be ignored for scoring purposes.
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using System;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components
|
namespace osu.Game.Screens.Edit.Components
|
||||||
@ -35,9 +35,7 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
trackTimer.Text = editorClock.CurrentTime.ToEditorFormattedString();
|
||||||
var timespan = TimeSpan.FromMilliseconds(editorClock.CurrentTime);
|
|
||||||
trackTimer.Text = $"{(timespan < TimeSpan.Zero ? "-" : string.Empty)}{timespan:mm\\:ss\\:fff}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,70 +1,50 @@
|
|||||||
// 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.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The part of the timeline that displays the control points.
|
/// The part of the timeline that displays the control points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ControlPointPart : TimelinePart
|
public class ControlPointPart : TimelinePart<GroupVisualisation>
|
||||||
{
|
{
|
||||||
|
private IBindableList<ControlPointGroup> controlPointGroups;
|
||||||
|
|
||||||
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.LoadBeatmap(beatmap);
|
base.LoadBeatmap(beatmap);
|
||||||
|
|
||||||
ControlPointInfo cpi = beatmap.Beatmap.ControlPointInfo;
|
controlPointGroups = beatmap.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
||||||
|
controlPointGroups.BindCollectionChanged((sender, args) =>
|
||||||
|
{
|
||||||
|
switch (args.Action)
|
||||||
|
{
|
||||||
|
case NotifyCollectionChangedAction.Reset:
|
||||||
|
Clear();
|
||||||
|
break;
|
||||||
|
|
||||||
cpi.TimingPoints.ForEach(addTimingPoint);
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
foreach (var group in args.NewItems.OfType<ControlPointGroup>())
|
||||||
|
Add(new GroupVisualisation(group));
|
||||||
|
break;
|
||||||
|
|
||||||
// Consider all non-timing points as the same type
|
case NotifyCollectionChangedAction.Remove:
|
||||||
cpi.SamplePoints.Select(c => (ControlPoint)c)
|
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
|
||||||
.Concat(cpi.EffectPoints)
|
{
|
||||||
.Concat(cpi.DifficultyPoints)
|
var matching = Children.SingleOrDefault(gv => gv.Group == group);
|
||||||
.Distinct()
|
|
||||||
// Non-timing points should not be added where there are timing points
|
matching?.Expire();
|
||||||
.Where(c => cpi.TimingPointAt(c.Time).Time != c.Time)
|
|
||||||
.ForEach(addNonTimingPoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTimingPoint(ControlPoint controlPoint) => Add(new TimingPointVisualisation(controlPoint));
|
break;
|
||||||
private void addNonTimingPoint(ControlPoint controlPoint) => Add(new NonTimingPointVisualisation(controlPoint));
|
|
||||||
|
|
||||||
private class TimingPointVisualisation : ControlPointVisualisation
|
|
||||||
{
|
|
||||||
public TimingPointVisualisation(ControlPoint controlPoint)
|
|
||||||
: base(controlPoint)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours) => Colour = colours.YellowDark;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NonTimingPointVisualisation : ControlPointVisualisation
|
|
||||||
{
|
|
||||||
public NonTimingPointVisualisation(ControlPoint controlPoint)
|
|
||||||
: base(controlPoint)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours) => Colour = colours.Green;
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class ControlPointVisualisation : PointVisualisation
|
|
||||||
{
|
|
||||||
protected ControlPointVisualisation(ControlPoint controlPoint)
|
|
||||||
: base(controlPoint.Time)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||||
|
{
|
||||||
|
public class GroupVisualisation : PointVisualisation
|
||||||
|
{
|
||||||
|
public readonly ControlPointGroup Group;
|
||||||
|
|
||||||
|
private BindableList<ControlPoint> controlPoints;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
public GroupVisualisation(ControlPointGroup group)
|
||||||
|
: base(group.Time)
|
||||||
|
{
|
||||||
|
Group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
controlPoints = (BindableList<ControlPoint>)Group.ControlPoints.GetBoundCopy();
|
||||||
|
controlPoints.BindCollectionChanged((_, __) =>
|
||||||
|
{
|
||||||
|
if (controlPoints.Count == 0)
|
||||||
|
{
|
||||||
|
Colour = Color4.Transparent;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Colour = controlPoints.Any(c => c is TimingControlPoint) ? colours.YellowDark : colours.Green;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,9 +14,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
public class TimelineArea : Container
|
public class TimelineArea : Container
|
||||||
{
|
{
|
||||||
private readonly Timeline timeline = new Timeline { RelativeSizeAxes = Axes.Both };
|
public readonly Timeline Timeline = new Timeline { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
protected override Container<Drawable> Content => timeline;
|
protected override Container<Drawable> Content => Timeline;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -107,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timeline
|
Timeline
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ColumnDimensions = new[]
|
ColumnDimensions = new[]
|
||||||
@ -121,9 +121,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
waveformCheckbox.Current.Value = true;
|
waveformCheckbox.Current.Value = true;
|
||||||
|
|
||||||
timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
|
Timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeZoom(float change) => timeline.Zoom += change;
|
private void changeZoom(float change) => Timeline.Zoom += change;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,23 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -34,11 +39,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
private readonly List<Container> shadowComponents = new List<Container>();
|
private readonly List<Container> shadowComponents = new List<Container>();
|
||||||
|
|
||||||
|
private DrawableHitObject drawableHitObject;
|
||||||
|
|
||||||
|
private Bindable<Color4> comboColour;
|
||||||
|
|
||||||
|
private readonly Container mainComponents;
|
||||||
|
|
||||||
|
private readonly OsuSpriteText comboIndexText;
|
||||||
|
|
||||||
|
private Bindable<int> comboIndex;
|
||||||
|
|
||||||
private const float thickness = 5;
|
private const float thickness = 5;
|
||||||
|
|
||||||
private const float shadow_radius = 5;
|
private const float shadow_radius = 5;
|
||||||
|
|
||||||
private const float circle_size = 16;
|
private const float circle_size = 24;
|
||||||
|
|
||||||
public TimelineHitObjectBlueprint(HitObject hitObject)
|
public TimelineHitObjectBlueprint(HitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
@ -54,14 +69,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
mainComponents = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
},
|
||||||
|
comboIndexText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.Numeric.With(size: circle_size / 2, weight: FontWeight.Black),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
circle = new Circle
|
circle = new Circle
|
||||||
{
|
{
|
||||||
Size = new Vector2(circle_size),
|
Size = new Vector2(circle_size),
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativePositionAxes = Axes.X,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Colour = Color4.White,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Shadow,
|
Type = EdgeEffectType.Shadow,
|
||||||
@ -77,7 +106,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
DragBar dragBarUnderlay;
|
DragBar dragBarUnderlay;
|
||||||
Container extensionBar;
|
Container extensionBar;
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
mainComponents.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
extensionBar = new Container
|
extensionBar = new Container
|
||||||
{
|
{
|
||||||
@ -117,12 +146,54 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddInternal(circle);
|
mainComponents.Add(circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateShadows();
|
updateShadows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(HitObjectComposer composer)
|
||||||
|
{
|
||||||
|
if (composer != null)
|
||||||
|
{
|
||||||
|
// best effort to get the drawable representation for grabbing colour and what not.
|
||||||
|
drawableHitObject = composer.HitObjects.FirstOrDefault(d => d.HitObject == HitObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (HitObject is IHasComboInformation comboInfo)
|
||||||
|
{
|
||||||
|
comboIndex = comboInfo.IndexInCurrentComboBindable.GetBoundCopy();
|
||||||
|
comboIndex.BindValueChanged(combo =>
|
||||||
|
{
|
||||||
|
comboIndexText.Text = (combo.NewValue + 1).ToString();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawableHitObject != null)
|
||||||
|
{
|
||||||
|
comboColour = drawableHitObject.AccentColour.GetBoundCopy();
|
||||||
|
comboColour.BindValueChanged(colour =>
|
||||||
|
{
|
||||||
|
if (HitObject is IHasDuration)
|
||||||
|
mainComponents.Colour = ColourInfo.GradientHorizontal(drawableHitObject.AccentColour.Value, Color4.White);
|
||||||
|
else
|
||||||
|
mainComponents.Colour = drawableHitObject.AccentColour.Value;
|
||||||
|
|
||||||
|
var col = mainComponents.Colour.TopLeft.Linear;
|
||||||
|
float brightness = col.R + col.G + col.B;
|
||||||
|
|
||||||
|
// decide the combo index colour based on brightness?
|
||||||
|
comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using 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.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -18,11 +22,23 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateMainContent()
|
private Ruleset ruleset;
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
{
|
{
|
||||||
var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance();
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
|
ruleset = parent.Get<IBindable<WorkingBeatmap>>().Value.BeatmapInfo.Ruleset?.CreateInstance();
|
||||||
composer = ruleset?.CreateHitObjectComposer();
|
composer = ruleset?.CreateHitObjectComposer();
|
||||||
|
|
||||||
|
// make the composer available to the timeline and other components in this screen.
|
||||||
|
dependencies.CacheAs(composer);
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateMainContent()
|
||||||
|
{
|
||||||
if (ruleset == null || composer == null)
|
if (ruleset == null || composer == null)
|
||||||
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
|
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
UpdateClockSource();
|
UpdateClockSource();
|
||||||
|
|
||||||
dependencies.CacheAs(clock);
|
dependencies.CacheAs(clock);
|
||||||
|
dependencies.CacheAs<ISamplePlaybackDisabler>(clock);
|
||||||
AddInternal(clock);
|
AddInternal(clock);
|
||||||
|
|
||||||
// todo: remove caching of this and consume via editorBeatmap?
|
// todo: remove caching of this and consume via editorBeatmap?
|
||||||
|
@ -7,17 +7,18 @@ using osu.Framework.Audio.Track;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Transforms;
|
using osu.Framework.Graphics.Transforms;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
|
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock, ISamplePlaybackDisabler
|
||||||
{
|
{
|
||||||
public IBindable<Track> Track => track;
|
public IBindable<Track> Track => track;
|
||||||
|
|
||||||
@ -31,6 +32,10 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private readonly DecoupleableInterpolatingFramedClock underlyingClock;
|
private readonly DecoupleableInterpolatingFramedClock underlyingClock;
|
||||||
|
|
||||||
|
public IBindable<bool> SamplePlaybackDisabled => samplePlaybackDisabled;
|
||||||
|
|
||||||
|
private readonly Bindable<bool> samplePlaybackDisabled = new Bindable<bool>();
|
||||||
|
|
||||||
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||||
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
|
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
|
||||||
{
|
{
|
||||||
@ -166,11 +171,14 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
|
samplePlaybackDisabled.Value = true;
|
||||||
underlyingClock.Stop();
|
underlyingClock.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Seek(double position)
|
public bool Seek(double position)
|
||||||
{
|
{
|
||||||
|
samplePlaybackDisabled.Value = true;
|
||||||
|
|
||||||
ClearTransforms();
|
ClearTransforms();
|
||||||
return underlyingClock.Seek(position);
|
return underlyingClock.Seek(position);
|
||||||
}
|
}
|
||||||
@ -211,8 +219,35 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private const double transform_time = 300;
|
private const double transform_time = 300;
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
updateSeekingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSeekingState()
|
||||||
|
{
|
||||||
|
if (samplePlaybackDisabled.Value)
|
||||||
|
{
|
||||||
|
if (track.Value?.IsRunning != true)
|
||||||
|
{
|
||||||
|
// seeking in the editor can happen while the track isn't running.
|
||||||
|
// in this case we always want to expose ourselves as seeking (to avoid sample playback).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we are either running a seek tween or doing an immediate seek.
|
||||||
|
// in the case of an immediate seek the seeking bool will be set to false after one update.
|
||||||
|
// this allows for silencing hit sounds and the likes.
|
||||||
|
samplePlaybackDisabled.Value = Transforms.Any();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SeekTo(double seekDestination)
|
public void SeekTo(double seekDestination)
|
||||||
{
|
{
|
||||||
|
samplePlaybackDisabled.Value = true;
|
||||||
|
|
||||||
if (IsRunning)
|
if (IsRunning)
|
||||||
Seek(seekDestination);
|
Seek(seekDestination);
|
||||||
else
|
else
|
||||||
|
@ -115,8 +115,16 @@ namespace osu.Game.Screens.Edit
|
|||||||
new TimelineTickDisplay(),
|
new TimelineTickDisplay(),
|
||||||
CreateTimelineContent(),
|
CreateTimelineContent(),
|
||||||
}
|
}
|
||||||
}, timelineContainer.Add);
|
}, t =>
|
||||||
|
{
|
||||||
|
timelineContainer.Add(t);
|
||||||
|
OnTimelineLoaded(t);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnTimelineLoaded(TimelineArea timelineArea)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Drawable CreateMainContent();
|
protected abstract Drawable CreateMainContent();
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -89,7 +90,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = $"{group.Time:n0}ms",
|
Text = group.Time.ToEditorFormattedString(),
|
||||||
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
|
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
|
||||||
},
|
},
|
||||||
new ControlGroupAttributes(group),
|
new ControlGroupAttributes(group),
|
||||||
|
@ -12,6 +12,8 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Timing
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
@ -29,6 +31,11 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateTimelineContent() => new ControlPointPart
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
|
||||||
protected override Drawable CreateMainContent() => new GridContainer
|
protected override Drawable CreateMainContent() => new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -58,6 +65,12 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnTimelineLoaded(TimelineArea timelineArea)
|
||||||
|
{
|
||||||
|
base.OnTimelineLoaded(timelineArea);
|
||||||
|
timelineArea.Timeline.Zoom = timelineArea.Timeline.MinZoom;
|
||||||
|
}
|
||||||
|
|
||||||
public class ControlPointList : CompositeDrawable
|
public class ControlPointList : CompositeDrawable
|
||||||
{
|
{
|
||||||
private OsuButton deleteButton;
|
private OsuButton deleteButton;
|
||||||
@ -129,11 +142,12 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true);
|
selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true);
|
||||||
|
|
||||||
controlGroups = Beatmap.Value.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
controlGroups = Beatmap.Value.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
||||||
controlGroups.CollectionChanged += (sender, args) => createContent();
|
|
||||||
createContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createContent() => table.ControlGroups = controlGroups;
|
controlGroups.BindCollectionChanged((sender, args) =>
|
||||||
|
{
|
||||||
|
table.ControlGroups = controlGroups;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void delete()
|
private void delete()
|
||||||
{
|
{
|
||||||
|
@ -103,12 +103,17 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
private const double sane_maximum = 240;
|
private const double sane_maximum = 240;
|
||||||
|
|
||||||
private readonly BindableNumber<double> beatLengthBindable = new TimingControlPoint().BeatLengthBindable;
|
private readonly BindableNumber<double> beatLengthBindable = new TimingControlPoint().BeatLengthBindable;
|
||||||
private readonly BindableDouble bpmBindable = new BindableDouble();
|
|
||||||
|
private readonly BindableDouble bpmBindable = new BindableDouble(60000 / TimingControlPoint.DEFAULT_BEAT_LENGTH)
|
||||||
|
{
|
||||||
|
MinValue = sane_minimum,
|
||||||
|
MaxValue = sane_maximum,
|
||||||
|
};
|
||||||
|
|
||||||
public BPMSlider()
|
public BPMSlider()
|
||||||
{
|
{
|
||||||
beatLengthBindable.BindValueChanged(beatLength => updateCurrent(beatLengthToBpm(beatLength.NewValue)), true);
|
beatLengthBindable.BindValueChanged(beatLength => updateCurrent(beatLengthToBpm(beatLength.NewValue)), true);
|
||||||
bpmBindable.BindValueChanged(bpm => bpmBindable.Default = beatLengthBindable.Value = beatLengthToBpm(bpm.NewValue));
|
bpmBindable.BindValueChanged(bpm => beatLengthBindable.Value = beatLengthToBpm(bpm.NewValue));
|
||||||
|
|
||||||
base.Bindable = bpmBindable;
|
base.Bindable = bpmBindable;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
|
||||||
@ -14,12 +16,17 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <see cref="IFrameBasedClock"/>, as this should only be done once to ensure accuracy.
|
/// <see cref="IFrameBasedClock"/>, as this should only be done once to ensure accuracy.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GameplayClock : IFrameBasedClock
|
public class GameplayClock : IFrameBasedClock, ISamplePlaybackDisabler
|
||||||
{
|
{
|
||||||
private readonly IFrameBasedClock underlyingClock;
|
private readonly IFrameBasedClock underlyingClock;
|
||||||
|
|
||||||
public readonly BindableBool IsPaused = new BindableBool();
|
public readonly BindableBool IsPaused = new BindableBool();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All adjustments applied to this clock which don't come from gameplay or mods.
|
||||||
|
/// </summary>
|
||||||
|
public virtual IEnumerable<Bindable<double>> NonGameplayAdjustments => Enumerable.Empty<Bindable<double>>();
|
||||||
|
|
||||||
public GameplayClock(IFrameBasedClock underlyingClock)
|
public GameplayClock(IFrameBasedClock underlyingClock)
|
||||||
{
|
{
|
||||||
this.underlyingClock = underlyingClock;
|
this.underlyingClock = underlyingClock;
|
||||||
@ -29,6 +36,23 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public double Rate => underlyingClock.Rate;
|
public double Rate => underlyingClock.Rate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rate of gameplay when playback is at 100%.
|
||||||
|
/// This excludes any seeking / user adjustments.
|
||||||
|
/// </summary>
|
||||||
|
public double TrueGameplayRate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
double baseRate = Rate;
|
||||||
|
|
||||||
|
foreach (var adjustment in NonGameplayAdjustments)
|
||||||
|
baseRate /= adjustment.Value;
|
||||||
|
|
||||||
|
return baseRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsRunning => underlyingClock.IsRunning;
|
public bool IsRunning => underlyingClock.IsRunning;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -48,5 +72,7 @@ namespace osu.Game.Screens.Play
|
|||||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||||
|
|
||||||
public IClock Source => underlyingClock;
|
public IClock Source => underlyingClock;
|
||||||
|
|
||||||
|
public IBindable<bool> SamplePlaybackDisabled => IsPaused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -50,8 +51,11 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The final clock which is exposed to underlying components.
|
/// The final clock which is exposed to underlying components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Cached]
|
public GameplayClock GameplayClock => localGameplayClock;
|
||||||
public readonly GameplayClock GameplayClock;
|
|
||||||
|
[Cached(typeof(GameplayClock))]
|
||||||
|
[Cached(typeof(ISamplePlaybackDisabler))]
|
||||||
|
private readonly LocalGameplayClock localGameplayClock;
|
||||||
|
|
||||||
private Bindable<double> userAudioOffset;
|
private Bindable<double> userAudioOffset;
|
||||||
|
|
||||||
@ -79,7 +83,7 @@ namespace osu.Game.Screens.Play
|
|||||||
userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock);
|
userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock);
|
||||||
|
|
||||||
// the clock to be exposed via DI to children.
|
// the clock to be exposed via DI to children.
|
||||||
GameplayClock = new GameplayClock(userOffsetClock);
|
localGameplayClock = new LocalGameplayClock(userOffsetClock);
|
||||||
|
|
||||||
GameplayClock.IsPaused.BindTo(IsPaused);
|
GameplayClock.IsPaused.BindTo(IsPaused);
|
||||||
}
|
}
|
||||||
@ -200,7 +204,9 @@ namespace osu.Game.Screens.Play
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
if (!IsPaused.Value)
|
if (!IsPaused.Value)
|
||||||
|
{
|
||||||
userOffsetClock.ProcessFrame();
|
userOffsetClock.ProcessFrame();
|
||||||
|
}
|
||||||
|
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
||||||
@ -215,6 +221,9 @@ namespace osu.Game.Screens.Play
|
|||||||
track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||||
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||||
|
|
||||||
|
localGameplayClock.MutableNonGameplayAdjustments.Add(pauseFreqAdjust);
|
||||||
|
localGameplayClock.MutableNonGameplayAdjustments.Add(UserPlaybackRate);
|
||||||
|
|
||||||
speedAdjustmentsApplied = true;
|
speedAdjustmentsApplied = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,9 +240,24 @@ namespace osu.Game.Screens.Play
|
|||||||
track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||||
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||||
|
|
||||||
|
localGameplayClock.MutableNonGameplayAdjustments.Remove(pauseFreqAdjust);
|
||||||
|
localGameplayClock.MutableNonGameplayAdjustments.Remove(UserPlaybackRate);
|
||||||
|
|
||||||
speedAdjustmentsApplied = false;
|
speedAdjustmentsApplied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class LocalGameplayClock : GameplayClock
|
||||||
|
{
|
||||||
|
public readonly List<Bindable<double>> MutableNonGameplayAdjustments = new List<Bindable<double>>();
|
||||||
|
|
||||||
|
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => MutableNonGameplayAdjustments;
|
||||||
|
|
||||||
|
public LocalGameplayClock(FramedOffsetClock underlyingClock)
|
||||||
|
: base(underlyingClock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class HardwareCorrectionOffsetClock : FramedOffsetClock
|
private class HardwareCorrectionOffsetClock : FramedOffsetClock
|
||||||
{
|
{
|
||||||
// we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this.
|
// we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this.
|
||||||
|
20
osu.Game/Screens/Play/ISamplePlaybackDisabler.cs
Normal file
20
osu.Game/Screens/Play/ISamplePlaybackDisabler.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Allows a component to disable sample playback dynamically as required.
|
||||||
|
/// Handled by <see cref="PausableSkinnableSound"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface ISamplePlaybackDisabler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether sample playback should be disabled (or paused for looping samples).
|
||||||
|
/// </summary>
|
||||||
|
IBindable<bool> SamplePlaybackDisabled { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -191,9 +191,25 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
dependencies.CacheAs(gameplayBeatmap);
|
dependencies.CacheAs(gameplayBeatmap);
|
||||||
|
|
||||||
addUnderlayComponents(GameplayClockContainer);
|
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
|
||||||
addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap);
|
|
||||||
addOverlayComponents(GameplayClockContainer, Beatmap.Value);
|
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
|
||||||
|
// full access to all skin sources.
|
||||||
|
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap));
|
||||||
|
|
||||||
|
// load the skinning hierarchy first.
|
||||||
|
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
|
||||||
|
GameplayClockContainer.Add(beatmapSkinProvider.WithChild(rulesetSkinProvider));
|
||||||
|
|
||||||
|
rulesetSkinProvider.AddRange(new[]
|
||||||
|
{
|
||||||
|
// underlay and gameplay should have access the to skinning sources.
|
||||||
|
createUnderlayComponents(),
|
||||||
|
createGameplayComponents(Beatmap.Value, playableBeatmap)
|
||||||
|
});
|
||||||
|
|
||||||
|
// add the overlay components as a separate step as they proxy some elements from the above underlay/gameplay components.
|
||||||
|
GameplayClockContainer.Add(createOverlayComponents(Beatmap.Value));
|
||||||
|
|
||||||
if (!DrawableRuleset.AllowGameplayOverlays)
|
if (!DrawableRuleset.AllowGameplayOverlays)
|
||||||
{
|
{
|
||||||
@ -238,32 +254,15 @@ namespace osu.Game.Screens.Play
|
|||||||
breakTracker.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
|
breakTracker.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addUnderlayComponents(Container target)
|
private Drawable createUnderlayComponents() =>
|
||||||
|
DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
private Drawable createGameplayComponents(WorkingBeatmap working, IBeatmap playableBeatmap) => new ScalingContainer(ScalingMode.Gameplay)
|
||||||
{
|
{
|
||||||
target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both });
|
Children = new Drawable[]
|
||||||
}
|
|
||||||
|
|
||||||
private void addGameplayComponents(Container target, WorkingBeatmap working, IBeatmap playableBeatmap)
|
|
||||||
{
|
{
|
||||||
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(working.Skin);
|
DrawableRuleset.With(r =>
|
||||||
|
r.FrameStableComponents.Children = new Drawable[]
|
||||||
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
|
|
||||||
// full access to all skin sources.
|
|
||||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap));
|
|
||||||
|
|
||||||
// load the skinning hierarchy first.
|
|
||||||
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
|
|
||||||
target.Add(new ScalingContainer(ScalingMode.Gameplay)
|
|
||||||
.WithChild(beatmapSkinProvider
|
|
||||||
.WithChild(target = rulesetSkinProvider)));
|
|
||||||
|
|
||||||
target.AddRange(new Drawable[]
|
|
||||||
{
|
|
||||||
DrawableRuleset,
|
|
||||||
new ComboEffects(ScoreProcessor)
|
|
||||||
});
|
|
||||||
|
|
||||||
DrawableRuleset.FrameStableComponents.AddRange(new Drawable[]
|
|
||||||
{
|
{
|
||||||
ScoreProcessor,
|
ScoreProcessor,
|
||||||
HealthProcessor,
|
HealthProcessor,
|
||||||
@ -271,12 +270,15 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
Breaks = working.Beatmap.Breaks
|
Breaks = working.Beatmap.Breaks
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
|
new ComboEffects(ScoreProcessor)
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private void addOverlayComponents(Container target, WorkingBeatmap working)
|
private Drawable createOverlayComponents(WorkingBeatmap working) => new Container
|
||||||
{
|
{
|
||||||
target.AddRange(new[]
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
{
|
{
|
||||||
DimmableStoryboard.OverlayLayerContainer.CreateProxy(),
|
DimmableStoryboard.OverlayLayerContainer.CreateProxy(),
|
||||||
BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
||||||
@ -342,8 +344,8 @@ namespace osu.Game.Screens.Play
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private void onBreakTimeChanged(ValueChangedEvent<bool> isBreakTime)
|
private void onBreakTimeChanged(ValueChangedEvent<bool> isBreakTime)
|
||||||
{
|
{
|
||||||
|
@ -336,7 +336,7 @@ namespace osu.Game.Skinning
|
|||||||
case HitResult.Meh:
|
case HitResult.Meh:
|
||||||
return this.GetAnimation("hit50", true, false);
|
return this.GetAnimation("hit50", true, false);
|
||||||
|
|
||||||
case HitResult.Good:
|
case HitResult.Ok:
|
||||||
return this.GetAnimation("hit100", true, false);
|
return this.GetAnimation("hit100", true, false);
|
||||||
|
|
||||||
case HitResult.Great:
|
case HitResult.Great:
|
||||||
|
66
osu.Game/Skinning/PausableSkinnableSound.cs
Normal file
66
osu.Game/Skinning/PausableSkinnableSound.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class PausableSkinnableSound : SkinnableSound
|
||||||
|
{
|
||||||
|
protected bool RequestedPlaying { get; private set; }
|
||||||
|
|
||||||
|
public PausableSkinnableSound(ISampleInfo hitSamples)
|
||||||
|
: base(hitSamples)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PausableSkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
||||||
|
: base(hitSamples)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IBindable<bool> samplePlaybackDisabled = new Bindable<bool>();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(ISamplePlaybackDisabler samplePlaybackDisabler)
|
||||||
|
{
|
||||||
|
// if in a gameplay context, pause sample playback when gameplay is paused.
|
||||||
|
if (samplePlaybackDisabler != null)
|
||||||
|
{
|
||||||
|
samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
|
||||||
|
samplePlaybackDisabled.BindValueChanged(disabled =>
|
||||||
|
{
|
||||||
|
if (RequestedPlaying)
|
||||||
|
{
|
||||||
|
if (disabled.NewValue)
|
||||||
|
base.Stop();
|
||||||
|
// it's not easy to know if a sample has finished playing (to end).
|
||||||
|
// to keep things simple only resume playing looping samples.
|
||||||
|
else if (Looping)
|
||||||
|
base.Play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Play()
|
||||||
|
{
|
||||||
|
RequestedPlaying = true;
|
||||||
|
|
||||||
|
if (samplePlaybackDisabled.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
base.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Stop()
|
||||||
|
{
|
||||||
|
RequestedPlaying = false;
|
||||||
|
base.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,6 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
|||||||
using osu.Framework.Graphics.Audio;
|
using osu.Framework.Graphics.Audio;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Screens.Play;
|
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
@ -22,8 +21,6 @@ namespace osu.Game.Skinning
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private ISampleStore samples { get; set; }
|
private ISampleStore samples { get; set; }
|
||||||
|
|
||||||
private bool requestedPlaying;
|
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
public override bool RemoveCompletedTransforms => false;
|
public override bool RemoveCompletedTransforms => false;
|
||||||
|
|
||||||
@ -37,7 +34,7 @@ namespace osu.Game.Skinning
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected bool PlayWhenZeroVolume => Looping;
|
protected bool PlayWhenZeroVolume => Looping;
|
||||||
|
|
||||||
private readonly AudioContainer<DrawableSample> samplesContainer;
|
protected readonly AudioContainer<DrawableSample> SamplesContainer;
|
||||||
|
|
||||||
public SkinnableSound(ISampleInfo hitSamples)
|
public SkinnableSound(ISampleInfo hitSamples)
|
||||||
: this(new[] { hitSamples })
|
: this(new[] { hitSamples })
|
||||||
@ -47,28 +44,7 @@ namespace osu.Game.Skinning
|
|||||||
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
||||||
{
|
{
|
||||||
this.hitSamples = hitSamples.ToArray();
|
this.hitSamples = hitSamples.ToArray();
|
||||||
InternalChild = samplesContainer = new AudioContainer<DrawableSample>();
|
InternalChild = SamplesContainer = new AudioContainer<DrawableSample>();
|
||||||
}
|
|
||||||
|
|
||||||
private Bindable<bool> gameplayClockPaused;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
|
||||||
private void load(GameplayClock gameplayClock)
|
|
||||||
{
|
|
||||||
// if in a gameplay context, pause sample playback when gameplay is paused.
|
|
||||||
gameplayClockPaused = gameplayClock?.IsPaused.GetBoundCopy();
|
|
||||||
gameplayClockPaused?.BindValueChanged(paused =>
|
|
||||||
{
|
|
||||||
if (requestedPlaying)
|
|
||||||
{
|
|
||||||
if (paused.NewValue)
|
|
||||||
stop();
|
|
||||||
// it's not easy to know if a sample has finished playing (to end).
|
|
||||||
// to keep things simple only resume playing looping samples.
|
|
||||||
else if (Looping)
|
|
||||||
play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool looping;
|
private bool looping;
|
||||||
@ -82,34 +58,22 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
looping = value;
|
looping = value;
|
||||||
|
|
||||||
samplesContainer.ForEach(c => c.Looping = looping);
|
SamplesContainer.ForEach(c => c.Looping = looping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Play()
|
public virtual void Play()
|
||||||
{
|
{
|
||||||
requestedPlaying = true;
|
SamplesContainer.ForEach(c =>
|
||||||
play();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void play()
|
|
||||||
{
|
|
||||||
samplesContainer.ForEach(c =>
|
|
||||||
{
|
{
|
||||||
if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0)
|
if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0)
|
||||||
c.Play();
|
c.Play();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public virtual void Stop()
|
||||||
{
|
{
|
||||||
requestedPlaying = false;
|
SamplesContainer.ForEach(c => c.Stop());
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stop()
|
|
||||||
{
|
|
||||||
samplesContainer.ForEach(c => c.Stop());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
@ -138,33 +102,33 @@ namespace osu.Game.Skinning
|
|||||||
return ch;
|
return ch;
|
||||||
}).Where(c => c != null);
|
}).Where(c => c != null);
|
||||||
|
|
||||||
samplesContainer.ChildrenEnumerable = channels.Select(c => new DrawableSample(c));
|
SamplesContainer.ChildrenEnumerable = channels.Select(c => new DrawableSample(c));
|
||||||
|
|
||||||
// Start playback internally for the new samples if the previous ones were playing beforehand.
|
// Start playback internally for the new samples if the previous ones were playing beforehand.
|
||||||
if (wasPlaying)
|
if (wasPlaying)
|
||||||
play();
|
Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Re-expose AudioContainer
|
#region Re-expose AudioContainer
|
||||||
|
|
||||||
public BindableNumber<double> Volume => samplesContainer.Volume;
|
public BindableNumber<double> Volume => SamplesContainer.Volume;
|
||||||
|
|
||||||
public BindableNumber<double> Balance => samplesContainer.Balance;
|
public BindableNumber<double> Balance => SamplesContainer.Balance;
|
||||||
|
|
||||||
public BindableNumber<double> Frequency => samplesContainer.Frequency;
|
public BindableNumber<double> Frequency => SamplesContainer.Frequency;
|
||||||
|
|
||||||
public BindableNumber<double> Tempo => samplesContainer.Tempo;
|
public BindableNumber<double> Tempo => SamplesContainer.Tempo;
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
||||||
=> samplesContainer.AddAdjustment(type, adjustBindable);
|
=> SamplesContainer.AddAdjustment(type, adjustBindable);
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
||||||
=> samplesContainer.RemoveAdjustment(type, adjustBindable);
|
=> SamplesContainer.RemoveAdjustment(type, adjustBindable);
|
||||||
|
|
||||||
public void RemoveAllAdjustments(AdjustableProperty type)
|
public void RemoveAllAdjustments(AdjustableProperty type)
|
||||||
=> samplesContainer.RemoveAllAdjustments(type);
|
=> SamplesContainer.RemoveAllAdjustments(type);
|
||||||
|
|
||||||
public bool IsPlaying => samplesContainer.Any(s => s.Playing);
|
public bool IsPlaying => SamplesContainer.Any(s => s.Playing);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Storyboards.Drawables
|
namespace osu.Game.Storyboards.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableStoryboardSample : Component
|
public class DrawableStoryboardSample : PausableSkinnableSound
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of time allowable beyond the start time of the sample, for the sample to start.
|
/// The amount of time allowable beyond the start time of the sample, for the sample to start.
|
||||||
@ -21,38 +19,37 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
|
|
||||||
private readonly StoryboardSampleInfo sampleInfo;
|
private readonly StoryboardSampleInfo sampleInfo;
|
||||||
|
|
||||||
protected SampleChannel Channel { get; private set; }
|
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
public DrawableStoryboardSample(StoryboardSampleInfo sampleInfo)
|
public DrawableStoryboardSample(StoryboardSampleInfo sampleInfo)
|
||||||
|
: base(sampleInfo)
|
||||||
{
|
{
|
||||||
this.sampleInfo = sampleInfo;
|
this.sampleInfo = sampleInfo;
|
||||||
LifetimeStart = sampleInfo.StartTime;
|
LifetimeStart = sampleInfo.StartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[Resolved]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, IBindable<IReadOnlyList<Mod>> mods)
|
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||||
{
|
|
||||||
Channel = beatmap.Value.Skin.GetSample(sampleInfo);
|
|
||||||
if (Channel == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Channel.Volume.Value = sampleInfo.Volume / 100.0;
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin, allowFallback);
|
||||||
|
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToSample>())
|
foreach (var mod in mods.Value.OfType<IApplicableToSample>())
|
||||||
mod.ApplyToSample(Channel);
|
{
|
||||||
|
foreach (var sample in SamplesContainer)
|
||||||
|
mod.ApplyToSample(sample);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// TODO: this logic will need to be consolidated with other game samples like hit sounds.
|
|
||||||
if (Time.Current < sampleInfo.StartTime)
|
if (Time.Current < sampleInfo.StartTime)
|
||||||
{
|
{
|
||||||
// We've rewound before the start time of the sample
|
// We've rewound before the start time of the sample
|
||||||
Channel?.Stop();
|
Stop();
|
||||||
|
|
||||||
// In the case that the user fast-forwards to a point far beyond the start time of the sample,
|
// In the case that the user fast-forwards to a point far beyond the start time of the sample,
|
||||||
// we want to be able to fall into the if-conditional below (therefore we must not have a life time end)
|
// we want to be able to fall into the if-conditional below (therefore we must not have a life time end)
|
||||||
@ -63,8 +60,8 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
{
|
{
|
||||||
// We've passed the start time of the sample. We only play the sample if we're within an allowable range
|
// We've passed the start time of the sample. We only play the sample if we're within an allowable range
|
||||||
// from the sample's start, to reduce layering if we've been fast-forwarded far into the future
|
// from the sample's start, to reduce layering if we've been fast-forwarded far into the future
|
||||||
if (Time.Current - sampleInfo.StartTime < allowable_late_start)
|
if (!RequestedPlaying && Time.Current - sampleInfo.StartTime < allowable_late_start)
|
||||||
Channel?.Play();
|
Play();
|
||||||
|
|
||||||
// In the case that the user rewinds to a point far behind the start time of the sample,
|
// In the case that the user rewinds to a point far behind the start time of the sample,
|
||||||
// we want to be able to fall into the if-conditional above (therefore we must not have a life time start)
|
// we want to be able to fall into the if-conditional above (therefore we must not have a life time start)
|
||||||
@ -72,13 +69,5 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
LifetimeEnd = sampleInfo.StartTime;
|
LifetimeEnd = sampleInfo.StartTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
Channel?.Stop();
|
|
||||||
Channel = null;
|
|
||||||
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,10 @@ namespace osu.Game.Tests
|
|||||||
|
|
||||||
Statistics[HitResult.Miss] = 1;
|
Statistics[HitResult.Miss] = 1;
|
||||||
Statistics[HitResult.Meh] = 50;
|
Statistics[HitResult.Meh] = 50;
|
||||||
Statistics[HitResult.Good] = 100;
|
Statistics[HitResult.Ok] = 100;
|
||||||
|
Statistics[HitResult.Good] = 200;
|
||||||
Statistics[HitResult.Great] = 300;
|
Statistics[HitResult.Great] = 300;
|
||||||
|
Statistics[HitResult.Perfect] = 320;
|
||||||
Statistics[HitResult.SmallTickHit] = 50;
|
Statistics[HitResult.SmallTickHit] = 50;
|
||||||
Statistics[HitResult.SmallTickMiss] = 25;
|
Statistics[HitResult.SmallTickMiss] = 25;
|
||||||
Statistics[HitResult.LargeTickHit] = 100;
|
Statistics[HitResult.LargeTickHit] = 100;
|
||||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor();
|
protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor();
|
||||||
protected new readonly EditorClock Clock;
|
protected new readonly EditorClock Clock;
|
||||||
|
|
||||||
|
protected virtual bool ScrollUsingMouseWheel => true;
|
||||||
|
|
||||||
protected EditorClockTestScene()
|
protected EditorClockTestScene()
|
||||||
{
|
{
|
||||||
Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
|
Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
|
||||||
@ -57,6 +59,9 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e)
|
protected override bool OnScroll(ScrollEvent e)
|
||||||
{
|
{
|
||||||
|
if (!ScrollUsingMouseWheel)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (e.ScrollDelta.Y > 0)
|
if (e.ScrollDelta.Y > 0)
|
||||||
Clock.SeekBackward(true);
|
Clock.SeekBackward(true);
|
||||||
else
|
else
|
||||||
|
@ -26,6 +26,8 @@ namespace osu.Game.Users
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Background.Width = 0.5f;
|
Background.Width = 0.5f;
|
||||||
|
Background.Origin = Anchor.CentreRight;
|
||||||
|
Background.Anchor = Anchor.CentreRight;
|
||||||
Background.Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(1), Color4.White.Opacity(0.3f));
|
Background.Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(1), Color4.White.Opacity(0.3f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2020.925.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2020.930.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||||
<PackageReference Include="Sentry" Version="2.1.6" />
|
<PackageReference Include="Sentry" Version="2.1.6" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.925.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.930.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
@ -80,7 +80,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2020.925.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2020.930.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user