mirror of
https://github.com/ppy/osu.git
synced 2024-11-15 12:27:26 +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));
|
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
|
||||||
|
|
||||||
public bool Equals(BananaHitSampleInfo? other)
|
public bool Equals(BananaHitSampleInfo? other)
|
||||||
|
@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
if (slider == null) return;
|
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());
|
slider.Samples.Add(sample.With());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,12 +60,18 @@ namespace osu.Game.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Volume { get; }
|
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;
|
Name = name;
|
||||||
Bank = bank;
|
Bank = bank;
|
||||||
Suffix = suffix;
|
Suffix = suffix;
|
||||||
Volume = volume;
|
Volume = volume;
|
||||||
|
EditorAutoBank = editorAutoBank;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -92,9 +98,10 @@ namespace osu.Game.Audio
|
|||||||
/// <param name="newBank">An optional new sample bank.</param>
|
/// <param name="newBank">An optional new sample bank.</param>
|
||||||
/// <param name="newSuffix">An optional new lookup suffix.</param>
|
/// <param name="newSuffix">An optional new lookup suffix.</param>
|
||||||
/// <param name="newVolume">An optional new volume.</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>
|
/// <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)
|
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));
|
=> new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume), newEditorAutoBank.GetOr(EditorAutoBank));
|
||||||
|
|
||||||
public virtual bool Equals(HitSampleInfo? other)
|
public virtual bool Equals(HitSampleInfo? other)
|
||||||
=> other != null && Name == other.Name && Bank == other.Bank && Suffix == other.Suffix;
|
=> 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)
|
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false)
|
||||||
{
|
{
|
||||||
LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank);
|
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();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
|
|
||||||
// Fall back to using the normal sample bank otherwise.
|
// Fall back to using the normal sample bank otherwise.
|
||||||
if (Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) is HitSampleInfo existingNormal)
|
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);
|
return new HitSampleInfo(sampleName);
|
||||||
}
|
}
|
||||||
|
@ -204,8 +204,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
if (stringBank == @"none")
|
if (stringBank == @"none")
|
||||||
stringBank = null;
|
stringBank = null;
|
||||||
string stringAddBank = addBank.ToString().ToLowerInvariant();
|
string stringAddBank = addBank.ToString().ToLowerInvariant();
|
||||||
|
|
||||||
if (stringAddBank == @"none")
|
if (stringAddBank == @"none")
|
||||||
|
{
|
||||||
|
bankInfo.EditorAutoBank = true;
|
||||||
stringAddBank = null;
|
stringAddBank = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bankInfo.EditorAutoBank = false;
|
||||||
|
|
||||||
bankInfo.BankForNormal = stringBank;
|
bankInfo.BankForNormal = stringBank;
|
||||||
bankInfo.BankForAdditions = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank;
|
bankInfo.BankForAdditions = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank;
|
||||||
@ -477,7 +483,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
if (string.IsNullOrEmpty(bankInfo.Filename))
|
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.
|
// 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
|
// 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)));
|
type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal)));
|
||||||
@ -489,13 +495,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type.HasFlag(LegacyHitSoundType.Finish))
|
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))
|
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))
|
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;
|
return soundTypes;
|
||||||
}
|
}
|
||||||
@ -534,6 +540,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int CustomSampleBank;
|
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();
|
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,21 +569,21 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool BankSpecified;
|
public bool BankSpecified;
|
||||||
|
|
||||||
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, int customSampleBank = 0, bool isLayered = false)
|
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)
|
: base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume, editorAutoBank)
|
||||||
{
|
{
|
||||||
CustomSampleBank = customSampleBank;
|
CustomSampleBank = customSampleBank;
|
||||||
BankSpecified = !string.IsNullOrEmpty(bank);
|
BankSpecified = !string.IsNullOrEmpty(bank);
|
||||||
IsLayered = isLayered;
|
IsLayered = isLayered;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
=> With(newName, newBank, newVolume);
|
=> 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<int> newCustomSampleBank = default,
|
||||||
Optional<bool> newIsLayered = 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)
|
public bool Equals(LegacyHitSampleInfo? other)
|
||||||
// The additions to equality checks here are *required* to ensure that pooling works correctly.
|
// 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)
|
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<int> newCustomSampleBank = default,
|
||||||
Optional<bool> newIsLayered = default)
|
Optional<bool> newIsLayered = default)
|
||||||
=> new FileHitSampleInfo(Filename, newVolume.GetOr(Volume));
|
=> new FileHitSampleInfo(Filename, newVolume.GetOr(Volume));
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -14,7 +16,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||||
{
|
{
|
||||||
public partial class DrawableTernaryButton : OsuButton
|
public partial class DrawableTernaryButton : OsuButton, IHasTooltip
|
||||||
{
|
{
|
||||||
private Color4 defaultBackgroundColour;
|
private Color4 defaultBackgroundColour;
|
||||||
private Color4 defaultIconColour;
|
private Color4 defaultIconColour;
|
||||||
@ -58,12 +60,16 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Button.Bindable.BindValueChanged(_ => updateSelectionState(), true);
|
Button.Bindable.BindValueChanged(_ => updateSelectionState(), true);
|
||||||
|
Button.Enabled.BindTo(Enabled);
|
||||||
|
|
||||||
Action = onAction;
|
Action = onAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAction()
|
private void onAction()
|
||||||
{
|
{
|
||||||
|
if (!Button.Enabled.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
Button.Toggle();
|
Button.Toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,5 +104,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
|||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
X = 40f
|
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<TernaryState> Bindable;
|
||||||
|
|
||||||
|
public readonly Bindable<bool> Enabled = new Bindable<bool>(true);
|
||||||
|
|
||||||
public readonly string Description;
|
public readonly string Description;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -19,6 +21,8 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Func<Drawable>? CreateIcon;
|
public readonly Func<Drawable>? CreateIcon;
|
||||||
|
|
||||||
|
public string Tooltip { get; set; } = string.Empty;
|
||||||
|
|
||||||
public TernaryButton(Bindable<TernaryState> bindable, string description, Func<Drawable>? createIcon = null)
|
public TernaryButton(Bindable<TernaryState> bindable, string description, Func<Drawable>? createIcon = null)
|
||||||
{
|
{
|
||||||
Bindable = bindable;
|
Bindable = bindable;
|
||||||
|
@ -68,6 +68,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
SampleBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionBankStates).ToArray();
|
SampleBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionBankStates).ToArray();
|
||||||
SampleAdditionBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionAdditionBankStates).ToArray();
|
SampleAdditionBankTernaryStates = createSampleBankTernaryButtons(SelectionHandler.SelectionAdditionBankStates).ToArray();
|
||||||
|
|
||||||
|
SelectionHandler.AutoSelectionBankEnabled.BindValueChanged(_ => updateAutoBankTernaryButtonTooltip(), true);
|
||||||
|
SelectionHandler.SelectionAdditionBanksEnabled.BindValueChanged(_ => updateAdditionBankTernaryButtonTooltips(), true);
|
||||||
|
|
||||||
AddInternal(new DrawableRulesetDependenciesProvidingContainer(Composer.Ruleset)
|
AddInternal(new DrawableRulesetDependenciesProvidingContainer(Composer.Ruleset)
|
||||||
{
|
{
|
||||||
Child = placementBlueprintContainer
|
Child = placementBlueprintContainer
|
||||||
@ -288,6 +291,26 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return null;
|
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
|
#region Placement
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -10,7 +11,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
// bring in updates from selection changes
|
// bring in updates from selection changes
|
||||||
EditorBeatmap.HitObjectUpdated += _ => Scheduler.AddOnce(UpdateTernaryStates);
|
EditorBeatmap.HitObjectUpdated += _ => Scheduler.AddOnce(UpdateTernaryStates);
|
||||||
|
|
||||||
SelectedItems.CollectionChanged += (_, _) => Scheduler.AddOnce(UpdateTernaryStates);
|
SelectedItems.CollectionChanged += onSelectedItemsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DeleteItems(IEnumerable<HitObject> items) => EditorBeatmap.RemoveRange(items);
|
protected override void DeleteItems(IEnumerable<HitObject> items) => EditorBeatmap.RemoveRange(items);
|
||||||
@ -64,6 +64,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Dictionary<string, Bindable<TernaryState>> SelectionAdditionBankStates = new Dictionary<string, Bindable<TernaryState>>();
|
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>
|
/// <summary>
|
||||||
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
|
/// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -153,10 +163,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
}
|
}
|
||||||
else
|
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.
|
// 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).
|
// 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)))
|
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
|
||||||
@ -164,9 +170,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
// Never remove a sample bank.
|
// Never remove a sample bank.
|
||||||
// These are basically radio buttons, not toggles.
|
// 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)))
|
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;
|
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;
|
break;
|
||||||
|
|
||||||
@ -183,14 +197,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
}
|
}
|
||||||
else
|
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 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)))
|
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;
|
SelectionAdditionBankStates[bankName] = bindable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start with normal selected.
|
resetTernaryStates();
|
||||||
SelectionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
|
|
||||||
SelectionAdditionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
|
|
||||||
|
|
||||||
foreach (string sampleName in HitSampleInfo.AllAdditions)
|
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>
|
/// <summary>
|
||||||
/// Called when context menu ternary states may need to be recalculated (selection changed or hitobject updated).
|
/// Called when context menu ternary states may need to be recalculated (selection changed or hitobject updated).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void UpdateTernaryStates()
|
protected virtual void UpdateTernaryStates()
|
||||||
{
|
{
|
||||||
SelectionNewComboState.Value = GetStateFromSelection(SelectedItems.OfType<IHasComboInformation>(), h => h.NewCombo);
|
SelectionNewComboState.Value = GetStateFromSelection(SelectedItems.OfType<IHasComboInformation>(), h => h.NewCombo);
|
||||||
|
AutoSelectionBankEnabled.Value = SelectedItems.Count == 0;
|
||||||
|
|
||||||
var samplesInSelection = SelectedItems.SelectMany(enumerateAllSamples).ToArray();
|
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);
|
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)
|
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)
|
private IEnumerable<IList<HitSampleInfo>> enumerateAllSamples(HitObject hitObject)
|
||||||
{
|
{
|
||||||
yield return hitObject.Samples;
|
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>
|
/// <param name="bankName">The name of the sample bank.</param>
|
||||||
public void SetSampleAdditionBank(string bankName)
|
public void SetSampleAdditionBank(string bankName)
|
||||||
{
|
{
|
||||||
bool hasRelevantBank(HitObject hitObject)
|
bool hasRelevantBank(HitObject hitObject) =>
|
||||||
{
|
bankName == HIT_BANK_AUTO
|
||||||
bool result = hitObject.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
|
? 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 (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedItems.All(hasRelevantBank))
|
if (SelectedItems.All(hasRelevantBank))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EditorBeatmap.PerformOnSelection(h =>
|
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;
|
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)
|
if (h is IHasRepeats hasRepeats)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
|
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);
|
EditorBeatmap.Update(h);
|
||||||
@ -407,9 +427,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
var hitSample = h.CreateHitSampleInfo(sampleName);
|
var hitSample = h.CreateHitSampleInfo(sampleName);
|
||||||
|
|
||||||
string? existingAdditionBank = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL)?.Bank;
|
HitSampleInfo? existingAddition = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL);
|
||||||
if (existingAdditionBank != null)
|
if (existingAddition != null)
|
||||||
hitSample = hitSample.With(newBank: existingAdditionBank);
|
hitSample = hitSample.With(newBank: existingAddition.Bank, newEditorAutoBank: existingAddition.EditorAutoBank);
|
||||||
|
|
||||||
node.Add(hitSample);
|
node.Add(hitSample);
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
public static string? GetAdditionBankValue(IEnumerable<HitSampleInfo> samples)
|
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)
|
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++)
|
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);
|
relevantSamples[i] = relevantSamples[i].With(newBank: newBank);
|
||||||
}
|
}
|
||||||
@ -331,11 +335,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
updateAllRelevantSamples((_, relevantSamples) =>
|
updateAllRelevantSamples((_, relevantSamples) =>
|
||||||
{
|
{
|
||||||
|
string normalBank = relevantSamples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank ?? HitSampleInfo.BANK_SOFT;
|
||||||
|
|
||||||
for (int i = 0; i < relevantSamples.Count; i++)
|
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;
|
selectionSampleStates[sampleName] = bindable;
|
||||||
}
|
}
|
||||||
|
|
||||||
banks.AddRange(HitSampleInfo.AllBanks);
|
banks.AddRange(HitSampleInfo.AllBanks.Prepend(EditorSelectionHandler.HIT_BANK_AUTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTernaryStates()
|
private void updateTernaryStates()
|
||||||
@ -448,7 +461,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
if (string.IsNullOrEmpty(newBank))
|
if (string.IsNullOrEmpty(newBank))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (e.ShiftPressed)
|
if (e.ShiftPressed && newBank != EditorSelectionHandler.HIT_BANK_AUTO)
|
||||||
{
|
{
|
||||||
setBank(newBank);
|
setBank(newBank);
|
||||||
updatePrimaryBankState();
|
updatePrimaryBankState();
|
||||||
@ -462,7 +475,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var item = togglesCollection.ElementAtOrDefault(rightIndex);
|
var item = togglesCollection.ElementAtOrDefault(rightIndex - 1);
|
||||||
|
|
||||||
if (item is not DrawableTernaryButton button) return base.OnKeyDown(e);
|
if (item is not DrawableTernaryButton button) return base.OnKeyDown(e);
|
||||||
|
|
||||||
@ -476,18 +489,22 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case Key.W:
|
case Key.Q:
|
||||||
index = 0;
|
index = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.E:
|
case Key.W:
|
||||||
index = 1;
|
index = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Key.R:
|
case Key.E:
|
||||||
index = 2;
|
index = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Key.R:
|
||||||
|
index = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
index = -1;
|
index = -1;
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user