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

Merge branch 'ppy:master' into organize-room-pills

This commit is contained in:
timiimit 2023-05-21 16:36:53 +02:00 committed by GitHub
commit 4441718437
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 220 additions and 90 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)

7
global.json Normal file
View File

@ -0,0 +1,7 @@
{
"sdk": {
"version": "6.0.100",
"rollForward": "latestFeature"
}
}

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

@ -1,10 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
@ -24,9 +21,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
private bool isPlacingEnd;
[Resolved(CanBeNull = true)]
[CanBeNull]
private IBeatSnapProvider beatSnapProvider { get; set; }
[Resolved]
private IBeatSnapProvider? beatSnapProvider { get; set; }
public SpinnerPlacementBlueprint()
: base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 })

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

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq;
using osu.Game.Rulesets.Objects.Types;
@ -22,34 +20,17 @@ namespace osu.Game.Beatmaps
public virtual void PreProcess()
{
IHasComboInformation lastObj = null;
bool isFirst = true;
IHasComboInformation? lastObj = null;
foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>())
{
if (isFirst)
if (lastObj == null)
{
obj.NewCombo = true;
// first hitobject should always be marked as a new combo for sanity.
isFirst = false;
}
obj.ComboIndex = lastObj?.ComboIndex ?? 0;
obj.ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0;
obj.IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0;
if (obj.NewCombo)
{
obj.IndexInCurrentCombo = 0;
obj.ComboIndex++;
obj.ComboIndexWithOffsets += obj.ComboOffset + 1;
if (lastObj != null)
lastObj.LastInCombo = true;
obj.NewCombo = true;
}
obj.UpdateComboInformation(lastObj);
lastObj = obj;
}
}

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

@ -77,10 +77,10 @@ namespace osu.Game.Graphics.UserInterface
private void updateState()
{
hoverClickSounds.Enabled.Value = !Item.Action.Disabled;
Alpha = Item.Action.Disabled ? 0.2f : 1;
hoverClickSounds.Enabled.Value = IsActionable;
Alpha = IsActionable ? 1 : 0.2f;
if (IsHovered && !Item.Action.Disabled)
if (IsHovered && IsActionable)
{
text.BoldText.FadeIn(transition_length, Easing.OutQuint);
text.NormalText.FadeOut(transition_length, Easing.OutQuint);

View File

@ -77,6 +77,14 @@ namespace osu.Game.Overlays
base.Content.Add(mainContent);
}
protected override void LoadComplete()
{
base.LoadComplete();
// Ensure the scroll-to-top button is displayed above the fixed header.
AddInternal(ScrollFlow.Button.CreateProxy());
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();

View File

@ -31,7 +31,7 @@ namespace osu.Game.Overlays
/// </summary>
private const int button_scroll_position = 200;
protected ScrollBackButton Button;
public ScrollBackButton Button { get; private set; }
private readonly Bindable<float?> lastScrollTarget = new Bindable<float?>();

View File

@ -193,6 +193,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
currentDisplay.BindValueChanged(display => Schedule(() =>
{
// there is no easy way to perform an atomic swap on the `resolutions` list, which is bound to the dropdown via `ItemSource`.
// this means, if the old resolution isn't saved, the `RemoveRange()` call below will cause `resolutionDropdown.Current` to reset value,
// therefore making it impossible to select any dropdown value other than "Default".
// to circumvent this locally, store the old value here, so that we can attempt to restore it later.
var oldResolution = resolutionDropdown.Current.Value;
resolutions.RemoveRange(1, resolutions.Count - 1);
if (display.NewValue != null)
@ -204,6 +210,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
.Distinct());
}
if (resolutions.Contains(oldResolution))
resolutionDropdown.Current.Value = oldResolution;
updateDisplaySettingsVisibility();
}), true);
@ -244,7 +253,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
Scheduler.AddOnce(d =>
{
displayDropdown.Items = d;
if (!displayDropdown.Items.SequenceEqual(d, DisplayListComparer.DEFAULT))
displayDropdown.Items = d;
updateDisplaySettingsVisibility();
}, displays);
}
@ -376,5 +386,43 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
}
}
}
/// <summary>
/// Contrary to <see cref="Display.Equals(osu.Framework.Platform.Display?)"/>, this comparer disregards the value of <see cref="Display.Bounds"/>.
/// We want to just show a list of displays, and for the purposes of settings we don't care about their bounds when it comes to the list.
/// However, <see cref="IWindow.DisplaysChanged"/> fires even if only the resolution of the current display was changed
/// (because it causes the bounds of all displays to also change).
/// We're not interested in those changes, so compare only the rest that we actually care about.
/// This helps to avoid a bindable/event feedback loop, in which a resolution change
/// would trigger a display "change", which would in turn reset resolution again.
/// </summary>
private class DisplayListComparer : IEqualityComparer<Display>
{
public static readonly DisplayListComparer DEFAULT = new DisplayListComparer();
public bool Equals(Display? x, Display? y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
return x.Index == y.Index
&& x.Name == y.Name
&& x.DisplayModes.SequenceEqual(y.DisplayModes);
}
public int GetHashCode(Display obj)
{
var hashCode = new HashCode();
hashCode.Add(obj.Index);
hashCode.Add(obj.Name);
hashCode.Add(obj.DisplayModes.Length);
foreach (var displayMode in obj.DisplayModes)
hashCode.Add(displayMode);
return hashCode.ToHashCode();
}
}
}
}

View File

