Skip to content

Commit 0a78eb9

Browse files
OliBombypeppy
authored andcommitted
Implement auto additions editor-only
1 parent 1b42155 commit 0a78eb9

File tree

6 files changed

+87
-60
lines changed

6 files changed

+87
-60
lines changed

osu.Game/Audio/HitSampleInfo.cs

+10-3
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,18 @@ public class HitSampleInfo : ISampleInfo, IEquatable<HitSampleInfo>
6060
/// </summary>
6161
public int Volume { get; }
6262

63-
public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100)
63+
/// <summary>
64+
/// Whether this sample should automatically assign the bank of the normal sample whenever it is set in the editor.
65+
/// </summary>
66+
public bool EditorAutoBank { get; }
67+
68+
public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100, bool editorAutoBank = true)
6469
{
6570
Name = name;
6671
Bank = bank;
6772
Suffix = suffix;
6873
Volume = volume;
74+
EditorAutoBank = editorAutoBank;
6975
}
7076

7177
/// <summary>
@@ -92,9 +98,10 @@ public virtual IEnumerable<string> LookupNames
9298
/// <param name="newBank">An optional new sample bank.</param>
9399
/// <param name="newSuffix">An optional new lookup suffix.</param>
94100
/// <param name="newVolume">An optional new volume.</param>
101+
/// <param name="newEditorAutoBank">An optional new editor auto bank flag.</param>
95102
/// <returns>The new <see cref="HitSampleInfo"/>.</returns>
96-
public virtual HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
97-
=> new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume));
103+
public virtual HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default)
104+
=> new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume), newEditorAutoBank.GetOr(EditorAutoBank));
98105

99106
public virtual bool Equals(HitSampleInfo? other)
100107
=> other != null && Name == other.Name && Bank == other.Bank && Suffix == other.Suffix;

osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ private void addEndTimeData(TextWriter writer, HitObject hitObject)
539539
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false)
540540
{
541541
LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank);
542-
LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL)?.Bank);
542+
LegacySampleBank addBank = toLegacySampleBank(samples.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name != HitSampleInfo.HIT_NORMAL && !s.EditorAutoBank)?.Bank);
543543

544544
StringBuilder sb = new StringBuilder();
545545

osu.Game/Rulesets/Objects/HitObject.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public HitSampleInfo CreateHitSampleInfo(string sampleName = HitSampleInfo.HIT_N
233233

234234
// Fall back to using the normal sample bank otherwise.
235235
if (Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) is HitSampleInfo existingNormal)
236-
return existingNormal.With(newName: sampleName);
236+
return existingNormal.With(newName: sampleName, newEditorAutoBank: true);
237237

238238
return new HitSampleInfo(sampleName);
239239
}

osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs

+22-11
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,14 @@ private void readCustomSampleBanks(string str, SampleBankInfo bankInfo, bool ban
204204
if (stringBank == @"none")
205205
stringBank = null;
206206
string stringAddBank = addBank.ToString().ToLowerInvariant();
207+
207208
if (stringAddBank == @"none")
209+
{
210+
bankInfo.EditorAutoBank = true;
208211
stringAddBank = null;
212+
}
213+
else
214+
bankInfo.EditorAutoBank = false;
209215

210216
bankInfo.BankForNormal = stringBank;
211217
bankInfo.BankForAdditions = string.IsNullOrEmpty(stringAddBank) ? stringBank : stringAddBank;
@@ -477,7 +483,7 @@ private List<HitSampleInfo> convertSoundType(LegacyHitSoundType type, SampleBank
477483

478484
if (string.IsNullOrEmpty(bankInfo.Filename))
479485
{
480-
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank,
486+
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, true, bankInfo.CustomSampleBank,
481487
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
482488
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
483489
type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal)));
@@ -489,13 +495,13 @@ private List<HitSampleInfo> convertSoundType(LegacyHitSoundType type, SampleBank
489495
}
490496

491497
if (type.HasFlag(LegacyHitSoundType.Finish))
492-
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
498+
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.EditorAutoBank, bankInfo.CustomSampleBank));
493499

494500
if (type.HasFlag(LegacyHitSoundType.Whistle))
495-
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
501+
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.EditorAutoBank, bankInfo.CustomSampleBank));
496502

497503
if (type.HasFlag(LegacyHitSoundType.Clap))
498-
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
504+
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.EditorAutoBank, bankInfo.CustomSampleBank));
499505

500506
return soundTypes;
501507
}
@@ -534,6 +540,11 @@ private class SampleBankInfo
534540
/// </summary>
535541
public int CustomSampleBank;
536542

543+
/// <summary>
544+
/// Whether the bank for additions should be inherited from the normal sample in edit.
545+
/// </summary>
546+
public bool EditorAutoBank;
547+
537548
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
538549
}
539550

@@ -558,21 +569,21 @@ public class LegacyHitSampleInfo : HitSampleInfo, IEquatable<LegacyHitSampleInfo
558569
/// </summary>
559570
public bool BankSpecified;
560571

561-
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, int customSampleBank = 0, bool isLayered = false)
562-
: base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume)
572+
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, bool editorAutoBank = false, int customSampleBank = 0, bool isLayered = false)
573+
: base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume, editorAutoBank)
563574
{
564575
CustomSampleBank = customSampleBank;
565576
BankSpecified = !string.IsNullOrEmpty(bank);
566577
IsLayered = isLayered;
567578
}
568579

569-
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
570-
=> With(newName, newBank, newVolume);
580+
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)
581+
=> With(newName, newBank, newVolume, newEditorAutoBank);
571582

572-
public virtual LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default,
583+
public virtual LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default,
573584
Optional<int> newCustomSampleBank = default,
574585
Optional<bool> newIsLayered = default)
575-
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
586+
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newEditorAutoBank.GetOr(EditorAutoBank), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
576587

