mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 13:22:55 +08:00
Merge branch 'master' into go-forward
This commit is contained in:
commit
8f3f872182
30
.vscode/launch.json
vendored
30
.vscode/launch.json
vendored
@ -11,7 +11,11 @@
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build tests (Debug)",
|
||||
"env": {},
|
||||
"linux": {
|
||||
"env": {
|
||||
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||
}
|
||||
},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
@ -24,7 +28,11 @@
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build tests (Release)",
|
||||
"env": {},
|
||||
"linux": {
|
||||
"env": {
|
||||
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||
}
|
||||
},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
@ -33,11 +41,15 @@
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
"args": [
|
||||
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll",
|
||||
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build osu! (Debug)",
|
||||
"env": {},
|
||||
"linux": {
|
||||
"env": {
|
||||
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||
}
|
||||
},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
@ -46,12 +58,16 @@
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
"args": [
|
||||
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll",
|
||||
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build osu! (Release)",
|
||||
"env": {},
|
||||
"linux": {
|
||||
"env": {
|
||||
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||
}
|
||||
},
|
||||
"console": "internalConsole"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -153,9 +153,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
if (!(obj.HitObject is IHasEndTime endTime))
|
||||
continue;
|
||||
|
||||
if (!obj.HasNestedHitObjects)
|
||||
continue;
|
||||
|
||||
foreach (var nested in obj.NestedHitObjects)
|
||||
{
|
||||
double finalPosition = (nested.HitObject.StartTime - obj.HitObject.StartTime) / endTime.Duration;
|
||||
|
@ -93,9 +93,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
base.AccentColour = value;
|
||||
Body.AccentColour = AccentColour;
|
||||
Ball.AccentColour = AccentColour;
|
||||
if (HasNestedHitObjects)
|
||||
foreach (var drawableHitObject in NestedHitObjects)
|
||||
drawableHitObject.AccentColour = AccentColour;
|
||||
|
||||
foreach (var drawableHitObject in NestedHitObjects)
|
||||
drawableHitObject.AccentColour = AccentColour;
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
if (!userTriggered && Time.Current >= slider.EndTime)
|
||||
{
|
||||
var judgementsCount = NestedHitObjects.Count;
|
||||
var judgementsCount = NestedHitObjects.Count();
|
||||
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
|
||||
|
||||
var hitFraction = (double)judgementsHit / judgementsCount;
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
@ -211,5 +212,41 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeCustomSamples()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||
using (var resStream = Resource.OpenResource("custom-samples.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||
|
||||
Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[0]).Name);
|
||||
Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[1]).Name);
|
||||
Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
|
||||
Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[3]).Name);
|
||||
}
|
||||
|
||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeCustomHitObjectSamples()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||
using (var resStream = Resource.OpenResource("custom-hitobject-samples.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||
|
||||
Assert.AreEqual("hit_1.wav", hitObjects[0].Samples[0].LookupNames.First());
|
||||
Assert.AreEqual("hit_2.wav", hitObjects[1].Samples[0].LookupNames.First());
|
||||
Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name);
|
||||
Assert.AreEqual("hit_1.wav", hitObjects[3].Samples[0].LookupNames.First());
|
||||
}
|
||||
|
||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,11 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
public void TestParity(string beatmap)
|
||||
{
|
||||
var legacy = decode(beatmap, out Beatmap json);
|
||||
json.WithDeepEqual(legacy).IgnoreProperty(r => r.DeclaringType == typeof(HitWindows)).Assert();
|
||||
json.WithDeepEqual(legacy)
|
||||
.IgnoreProperty(r => r.DeclaringType == typeof(HitWindows)
|
||||
// Todo: CustomSampleBank shouldn't exist going forward, we need a conversion mechanism
|
||||
|| r.Name == nameof(LegacyDecoder<Beatmap>.LegacySampleControlPoint.CustomSampleBank))
|
||||
.Assert();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
16
osu.Game.Tests/Resources/custom-hitobject-samples.osu
Normal file
16
osu.Game.Tests/Resources/custom-hitobject-samples.osu
Normal file
@ -0,0 +1,16 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
SampleSet: Normal
|
||||
|
||||
[TimingPoints]
|
||||
2170,468.75,4,1,0,40,1,0
|
||||
2638,-100,4,1,1,40,0,0
|
||||
3107,-100,4,1,2,40,0,0
|
||||
3576,-100,4,1,0,40,0,0
|
||||
|
||||
[HitObjects]
|
||||
255,193,2170,1,0,0:0:0:0:hit_1.wav
|
||||
256,191,2638,5,0,0:0:0:0:hit_2.wav
|
||||
255,193,3107,1,0,0:0:0:0:
|
||||
256,191,3576,1,0,0:0:0:0:hit_1.wav
|
16
osu.Game.Tests/Resources/custom-samples.osu
Normal file
16
osu.Game.Tests/Resources/custom-samples.osu
Normal file
@ -0,0 +1,16 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
SampleSet: Normal
|
||||
|
||||
[TimingPoints]
|
||||
2170,468.75,4,1,0,40,1,0
|
||||
2638,-100,4,1,1,40,0,0
|
||||
3107,-100,4,1,2,40,0,0
|
||||
3576,-100,4,1,0,40,0,0
|
||||
|
||||
[HitObjects]
|
||||
255,193,2170,1,0,0:0:0:0:
|
||||
256,191,2638,5,0,0:0:0:0:
|
||||
255,193,3107,1,0,0:0:0:0:
|
||||
256,191,3576,1,0,0:0:0:0:
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Audio
|
||||
{
|
||||
@ -32,5 +33,21 @@ namespace osu.Game.Audio
|
||||
/// The sample volume.
|
||||
/// </summary>
|
||||
public int Volume;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
||||
/// </summary>
|
||||
public virtual IEnumerable<string> LookupNames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Namespace))
|
||||
yield return $"{Namespace}/{Bank}-{Name}";
|
||||
|
||||
yield return $"{Bank}-{Name}"; // Without namespace as a fallback even when we have a namespace
|
||||
}
|
||||
}
|
||||
|
||||
public SampleInfo Clone() => (SampleInfo)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,15 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
|
||||
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
|
||||
|
||||
public bool Equals(ControlPoint other) => Time.Equals(other?.Time);
|
||||
/// <summary>
|
||||
/// Whether this <see cref="ControlPoint"/> provides the same parametric changes as another <see cref="ControlPoint"/>.
|
||||
/// Basically an equality check without considering the <see cref="Time"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="ControlPoint"/> to compare to.</param>
|
||||
/// <returns>Whether this <see cref="ControlPoint"/> is equivalent to <paramref name="other"/>.</returns>
|
||||
public virtual bool EquivalentTo(ControlPoint other) => true;
|
||||
|
||||
public bool Equals(ControlPoint other)
|
||||
=> EquivalentTo(other) && Time.Equals(other?.Time);
|
||||
}
|
||||
}
|
||||
|
@ -17,5 +17,10 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
}
|
||||
|
||||
private double speedMultiplier = 1;
|
||||
|
||||
public override bool EquivalentTo(ControlPoint other)
|
||||
=> base.EquivalentTo(other)
|
||||
&& other is DifficultyControlPoint difficulty
|
||||
&& SpeedMultiplier.Equals(difficulty.SpeedMultiplier);
|
||||
}
|
||||
}
|
||||
|
@ -14,5 +14,11 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
/// Whether the first bar line of this control point is ignored.
|
||||
/// </summary>
|
||||
public bool OmitFirstBarLine;
|
||||
|
||||
public override bool EquivalentTo(ControlPoint other)
|
||||
=> base.EquivalentTo(other)
|
||||
&& other is EffectControlPoint effect
|
||||
&& KiaiMode.Equals(effect.KiaiMode)
|
||||
&& OmitFirstBarLine.Equals(effect.OmitFirstBarLine);
|
||||
}
|
||||
}
|
||||
|
@ -30,5 +30,25 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
Name = sampleName,
|
||||
Volume = SampleVolume,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Applies <see cref="SampleBank"/> and <see cref="SampleVolume"/> to a <see cref="SampleInfo"/> if necessary, returning the modified <see cref="SampleInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="sampleInfo">The <see cref="SampleInfo"/>. This will not be modified.</param>
|
||||
/// <returns>The modified <see cref="SampleInfo"/>. This does not share a reference with <paramref name="sampleInfo"/>.</returns>
|
||||
public virtual SampleInfo ApplyTo(SampleInfo sampleInfo)
|
||||
{
|
||||
var newSampleInfo = sampleInfo.Clone();
|
||||
newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank;
|
||||
newSampleInfo.Name = sampleInfo.Name;
|
||||
newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume;
|
||||
return newSampleInfo;
|
||||
}
|
||||
|
||||
public override bool EquivalentTo(ControlPoint other)
|
||||
=> base.EquivalentTo(other)
|
||||
&& other is SampleControlPoint sample
|
||||
&& SampleBank.Equals(sample.SampleBank)
|
||||
&& SampleVolume.Equals(sample.SampleVolume);
|
||||
}
|
||||
}
|
||||
|
@ -23,5 +23,11 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
}
|
||||
|
||||
private double beatLength = 1000;
|
||||
|
||||
public override bool EquivalentTo(ControlPoint other)
|
||||
=> base.EquivalentTo(other)
|
||||
&& other is TimingControlPoint timing
|
||||
&& TimeSignature.Equals(timing.TimeSignature)
|
||||
&& BeatLength.Equals(timing.BeatLength);
|
||||
}
|
||||
}
|
||||
|
@ -289,9 +289,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (split.Length >= 4)
|
||||
sampleSet = (LegacySampleBank)int.Parse(split[3]);
|
||||
|
||||
//SampleBank sampleBank = SampleBank.Default;
|
||||
//if (split.Length >= 5)
|
||||
// sampleBank = (SampleBank)int.Parse(split[4]);
|
||||
int customSampleBank = 0;
|
||||
if (split.Length >= 5)
|
||||
customSampleBank = int.Parse(split[4]);
|
||||
|
||||
int sampleVolume = defaultSampleVolume;
|
||||
if (split.Length >= 6)
|
||||
@ -314,13 +314,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (stringSampleSet == @"none")
|
||||
stringSampleSet = @"normal";
|
||||
|
||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time);
|
||||
SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time);
|
||||
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
|
||||
|
||||
if (timingChange)
|
||||
{
|
||||
beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint
|
||||
handleTimingControlPoint(new TimingControlPoint
|
||||
{
|
||||
Time = time,
|
||||
BeatLength = beatLength,
|
||||
@ -328,41 +324,68 @@ namespace osu.Game.Beatmaps.Formats
|
||||
});
|
||||
}
|
||||
|
||||
if (speedMultiplier != difficultyPoint.SpeedMultiplier)
|
||||
handleDifficultyControlPoint(new DifficultyControlPoint
|
||||
{
|
||||
beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time);
|
||||
beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint
|
||||
{
|
||||
Time = time,
|
||||
SpeedMultiplier = speedMultiplier
|
||||
});
|
||||
}
|
||||
Time = time,
|
||||
SpeedMultiplier = speedMultiplier
|
||||
});
|
||||
|
||||
if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume)
|
||||
handleEffectControlPoint(new EffectControlPoint
|
||||
{
|
||||
beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint
|
||||
{
|
||||
Time = time,
|
||||
SampleBank = stringSampleSet,
|
||||
SampleVolume = sampleVolume
|
||||
});
|
||||
}
|
||||
Time = time,
|
||||
KiaiMode = kiaiMode,
|
||||
OmitFirstBarLine = omitFirstBarSignature
|
||||
});
|
||||
|
||||
if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine)
|
||||
handleSampleControlPoint(new LegacySampleControlPoint
|
||||
{
|
||||
beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint
|
||||
{
|
||||
Time = time,
|
||||
KiaiMode = kiaiMode,
|
||||
OmitFirstBarLine = omitFirstBarSignature
|
||||
});
|
||||
}
|
||||
Time = time,
|
||||
SampleBank = stringSampleSet,
|
||||
SampleVolume = sampleVolume,
|
||||
CustomSampleBank = customSampleBank
|
||||
});
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTimingControlPoint(TimingControlPoint newPoint)
|
||||
{
|
||||
beatmap.ControlPointInfo.TimingPoints.Add(newPoint);
|
||||
}
|
||||
|
||||
private void handleDifficultyControlPoint(DifficultyControlPoint newPoint)
|
||||
{
|
||||
var existing = beatmap.ControlPointInfo.DifficultyPointAt(newPoint.Time);
|
||||
|
||||
if (newPoint.EquivalentTo(existing))
|
||||
return;
|
||||
|
||||
beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == newPoint.Time);
|
||||
beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
|
||||
}
|
||||
|
||||
private void handleEffectControlPoint(EffectControlPoint newPoint)
|
||||
{
|
||||
var existing = beatmap.ControlPointInfo.EffectPointAt(newPoint.Time);
|
||||
|
||||
if (newPoint.EquivalentTo(existing))
|
||||
return;
|
||||
|
||||
beatmap.ControlPointInfo.EffectPoints.Add(newPoint);
|
||||
}
|
||||
|
||||
private void handleSampleControlPoint(SampleControlPoint newPoint)
|
||||
{
|
||||
var existing = beatmap.ControlPointInfo.SamplePointAt(newPoint.Time);
|
||||
|
||||
if (newPoint.EquivalentTo(existing))
|
||||
return;
|
||||
|
||||
beatmap.ControlPointInfo.SamplePoints.Add(newPoint);
|
||||
}
|
||||
|
||||
private void handleHitObject(string line)
|
||||
{
|
||||
// If the ruleset wasn't specified, assume the osu!standard ruleset.
|
||||
|
@ -5,6 +5,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
@ -167,5 +169,25 @@ namespace osu.Game.Beatmaps.Formats
|
||||
Pass = 2,
|
||||
Foreground = 3
|
||||
}
|
||||
|
||||
internal class LegacySampleControlPoint : SampleControlPoint
|
||||
{
|
||||
public int CustomSampleBank;
|
||||
|
||||
public override SampleInfo ApplyTo(SampleInfo sampleInfo)
|
||||
{
|
||||
var baseInfo = base.ApplyTo(sampleInfo);
|
||||
|
||||
if (CustomSampleBank > 1)
|
||||
baseInfo.Name += CustomSampleBank;
|
||||
|
||||
return baseInfo;
|
||||
}
|
||||
|
||||
public override bool EquivalentTo(ControlPoint other)
|
||||
=> base.EquivalentTo(other)
|
||||
&& other is LegacySampleControlPoint legacy
|
||||
&& CustomSampleBank == legacy.CustomSampleBank;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +250,8 @@ namespace osu.Game
|
||||
new VolumeControlReceptor
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ActionRequested = action => volume.Adjust(action)
|
||||
ActionRequested = action => volume.Adjust(action),
|
||||
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||
},
|
||||
mainContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
|
||||
|
@ -70,10 +70,10 @@ namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
if (actionInvoked) return;
|
||||
|
||||
Hide();
|
||||
|
||||
actionInvoked = true;
|
||||
action?.Invoke();
|
||||
|
||||
Hide();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
{
|
||||
if (bindTarget.IsHovered)
|
||||
{
|
||||
bindTarget.UpdateKeyCombination(new KeyCombination(KeyCombination.FromInputState(state).Keys.Append(state.Mouse.ScrollDelta.Y > 0 ? InputKey.MouseWheelUp : InputKey.MouseWheelDown)));
|
||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state, state.Mouse.ScrollDelta));
|
||||
finalise();
|
||||
return true;
|
||||
}
|
||||
|
@ -9,11 +9,13 @@ using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Overlays.Volume
|
||||
{
|
||||
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||
public class VolumeControlReceptor : Container, IScrollBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||
{
|
||||
public Func<GlobalAction, bool> ActionRequested;
|
||||
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
|
||||
|
||||
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
|
||||
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,15 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Input.Bindings;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Volume
|
||||
{
|
||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
||||
public class VolumeMeter : Container
|
||||
{
|
||||
private CircularProgress volumeCircle;
|
||||
private CircularProgress volumeCircleGlow;
|
||||
@ -226,59 +224,27 @@ namespace osu.Game.Overlays.Volume
|
||||
|
||||
private const float adjust_step = 0.05f;
|
||||
|
||||
public void Increase() => adjust(1);
|
||||
public void Decrease() => adjust(-1);
|
||||
|
||||
private void adjust(int direction)
|
||||
{
|
||||
float amount = adjust_step * direction;
|
||||
|
||||
// handle the case where the OnPressed action was actually a mouse wheel.
|
||||
// this allows for precise wheel handling.
|
||||
var state = GetContainingInputManager().CurrentState;
|
||||
if (state.Mouse?.ScrollDelta.Y != 0)
|
||||
{
|
||||
OnScroll(state);
|
||||
return;
|
||||
}
|
||||
|
||||
Volume += amount;
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!IsHovered) return false;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.DecreaseVolume:
|
||||
Decrease();
|
||||
return true;
|
||||
case GlobalAction.IncreaseVolume:
|
||||
Increase();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public void Increase(double amount = 1, bool isPrecise = false) => adjust(amount, isPrecise);
|
||||
public void Decrease(double amount = 1, bool isPrecise = false) => adjust(-amount, isPrecise);
|
||||
|
||||
// because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible.
|
||||
private double scrollAmount;
|
||||
private double adjustAccumulator;
|
||||
|
||||
private void adjust(double delta, bool isPrecise)
|
||||
{
|
||||
adjustAccumulator += delta * adjust_step * (isPrecise ? 0.1 : 1);
|
||||
if (Math.Abs(adjustAccumulator) < Bindable.Precision)
|
||||
return;
|
||||
Volume += adjustAccumulator;
|
||||
adjustAccumulator = 0;
|
||||
}
|
||||
|
||||
protected override bool OnScroll(InputState state)
|
||||
{
|
||||
scrollAmount += adjust_step * state.Mouse.ScrollDelta.Y * (state.Mouse.HasPreciseScroll ? 0.1f : 1);
|
||||
|
||||
if (Math.Abs(scrollAmount) < Bindable.Precision)
|
||||
return true;
|
||||
|
||||
Volume += scrollAmount;
|
||||
scrollAmount = 0;
|
||||
adjust(state.Mouse.ScrollDelta.Y, state.Mouse.HasPreciseScroll);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
private const float transition_length = 500;
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Overlays
|
||||
muteButton.Current.ValueChanged += _ => Show();
|
||||
}
|
||||
|
||||
public bool Adjust(GlobalAction action)
|
||||
public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false)
|
||||
{
|
||||
if (!IsLoaded) return false;
|
||||
|
||||
@ -103,13 +103,13 @@ namespace osu.Game.Overlays
|
||||
if (State == Visibility.Hidden)
|
||||
Show();
|
||||
else
|
||||
volumeMeterMaster.Decrease();
|
||||
volumeMeterMaster.Decrease(amount, isPrecise);
|
||||
return true;
|
||||
case GlobalAction.IncreaseVolume:
|
||||
if (State == Visibility.Hidden)
|
||||
Show();
|
||||
else
|
||||
volumeMeterMaster.Increase();
|
||||
volumeMeterMaster.Increase(amount, isPrecise);
|
||||
return true;
|
||||
case GlobalAction.ToggleMute:
|
||||
Show();
|
||||
|
@ -33,8 +33,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
|
||||
|
||||
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
|
||||
public bool HasNestedHitObjects => nestedHitObjects.IsValueCreated;
|
||||
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects.Value;
|
||||
public IEnumerable<DrawableHitObject> NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty<DrawableHitObject>();
|
||||
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgement;
|
||||
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
|
||||
@ -50,12 +49,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been hit.
|
||||
/// </summary>
|
||||
public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (!HasNestedHitObjects || NestedHitObjects.All(n => n.IsHit));
|
||||
public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && NestedHitObjects.All(n => n.IsHit);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||
/// </summary>
|
||||
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (!HasNestedHitObjects || NestedHitObjects.All(h => h.AllJudged));
|
||||
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && NestedHitObjects.All(h => h.AllJudged);
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> can be judged.
|
||||
@ -90,13 +89,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
if (HitObject.SampleControlPoint == null)
|
||||
throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
||||
AddInternal(Samples = new SkinnableSound(samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank,
|
||||
Name = s.Name,
|
||||
Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume,
|
||||
Namespace = SampleNamespace
|
||||
}).ToArray()));
|
||||
|
||||
samples = samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).ToArray();
|
||||
foreach (var s in samples)
|
||||
s.Namespace = SampleNamespace;
|
||||
|
||||
AddInternal(Samples = new SkinnableSound(samples));
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,9 +204,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
if (HasNestedHitObjects)
|
||||
foreach (var d in NestedHitObjects)
|
||||
judgementOccurred |= d.UpdateJudgement(userTriggered);
|
||||
foreach (var d in NestedHitObjects)
|
||||
judgementOccurred |= d.UpdateJudgement(userTriggered);
|
||||
|
||||
if (!ProvidesJudgement || judgementFinalized || judgementOccurred)
|
||||
return judgementOccurred;
|
||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Audio;
|
||||
using System.Linq;
|
||||
@ -196,9 +197,6 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
||||
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
||||
|
||||
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
|
||||
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
|
||||
|
||||
string stringBank = bank.ToString().ToLower();
|
||||
if (stringBank == @"none")
|
||||
stringBank = null;
|
||||
@ -211,6 +209,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
if (split.Length > 3)
|
||||
bankInfo.Volume = int.Parse(split[3]);
|
||||
|
||||
bankInfo.Filename = split.Length > 4 ? split[4] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -252,6 +252,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
private List<SampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
||||
{
|
||||
// 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))
|
||||
return new List<SampleInfo> { new FileSampleInfo { Filename = bankInfo.Filename } };
|
||||
|
||||
var soundTypes = new List<SampleInfo>
|
||||
{
|
||||
new SampleInfo
|
||||
@ -297,14 +301,24 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
private class SampleBankInfo
|
||||
{
|
||||
public string Filename;
|
||||
|
||||
public string Normal;
|
||||
public string Add;
|
||||
public int Volume;
|
||||
|
||||
public SampleBankInfo Clone()
|
||||
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||
}
|
||||
|
||||
private class FileSampleInfo : SampleInfo
|
||||
{
|
||||
public string Filename;
|
||||
|
||||
public override IEnumerable<string> LookupNames => new[]
|
||||
{
|
||||
return (SampleBankInfo)MemberwiseClone();
|
||||
}
|
||||
Filename,
|
||||
Path.ChangeExtension(Filename, null)
|
||||
};
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -47,13 +47,10 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.HasNestedHitObjects)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,13 +48,10 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.HasNestedHitObjects)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||
UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,8 @@ namespace osu.Game.Screens.Select
|
||||
private readonly Box box;
|
||||
private readonly Box light;
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos);
|
||||
|
||||
public FooterButton()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
|
@ -44,19 +44,17 @@ namespace osu.Game.Skinning
|
||||
|
||||
private SampleChannel loadChannel(SampleInfo info, Func<string, SampleChannel> getSampleFunction)
|
||||
{
|
||||
SampleChannel ch = null;
|
||||
foreach (var lookup in info.LookupNames)
|
||||
{
|
||||
var ch = getSampleFunction($"Gameplay/{lookup}");
|
||||
if (ch == null)
|
||||
continue;
|
||||
|
||||
if (info.Namespace != null)
|
||||
ch = getSampleFunction($"Gameplay/{info.Namespace}/{info.Bank}-{info.Name}");
|
||||
|
||||
// try without namespace as a fallback.
|
||||
if (ch == null)
|
||||
ch = getSampleFunction($"Gameplay/{info.Bank}-{info.Name}");
|
||||
|
||||
if (ch != null)
|
||||
ch.Volume.Value = info.Volume / 100.0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
return ch;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.629.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.705.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.17.1" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user