diff --git a/osu.Game.Rulesets.Karaoke.Tests/Editor/ChangeHandlers/BaseChangeHandlerTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Editor/ChangeHandlers/BaseChangeHandlerTest.cs index 761f9b9dd..e613785c3 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Editor/ChangeHandlers/BaseChangeHandlerTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Editor/ChangeHandlers/BaseChangeHandlerTest.cs @@ -12,6 +12,9 @@ using osu.Game.Rulesets.Karaoke.Configuration; using osu.Game.Rulesets.Karaoke.Edit.Utils; using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Objects.Types; +using osu.Game.Rulesets.Karaoke.Objects.Workings; +using osu.Game.Rulesets.Karaoke.Stages; using osu.Game.Rulesets.Karaoke.Stages.Types; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; @@ -233,13 +236,29 @@ protected void AssertWorkingPropertyInHitObjectValid() return editorBeatmap.HitObjects.OfType().All(hitObject => hitObject switch { - Lyric lyric => lyric.GetAllInvalidWorkingProperties().Length == 0, - Note note => note.GetAllInvalidWorkingProperties().Length == 0, + Lyric lyric => !hasInvalidWorkingProperty(lyric), + Note note => !hasInvalidWorkingProperty(note), _ => throw new NotSupportedException(), }); }); } + private static bool hasInvalidWorkingProperty(Lyric lyric) + { + if (lyric is not (IHasWorkingProperty workingProperty and IHasWorkingProperty stageWorkingProperty)) + throw new NotSupportedException(); + + return workingProperty.HasInvalidWorkingProperty() || stageWorkingProperty.HasInvalidWorkingProperty(); + } + + private static bool hasInvalidWorkingProperty(Note note) + { + if (note is not (IHasWorkingProperty workingProperty and IHasWorkingProperty stageWorkingProperty)) + throw new NotSupportedException(); + + return workingProperty.HasInvalidWorkingProperty() || stageWorkingProperty.HasInvalidWorkingProperty(); + } + private partial class MockEditorChangeHandler : TransactionalCommitComponent, IEditorChangeHandler { public event Action? OnStateChange; diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs index caac38090..3a57c7ca3 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs @@ -9,9 +9,9 @@ namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; -public abstract class HitObjectWorkingPropertyValidatorTest +public abstract class HitObjectWorkingPropertyValidatorTest where TFlag : struct, Enum - where THitObject : KaraokeHitObject, IHasWorkingProperty, new() + where THitObject : KaraokeHitObject, IHasWorkingProperty, new() { [Test] public void CheckInitialState([Values] TFlag flag) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricStageWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricStageWorkingPropertyValidatorTest.cs new file mode 100644 index 000000000..e64b6f499 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricStageWorkingPropertyValidatorTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Objects.Stages.Classic; +using osu.Game.Rulesets.Karaoke.Objects.Workings; +using osu.Game.Rulesets.Karaoke.Stages; +using osu.Game.Rulesets.Karaoke.Stages.Classic; + +namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; + +public class LyricStageWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest +{ + [Test] + public void TestStartTime() + { + var lyric = new Lyric(); + + // state is valid because assign the property. + Assert.DoesNotThrow(() => lyric.StartTime = 1000); + AssetIsValid(lyric, LyricStageWorkingProperty.StartTime, true); + } + + [Test] + public void TestDuration() + { + var lyric = new Lyric(); + + // state is valid because assign the property. + Assert.DoesNotThrow(() => lyric.Duration = 1000); + AssetIsValid(lyric, LyricStageWorkingProperty.Duration, true); + } + + [Test] + public void TestTiming() + { + var lyric = new Lyric(); + + // state is still invalid because duration is not assign. + Assert.DoesNotThrow(() => lyric.StartTime = 1000); + AssetIsValid(lyric, LyricStageWorkingProperty.Timing, false); + + // ok, should be valid now. + Assert.DoesNotThrow(() => lyric.Duration = 1000); + AssetIsValid(lyric, LyricStageWorkingProperty.Timing, true); + } + + [Test] + public void TestEffectApplier() + { + var lyric = new Lyric(); + + // state is valid because assign the property. + Assert.DoesNotThrow(() => lyric.EffectApplier = new LyricClassicStageEffectApplier(Array.Empty(), new ClassicStageDefinition())); + AssetIsValid(lyric, LyricStageWorkingProperty.EffectApplier, true); + } + + protected override bool IsInitialStateValid(LyricStageWorkingProperty flag) + { + return new LyricStageWorkingPropertyValidator(new Lyric()).IsValid(flag); + } +} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs index 03a067031..8a698bb0c 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs @@ -7,51 +7,14 @@ using osu.Game.Rulesets.Karaoke.Beatmaps; using osu.Game.Rulesets.Karaoke.Beatmaps.Metadatas; using osu.Game.Rulesets.Karaoke.Objects; -using osu.Game.Rulesets.Karaoke.Objects.Stages.Classic; using osu.Game.Rulesets.Karaoke.Objects.Workings; -using osu.Game.Rulesets.Karaoke.Stages; -using osu.Game.Rulesets.Karaoke.Stages.Classic; using osu.Game.Rulesets.Karaoke.Tests.Extensions; using osu.Game.Rulesets.Karaoke.Tests.Helper; namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; -public class LyricWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest +public class LyricWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest { - [Test] - public void TestStartTime() - { - var lyric = new Lyric(); - - // state is valid because assign the property. - Assert.DoesNotThrow(() => lyric.StartTime = 1000); - AssetIsValid(lyric, LyricWorkingProperty.StartTime, true); - } - - [Test] - public void TestDuration() - { - var lyric = new Lyric(); - - // state is valid because assign the property. - Assert.DoesNotThrow(() => lyric.Duration = 1000); - AssetIsValid(lyric, LyricWorkingProperty.Duration, true); - } - - [Test] - public void TestTiming() - { - var lyric = new Lyric(); - - // state is still invalid because duration is not assign. - Assert.DoesNotThrow(() => lyric.StartTime = 1000); - AssetIsValid(lyric, LyricWorkingProperty.Timing, false); - - // ok, should be valid now. - Assert.DoesNotThrow(() => lyric.Duration = 1000); - AssetIsValid(lyric, LyricWorkingProperty.Timing, true); - } - [Test] public void TestSingers() { @@ -189,16 +152,6 @@ public void TestReferenceLyric() Assert.Throws(() => lyric.ReferenceLyric = null); } - [Test] - public void TestEffectApplier() - { - var lyric = new Lyric(); - - // state is valid because assign the property. - Assert.DoesNotThrow(() => lyric.EffectApplier = new LyricClassicStageEffectApplier(Array.Empty(), new ClassicStageDefinition())); - AssetIsValid(lyric, LyricWorkingProperty.EffectApplier, true); - } - protected override bool IsInitialStateValid(LyricWorkingProperty flag) { return new LyricWorkingPropertyValidator(new Lyric()).IsValid(flag); diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs index a9992adb4..1df723032 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs @@ -1,19 +1,16 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. -using System; using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Beatmaps; using osu.Game.Rulesets.Karaoke.Objects; -using osu.Game.Rulesets.Karaoke.Objects.Stages.Classic; using osu.Game.Rulesets.Karaoke.Objects.Workings; -using osu.Game.Rulesets.Karaoke.Stages; -using osu.Game.Rulesets.Karaoke.Stages.Classic; using osu.Game.Rulesets.Karaoke.Tests.Extensions; using osu.Game.Rulesets.Karaoke.Tests.Helper; namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; -public class NoteWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest +public class NoteWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest { [Test] public void TestPage() @@ -75,16 +72,6 @@ public void TestReferenceLyric() Assert.Throws(() => note.ReferenceLyric = null); } - [Test] - public void TestEffectApplier() - { - var note = new Note(); - - // page state is valid because assign the property. - Assert.DoesNotThrow(() => note.EffectApplier = new NoteClassicStageEffectApplier(Array.Empty(), new ClassicStageDefinition())); - AssetIsValid(note, NoteWorkingProperty.EffectApplier, true); - } - protected override bool IsInitialStateValid(NoteWorkingProperty flag) { return new NoteWorkingPropertyValidator(new Note()).IsValid(flag); diff --git a/osu.Game.Rulesets.Karaoke/Beatmaps/KaraokeBeatmapProcessor.cs b/osu.Game.Rulesets.Karaoke/Beatmaps/KaraokeBeatmapProcessor.cs index 487a4dfd7..e2f3190ec 100644 --- a/osu.Game.Rulesets.Karaoke/Beatmaps/KaraokeBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Karaoke/Beatmaps/KaraokeBeatmapProcessor.cs @@ -1,6 +1,7 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; @@ -41,10 +42,10 @@ private void applyStage(KaraokeBeatmap beatmap) // should invalidate the working property here because the stage info is changed. beatmap.HitObjects.OfType().ForEach(x => { - x.InvalidateWorkingProperty(LyricWorkingProperty.Timing); - x.InvalidateWorkingProperty(LyricWorkingProperty.EffectApplier); + x.InvalidateWorkingProperty(LyricStageWorkingProperty.Timing); + x.InvalidateWorkingProperty(LyricStageWorkingProperty.EffectApplier); }); - beatmap.HitObjects.OfType().ForEach(x => x.InvalidateWorkingProperty(NoteWorkingProperty.EffectApplier)); + beatmap.HitObjects.OfType().ForEach(x => x.InvalidateWorkingProperty(NoteStageWorkingProperty.EffectApplier)); } if (beatmap.CurrentStageInfo is IHasCalculatedProperty calculatedProperty) @@ -60,9 +61,18 @@ private void applyInvalidProperty(KaraokeBeatmap beatmap) { // should convert to array here because validate the working property might change the start-time and the end time. // which will cause got the wrong item in the array. - foreach (var hitObject in beatmap.HitObjects.OfType().ToArray()) + foreach (var hitObject in beatmap.HitObjects.OfType>().ToArray()) { hitObject.ValidateWorkingProperty(beatmap); } + + var stageInfo = beatmap.CurrentStageInfo; + if (stageInfo == null) + throw new InvalidCastException(); + + foreach (var hitObject in beatmap.HitObjects.OfType>().ToArray()) + { + hitObject.ValidateWorkingProperty(stageInfo); + } } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/BeatmapPropertyChangeHandler.cs b/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/BeatmapPropertyChangeHandler.cs index 740b3a9da..478a747cf 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/BeatmapPropertyChangeHandler.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/BeatmapPropertyChangeHandler.cs @@ -89,7 +89,7 @@ protected void PerformOnSelection(Action action) where T : HitObject protected void InvalidateAllHitObjectWorkingProperty(TWorkingProperty property) where TWorkingProperty : struct, Enum { - foreach (var hitObject in KaraokeBeatmap.HitObjects.OfType>()) + foreach (var hitObject in KaraokeBeatmap.HitObjects.OfType>()) { hitObject.InvalidateWorkingProperty(property); } diff --git a/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/Stages/ClassicStageChangeHandler.cs b/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/Stages/ClassicStageChangeHandler.cs index 277993021..b79b04f6b 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/Stages/ClassicStageChangeHandler.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/ChangeHandlers/Stages/ClassicStageChangeHandler.cs @@ -119,7 +119,7 @@ private void performTimingInfoChanged(Action action) { action(stageInfo.LyricTimingInfo); - InvalidateAllHitObjectWorkingProperty(LyricWorkingProperty.Timing); + InvalidateAllHitObjectWorkingProperty(LyricStageWorkingProperty.Timing); }); } } diff --git a/osu.Game.Rulesets.Karaoke/Mods/ModStage.cs b/osu.Game.Rulesets.Karaoke/Mods/ModStage.cs index e58c337a4..c4f19bc47 100644 --- a/osu.Game.Rulesets.Karaoke/Mods/ModStage.cs +++ b/osu.Game.Rulesets.Karaoke/Mods/ModStage.cs @@ -38,10 +38,10 @@ public override void ApplyToBeatmap(IBeatmap beatmap) // has the same logic in the beatmap processor. beatmap.HitObjects.OfType().ForEach(x => { - x.InvalidateWorkingProperty(LyricWorkingProperty.Timing); - x.InvalidateWorkingProperty(LyricWorkingProperty.EffectApplier); + x.InvalidateWorkingProperty(LyricStageWorkingProperty.Timing); + x.InvalidateWorkingProperty(LyricStageWorkingProperty.EffectApplier); }); - beatmap.HitObjects.OfType().ForEach(x => x.InvalidateWorkingProperty(NoteWorkingProperty.EffectApplier)); + beatmap.HitObjects.OfType().ForEach(x => x.InvalidateWorkingProperty(NoteStageWorkingProperty.EffectApplier)); } protected abstract void ApplyToCurrentStageInfo(TStageInfo stageInfo); diff --git a/osu.Game.Rulesets.Karaoke/Objects/Lyric.cs b/osu.Game.Rulesets.Karaoke/Objects/Lyric.cs index ba9b9d141..dda640db0 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Lyric.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Lyric.cs @@ -188,6 +188,7 @@ public IReferenceLyricPropertyConfig? ReferenceLyricConfig public Lyric() { workingPropertyValidator = new LyricWorkingPropertyValidator(this); + stageWorkingPropertyValidator = new LyricStageWorkingPropertyValidator(this); initInternalBindingEvent(); initReferenceLyricEvent(); diff --git a/osu.Game.Rulesets.Karaoke/Objects/Lyric_Working.cs b/osu.Game.Rulesets.Karaoke/Objects/Lyric_Working.cs index 79966e678..44fcfccfc 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Lyric_Working.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Lyric_Working.cs @@ -21,38 +21,38 @@ namespace osu.Game.Rulesets.Karaoke.Objects; /// Placing the properties that set by or being calculated. /// Those properties will not be saved into the beatmap. /// -public partial class Lyric : IHasWorkingProperty, IHasEffectApplier +public partial class Lyric : IHasWorkingProperty, IHasWorkingProperty, IHasEffectApplier { [JsonIgnore] private readonly LyricWorkingPropertyValidator workingPropertyValidator; + [JsonIgnore] + private readonly LyricStageWorkingPropertyValidator stageWorkingPropertyValidator; + public bool InvalidateWorkingProperty(LyricWorkingProperty workingProperty) => workingPropertyValidator.Invalidate(workingProperty); + public bool InvalidateWorkingProperty(LyricStageWorkingProperty workingProperty) + => stageWorkingPropertyValidator.Invalidate(workingProperty); + private void updateStateByWorkingProperty(LyricWorkingProperty workingProperty) => workingPropertyValidator.UpdateStateByWorkingProperty(workingProperty); - public LyricWorkingProperty[] GetAllInvalidWorkingProperties() + private void updateStateByWorkingProperty(LyricStageWorkingProperty workingProperty) + => stageWorkingPropertyValidator.UpdateStateByWorkingProperty(workingProperty); + + LyricWorkingProperty[] IHasWorkingProperty.GetAllInvalidWorkingProperties() => workingPropertyValidator.GetAllInvalidFlags(); + LyricStageWorkingProperty[] IHasWorkingProperty.GetAllInvalidWorkingProperties() + => stageWorkingPropertyValidator.GetAllInvalidFlags(); + public void ValidateWorkingProperty(KaraokeBeatmap beatmap) { - foreach (var flag in GetAllInvalidWorkingProperties()) + foreach (var flag in workingPropertyValidator.GetAllInvalidFlags()) { switch (flag) { - case LyricWorkingProperty.StartTime: - StartTime = getStartTime(beatmap, this); - break; - - case LyricWorkingProperty.Duration: - Duration = getDuration(beatmap, this); - break; - - case LyricWorkingProperty.Timing: - // start time and duration should be set by other condition. - break; - case LyricWorkingProperty.Singers: Singers = getSingers(beatmap, SingerIds); break; @@ -65,35 +65,11 @@ public void ValidateWorkingProperty(KaraokeBeatmap beatmap) ReferenceLyric = findLyricById(beatmap, ReferenceLyricId); break; - case LyricWorkingProperty.EffectApplier: - EffectApplier = getStageEffectApplier(beatmap, this); - break; - default: throw new ArgumentOutOfRangeException(); } } - static double getStartTime(KaraokeBeatmap beatmap, KaraokeHitObject lyric) - { - var stageInfo = beatmap.CurrentStageInfo; - if (stageInfo == null) - throw new InvalidCastException(); - - (double? startTime, double? _) = stageInfo.GetStartAndEndTime(lyric); - return startTime ?? 0; - } - - static double getDuration(KaraokeBeatmap beatmap, KaraokeHitObject lyric) - { - var stageInfo = beatmap.CurrentStageInfo; - if (stageInfo == null) - throw new InvalidCastException(); - - (double? startTime, double? endTime) = stageInfo.GetStartAndEndTime(lyric); - return endTime - startTime ?? 0; - } - static IDictionary getSingers(KaraokeBeatmap beatmap, IEnumerable singerIds) => beatmap.SingerInfo.GetSingerByIds(singerIds.ToArray()); @@ -102,14 +78,45 @@ static IDictionary getSingers(KaraokeBeatmap beatmap, IEn static Lyric? findLyricById(IBeatmap beatmap, ElementId? id) => id == null ? null : beatmap.HitObjects.OfType().Single(x => x.ID == id); + } - static IStageEffectApplier getStageEffectApplier(KaraokeBeatmap beatmap, KaraokeHitObject lyric) + public void ValidateWorkingProperty(StageInfo stageInfo) + { + foreach (var flag in stageWorkingPropertyValidator.GetAllInvalidFlags()) { - var stageInfo = beatmap.CurrentStageInfo; - if (stageInfo == null) - throw new InvalidCastException(); + switch (flag) + { + case LyricStageWorkingProperty.StartTime: + StartTime = getStartTime(stageInfo, this); + break; + + case LyricStageWorkingProperty.Duration: + Duration = getDuration(stageInfo, this); + break; - return stageInfo.GetStageAppliers(lyric); + case LyricStageWorkingProperty.Timing: + // start time and duration should be set by other condition. + break; + + case LyricStageWorkingProperty.EffectApplier: + EffectApplier = stageInfo.GetStageAppliers(this); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + static double getStartTime(StageInfo stageInfo, KaraokeHitObject lyric) + { + (double? startTime, double? _) = stageInfo.GetStartAndEndTime(lyric); + return startTime ?? 0; + } + + static double getDuration(StageInfo stageInfo, KaraokeHitObject lyric) + { + (double? startTime, double? endTime) = stageInfo.GetStartAndEndTime(lyric); + return endTime - startTime ?? 0; } } @@ -132,7 +139,7 @@ public override double StartTime set { base.StartTime = value; - updateStateByWorkingProperty(LyricWorkingProperty.StartTime); + updateStateByWorkingProperty(LyricStageWorkingProperty.StartTime); } } @@ -149,7 +156,7 @@ public double Duration set { DurationBindable.Value = value; - updateStateByWorkingProperty(LyricWorkingProperty.Duration); + updateStateByWorkingProperty(LyricStageWorkingProperty.Duration); } } @@ -228,7 +235,7 @@ public IStageEffectApplier EffectApplier { EffectApplierBindable.Value = value; - updateStateByWorkingProperty(LyricWorkingProperty.EffectApplier); + updateStateByWorkingProperty(LyricStageWorkingProperty.EffectApplier); } } } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Note.cs b/osu.Game.Rulesets.Karaoke/Objects/Note.cs index db516c93a..fcacb5765 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Note.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Note.cs @@ -127,6 +127,7 @@ public int ReferenceTimeTagIndex public Note() { workingPropertyValidator = new NoteWorkingPropertyValidator(this); + stageWorkingPropertyValidator = new NoteStageWorkingPropertyValidator(this); initInternalBindingEvent(); initReferenceLyricEvent(); diff --git a/osu.Game.Rulesets.Karaoke/Objects/Note_Working.cs b/osu.Game.Rulesets.Karaoke/Objects/Note_Working.cs index 63288ed23..e9ee25817 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Note_Working.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Note_Working.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Karaoke.Objects.Stages; using osu.Game.Rulesets.Karaoke.Objects.Types; using osu.Game.Rulesets.Karaoke.Objects.Workings; +using osu.Game.Rulesets.Karaoke.Stages; namespace osu.Game.Rulesets.Karaoke.Objects; @@ -20,23 +21,35 @@ namespace osu.Game.Rulesets.Karaoke.Objects; /// Placing the properties that set by or being calculated. /// Those properties will not be saved into the beatmap. /// -public partial class Note : IHasWorkingProperty, IHasEffectApplier +public partial class Note : IHasWorkingProperty, IHasWorkingProperty, IHasEffectApplier { [JsonIgnore] private readonly NoteWorkingPropertyValidator workingPropertyValidator; + [JsonIgnore] + private readonly NoteStageWorkingPropertyValidator stageWorkingPropertyValidator; + public bool InvalidateWorkingProperty(NoteWorkingProperty workingProperty) => workingPropertyValidator.Invalidate(workingProperty); + public bool InvalidateWorkingProperty(NoteStageWorkingProperty workingProperty) + => stageWorkingPropertyValidator.Invalidate(workingProperty); + private void updateStateByWorkingProperty(NoteWorkingProperty workingProperty) => workingPropertyValidator.UpdateStateByWorkingProperty(workingProperty); - public NoteWorkingProperty[] GetAllInvalidWorkingProperties() + private void updateStateByWorkingProperty(NoteStageWorkingProperty workingProperty) + => stageWorkingPropertyValidator.UpdateStateByWorkingProperty(workingProperty); + + NoteWorkingProperty[] IHasWorkingProperty.GetAllInvalidWorkingProperties() => workingPropertyValidator.GetAllInvalidFlags(); + NoteStageWorkingProperty[] IHasWorkingProperty.GetAllInvalidWorkingProperties() + => stageWorkingPropertyValidator.GetAllInvalidFlags(); + public void ValidateWorkingProperty(KaraokeBeatmap beatmap) { - foreach (var flag in GetAllInvalidWorkingProperties()) + foreach (var flag in workingPropertyValidator.GetAllInvalidFlags()) { switch (flag) { @@ -48,10 +61,6 @@ public void ValidateWorkingProperty(KaraokeBeatmap beatmap) ReferenceLyric = findLyricById(beatmap, ReferenceLyricId); break; - case NoteWorkingProperty.EffectApplier: - EffectApplier = getStageEffectApplier(beatmap, this); - break; - default: throw new ArgumentOutOfRangeException(); } @@ -62,14 +71,21 @@ public void ValidateWorkingProperty(KaraokeBeatmap beatmap) static Lyric? findLyricById(IBeatmap beatmap, ElementId? id) => id == null ? null : beatmap.HitObjects.OfType().Single(x => x.ID == id); + } - static IStageEffectApplier getStageEffectApplier(KaraokeBeatmap beatmap, Note note) + public void ValidateWorkingProperty(StageInfo stageInfo) + { + foreach (var flag in stageWorkingPropertyValidator.GetAllInvalidFlags()) { - var stageInfo = beatmap.CurrentStageInfo; - if (stageInfo == null) - throw new InvalidCastException(); + switch (flag) + { + case NoteStageWorkingProperty.EffectApplier: + EffectApplier = stageInfo.GetStageAppliers(this); + break; - return stageInfo.GetStageAppliers(note); + default: + throw new ArgumentOutOfRangeException(); + } } } @@ -173,7 +189,7 @@ public IStageEffectApplier EffectApplier { EffectApplierBindable.Value = value; - updateStateByWorkingProperty(NoteWorkingProperty.EffectApplier); + updateStateByWorkingProperty(NoteStageWorkingProperty.EffectApplier); } } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Types/IHasWorkingProperty.cs b/osu.Game.Rulesets.Karaoke/Objects/Types/IHasWorkingProperty.cs index 3663de913..08b69dc58 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Types/IHasWorkingProperty.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Types/IHasWorkingProperty.cs @@ -2,19 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Rulesets.Karaoke.Beatmaps; namespace osu.Game.Rulesets.Karaoke.Objects.Types; -public interface IHasWorkingProperty : IHasWorkingProperty +public interface IHasWorkingProperty : IHasWorkingProperty where TWorkingProperty : struct, Enum { bool InvalidateWorkingProperty(TWorkingProperty workingProperty); TWorkingProperty[] GetAllInvalidWorkingProperties(); + + bool HasInvalidWorkingProperty() => GetAllInvalidWorkingProperties().Length > 0; } -public interface IHasWorkingProperty +public interface IHasWorkingProperty { - void ValidateWorkingProperty(KaraokeBeatmap beatmap); + void ValidateWorkingProperty(TFillProperty fillProperty); } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricStageWorkingProperty.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricStageWorkingProperty.cs new file mode 100644 index 000000000..cf700c229 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricStageWorkingProperty.cs @@ -0,0 +1,33 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Rulesets.Karaoke.Objects.Workings; + +/// +/// Specifies which properties in the are being invalidated. +/// +[Flags] +public enum LyricStageWorkingProperty +{ + /// + /// is being invalidated. + /// + StartTime = 1, + + /// + /// is being invalidated. + /// + Duration = 1 << 1, + + /// + /// and is being invalidated. + /// + Timing = StartTime | Duration, + + /// + /// is being invalidated. + /// + EffectApplier = 1 << 2, +} diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricStageWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricStageWorkingPropertyValidator.cs new file mode 100644 index 000000000..2e52ea4a6 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricStageWorkingPropertyValidator.cs @@ -0,0 +1,16 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Karaoke.Objects.Workings; + +public class LyricStageWorkingPropertyValidator : HitObjectWorkingPropertyValidator +{ + public LyricStageWorkingPropertyValidator(Lyric hitObject) + : base(hitObject) + { + } + + protected override bool HasDataProperty(LyricStageWorkingProperty flags) => false; + + protected override bool IsWorkingPropertySynced(Lyric hitObject, LyricStageWorkingProperty flags) => true; +} diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingProperty.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingProperty.cs index 7f45ff25d..a921200aa 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingProperty.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingProperty.cs @@ -11,38 +11,18 @@ namespace osu.Game.Rulesets.Karaoke.Objects.Workings; [Flags] public enum LyricWorkingProperty { - /// - /// is being invalidated. - /// - StartTime = 1, - - /// - /// is being invalidated. - /// - Duration = 1 << 1, - - /// - /// and is being invalidated. - /// - Timing = StartTime | Duration, - /// /// is being invalidated. /// - Singers = 1 << 2, + Singers = 1, /// /// is being invalidated. /// - Page = 1 << 3, + Page = 1 << 1, /// /// is being invalidated. /// - ReferenceLyric = 1 << 4, - - /// - /// is being invalidated. - /// - EffectApplier = 1 << 5, + ReferenceLyric = 1 << 2, } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs index 901f7ce30..06b8954c8 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs @@ -18,26 +18,18 @@ public LyricWorkingPropertyValidator(Lyric hitObject) protected override bool HasDataProperty(LyricWorkingProperty flags) => flags switch { - LyricWorkingProperty.StartTime => false, - LyricWorkingProperty.Duration => false, - LyricWorkingProperty.Timing => false, LyricWorkingProperty.Singers => true, LyricWorkingProperty.Page => false, LyricWorkingProperty.ReferenceLyric => true, - LyricWorkingProperty.EffectApplier => false, _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null), }; protected override bool IsWorkingPropertySynced(Lyric hitObject, LyricWorkingProperty flags) => flags switch { - LyricWorkingProperty.StartTime => true, - LyricWorkingProperty.Duration => true, - LyricWorkingProperty.Timing => true, LyricWorkingProperty.Singers => isWorkingSingerSynced(hitObject), LyricWorkingProperty.Page => true, LyricWorkingProperty.ReferenceLyric => isReferenceLyricSynced(hitObject), - LyricWorkingProperty.EffectApplier => true, _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null), }; diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteStageWorkingProperty.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteStageWorkingProperty.cs new file mode 100644 index 000000000..107c34a6d --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteStageWorkingProperty.cs @@ -0,0 +1,18 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Rulesets.Karaoke.Objects.Workings; + +/// +/// Specifies which properties in the are being invalidated. +/// +[Flags] +public enum NoteStageWorkingProperty +{ + /// + /// is being invalidated. + /// + EffectApplier = 1, +} diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteStageWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteStageWorkingPropertyValidator.cs new file mode 100644 index 000000000..d9284a841 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteStageWorkingPropertyValidator.cs @@ -0,0 +1,16 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Karaoke.Objects.Workings; + +public class NoteStageWorkingPropertyValidator : HitObjectWorkingPropertyValidator +{ + public NoteStageWorkingPropertyValidator(Note hitObject) + : base(hitObject) + { + } + + protected override bool HasDataProperty(NoteStageWorkingProperty flags) => false; + + protected override bool IsWorkingPropertySynced(Note hitObject, NoteStageWorkingProperty flags) => true; +} diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingProperty.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingProperty.cs index bc7daf86b..649413f40 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingProperty.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingProperty.cs @@ -20,9 +20,4 @@ public enum NoteWorkingProperty /// is being invalidated. /// ReferenceLyric = 1 << 1, - - /// - /// is being invalidated. - /// - EffectApplier = 1 << 2, } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs index a30a23bf7..5b032e94d 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs @@ -17,7 +17,6 @@ protected override bool HasDataProperty(NoteWorkingProperty flags) => { NoteWorkingProperty.Page => false, NoteWorkingProperty.ReferenceLyric => true, - NoteWorkingProperty.EffectApplier => false, _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null), }; @@ -26,7 +25,6 @@ protected override bool IsWorkingPropertySynced(Note hitObject, NoteWorkingPrope { NoteWorkingProperty.Page => true, NoteWorkingProperty.ReferenceLyric => hitObject.ReferenceLyric?.ID == hitObject.ReferenceLyricId, - NoteWorkingProperty.EffectApplier => true, _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null), }; } diff --git a/osu.Game.Rulesets.Karaoke/Stages/Preview/PreviewStageInfo.cs b/osu.Game.Rulesets.Karaoke/Stages/Preview/PreviewStageInfo.cs index 38fa1aa5a..aa0331f41 100644 --- a/osu.Game.Rulesets.Karaoke/Stages/Preview/PreviewStageInfo.cs +++ b/osu.Game.Rulesets.Karaoke/Stages/Preview/PreviewStageInfo.cs @@ -86,8 +86,8 @@ public void ValidateCalculatedProperty(IBeatmap beatmap) layoutCategory.AddToMapping(element, lyric); // Need to invalidate the working property in the lyric to let the property re-fill in the beatmap processor. - lyric.InvalidateWorkingProperty(LyricWorkingProperty.Timing); - lyric.InvalidateWorkingProperty(LyricWorkingProperty.EffectApplier); + lyric.InvalidateWorkingProperty(LyricStageWorkingProperty.Timing); + lyric.InvalidateWorkingProperty(LyricStageWorkingProperty.EffectApplier); } calculatedPropertyIsUpdated = true;