577588
public bool Equals(LegacyHitSampleInfo? other)
578589
// The additions to equality checks here are *required* to ensure that pooling works correctly.
@@ -604,7 +615,7 @@ public FileHitSampleInfo(string filename, int volume)
604615
Path.ChangeExtension(Filename, null)
605616
};
606617

607-
public sealed override LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default,
618+
public sealed override LegacyHitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<int> newVolume = default, Optional<bool> newEditorAutoBank = default,
608619
Optional<int> newCustomSampleBank = default,
609620
Optional<bool> newIsLayered = default)
610621
=> new FileHitSampleInfo(Filename, newVolume.GetOr(Volume));

osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs

+26-34
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,23 @@ private void createStateBindables()
152152
}
153153
else
154154
{
155-
// Auto should never apply when there is a selection made.
156-
if (bankName == HIT_BANK_AUTO)
157-
break;
158-
159155
// Completely empty selections should be allowed in the case that none of the selected objects have any addition samples.
160156
// This is also required to stop a bindable feedback loop when a HitObject has zero addition samples (and LINQ `All` below becomes true).
161157
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
162158
break;
163159

164160
// Never remove a sample bank.
165161
// These are basically radio buttons, not toggles.
166-
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName)))
167-
bindable.Value = TernaryState.True;
162+
if (bankName == HIT_BANK_AUTO)
163+
{
164+
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.EditorAutoBank)))
165+
bindable.Value = TernaryState.True;
166+
}
167+
else
168+
{
169+
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName && !s.EditorAutoBank)))
170+
bindable.Value = TernaryState.True;
171+
}
168172
}
169173

170174
break;
@@ -182,14 +186,6 @@ private void createStateBindables()
182186
}
183187
else
184188
{
185-
// Auto should just not apply if there's a selection already made.
186-
// Maybe we could make it a disabled button in the future, but right now the editor buttons don't support disabled state.
187-
if (bankName == HIT_BANK_AUTO)
188-
{
189-
bindable.Value = TernaryState.False;
190-
break;
191-
}
192-
193189
// If none of the selected objects have any addition samples, we should not apply the addition bank.
194190
if (SelectedItems.SelectMany(enumerateAllSamples).All(h => h.All(o => o.Name == HitSampleInfo.HIT_NORMAL)))
195191
{
@@ -209,7 +205,7 @@ private void createStateBindables()
209205

210206
// start with normal selected.
211207
SelectionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
212-
SelectionAdditionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True;
208+
SelectionAdditionBankStates[HIT_BANK_AUTO].Value = TernaryState.True;
213209

214210
foreach (string sampleName in HitSampleInfo.AllAdditions)
215211
{
@@ -272,7 +268,7 @@ protected virtual void UpdateTernaryStates()
272268

273269
foreach ((string bankName, var bindable) in SelectionAdditionBankStates)
274270
{
275-
bindable.Value = GetStateFromSelection(samplesInSelection.SelectMany(s => s).Where(o => o.Name != HitSampleInfo.HIT_NORMAL), h => h.Bank == bankName);
271+
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));
276272
}
277273
}
278274

@@ -336,33 +332,29 @@ bool hasRelevantBank(HitObject hitObject)
336332
/// <param name="bankName">The name of the sample bank.</param>
337333
public void SetSampleAdditionBank(string bankName)
338334
{
339-
bool hasRelevantBank(HitObject hitObject)
340-
{
341-
bool result = hitObject.Samples.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
342-
343-
if (hitObject is IHasRepeats hasRepeats)
344-
{
345-
foreach (var node in hasRepeats.NodeSamples)
346-
result &= node.Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName);
347-
}
348-
349-
return result;
350-
}
335+
bool hasRelevantBank(HitObject hitObject) =>
336+
bankName == HIT_BANK_AUTO
337+
? enumerateAllSamples(hitObject).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.EditorAutoBank)
338+
: enumerateAllSamples(hitObject).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName && !s.EditorAutoBank);
351339

352340
if (SelectedItems.All(hasRelevantBank))
353341
return;
354342

355343
EditorBeatmap.PerformOnSelection(h =>
356344
{
357-
if (enumerateAllSamples(h).SelectMany(o => o).Where(o => o.Name != HitSampleInfo.HIT_NORMAL).All(s => s.Bank == bankName))
345+
if (hasRelevantBank(h))
358346
return;
359347

360-
h.Samples = h.Samples.Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
348+
string normalBank = h.Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank ?? HitSampleInfo.BANK_SOFT;
349+
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();
361350

362351
if (h is IHasRepeats hasRepeats)
363352
{
364353
for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
365-
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.Name != HitSampleInfo.HIT_NORMAL ? s.With(newBank: bankName) : s).ToList();
354+
{
355+
normalBank = hasRepeats.NodeSamples[i].FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank ?? HitSampleInfo.BANK_SOFT;
356+
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();
357+
}
366358
}
367359

368360
EditorBeatmap.Update(h);
@@ -406,9 +398,9 @@ public void AddHitSample(string sampleName)
406398

407399
var hitSample = h.CreateHitSampleInfo(sampleName);
408400

409-
string? existingAdditionBank = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL)?.Bank;
410-
if (existingAdditionBank != null)
411-
hitSample = hitSample.With(newBank: existingAdditionBank);
401+
HitSampleInfo? existingAddition = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL);
402+
if (existingAddition != null)
403+
hitSample = hitSample.With(newBank: existingAddition.Bank, newEditorAutoBank: existingAddition.EditorAutoBank);
412404

413405
node.Add(hitSample);
414406
}

0 commit comments

Comments
 (0)