mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 14:17:26 +08:00
Merge branch 'immutable-hit-samples' into hit-sample-pooling
This commit is contained in:
commit
2bb7484608
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
NewCombo = i % 8 == 0,
|
NewCombo = i % 8 == 0,
|
||||||
Samples = new List<HitSampleInfo>(new[]
|
Samples = new List<HitSampleInfo>(new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo { Bank = "normal", Name = "hitnormal", Volume = 100 }
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal")
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,16 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public override IEnumerable<string> LookupNames => lookup_names;
|
public override IEnumerable<string> LookupNames => lookup_names;
|
||||||
|
|
||||||
public bool Equals(BananaHitSampleInfo other) => true;
|
public BananaHitSampleInfo()
|
||||||
|
: base(string.Empty)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is BananaHitSampleInfo other && Equals(other);
|
public bool Equals(BananaHitSampleInfo other)
|
||||||
|
=> other != null;
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
=> Equals((BananaHitSampleInfo)obj);
|
||||||
|
|
||||||
public override int GetHashCode() => lookup_names.GetHashCode();
|
public override int GetHashCode() => lookup_names.GetHashCode();
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
base.CreateNestedHitObjects(cancellationToken);
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
var dropletSamples = Samples.Select(s => new HitSampleInfo
|
var dropletSamples = Samples.Select(s => s.With(@"slidertick")).ToList();
|
||||||
{
|
|
||||||
Bank = s.Bank,
|
|
||||||
Name = @"slidertick",
|
|
||||||
Volume = s.Volume
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
int nodeIndex = 0;
|
int nodeIndex = 0;
|
||||||
SliderEventDescriptor? lastEvent = null;
|
SliderEventDescriptor? lastEvent = null;
|
||||||
|
@ -78,9 +78,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
private double originalStartTime;
|
private double originalStartTime;
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (PlacementActive)
|
if (PlacementActive)
|
||||||
{
|
{
|
||||||
|
@ -48,9 +48,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (!PlacementActive)
|
if (!PlacementActive)
|
||||||
Column = result.Playfield as Column;
|
Column = result.Playfield as Column;
|
||||||
|
@ -22,9 +22,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
InternalChild = piece = new EditNotePiece { Origin = Anchor.Centre };
|
InternalChild = piece = new EditNotePiece { Origin = Anchor.Centre };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (result.Playfield != null)
|
if (result.Playfield != null)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneObjectBeatSnap : TestSceneOsuEditor
|
||||||
|
{
|
||||||
|
private OsuPlayfield playfield;
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false);
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
AddStep("get playfield", () => playfield = Editor.ChildrenOfType<OsuPlayfield>().First());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatSnapHitCircle()
|
||||||
|
{
|
||||||
|
double firstTimingPointTime() => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time;
|
||||||
|
|
||||||
|
AddStep("seek some milliseconds forward", () => EditorClock.Seek(firstTimingPointTime() + 10));
|
||||||
|
|
||||||
|
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddStep("enter placement mode", () => InputManager.Key(Key.Number2));
|
||||||
|
AddStep("place first object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("ensure object snapped back to correct time", () => EditorBeatmap.HitObjects.First().StartTime == firstTimingPointTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
AddStep("get playfield", () => playfield = Editor.ChildrenOfType<OsuPlayfield>().First());
|
AddStep("get playfield", () => playfield = Editor.ChildrenOfType<OsuPlayfield>().First());
|
||||||
|
AddStep("seek to first control point", () => EditorClock.Seek(Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.First().Time));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
@ -66,13 +67,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
AddStep("start slider placement", () => InputManager.Click(MouseButton.Left));
|
AddStep("start slider placement", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
AddStep("move to place end", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.185f, 0)));
|
AddStep("move to place end", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.225f, 0)));
|
||||||
|
|
||||||
AddStep("end slider placement", () => InputManager.Click(MouseButton.Right));
|
AddStep("end slider placement", () => InputManager.Click(MouseButton.Right));
|
||||||
|
|
||||||
AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2));
|
AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2));
|
||||||
|
|
||||||
AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.20f, 0)));
|
AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.235f, 0)));
|
||||||
|
|
||||||
AddStep("place second object", () => InputManager.Click(MouseButton.Left));
|
AddStep("place second object", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
@ -108,8 +108,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP },
|
new HitSampleInfo(HitSampleInfo.HIT_CLAP),
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE },
|
new HitSampleInfo(HitSampleInfo.HIT_WHISTLE),
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("head samples updated", () => assertSamples(slider.HitObject.HeadCircle));
|
AddAssert("head samples updated", () => assertSamples(slider.HitObject.HeadCircle));
|
||||||
@ -136,15 +136,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
slider = (DrawableSlider)createSlider(repeats: 1);
|
slider = (DrawableSlider)createSlider(repeats: 1);
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
slider.HitObject.NodeSamples.Add(new List<HitSampleInfo> { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } });
|
slider.HitObject.NodeSamples.Add(new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_FINISH) });
|
||||||
|
|
||||||
Add(slider);
|
Add(slider);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
AddStep("change samples", () => slider.HitObject.Samples = new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP },
|
new HitSampleInfo(HitSampleInfo.HIT_CLAP),
|
||||||
new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE },
|
new HitSampleInfo(HitSampleInfo.HIT_WHISTLE),
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("head samples not updated", () => assertSamples(slider.HitObject.HeadCircle));
|
AddAssert("head samples not updated", () => assertSamples(slider.HitObject.HeadCircle));
|
||||||
|
@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,9 +67,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
|
@ -113,8 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
{
|
{
|
||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("sliderslide");
|
||||||
clone.Name = "sliderslide";
|
|
||||||
|
|
||||||
slidingSample.Samples = new ISampleInfo[] { clone };
|
slidingSample.Samples = new ISampleInfo[] { clone };
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
{
|
{
|
||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample).With("spinnerspin");
|
||||||
clone.Name = "spinnerspin";
|
|
||||||
|
|
||||||
spinningSample.Samples = new ISampleInfo[] { clone };
|
spinningSample.Samples = new ISampleInfo[] { clone };
|
||||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||||
|
@ -221,14 +221,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
var sampleList = new List<HitSampleInfo>();
|
var sampleList = new List<HitSampleInfo>();
|
||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
{
|
sampleList.Add(firstSample.With("slidertick"));
|
||||||
sampleList.Add(new HitSampleInfo
|
|
||||||
{
|
|
||||||
Bank = firstSample.Bank,
|
|
||||||
Volume = firstSample.Volume,
|
|
||||||
Name = @"slidertick",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var tick in NestedHitObjects.OfType<SliderTick>())
|
foreach (var tick in NestedHitObjects.OfType<SliderTick>())
|
||||||
tick.Samples = sampleList;
|
tick.Samples = sampleList;
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
public SpinnerBonusTick()
|
public SpinnerBonusTick()
|
||||||
{
|
{
|
||||||
Samples.Add(new HitSampleInfo { Name = "spinnerbonus" });
|
Samples.Add(new HitSampleInfo("spinnerbonus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new OsuSpinnerBonusTickJudgement();
|
public override Judgement CreateJudgement() => new OsuSpinnerBonusTickJudgement();
|
||||||
|
@ -43,10 +43,10 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
EndPlacement(true);
|
EndPlacement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
base.UpdatePosition(result);
|
base.UpdateTimeAndPosition(result);
|
||||||
|
|
||||||
if (PlacementActive)
|
if (PlacementActive)
|
||||||
{
|
{
|
||||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (isRimType != rimSamples.Any())
|
if (isRimType != rimSamples.Any())
|
||||||
{
|
{
|
||||||
if (isRimType)
|
if (isRimType)
|
||||||
HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP });
|
HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var sample in rimSamples)
|
foreach (var sample in rimSamples)
|
||||||
@ -125,9 +125,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (s.Name != HitSampleInfo.HIT_FINISH)
|
if (s.Name != HitSampleInfo.HIT_FINISH)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var sClone = s.Clone();
|
corrected[i] = s.With(HitSampleInfo.HIT_WHISTLE);
|
||||||
sClone.Name = HitSampleInfo.HIT_WHISTLE;
|
|
||||||
corrected[i] = sClone;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return corrected;
|
return corrected;
|
||||||
|
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (isStrong.Value != strongSamples.Any())
|
if (isStrong.Value != strongSamples.Any())
|
||||||
{
|
{
|
||||||
if (isStrong.Value)
|
if (isStrong.Value)
|
||||||
HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH });
|
HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var sample in strongSamples)
|
foreach (var sample in strongSamples)
|
||||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
(OsuHitObject)current.HitObjects[0],
|
(OsuHitObject)current.HitObjects[0],
|
||||||
new HitCircle { StartTime = 2000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } } },
|
new HitCircle { StartTime = 2000, Samples = { new HitSampleInfo(HitSampleInfo.HIT_FINISH) } },
|
||||||
(OsuHitObject)current.HitObjects[2],
|
(OsuHitObject)current.HitObjects[2],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -268,12 +268,12 @@ namespace osu.Game.Tests.Editing
|
|||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
(OsuHitObject)current.HitObjects[0],
|
(OsuHitObject)current.HitObjects[0],
|
||||||
new HitCircle { StartTime = 1000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } } },
|
new HitCircle { StartTime = 1000, Samples = { new HitSampleInfo(HitSampleInfo.HIT_FINISH) } },
|
||||||
(OsuHitObject)current.HitObjects[2],
|
(OsuHitObject)current.HitObjects[2],
|
||||||
(OsuHitObject)current.HitObjects[3],
|
(OsuHitObject)current.HitObjects[3],
|
||||||
new HitCircle { StartTime = 2250, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE } } },
|
new HitCircle { StartTime = 2250, Samples = { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) } },
|
||||||
(OsuHitObject)current.HitObjects[5],
|
(OsuHitObject)current.HitObjects[5],
|
||||||
new HitCircle { StartTime = 3000, Samples = { new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP } } },
|
new HitCircle { StartTime = 3000, Samples = { new HitSampleInfo(HitSampleInfo.HIT_CLAP) } },
|
||||||
(OsuHitObject)current.HitObjects[7],
|
(OsuHitObject)current.HitObjects[7],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Audio
|
namespace osu.Game.Audio
|
||||||
{
|
{
|
||||||
@ -23,25 +25,33 @@ namespace osu.Game.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_CLAP, HIT_FINISH };
|
public static IEnumerable<string> AllAdditions => new[] { HIT_WHISTLE, HIT_CLAP, HIT_FINISH };
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The bank to load the sample from.
|
|
||||||
/// </summary>
|
|
||||||
public string Bank;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the sample to load.
|
/// The name of the sample to load.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name;
|
public readonly string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bank to load the sample from.
|
||||||
|
/// </summary>
|
||||||
|
public readonly string? Bank;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
|
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Suffix;
|
public readonly string? Suffix;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sample volume.
|
/// The sample volume.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Volume { get; set; }
|
public int Volume { get; }
|
||||||
|
|
||||||
|
public HitSampleInfo(string name, string? bank = null, string? suffix = null, int volume = 100)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Bank = bank;
|
||||||
|
Suffix = suffix;
|
||||||
|
Volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
||||||
@ -57,18 +67,23 @@ namespace osu.Game.Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HitSampleInfo Clone() => (HitSampleInfo)MemberwiseClone();
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="HitSampleInfo"/> with overridden values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">An optional new sample name.</param>
|
||||||
|
/// <param name="bank">An optional new sample bank.</param>
|
||||||
|
/// <param name="suffix">An optional new lookup suffix.</param>
|
||||||
|
/// <param name="volume">An optional new volume.</param>
|
||||||
|
/// <returns>The new <see cref="HitSampleInfo"/>.</returns>
|
||||||
|
public virtual HitSampleInfo With(Optional<string> name = default, Optional<string?> bank = default, Optional<string?> suffix = default, Optional<int> volume = default)
|
||||||
|
=> new HitSampleInfo(name.GetOr(Name), bank.GetOr(Bank), suffix.GetOr(Suffix), volume.GetOr(Volume));
|
||||||
|
|
||||||
public bool Equals(HitSampleInfo other)
|
public bool Equals(HitSampleInfo? other)
|
||||||
=> other != null && Bank == other.Bank && Name == other.Name && Suffix == other.Suffix;
|
=> other != null && Name == other.Name && Bank == other.Bank && Suffix == other.Suffix;
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object? obj)
|
||||||
=> obj is HitSampleInfo other && Equals(other);
|
=> Equals((HitSampleInfo?)obj);
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] // This will have to be addressed eventually
|
public override int GetHashCode() => HashCode.Combine(Name, Bank, Suffix);
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(Bank, Name, Suffix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sampleName">The name of the same.</param>
|
/// <param name="sampleName">The name of the same.</param>
|
||||||
/// <returns>A populated <see cref="HitSampleInfo"/>.</returns>
|
/// <returns>A populated <see cref="HitSampleInfo"/>.</returns>
|
||||||
public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) => new HitSampleInfo
|
public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) => new HitSampleInfo(sampleName, SampleBank, volume: SampleVolume);
|
||||||
{
|
|
||||||
Bank = SampleBank,
|
|
||||||
Name = sampleName,
|
|
||||||
Volume = SampleVolume,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies <see cref="SampleBank"/> and <see cref="SampleVolume"/> to a <see cref="HitSampleInfo"/> if necessary, returning the modified <see cref="HitSampleInfo"/>.
|
/// Applies <see cref="SampleBank"/> and <see cref="SampleVolume"/> to a <see cref="HitSampleInfo"/> if necessary, returning the modified <see cref="HitSampleInfo"/>.
|
||||||
@ -71,12 +66,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// <param name="hitSampleInfo">The <see cref="HitSampleInfo"/>. This will not be modified.</param>
|
/// <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>
|
/// <returns>The modified <see cref="HitSampleInfo"/>. This does not share a reference with <paramref name="hitSampleInfo"/>.</returns>
|
||||||
public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
||||||
{
|
=> hitSampleInfo.With(bank: hitSampleInfo.Bank ?? SampleBank, volume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume);
|
||||||
var newSampleInfo = hitSampleInfo.Clone();
|
|
||||||
newSampleInfo.Bank = hitSampleInfo.Bank ?? SampleBank;
|
|
||||||
newSampleInfo.Volume = hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume;
|
|
||||||
return newSampleInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsRedundant(ControlPoint existing)
|
public override bool IsRedundant(ControlPoint existing)
|
||||||
=> existing is SampleControlPoint existingSample
|
=> existing is SampleControlPoint existingSample
|
||||||
|
@ -192,7 +192,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
|
var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
|
||||||
|
|
||||||
// Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)
|
// Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)
|
||||||
HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo());
|
HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo(string.Empty));
|
||||||
|
|
||||||
// Convert effect flags to the legacy format
|
// Convert effect flags to the legacy format
|
||||||
LegacyEffectFlags effectFlags = LegacyEffectFlags.None;
|
LegacyEffectFlags effectFlags = LegacyEffectFlags.None;
|
||||||
|
@ -182,11 +182,8 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var baseInfo = base.ApplyTo(hitSampleInfo);
|
var baseInfo = base.ApplyTo(hitSampleInfo);
|
||||||
|
|
||||||
if (baseInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy
|
if (baseInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0)
|
||||||
&& legacy.CustomSampleBank == 0)
|
return legacy.With(customSampleBank: CustomSampleBank);
|
||||||
{
|
|
||||||
legacy.CustomSampleBank = CustomSampleBank;
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseInfo;
|
return baseInfo;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osuTK;
|
using System.Collections.Generic;
|
||||||
using osuTK.Graphics;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using System.Collections.Generic;
|
using osuTK.Graphics;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
@ -26,7 +25,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
public virtual IEnumerable<string> FilterTerms => new[] { Header };
|
public virtual IEnumerable<string> FilterTerms => new[] { Header };
|
||||||
|
|
||||||
private const int header_size = 26;
|
private const int header_size = 26;
|
||||||
private const int header_margin = 25;
|
private const int margin = 20;
|
||||||
private const int border_size = 2;
|
private const int border_size = 2;
|
||||||
|
|
||||||
public bool MatchingFilter
|
public bool MatchingFilter
|
||||||
@ -38,7 +37,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
|
|
||||||
protected SettingsSection()
|
protected SettingsSection()
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 20 };
|
Margin = new MarginPadding { Top = margin };
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
@ -46,10 +45,9 @@ namespace osu.Game.Overlays.Settings
|
|||||||
{
|
{
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = header_size + header_margin
|
Top = header_size
|
||||||
},
|
},
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 30),
|
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
};
|
};
|
||||||
@ -70,7 +68,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
{
|
{
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = 20 + border_size,
|
Top = margin + border_size,
|
||||||
Bottom = 10,
|
Bottom = 10,
|
||||||
},
|
},
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
@ -82,7 +80,11 @@ namespace osu.Game.Overlays.Settings
|
|||||||
Font = OsuFont.GetFont(size: header_size),
|
Font = OsuFont.GetFont(size: header_size),
|
||||||
Text = Header,
|
Text = Header,
|
||||||
Colour = colours.Yellow,
|
Colour = colours.Yellow,
|
||||||
Margin = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS }
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = SettingsPanel.CONTENT_MARGINS,
|
||||||
|
Right = SettingsPanel.CONTENT_MARGINS
|
||||||
|
}
|
||||||
},
|
},
|
||||||
FlowContent
|
FlowContent
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
FlowContent = new FillFlowContainer
|
FlowContent = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 5),
|
Spacing = new Vector2(0, 8),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
};
|
};
|
||||||
@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = Header.ToUpperInvariant(),
|
Text = Header.ToUpperInvariant(),
|
||||||
Margin = new MarginPadding { Bottom = 10, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS },
|
Margin = new MarginPadding { Vertical = 30, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS },
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
||||||
},
|
},
|
||||||
FlowContent
|
FlowContent
|
||||||
|
@ -85,10 +85,10 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the position of this <see cref="PlacementBlueprint"/> to a new screen-space position.
|
/// Updates the time and position of this <see cref="PlacementBlueprint"/> based on the provided snap information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="result">The snap result information.</param>
|
/// <param name="result">The snap result information.</param>
|
||||||
public virtual void UpdatePosition(SnapResult result)
|
public virtual void UpdateTimeAndPosition(SnapResult result)
|
||||||
{
|
{
|
||||||
if (!PlacementActive)
|
if (!PlacementActive)
|
||||||
HitObject.StartTime = result.Time ?? EditorClock?.CurrentTime ?? Time.Current;
|
HitObject.StartTime = result.Time ?? EditorClock?.CurrentTime ?? Time.Current;
|
||||||
|
@ -5,7 +5,6 @@ using osuTK;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
@ -14,6 +13,7 @@ using JetBrains.Annotations;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
{
|
{
|
||||||
@ -428,62 +428,25 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
||||||
{
|
{
|
||||||
return new List<HitSampleInfo>
|
return new List<HitSampleInfo> { new FileHitSampleInfo(bankInfo.Filename, bankInfo.Volume) };
|
||||||
{
|
|
||||||
new FileHitSampleInfo
|
|
||||||
{
|
|
||||||
Filename = bankInfo.Filename,
|
|
||||||
Volume = bankInfo.Volume
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var soundTypes = new List<HitSampleInfo>
|
var soundTypes = new List<HitSampleInfo>
|
||||||
{
|
{
|
||||||
new LegacyHitSampleInfo
|
new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.Normal, bankInfo.Volume, bankInfo.CustomSampleBank,
|
||||||
{
|
|
||||||
Bank = bankInfo.Normal,
|
|
||||||
Name = HitSampleInfo.HIT_NORMAL,
|
|
||||||
Volume = bankInfo.Volume,
|
|
||||||
CustomSampleBank = 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
|
||||||
IsLayered = type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal)
|
type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal))
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type.HasFlag(LegacyHitSoundType.Finish))
|
if (type.HasFlag(LegacyHitSoundType.Finish))
|
||||||
{
|
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||||
soundTypes.Add(new LegacyHitSampleInfo
|
|
||||||
{
|
|
||||||
Bank = bankInfo.Add,
|
|
||||||
Name = HitSampleInfo.HIT_FINISH,
|
|
||||||
Volume = bankInfo.Volume,
|
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.HasFlag(LegacyHitSoundType.Whistle))
|
if (type.HasFlag(LegacyHitSoundType.Whistle))
|
||||||
{
|
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||||
soundTypes.Add(new LegacyHitSampleInfo
|
|
||||||
{
|
|
||||||
Bank = bankInfo.Add,
|
|
||||||
Name = HitSampleInfo.HIT_WHISTLE,
|
|
||||||
Volume = bankInfo.Volume,
|
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.HasFlag(LegacyHitSoundType.Clap))
|
if (type.HasFlag(LegacyHitSoundType.Clap))
|
||||||
{
|
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.Add, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||||
soundTypes.Add(new LegacyHitSampleInfo
|
|
||||||
{
|
|
||||||
Bank = bankInfo.Add,
|
|
||||||
Name = HitSampleInfo.HIT_CLAP,
|
|
||||||
Volume = bankInfo.Volume,
|
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return soundTypes;
|
return soundTypes;
|
||||||
}
|
}
|
||||||
@ -501,21 +464,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public class LegacyHitSampleInfo : HitSampleInfo, IEquatable<LegacyHitSampleInfo>
|
public class LegacyHitSampleInfo : HitSampleInfo, IEquatable<LegacyHitSampleInfo>
|
||||||
{
|
{
|
||||||
private int customSampleBank;
|
public readonly int CustomSampleBank;
|
||||||
|
|
||||||
public int CustomSampleBank
|
|
||||||
{
|
|
||||||
get => customSampleBank;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
customSampleBank = value;
|
|
||||||
|
|
||||||
if (value >= 2)
|
|
||||||
Suffix = value.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this hit sample is layered.
|
/// Whether this hit sample is layered.
|
||||||
@ -524,30 +477,40 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// Layered hit samples are automatically added in all modes (except osu!mania), but can be disabled
|
/// Layered hit samples are automatically added in all modes (except osu!mania), but can be disabled
|
||||||
/// using the <see cref="LegacySkinConfiguration.LegacySetting.LayeredHitSounds"/> skin config option.
|
/// using the <see cref="LegacySkinConfiguration.LegacySetting.LayeredHitSounds"/> skin config option.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public bool IsLayered { get; set; }
|
public readonly bool IsLayered;
|
||||||
|
|
||||||
public bool Equals(LegacyHitSampleInfo other)
|
public LegacyHitSampleInfo(string name, string? bank = null, int volume = 100, int customSampleBank = 0, bool isLayered = false)
|
||||||
=> other != null && base.Equals(other) && CustomSampleBank == other.CustomSampleBank;
|
: base(name, bank, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume)
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
=> obj is LegacyHitSampleInfo other && Equals(other);
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] // This will have to be addressed eventually
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
{
|
||||||
return HashCode.Combine(base.GetHashCode(), customSampleBank);
|
CustomSampleBank = customSampleBank;
|
||||||
|
IsLayered = isLayered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override HitSampleInfo With(Optional<string> name = default, Optional<string?> bank = default, Optional<string?> suffix = default, Optional<int> volume = default)
|
||||||
|
=> With(name, bank, volume);
|
||||||
|
|
||||||
|
public LegacyHitSampleInfo With(Optional<string> name = default, Optional<string?> bank = default, Optional<int> volume = default, Optional<int> customSampleBank = default,
|
||||||
|
Optional<bool> isLayered = default)
|
||||||
|
=> new LegacyHitSampleInfo(name.GetOr(Name), bank.GetOr(Bank), volume.GetOr(Volume), customSampleBank.GetOr(CustomSampleBank), isLayered.GetOr(IsLayered));
|
||||||
|
|
||||||
|
public bool Equals(LegacyHitSampleInfo? other)
|
||||||
|
=> base.Equals(other) && CustomSampleBank == other.CustomSampleBank && IsLayered == other.IsLayered;
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => Equals((LegacyHitSampleInfo?)obj);
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), CustomSampleBank, IsLayered);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileHitSampleInfo : LegacyHitSampleInfo, IEquatable<FileHitSampleInfo>
|
private class FileHitSampleInfo : LegacyHitSampleInfo, IEquatable<FileHitSampleInfo>
|
||||||
{
|
{
|
||||||
public string Filename;
|
public readonly string Filename;
|
||||||
|
|
||||||
public FileHitSampleInfo()
|
public FileHitSampleInfo(string filename, int volume)
|
||||||
{
|
// Force CSS=1 to make sure that the LegacyBeatmapSkin does not fall back to the user skin.
|
||||||
// Make sure that the LegacyBeatmapSkin does not fall back to the user skin.
|
|
||||||
// Note that this does not change the lookup names, as they are overridden locally.
|
// Note that this does not change the lookup names, as they are overridden locally.
|
||||||
CustomSampleBank = 1;
|
: base(string.Empty, customSampleBank: 1, volume: volume)
|
||||||
|
{
|
||||||
|
Filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> LookupNames => new[]
|
public override IEnumerable<string> LookupNames => new[]
|
||||||
@ -556,17 +519,21 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
Path.ChangeExtension(Filename, null)
|
Path.ChangeExtension(Filename, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
public bool Equals(FileHitSampleInfo other)
|
public override HitSampleInfo With(Optional<string> name = default, Optional<string?> bank = default, Optional<string?> suffix = default, Optional<int> volume = default)
|
||||||
=> other != null && Filename == other.Filename;
|
=> With(volume: volume);
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public FileHitSampleInfo With(Optional<string> filename = default, Optional<int> volume = default)
|
||||||
=> obj is FileHitSampleInfo other && Equals(other);
|
=> new FileHitSampleInfo(filename.GetOr(Filename), volume.GetOr(Volume));
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] // This will have to be addressed eventually
|
public bool Equals(FileHitSampleInfo? other)
|
||||||
public override int GetHashCode()
|
=> base.Equals(other) && Filename == other.Filename;
|
||||||
{
|
|
||||||
return HashCode.Combine(Filename);
|
public override bool Equals(object? obj)
|
||||||
}
|
=> Equals((FileHitSampleInfo?)obj);
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
private IEditorChangeHandler changeHandler { get; set; }
|
private IEditorChangeHandler changeHandler { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorClock editorClock { get; set; }
|
protected EditorClock EditorClock { get; private set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
protected EditorBeatmap Beatmap { get; private set; }
|
protected EditorBeatmap Beatmap { get; private set; }
|
||||||
@ -170,7 +170,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (clickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != clickedBlueprint)
|
if (clickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != clickedBlueprint)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
editorClock?.SeekTo(clickedBlueprint.HitObject.StartTime);
|
EditorClock?.SeekTo(clickedBlueprint.HitObject.StartTime);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,7 +381,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
case SelectionState.Selected:
|
case SelectionState.Selected:
|
||||||
// if the editor is playing, we generally don't want to deselect objects even if outside the selection area.
|
// if the editor is playing, we generally don't want to deselect objects even if outside the selection area.
|
||||||
if (!editorClock.IsRunning && !isValidForSelection())
|
if (!EditorClock.IsRunning && !isValidForSelection())
|
||||||
blueprint.Deselect();
|
blueprint.Deselect();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
case TernaryState.True:
|
case TernaryState.True:
|
||||||
if (existingSample == null)
|
if (existingSample == null)
|
||||||
samples.Add(new HitSampleInfo { Name = sampleName });
|
samples.Add(new HitSampleInfo(sampleName));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +157,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
var snapResult = Composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position);
|
var snapResult = Composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position);
|
||||||
|
|
||||||
currentPlacement.UpdatePosition(snapResult);
|
// if no time was found from positional snapping, we should still quantize to the beat.
|
||||||
|
snapResult.Time ??= Beatmap.SnapTime(EditorClock.CurrentTime, null);
|
||||||
|
|
||||||
|
currentPlacement.UpdateTimeAndPosition(snapResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -209,7 +212,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (blueprint != null)
|
if (blueprint != null)
|
||||||
{
|
{
|
||||||
// doing this post-creations as adding the default hit sample should be the case regardless of the ruleset.
|
// doing this post-creations as adding the default hit sample should be the case regardless of the ruleset.
|
||||||
blueprint.HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
|
blueprint.HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL));
|
||||||
|
|
||||||
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
||||||
|
|
||||||
|
@ -328,7 +328,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (h.Samples.Any(s => s.Name == sampleName))
|
if (h.Samples.Any(s => s.Name == sampleName))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
h.Samples.Add(new HitSampleInfo { Name = sampleName });
|
h.Samples.Add(new HitSampleInfo(sampleName));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorBeatmap.EndChange();
|
EditorBeatmap.EndChange();
|
||||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
currentBlueprint.UpdatePosition(SnapForBlueprint(currentBlueprint));
|
currentBlueprint.UpdateTimeAndPosition(SnapForBlueprint(currentBlueprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual SnapResult SnapForBlueprint(PlacementBlueprint blueprint) =>
|
protected virtual SnapResult SnapForBlueprint(PlacementBlueprint blueprint) =>
|
||||||
@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
if (drawable is PlacementBlueprint blueprint)
|
if (drawable is PlacementBlueprint blueprint)
|
||||||
{
|
{
|
||||||
blueprint.Show();
|
blueprint.Show();
|
||||||
blueprint.UpdatePosition(SnapForBlueprint(blueprint));
|
blueprint.UpdateTimeAndPosition(SnapForBlueprint(blueprint));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
45
osu.Game/Utils/Optional.cs
Normal file
45
osu.Game/Utils/Optional.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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 enable
|
||||||
|
|
||||||
|
namespace osu.Game.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A wrapper over a value and a boolean denoting whether the value is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of value stored.</typeparam>
|
||||||
|
public readonly ref struct Optional<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The stored value.
|
||||||
|
/// </summary>
|
||||||
|
public readonly T Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether <see cref="Value"/> is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If <typeparamref name="T"/> is a reference type, <c>null</c> may be valid for <see cref="Value"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public readonly bool HasValue;
|
||||||
|
|
||||||
|
private Optional(T value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
HasValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns <see cref="Value"/> if it's valid, or a given fallback value otherwise.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Shortcase for: <c>optional.HasValue ? optional.Value : fallback</c>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="fallback">The fallback value to return if <see cref="HasValue"/> is <c>false</c>.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T GetOr(T fallback) => HasValue ? Value : fallback;
|
||||||
|
|
||||||
|
public static implicit operator Optional<T>(T value) => new Optional<T>(value);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user