1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 12:33:01 +08:00

Merge pull request #23564 from peppy/fix-missing-sample-additions

Fix sample banks not transferring when adjusting via editor
This commit is contained in:
Bartłomiej Dach 2023-05-20 14:36:45 +02:00 committed by GitHub
commit 0e11adaaac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 81 additions and 36 deletions

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osuTK;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
public override IEnumerable<HitSampleInfo> GetSamples() => new[]
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK)
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
};
protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Pippidon.UI;
using osu.Game.Rulesets.Scoring;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
public override IEnumerable<HitSampleInfo> GetSamples() => new[]
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK)
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
};
protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests
NewCombo = i % 8 == 0,
Samples = new List<HitSampleInfo>(new[]
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100)
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
})
});
}

View File

@ -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)
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
public bool Equals(BananaHitSampleInfo? other)

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Objects
{
StartTime = time,
BananaIndex = i,
Samples = new List<HitSampleInfo> { new Banana.BananaHitSampleInfo(GetSampleInfo().Volume) }
Samples = new List<HitSampleInfo> { new Banana.BananaHitSampleInfo(CreateHitSampleInfo().Volume) }
});
time += spacing;

View File

@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
EndTime = Time.Current + delay + length,
Samples = new List<HitSampleInfo>
{
new HitSampleInfo("hitnormal")
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
}
};

View File

@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Objects
AddNested(i < SpinsRequired
? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration }
: new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { GetSampleInfo("spinnerbonus") } });
: new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { CreateHitSampleInfo("spinnerbonus") } });
}
}

View File

@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
if (isRimType != rimSamples.Any())
{
if (isRimType)
Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP));
Samples.Add(CreateHitSampleInfo(HitSampleInfo.HIT_CLAP));
else
{
foreach (var sample in rimSamples)

View File

@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
if (IsStrongBindable.Value != strongSamples.Any())
{
if (IsStrongBindable.Value)
Samples.Add(GetSampleInfo(HitSampleInfo.HIT_FINISH));
Samples.Add(CreateHitSampleInfo(HitSampleInfo.HIT_FINISH));
else
{
foreach (var sample in strongSamples)

View File

@ -24,7 +24,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
{
public partial class TestSceneHitObjectSamplePointAdjustments : EditorTestScene
public partial class TestSceneHitObjectSampleAdjustments : EditorTestScene
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Editing
Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2,
Samples = new List<HitSampleInfo>
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 80)
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: 80)
}
});
@ -58,6 +58,26 @@ namespace osu.Game.Tests.Visual.Editing
});
}
[Test]
public void TestAddSampleAddition()
{
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
AddStep("add clap addition", () => InputManager.Key(Key.R));
hitObjectHasSampleBank(0, "normal");
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP);
hitObjectHasSampleBank(1, "soft");
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP);
AddStep("remove clap addition", () => InputManager.Key(Key.R));
hitObjectHasSampleBank(0, "normal");
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL);
hitObjectHasSampleBank(1, "soft");
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL);
}
[Test]
public void TestPopoverHasFocus()
{
@ -271,6 +291,12 @@ namespace osu.Game.Tests.Visual.Editing
InputManager.Key(Key.Enter);
});
private void hitObjectHasSamples(int objectIndex, params string[] samples) => AddAssert($"{objectIndex.ToOrdinalWords()} has samples {string.Join(',', samples)}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
return h.Samples.Select(s => s.Name).SequenceEqual(samples);
});
private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () =>
{
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Utils;
namespace osu.Game.Audio
@ -32,7 +33,7 @@ namespace osu.Game.Audio
/// <summary>
/// The bank to load the sample from.
/// </summary>
public readonly string? Bank;
public readonly string Bank;
/// <summary>
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
@ -44,7 +45,7 @@ namespace osu.Game.Audio
/// </summary>
public int Volume { get; }
public HitSampleInfo(string name, string? bank = null, string? suffix = null, int volume = 0)
public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100)
{
Name = name;
Bank = bank;
@ -75,7 +76,7 @@ namespace osu.Game.Audio
/// <param name="newSuffix">An optional new lookup suffix.</param>
/// <param name="newVolume">An optional new volume.</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)
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 bool Equals(HitSampleInfo? other)

View File

@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="hitSampleInfo">The <see cref="HitSampleInfo"/>. This will not be modified.</param>
/// <returns>The modified <see cref="HitSampleInfo"/>. This does not share a reference with <paramref name="hitSampleInfo"/>.</returns>
public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
=> hitSampleInfo.With(newBank: hitSampleInfo.Bank ?? SampleBank, newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume);
=> hitSampleInfo.With(newBank: hitSampleInfo.Bank, newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume);
public override bool IsRedundant(ControlPoint? existing)
=> existing is SampleControlPoint existingSample

View File

@ -226,12 +226,16 @@ namespace osu.Game.Beatmaps.Formats
public override HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
{
var baseInfo = base.ApplyTo(hitSampleInfo);
if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy)
{
return legacy.With(
newCustomSampleBank: legacy.CustomSampleBank > 0 ? legacy.CustomSampleBank : CustomSampleBank,
newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume,
newBank: legacy.BankSpecified ? legacy.Bank : SampleBank
);
}
if (baseInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0)
return legacy.With(newCustomSampleBank: CustomSampleBank);
return baseInfo;
return base.ApplyTo(hitSampleInfo);
}
public override bool IsRedundant(ControlPoint? existing)

