mirror of
https://github.com/ppy/osu.git
synced 2024-11-15 09:47:24 +08:00
Merge pull request #29648 from OliBomby/auto-addition2
Add support for the auto sample addition bank in the editor
This commit is contained in:
commit
47aa2c2bfc
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
}
|
||||
|
||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
|
||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default)
|
||||
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
|
||||
|
||||
public bool Equals(BananaHitSampleInfo? other)
|
||||
|
@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
if (slider == null) return;
|
||||
|
||||
sample = new HitSampleInfo("hitwhistle", HitSampleInfo.BANK_SOFT, volume: 70);
|
||||
sample = new HitSampleInfo("hitwhistle", HitSampleInfo.BANK_SOFT, volume: 70, editorAutoBank: false);
|
||||
slider.Samples.Add(sample.With());
|
||||
});
|
||||
|
||||
|
@ -60,12 +60,18 @@ namespace osu.Game.Audio
|
||||
/// </summary>
|
||||
public int Volume { get; }
|
||||
|
||||
public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100)
|
||||
/// <summary>
|
||||
/// Whether this sample should automatically assign the bank of the normal sample whenever it is set in the editor.
|
||||
/// </summary>
|
||||
public bool EditorAutoBank { get; }
|
||||
|
||||
public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100, bool editorAutoBank = true)
|
||||
{
|
||||
Name = name;
|
||||
Bank = bank;
|
||||
Suffix = suffix;
|
||||
Volume = volume;
|
||||
EditorAutoBank = editorAutoBank;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -92,9 +98,10 @@ namespace osu.Game.Audio
|
||||
/// <param name="newBank">An optional new sample bank.</param>
|
||||
/// <param name="newSuffix">An optional new lookup suffix.</param>
|
||||
/// <param name="newVolume">An optional new volume.</param>
|
||||
/// <param name="newEditorAutoBank">An optional new editor auto bank flag.</param>
|
||||
/// <returns>The new <see cref="HitSampleInfo"/>.</returns>
|
||||
public virtual HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
|
||||
=> new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume));
|
||||
public virtual HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default)
|
||||
=> new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume), newEditorAutoBank.GetOr(EditorAutoBank));
|
||||
|
||||
public virtual bool Equals(HitSampleInfo? other)
|
||||
=> other != null && Name == other.Name && Bank == other.Bank && Suffix == other.Suffix;
|
||||
|
@ -539,7 +539,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false)
|
||||
{
|
||||
LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank);
|
||||
LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank);
|
||||
LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL && !s.EditorAutoBank)?.Bank);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
// Fall back to using the normal sample bank otherwise.
|
||||
if (Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) is HitSampleInfo existingNormal)
|
||||
return existingNormal.With(newName: sampleName);
|
||||
return existingNormal.With(newName: sampleName, newEditorAutoBank: true);
|
||||
|
||||
return new HitSampleInfo(sampleName);
|
||||
}
|
||||
|
@ -204,8 +204,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
if (stringBank == @"none")
|
||||
stringBank = null;
|
||||
string stringAddBank = addBank.ToString().ToLowerInvariant();
|
||||
|
||||
if (stringAddBank == @"none")
|
||||
{
|
||||
bankInfo.EditorAutoBank = true;
|
||||
stringAddBank = null;
|
||||
}
|
||||
else
|
||||
bankInfo.EditorAutoBank = false;
|
||||
|
||||
bankInfo.BankForNormal = stringBank;
|
||||
bankInfo.BankForAdditions = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank;
|
||||
@ -477,7 +483,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
if (string.IsNullOrEmpty(bankInfo.Filename))
|
||||
{
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank,
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, true, bankInfo.CustomSampleBank,
|
||||
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
|
||||
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
|
||||
type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal)));
|
||||
@ -489,13 +495,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
}
|
||||
|
||||
if (type.HasFlag(LegacyHitSoundType.Finish))
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.EditorAutoBank, bankInfo.CustomSampleBank));
|
||||
|
||||
if (type.HasFlag(LegacyHitSoundType.Whistle))
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.EditorAutoBank, bankInfo.CustomSampleBank));
|
||||
|
||||
if (type.HasFlag(LegacyHitSoundType.Clap))
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.EditorAutoBank, bankInfo.CustomSampleBank));
|
||||
|
||||
return soundTypes;
|
||||
}
|
||||
@ -534,6 +540,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// </summary>
|
||||
public int CustomSampleBank;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the bank for additions should be inherited from the normal sample in edit.
|
||||
/// </summary>
|
||||
public bool EditorAutoBank = true;
|
||||
|
||||
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||
}
|
||||
|
||||
@ -558,21 +569,21 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// </summary>
|
||||
public bool BankSpecified;
|
||||
|
||||
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, int customSampleBank = 0, bool isLayered = false)
|
||||
: base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume)
|
||||
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, bool editorAutoBank = false, int customSampleBank = 0, bool isLayered = false)
|
||||
: base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume, editorAutoBank)
|
||||
{
|
||||
CustomSampleBank = customSampleBank;
|
||||
BankSpecified = !string.IsNullOrEmpty(bank);
|
||||
IsLayered = isLayered;
|
||||
}
|
||||
|
||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
|
||||
=> With(newName, newBank, newVolume);
|
||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default)
|
||||
=> With(newName, newBank, newVolume, newEditorAutoBank);
|
||||
|
||||
public virtual LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default,
|
||||
public virtual LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default,
|
||||
Optional<int> newCustomSampleBank = default,
|
||||
Optional<bool> newIsLayered = default)
|
||||
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
|
||||
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newEditorAutoBank.GetOr(EditorAutoBank), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
|
||||
|
||||
public bool Equals(LegacyHitSampleInfo? other)
|
||||
// The additions to equality checks here are *required* to ensure that pooling works correctly.
|
||||
@ -604,7 +615,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
Path.ChangeExtension(Filename, null)
|
||||
};
|
||||
|
||||
public sealed override LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default,
|
||||
public sealed override LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default,
|
||||
Optional<int> newCustomSampleBank = default,
|
||||
Optional<bool> newIsLayered = default)
|
||||
=> new FileHitSampleInfo(Filename, newVolume.GetOr(Volume));
|
||||
|
@ -4,8 +4,10 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
@ -14,7 +16,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
{
|
||||
public partial class DrawableTernaryButton : OsuButton
|
||||
public partial class DrawableTernaryButton : OsuButton, IHasTooltip
|
||||
{
|
||||
private Color4 defaultBackgroundColour;
|
||||
private Color4 defaultIconColour;
|
||||
@ -58,12 +60,16 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
base.LoadComplete();
|
||||
|
||||
Button.Bindable.BindValueChanged(_ => updateSelectionState(), true);
|
||||
Button.Enabled.BindTo(Enabled);
|
||||
|
||||
Action = onAction;
|
||||
}
|
||||
|
||||
private void onAction()
|
||||
{
|
||||
if (!Button.Enabled.Value)
|
||||
return;
|
||||
|
||||
Button.Toggle();
|
||||
}
|
||||
|
||||
@ -98,5 +104,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
Anchor = Anchor.CentreLeft,
|
||||
X = 40f
|
||||
};
|
||||
|
||||
public LocalisableString TooltipText => Button.Tooltip;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
{
|
||||
public readonly Bindable<TernaryState> Bindable;
|
||||
|
||||
public readonly Bindable<bool> Enabled = new Bindable<bool>(true);
|
||||
|
||||
public readonly string Description;
|
||||
|
||||
/// <summary>
|
||||
@ -19,6 +21,8 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
/// </summary>
|
||||
public readonly Func<Drawable>? CreateIcon;
|
||||
|
||||
public string Tooltip { get; set; } = string.Empty;
|
||||
|
||||
public TernaryButton(Bindable<TernaryState> bindable, string description, Func<Drawable>? createIcon = null)
|
||||
{
|
||||
Bindable = bindable;
|
||||
|
@ -68,6 +68,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
SampleBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionBankStates).ToArray();
|
||||
SampleAdditionBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionAdditionBankStates).ToArray();
|
||||
|
||||
SelectionHandler.AutoSelectionBankEnabled.BindValueChanged(_ => updateAutoBankTernaryButtonTooltip(), true);
|
||||
SelectionHandler.SelectionAdditionBanksEnabled.BindValueChanged(_ => updateAdditionBankTernaryButtonTooltips(), true);
|
||||
|
||||
AddInternal(new DrawableRulesetDependenciesProvidingContainer(Composer.Ruleset)
|
||||
{
|
||||
Child = placementBlueprintContainer
|
||||
@ -288,6 +291,26 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateAutoBankTernaryButtonTooltip()
|
||||
{
|
||||
bool enabled = SelectionHandler.AutoSelectionBankEnabled.Value;
|
||||
|
||||
var autoBankButton = SampleBankTernaryStates.Single(t => t.Bindable == SelectionHandler.SelectionBankStates[EditorSelectionHandler.HIT_BANK_AUTO]);
|
||||
autoBankButton.Enabled.Value = enabled;
|
||||
autoBankButton.Tooltip = !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 SampleAdditionBankTernaryStates)
|
||||
{
|
||||
ternaryButton.Enabled.Value = enabled;
|
||||
ternaryButton.Tooltip = !enabled ? "Add an addition sample first to be able to set a bank" : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#region Placement
|
||||
|
||||
/// <summary>
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
@ -10,7 +11,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
// bring in updates from selection changes
|
||||
EditorBeatmap.HitObjectUpdated += _ => Scheduler.AddOnce(UpdateTernaryStates);
|
||||
|
||||
SelectedItems.CollectionChanged += (_, _) => Scheduler.AddOnce(UpdateTernaryStates);
|
||||
SelectedItems.CollectionChanged += onSelectedItemsChanged;
|
||||
}
|
||||
|
||||
protected override void DeleteItems(IEnumerable<HitObject> items) => EditorBeatmap.RemoveRange(items);
|
||||
@ -64,6 +64,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// </summary>
|
||||
public readonly Dictionary<string, Bindable<TernaryState>> SelectionAdditionBankStates = new Dictionary<string, Bindable<TernaryState>>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether there is no selection and the auto <see cref="SelectionBankStates"/> can be used.
|
||||
/// </summary>
|
||||
public readonly Bindable<bool> AutoSelectionBankEnabled = new Bindable<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the selection contains any addition samples and the <see cref="SelectionAdditionBankStates"/> can be used.
|
||||
/// </summary>
|
||||
public readonly Bindable<bool> SelectionAdditionBanksEnabled = new Bindable<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
|
||||
/// </summary>
|
||||
@ -153,10 +163,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
// Auto should never apply when there is a selection made.
|
||||
if (bankName == HIT_BANK_AUTO)
|
||||
break;
|
||||
|
||||
// Completely empty selections should be allowed in the case that none of the selected objects have any addition samples.
|
||||
// This is also required to stop a bindable feedback loop when a HitObject has zero addition samples (and LINQ `All` below becomes true).
|
||||
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
|
||||
@ -164,8 +170,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
// Never remove a sample bank.
|
||||
// These are basically radio buttons, not toggles.
|
||||
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName)))
|
||||
bindable.Value = TernaryState.True;
|
||||
if (bankName == HIT_BANK_AUTO)
|
||||
{
|
||||
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.EditorAutoBank)))
|
||||
bindable.Value = TernaryState.True;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName && !s.EditorAutoBank)))
|
||||
bindable.Value = TernaryState.True;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@ -183,14 +197,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
// Auto should just not apply if there's a selection already made.
|
||||
// Maybe we could make it a disabled button in the future, but right now the editor buttons don't support disabled state.
|
||||
if (bankName == HIT_BANK_AUTO)
|
||||
{
|
||||
bindable.Value = TernaryState.False;
|
||||
break;
|
||||
}
|
||||
|
||||
// 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)))
|
||||
{
|
||||
@ -208,9 +214,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
SelectionAdditionBankStates[bankName] = bindable;
|
||||
}
|
||||
|
||||
// start with normal selected.
|
||||
SelectionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
|
||||
SelectionAdditionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
|
||||
resetTernaryStates();
|
||||
|
||||
foreach (string sampleName in HitSampleInfo.AllAdditions)
|
||||
{
|
||||
@ -252,12 +256,21 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
};
|
||||
}
|
||||
|
||||
private void resetTernaryStates()
|
||||
{
|
||||
AutoSelectionBankEnabled.Value = true;
|
||||
SelectionAdditionBanksEnabled.Value = true;
|
||||
SelectionBankStates[HIT_BANK_AUTO].Value = TernaryState.True;
|
||||
SelectionAdditionBankStates[HIT_BANK_AUTO].Value = TernaryState.True;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when context menu ternary states may need to be recalculated (selection changed or hitobject updated).
|
||||
/// </summary>
|
||||
protected virtual void UpdateTernaryStates()
|
||||
{
|
||||
SelectionNewComboState.Value = GetStateFromSelection(SelectedItems.OfType<IHasComboInformation>(), h => h.NewCombo);
|
||||
AutoSelectionBankEnabled.Value = SelectedItems.Count == 0;
|
||||
|
||||
var samplesInSelection = SelectedItems.SelectMany(enumerateAllSamples).ToArray();
|
||||
|
||||
@ -271,12 +284,23 @@ 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)
|
||||
{
|
||||
bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name != HitSampleInfo.HIT_NORMAL), h => h.Bank == bankName);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private void onSelectedItemsChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
// Reset the ternary states when the selection is cleared.
|
||||
if (e.OldStartingIndex >= 0 && e.NewStartingIndex < 0)
|
||||
Scheduler.AddOnce(resetTernaryStates);
|
||||
else
|
||||
Scheduler.AddOnce(UpdateTernaryStates);
|
||||
}
|
||||
|
||||
private IEnumerable<IList<HitSampleInfo>> enumerateAllSamples(HitObject hitObject)
|
||||
{
|
||||
yield return hitObject.Samples;
|
||||
@ -337,33 +361,29 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <param name="bankName">The name of the sample bank.</param>
|
||||
public void SetSampleAdditionBank(string bankName)
|
||||
{
|
||||
bool hasRelevantBank(HitObject hitObject)
|
||||
{
|
||||
bool result = hitObject.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
|
||||
|
||||
if (hitObject is IHasRepeats hasRepeats)
|
||||
{
|
||||
foreach (var node in hasRepeats.NodeSamples)
|
||||
result &= node.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
bool hasRelevantBank(HitObject hitObject) =>
|
||||
bankName == HIT_BANK_AUTO
|
||||
? enumerateAllSamples(hitObject).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.EditorAutoBank)
|
||||
: enumerateAllSamples(hitObject).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName && !s.EditorAutoBank);
|
||||
|
||||
if (SelectedItems.All(hasRelevantBank))
|
||||
return;
|
||||
|
||||
EditorBeatmap.PerformOnSelection(h =>
|
||||
{
|
||||
if (enumerateAllSamples(h).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName))
|
||||
if (hasRelevantBank(h))
|
||||
return;
|
||||
|
||||
h.Samples = h.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
|
||||
string normalBank = h.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank ?? HitSampleInfo.BANK_SOFT;
|
||||
h.Samples = h.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? bankName == HIT_BANK_AUTO ? s.With(newBank: normalBank, newEditorAutoBank: true) : s.With(newBank: bankName, newEditorAutoBank: false) : s).ToList();
|
||||
|
||||
if (h is IHasRepeats hasRepeats)
|
||||
{
|
||||
for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
|
||||
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
|
||||
{
|
||||
normalBank = hasRepeats.NodeSamples[i].FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank ?? HitSampleInfo.BANK_SOFT;
|
||||
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? bankName == HIT_BANK_AUTO ? s.With(newBank: normalBank, newEditorAutoBank: true) : s.With(newBank: bankName, newEditorAutoBank: false) : s).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
EditorBeatmap.Update(h);
|
||||
@ -407,9 +427,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
var hitSample = h.CreateHitSampleInfo(sampleName);
|
||||
|
||||
string? existingAdditionBank = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL)?.Bank;
|
||||
if (existingAdditionBank != null)
|
||||
hitSample = hitSample.With(newBank: existingAdditionBank);
|
||||
HitSampleInfo? existingAddition = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL);
|
||||
if (existingAddition != null)
|
||||
hitSample = hitSample.With(newBank: existingAddition.Bank, newEditorAutoBank: existingAddition.EditorAutoBank);
|
||||
|
||||
node.Add(hitSample);
|
||||
}
|
||||
|
@ -107,7 +107,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
public static string? GetAdditionBankValue(IEnumerable<HitSampleInfo> samples)
|
||||
{
|
||||
return samples.FirstOrDefault(o => o.Name != HitSampleInfo.HIT_NORMAL)?.Bank;
|
||||
var firstAddition = samples.FirstOrDefault(o => o.Name != HitSampleInfo.HIT_NORMAL);
|
||||
if (firstAddition == null)
|
||||
return null;
|
||||
|
||||
return firstAddition.EditorAutoBank ? EditorSelectionHandler.HIT_BANK_AUTO : firstAddition.Bank;
|
||||
}
|
||||
|
||||
public static int GetVolumeValue(ICollection<HitSampleInfo> samples)
|
||||
@ -320,7 +324,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
for (int i = 0; i < relevantSamples.Count; i++)
|
||||
{
|
||||
if (relevantSamples[i].Name != HitSampleInfo.HIT_NORMAL) continue;
|
||||
if (relevantSamples[i].Name != HitSampleInfo.HIT_NORMAL && !relevantSamples[i].EditorAutoBank) continue;
|
||||
|
||||
relevantSamples[i] = relevantSamples[i].With(newBank: newBank);
|
||||
}
|
||||
@ -331,11 +335,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
updateAllRelevantSamples((_, relevantSamples) =>
|
||||
{
|
||||
string normalBank = relevantSamples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank ?? HitSampleInfo.BANK_SOFT;
|
||||
|
||||
for (int i = 0; i < relevantSamples.Count; i++)
|
||||
{
|
||||
if (relevantSamples[i].Name == HitSampleInfo.HIT_NORMAL) continue;
|
||||
if (relevantSamples[i].Name == HitSampleInfo.HIT_NORMAL)
|
||||
continue;
|
||||
|
||||
relevantSamples[i] = relevantSamples[i].With(newBank: newBank);
|
||||
// Addition samples with bank set to auto should inherit the bank of the normal sample
|
||||
if (newBank == EditorSelectionHandler.HIT_BANK_AUTO)
|
||||
{
|
||||
relevantSamples[i] = relevantSamples[i].With(newBank: normalBank, newEditorAutoBank: true);
|
||||
}
|
||||
else
|
||||
relevantSamples[i] = relevantSamples[i].With(newBank: newBank, newEditorAutoBank: false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -383,7 +396,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
selectionSampleStates[sampleName] = bindable;
|
||||
}
|
||||
|
||||
banks.AddRange(HitSampleInfo.AllBanks);
|
||||
banks.AddRange(HitSampleInfo.AllBanks.Prepend(EditorSelectionHandler.HIT_BANK_AUTO));
|
||||
}
|
||||
|
||||
private void updateTernaryStates()
|
||||
@ -448,7 +461,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
if (string.IsNullOrEmpty(newBank))
|
||||
return true;
|
||||
|
||||
if (e.ShiftPressed)
|
||||
if (e.ShiftPressed && newBank != EditorSelectionHandler.HIT_BANK_AUTO)
|
||||
{
|
||||
setBank(newBank);
|
||||
updatePrimaryBankState();
|
||||
@ -462,7 +475,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = togglesCollection.ElementAtOrDefault(rightIndex);
|
||||
var item = togglesCollection.ElementAtOrDefault(rightIndex - 1);
|
||||
|
||||
if (item is not DrawableTernaryButton button) return base.OnKeyDown(e);
|
||||
|
||||
@ -476,18 +489,22 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case Key.W:
|
||||
case Key.Q:
|
||||
index = 0;
|
||||
break;
|
||||
|
||||
case Key.E:
|
||||
case Key.W:
|
||||
index = 1;
|
||||
break;
|
||||
|
||||
case Key.R:
|
||||
case Key.E:
|
||||
index = 2;
|
||||
break;
|
||||
|
||||
case Key.R:
|
||||
index = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
index = -1;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user