diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 515fa4c0b5..6b18aac478 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -1046,6 +1046,174 @@ namespace osu.Game.Tests.Visual.Editing } } + [Test] + public void TestAddSoundBeforeSettingNonAutoAdditionBankOnSelectedObject() + { + AddStep("select first object", () => + { + EditorBeatmap.SelectedHitObjects.Clear(); + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("add finish sound", () => InputManager.Key(Key.E)); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasAutoAdditionBankFlag(0, true); + + AddStep("set drum addition bank", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.AltLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasAutoAdditionBankFlag(0, false); + } + + [Test] + public void TestAddSoundAfterSettingNonAutoAdditionBankOnSelectedObject() + { + AddStep("select first object", () => + { + EditorBeatmap.SelectedHitObjects.Clear(); + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("set drum addition bank", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.AltLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("add finish sound", () => InputManager.Key(Key.E)); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasAutoAdditionBankFlag(0, false); + } + + [Test] + public void TestSwitchSoundAfterSettingNonAutoAdditionBankOnSelectedObject() + { + AddStep("select first object", () => + { + EditorBeatmap.SelectedHitObjects.Clear(); + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("set drum addition bank", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.AltLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("add finish sound", () => InputManager.Key(Key.E)); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasAutoAdditionBankFlag(0, false); + + AddStep("remove finish sound", () => InputManager.Key(Key.E)); + AddStep("add whistle sound", () => InputManager.Key(Key.W)); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasAutoAdditionBankFlag(0, false); + } + + [Test] + public void TestAddSoundBeforeSettingAutoAdditionBankOnSelectedObject() + { + AddStep("select first object", () => + { + EditorBeatmap.SelectedHitObjects.Clear(); + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("add finish sound", () => InputManager.Key(Key.E)); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasAutoAdditionBankFlag(0, true); + + AddStep("set auto addition bank", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.Key(Key.Q); + InputManager.ReleaseKey(Key.AltLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasAutoAdditionBankFlag(0, true); + + AddStep("set drum normal bank", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasAutoAdditionBankFlag(0, true); + } + + [Test] + public void TestAddSoundAfterSettingAutoAdditionBankOnSelectedObject() + { + AddStep("select first object", () => + { + EditorBeatmap.SelectedHitObjects.Clear(); + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[0]); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("set auto addition bank", () => + { + InputManager.PressKey(Key.AltLeft); + InputManager.Key(Key.Q); + InputManager.ReleaseKey(Key.AltLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + + AddStep("add finish sound", () => InputManager.Key(Key.E)); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasAutoAdditionBankFlag(0, true); + + AddStep("set drum normal bank", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FINISH); + hitObjectHasSampleNormalBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasSampleAdditionBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasAutoAdditionBankFlag(0, true); + } + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () => { var samplePiece = this.ChildrenOfType().Single(piece => piece is not NodeSamplePointPiece && piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 4414e963bf..11ae0ef56d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -100,7 +100,6 @@ namespace osu.Game.Screens.Edit.Compose.Components kvp.Value.BindValueChanged(_ => updatePlacementSamples()); SelectionHandler.AutoSelectionBankEnabled.BindValueChanged(_ => updateAutoBankTernaryButtonTooltip(), true); - SelectionHandler.SelectionAdditionBanksEnabled.BindValueChanged(_ => updateAdditionBankTernaryButtonTooltips(), true); } protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) @@ -252,17 +251,6 @@ namespace osu.Game.Screens.Edit.Compose.Components autoBankButton.NormalButton.TooltipText = !enabled ? "Auto normal bank can only be used during hit object placement" : string.Empty; } - private void updateAdditionBankTernaryButtonTooltips() - { - bool enabled = SelectionHandler.SelectionAdditionBanksEnabled.Value; - - foreach (var ternaryButton in SampleBankTernaryStates) - { - ternaryButton.AdditionsButton.Enabled.Value = enabled; - ternaryButton.AdditionsButton.TooltipText = !enabled ? "Add an addition sample first to be able to set a bank" : string.Empty; - } - } - #region Placement /// diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 074d94eb26..9bee0a4fff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -85,11 +85,6 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public readonly Bindable AutoSelectionBankEnabled = new Bindable(); - /// - /// Whether the selection contains any addition samples and the can be used. - /// - public readonly Bindable SelectionAdditionBanksEnabled = new Bindable(); - /// /// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions) /// @@ -201,26 +196,18 @@ namespace osu.Game.Screens.Edit.Compose.Components break; case TernaryState.True: - if (SelectedItems.Count == 0) - { - // Ensure the user can't stack multiple bank selections when there's no hitobject selection. - // Note that in normal scenarios this is sorted out by the feedback from applying the bank to the selected objects. - foreach (var other in SelectionAdditionBankStates.Values) - { - if (other != bindable) - other.Value = TernaryState.False; - } - } - else - { - // If none of the selected objects have any addition samples, we should not apply the addition bank. - if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL))) - { - bindable.Value = TernaryState.False; - break; - } - + // If any of the selected objects have any addition samples, we should apply the addition bank. + if (SelectedItems.SelectMany(enumerateAllSamples).Any(h => h.Any(o => o.Name != HitSampleInfo.HIT_NORMAL))) SetSampleAdditionBank(bankName); + + // There are either no selected items, or none of the selected items have addition sounds. + // This state is basically the user pre-selecting an addition bank before actually adding an addition. + // Ensure the user can't stack multiple bank selections in this state. + // Note that in normal scenarios this is sorted out by the feedback from applying the bank to the selected objects. + foreach (var other in SelectionAdditionBankStates.Values) + { + if (other != bindable) + other.Value = TernaryState.False; } break; @@ -279,7 +266,6 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectionNewComboState.Value = TernaryState.False; AutoSelectionBankEnabled.Value = true; - SelectionAdditionBanksEnabled.Value = true; SelectionBankStates[HIT_BANK_AUTO].Value = TernaryState.True; SelectionAdditionBankStates[HIT_BANK_AUTO].Value = TernaryState.True; foreach (var (_, sampleState) in SelectionSampleStates) @@ -309,12 +295,17 @@ namespace osu.Game.Screens.Edit.Compose.Components bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name == HitSampleInfo.HIT_NORMAL), h => h.Bank == bankName); } - SelectionAdditionBanksEnabled.Value = samplesInSelection.SelectMany(s => s).Any(o => o.Name != HitSampleInfo.HIT_NORMAL); - - foreach ((string bankName, var bindable) in SelectionAdditionBankStates) + // if there are no addition samples in the selection, do not touch the state of addition bank bindables. + // this is to reduce annoyance from the bank resetting if the user wants to e.g. remove the only addition sound on an object, but then add another addition sound + // while keeping the bank the same. + // note that deselecting all objects will still reset the addition bank selection to auto via `ResetTernaryStates()`. this may need to be reconsidered later. + if (samplesInSelection.SelectMany(s => s).Any(o => o.Name != HitSampleInfo.HIT_NORMAL)) { - bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name != HitSampleInfo.HIT_NORMAL), - h => (bankName != HIT_BANK_AUTO && h.Bank == bankName && !h.EditorAutoBank) || (bankName == HIT_BANK_AUTO && h.EditorAutoBank)); + foreach ((string bankName, var bindable) in SelectionAdditionBankStates) + { + bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name != HitSampleInfo.HIT_NORMAL), + h => (bankName != HIT_BANK_AUTO && h.Bank == bankName && !h.EditorAutoBank) || (bankName == HIT_BANK_AUTO && h.EditorAutoBank)); + } } } } @@ -453,9 +444,24 @@ namespace osu.Game.Screens.Edit.Compose.Components EditorBeatmap.PerformOnSelection(h => { + string? forcedBank = null; + // if the selected object(s) only have normal samples, check whether the user has preselected a singular non-auto bank using `SelectionAdditionBankStates`. + // other scenarios are already handled by `CreateHitSampleInfo()`: + // - if the selected object(s) already have addition samples, `CreateHitSampleInfo()` will copy the bank from said addition samples. + // - if the selected object(s) do not have addition samples but the user has preselected auto bank, `CreateHitSampleInfo()` will use the auto bank anyway. + if (h.Samples.All(s => s.Name == HitSampleInfo.HIT_NORMAL)) + forcedBank = SelectionAdditionBankStates.SingleOrDefault(kv => kv.Value.Value == TernaryState.True).Key; + // Make sure there isn't already an existing sample if (h.Samples.All(s => s.Name != sampleName)) - h.Samples.Add(h.CreateHitSampleInfo(sampleName)); + { + var hitSample = h.CreateHitSampleInfo(sampleName); + + if (forcedBank != null && forcedBank != HIT_BANK_AUTO) + hitSample = hitSample.With(newBank: forcedBank, newEditorAutoBank: false); + + h.Samples.Add(hitSample); + } if (h is IHasRepeats hasRepeats) {