@ -249,12 +249,14 @@ namespace osu.Game.Overlays
private partial class ProfileSectionsContainer : SectionsContainer<ProfileSection>
{
private OverlayScrollContainer scroll = null!;
public ProfileSectionsContainer()
{
RelativeSizeAxes = Axes.Both;
}
protected override UserTrackingScrollContainer CreateScrollContainer() => new OverlayScrollContainer();
protected override UserTrackingScrollContainer CreateScrollContainer() => scroll = new OverlayScrollContainer();
// Reverse child ID is required so expanding beatmap panels can appear above sections below them.
// This can also be done by setting Depth when adding new sections above if using ReverseChildID turns out to have any issues.
@ -267,6 +269,14 @@ namespace osu.Game.Overlays
Padding = new MarginPadding { Horizontal = 10 },
Margin = new MarginPadding { Bottom = 10 },
};
protected override void LoadComplete()
{
base.LoadComplete();
// Ensure the scroll-to-top button is displayed above the fixed header.
AddInternal(scroll.Button.CreateProxy());
}
}
}
}

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq;
using System.Threading;
using osu.Framework.Allocation;
@ -16,6 +14,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose;
using osuTK;
@ -38,16 +37,18 @@ namespace osu.Game.Rulesets.Edit
/// </summary>
public readonly HitObject HitObject;
[Resolved(canBeNull: true)]
protected EditorClock EditorClock { get; private set; }
[Resolved]
protected EditorClock EditorClock { get; private set; } = null!;
[Resolved]
private EditorBeatmap beatmap { get; set; }
private EditorBeatmap beatmap { get; set; } = null!;
private Bindable<double> startTimeBindable;
private Bindable<double> startTimeBindable = null!;
private HitObject? getPreviousHitObject() => beatmap.HitObjects.TakeWhile(h => h.StartTime <= startTimeBindable.Value).LastOrDefault();
[Resolved]
private IPlacementHandler placementHandler { get; set; }
private IPlacementHandler placementHandler { get; set; } = null!;
/// <summary>
/// Whether this blueprint is currently in a state that can be committed.
@ -63,7 +64,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;
@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Edit
protected void BeginPlacement(bool commitStart = false)
{
// Take the hitnormal sample of the last hit object
var lastHitNormal = beatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL);
var lastHitNormal = getPreviousHitObject()?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL);
if (lastHitNormal != null)
HitObject.Samples[0] = lastHitNormal;
@ -148,7 +149,12 @@ namespace osu.Game.Rulesets.Edit
public virtual void UpdateTimeAndPosition(SnapResult result)
{
if (PlacementActive == PlacementState.Waiting)
HitObject.StartTime = result.Time ?? EditorClock?.CurrentTime ?? Time.Current;
{
HitObject.StartTime = result.Time ?? EditorClock.CurrentTime;
if (HitObject is IHasComboInformation comboInformation)
comboInformation.UpdateComboInformation(getPreviousHitObject() as IHasComboInformation);
}
}
/// <summary>

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

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Bindables;
using osu.Game.Skinning;
using osuTK.Graphics;
@ -65,5 +63,26 @@ namespace osu.Game.Rulesets.Objects.Types
{
return skin.GetConfig<SkinComboColourLookup, Color4>(new SkinComboColourLookup(comboIndex, combo))?.Value ?? Color4.White;
}
/// <summary>
/// Given the previous object in the beatmap, update relevant combo information.
/// </summary>
/// <param name="lastObj">The previous hitobject, or null if this is the first object in the beatmap.</param>
void UpdateComboInformation(IHasComboInformation? lastObj)
{
ComboIndex = lastObj?.ComboIndex ?? 0;
ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0;
IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0;
if (NewCombo || lastObj == null)
{
IndexInCurrentCombo = 0;
ComboIndex++;
ComboIndexWithOffsets += ComboOffset + 1;
if (lastObj != null)
lastObj.LastInCombo = true;
}
}
}
}

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);
});
}

View File

@ -83,8 +83,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
placementBlueprint = CreateBlueprintFor(obj.NewValue).AsNonNull();
placementBlueprint.Colour = Color4.MediumPurple;
placementBlueprint.Colour = OsuColour.Gray(0.9f);
// TODO: this is out of order, causing incorrect stacking height.
SelectionBlueprints.Add(placementBlueprint);
}
}

View File

@ -93,16 +93,22 @@ namespace osu.Game.Screens.Select
protected override bool OnKeyDown(KeyDownEvent e)
{
updateText(e.ShiftPressed);
updateText(e);
return base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyUpEvent e)
{
updateText(e.ShiftPressed);
updateText(e);
base.OnKeyUp(e);
}
protected override bool OnMouseDown(MouseDownEvent e)
{
updateText(e);
return base.OnMouseDown(e);
}
protected override bool OnClick(ClickEvent e)
{
try
@ -119,14 +125,15 @@ namespace osu.Game.Screens.Select
protected override void OnMouseUp(MouseUpEvent e)
{
base.OnMouseUp(e);
if (e.Button == MouseButton.Right && IsHovered)
{
rewindSearch = true;
TriggerClick();
return;
}
base.OnMouseUp(e);
updateText(e);
}
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
@ -151,10 +158,12 @@ namespace osu.Game.Screens.Select
}
}
private void updateText(bool rewind = false)
private void updateText(UIEvent e)
{
randomSpriteText.Alpha = rewind ? 0 : 1;
rewindSpriteText.Alpha = rewind ? 1 : 0;
bool aboutToRewind = e.ShiftPressed || e.CurrentState.Mouse.IsPressed(MouseButton.Right);
randomSpriteText.Alpha = aboutToRewind ? 0 : 1;
rewindSpriteText.Alpha = aboutToRewind ? 1 : 0;
}
}
}