View File

@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Edit
HitObject = hitObject;
// adding the default hit sample should be the case regardless of the ruleset.
HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK, volume: 100));
HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL));
RelativeSizeAxes = Axes.Both;

View File

@ -206,14 +206,20 @@ namespace osu.Game.Rulesets.Objects
}
/// <summary>
/// Create a SampleInfo based on the sample settings of the hit normal sample in <see cref="Samples"/>.
/// Create a <see cref="HitSampleInfo"/> based on the sample settings of the first <see cref="HitSampleInfo.HIT_NORMAL"/> sample in <see cref="Samples"/>.
/// If no sample is available, sane default settings will be used instead.
/// </summary>
/// <remarks>
/// In the case an existing sample exists, all settings apart from the sample name will be inherited. This includes volume, bank and suffix.
/// </remarks>
/// <param name="sampleName">The name of the sample.</param>
/// <returns>A populated <see cref="HitSampleInfo"/>.</returns>
protected HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL)
public HitSampleInfo CreateHitSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL)
{
var hitnormalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL);
return hitnormalSample == null ? new HitSampleInfo(sampleName) : hitnormalSample.With(newName: sampleName);
if (Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) is HitSampleInfo existingSample)
return existingSample.With(newName: sampleName);
return new HitSampleInfo(sampleName);
}
}

View File

@ -14,6 +14,7 @@ using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Skinning;
using osu.Game.Utils;
@ -446,9 +447,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (string.IsNullOrEmpty(bankInfo.Filename))
{
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, 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.HasFlagFast(LegacyHitSoundType.Normal)));
// 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.HasFlagFast(LegacyHitSoundType.Normal)));
}
else
{
@ -479,12 +480,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// The bank identifier to use for the base ("hitnormal") sample.
/// Transferred to <see cref="HitSampleInfo.Bank"/> when appropriate.
/// </summary>
[CanBeNull]
public string BankForNormal;
/// <summary>
/// The bank identifier to use for additions ("hitwhistle", "hitfinish", "hitclap").
/// Transferred to <see cref="HitSampleInfo.Bank"/> when appropriate.
/// </summary>
[CanBeNull]
public string BankForAdditions;
/// <summary>
@ -518,17 +521,24 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// </remarks>
public readonly bool IsLayered;
/// <summary>
/// Whether a bank was specified locally to the relevant hitobject.
/// If <c>false</c>, a bank will be retrieved from the closest control point.
/// </summary>
public bool BankSpecified;
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, int customSampleBank = 0, bool isLayered = false)
: base(name, bank, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume)
: base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume)
{
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)
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 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<int> newCustomSampleBank = default,
Optional<bool> newIsLayered = default)
=> new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered));
@ -563,7 +573,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<int> newCustomSampleBank = default,
Optional<bool> newIsLayered = default)
=> new FileHitSampleInfo(Filename, newVolume.GetOr(Volume));

View File

@ -185,7 +185,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
case TernaryState.True:
if (existingSample == null)
samples.Add(new HitSampleInfo(sampleName));
samples.Add(CurrentPlacement.HitObject.CreateHitSampleInfo(sampleName));
break;
}
}

View File

@ -122,7 +122,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (h.Samples.Any(s => s.Name == sampleName))
return;
h.Samples.Add(new HitSampleInfo(sampleName));
h.Samples.Add(h.CreateHitSampleInfo(sampleName));
EditorBeatmap.Update(h);
});
}