From dc322d1c63f33712d0d4a1488bda78b0d4222448 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 May 2021 20:22:48 +0900 Subject: [PATCH 1/5] Run all type and sample mutations through standardising methods --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 21 +++++---------- .../Objects/TaikoHitObject.cs | 26 +++++++++++++++++++ .../Objects/TaikoStrongableHitObject.cs | 22 +++++++++++----- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index f4a66c39a8..8ede21fdad 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -2,30 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; -using osu.Framework.Bindables; using osu.Game.Audio; namespace osu.Game.Rulesets.Taiko.Objects { public class Hit : TaikoStrongableHitObject { - public readonly Bindable TypeBindable = new Bindable(); - - /// - /// The that actuates this . - /// - public HitType Type + protected override void UpdateTypeFromSamples() { - get => TypeBindable.Value; - set - { - TypeBindable.Value = value; - updateSamplesFromType(); - } + base.UpdateTypeFromSamples(); + + Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; } - private void updateSamplesFromType() + protected override void UpdateSamplesFromType() { + base.UpdateSamplesFromType(); + var rimSamples = getRimSamples(); bool isRimType = Type == HitType.Rim; diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index f047c03f4b..46b864e7de 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -11,6 +12,17 @@ namespace osu.Game.Rulesets.Taiko.Objects { public abstract class TaikoHitObject : HitObject { + public readonly Bindable TypeBindable = new Bindable(); + + /// + /// The that actuates this . + /// + public HitType Type + { + get => TypeBindable.Value; + set => TypeBindable.Value = value; + } + /// /// Default size of a drawable taiko hit object. /// @@ -19,5 +31,19 @@ namespace osu.Game.Rulesets.Taiko.Objects public override Judgement CreateJudgement() => new TaikoJudgement(); protected override HitWindows CreateHitWindows() => new TaikoHitWindows(); + + protected TaikoHitObject() + { + SamplesBindable.BindCollectionChanged((_, __) => UpdateTypeFromSamples()); + TypeBindable.BindValueChanged(_ => UpdateSamplesFromType()); + } + + protected virtual void UpdateSamplesFromType() + { + } + + protected virtual void UpdateTypeFromSamples() + { + } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index cac56d1269..237000474d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -33,15 +33,25 @@ namespace osu.Game.Rulesets.Taiko.Objects public bool IsStrong { get => IsStrongBindable.Value; - set - { - IsStrongBindable.Value = value; - updateSamplesFromStrong(); - } + set => IsStrongBindable.Value = value; } - private void updateSamplesFromStrong() + protected TaikoStrongableHitObject() { + IsStrongBindable.BindValueChanged(_ => UpdateSamplesFromType()); + } + + protected override void UpdateTypeFromSamples() + { + base.UpdateTypeFromSamples(); + + IsStrong = getStrongSamples().Any(); + } + + protected override void UpdateSamplesFromType() + { + base.UpdateSamplesFromType(); + var strongSamples = getStrongSamples(); if (IsStrongBindable.Value != strongSamples.Any()) From 4c9d72e62ae4d82404aa69a0b0d34b81db4b93a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 23 May 2021 21:19:38 +0900 Subject: [PATCH 2/5] Ensure `EditorBeatmap.Update` is called inside `PerformOnSelection` calls --- osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs | 6 +++++- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 8 +++++++- .../Edit/Compose/Components/EditorSelectionHandler.cs | 7 ++++++- .../Components/Timeline/TimelineBlueprintContainer.cs | 6 +++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs index a24130d6ac..ab3b729307 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoSelectionHandler.cs @@ -69,7 +69,11 @@ namespace osu.Game.Rulesets.Taiko.Edit { EditorBeatmap.PerformOnSelection(h => { - if (h is Hit taikoHit) taikoHit.Type = state ? HitType.Rim : HitType.Centre; + if (h is Hit taikoHit) + { + taikoHit.Type = state ? HitType.Rim : HitType.Centre; + EditorBeatmap.Update(h); + } }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 5a6f98f504..22b211f257 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -77,7 +77,13 @@ namespace osu.Game.Screens.Edit.Compose.Components double offset = result.Time.Value - blueprints.First().Item.StartTime; if (offset != 0) - Beatmap.PerformOnSelection(obj => obj.StartTime += offset); + { + Beatmap.PerformOnSelection(obj => + { + obj.StartTime += offset; + Beatmap.Update(obj); + }); + } } return true; diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 2141c490df..246d4aa8d7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -125,6 +125,7 @@ namespace osu.Game.Screens.Edit.Compose.Components return; h.Samples.Add(new HitSampleInfo(sampleName)); + EditorBeatmap.Update(h); }); } @@ -134,7 +135,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The name of the hit sample. public void RemoveHitSample(string sampleName) { - EditorBeatmap.PerformOnSelection(h => h.SamplesBindable.RemoveAll(s => s.Name == sampleName)); + EditorBeatmap.PerformOnSelection(h => + { + h.SamplesBindable.RemoveAll(s => s.Name == sampleName); + EditorBeatmap.Update(h); + }); } /// diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 7c1bbd65f9..6f04f36b83 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -276,7 +276,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(selected.First().StartTime); double adjustment = timingPoint.BeatLength / EditorBeatmap.BeatDivisor * amount; - EditorBeatmap.PerformOnSelection(h => h.StartTime += adjustment); + EditorBeatmap.PerformOnSelection(h => + { + h.StartTime += adjustment; + EditorBeatmap.Update(h); + }); } } From 9223d85f37d33481fd0704349adaf19880728102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 13:22:58 +0900 Subject: [PATCH 3/5] Remove all local type update logic from `TaikoBeatmapConverter` I believe the original goal was to keep this in the converter with the idea that samples may not always be hard coupled to the strong/rim states. But for now I think we can assume this coupling is going to continue into the near future, so let's keep all the logic in `TaikoHitObject`. --- .../Beatmaps/TaikoBeatmapConverter.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index b51f096d7d..90c99316b1 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -79,8 +79,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // Old osu! used hit sounding to determine various hit type information IList samples = obj.Samples; - bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); - switch (obj) { case IHasDistance distanceData: @@ -94,15 +92,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) { IList currentSamples = allSamples[i]; - bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); - strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); yield return new Hit { StartTime = j, - Type = isRim ? HitType.Rim : HitType.Centre, Samples = currentSamples, - IsStrong = strong }; i = (i + 1) % allSamples.Count; @@ -117,7 +111,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { StartTime = obj.StartTime, Samples = obj.Samples, - IsStrong = strong, Duration = taikoDuration, TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4 }; @@ -143,16 +136,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps default: { - bool isRimDefinition(HitSampleInfo s) => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE; - - bool isRim = samples.Any(isRimDefinition); - yield return new Hit { StartTime = obj.StartTime, - Type = isRim ? HitType.Rim : HitType.Centre, Samples = samples, - IsStrong = strong }; break; From 912748b4280b152f3dda8bb9d49b074d5f6e80e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 13:24:22 +0900 Subject: [PATCH 4/5] Avoid bindable feedback causing overwrites --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 35 ------------------- .../Objects/TaikoHitObject.cs | 26 ++++++++++++-- .../Objects/TaikoStrongableHitObject.cs | 6 ++-- 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 8ede21fdad..6b6c04e92e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,45 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using osu.Game.Audio; - namespace osu.Game.Rulesets.Taiko.Objects { public class Hit : TaikoStrongableHitObject { - protected override void UpdateTypeFromSamples() - { - base.UpdateTypeFromSamples(); - - Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; - } - - protected override void UpdateSamplesFromType() - { - base.UpdateSamplesFromType(); - - var rimSamples = getRimSamples(); - - bool isRimType = Type == HitType.Rim; - - if (isRimType != rimSamples.Any()) - { - if (isRimType) - Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); - else - { - foreach (var sample in rimSamples) - Samples.Remove(sample); - } - } - } - - /// - /// Returns an array of any samples which would cause this object to be a "rim" type hit. - /// - private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); - protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; public class StrongNestedHit : StrongNestedHitObject diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 46b864e7de..71214a4017 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Bindables; +using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -35,15 +37,35 @@ namespace osu.Game.Rulesets.Taiko.Objects protected TaikoHitObject() { SamplesBindable.BindCollectionChanged((_, __) => UpdateTypeFromSamples()); - TypeBindable.BindValueChanged(_ => UpdateSamplesFromType()); + TypeBindable.BindValueChanged(_ => updateSamplesFromType()); } - protected virtual void UpdateSamplesFromType() + private void updateSamplesFromType() { + var rimSamples = getRimSamples(); + + bool isRimType = Type == HitType.Rim; + + if (isRimType != rimSamples.Any()) + { + if (isRimType) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + else + { + foreach (var sample in rimSamples) + Samples.Remove(sample); + } + } } protected virtual void UpdateTypeFromSamples() { + Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; } + + /// + /// Returns an array of any samples which would cause this object to be a "rim" type hit. + /// + private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 237000474d..5cddc00a1e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Objects protected TaikoStrongableHitObject() { - IsStrongBindable.BindValueChanged(_ => UpdateSamplesFromType()); + IsStrongBindable.BindValueChanged(_ => updateSamplesFromType()); } protected override void UpdateTypeFromSamples() @@ -48,10 +48,8 @@ namespace osu.Game.Rulesets.Taiko.Objects IsStrong = getStrongSamples().Any(); } - protected override void UpdateSamplesFromType() + private void updateSamplesFromType() { - base.UpdateSamplesFromType(); - var strongSamples = getStrongSamples(); if (IsStrongBindable.Value != strongSamples.Any()) From cbad7bb7f0d4f58707c737809e9384936a8e476c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 26 May 2021 13:40:36 +0900 Subject: [PATCH 5/5] Move taiko `Type` to `Hit` and localise all bind handling --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 49 +++++++++++++++++++ .../Objects/TaikoHitObject.cs | 48 ------------------ .../Objects/TaikoStrongableHitObject.cs | 5 +- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 6b6c04e92e..b4ed242893 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -1,10 +1,59 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using osu.Framework.Bindables; +using osu.Game.Audio; + namespace osu.Game.Rulesets.Taiko.Objects { public class Hit : TaikoStrongableHitObject { + public readonly Bindable TypeBindable = new Bindable(); + + /// + /// The that actuates this . + /// + public HitType Type + { + get => TypeBindable.Value; + set => TypeBindable.Value = value; + } + + public Hit() + { + TypeBindable.BindValueChanged(_ => updateSamplesFromType()); + SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples()); + } + + private void updateTypeFromSamples() + { + Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; + } + + /// + /// Returns an array of any samples which would cause this object to be a "rim" type hit. + /// + private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); + + private void updateSamplesFromType() + { + var rimSamples = getRimSamples(); + + bool isRimType = Type == HitType.Rim; + + if (isRimType != rimSamples.Any()) + { + if (isRimType) + Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + else + { + foreach (var sample in rimSamples) + Samples.Remove(sample); + } + } + } + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; public class StrongNestedHit : StrongNestedHitObject diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 71214a4017..f047c03f4b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -1,9 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using osu.Framework.Bindables; -using osu.Game.Audio; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; @@ -14,17 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Objects { public abstract class TaikoHitObject : HitObject { - public readonly Bindable TypeBindable = new Bindable(); - - /// - /// The that actuates this . - /// - public HitType Type - { - get => TypeBindable.Value; - set => TypeBindable.Value = value; - } - /// /// Default size of a drawable taiko hit object. /// @@ -33,39 +19,5 @@ namespace osu.Game.Rulesets.Taiko.Objects public override Judgement CreateJudgement() => new TaikoJudgement(); protected override HitWindows CreateHitWindows() => new TaikoHitWindows(); - - protected TaikoHitObject() - { - SamplesBindable.BindCollectionChanged((_, __) => UpdateTypeFromSamples()); - TypeBindable.BindValueChanged(_ => updateSamplesFromType()); - } - - private void updateSamplesFromType() - { - var rimSamples = getRimSamples(); - - bool isRimType = Type == HitType.Rim; - - if (isRimType != rimSamples.Any()) - { - if (isRimType) - Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); - else - { - foreach (var sample in rimSamples) - Samples.Remove(sample); - } - } - } - - protected virtual void UpdateTypeFromSamples() - { - Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre; - } - - /// - /// Returns an array of any samples which would cause this object to be a "rim" type hit. - /// - private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 5cddc00a1e..6c17573b50 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -39,12 +39,11 @@ namespace osu.Game.Rulesets.Taiko.Objects protected TaikoStrongableHitObject() { IsStrongBindable.BindValueChanged(_ => updateSamplesFromType()); + SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples()); } - protected override void UpdateTypeFromSamples() + private void updateTypeFromSamples() { - base.UpdateTypeFromSamples(); - IsStrong = getStrongSamples().Any(); }